diff --git a/application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg index 4e41fd77c6..37b3a199c6 100644 --- a/application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Bottom right elbow connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `-${dashWidth + (dashGap || dashWidth)}` : `${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "line", @@ -10,23 +11,132 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "animationDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -37,12 +147,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -51,32 +160,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg index 33942432cb..2feb95e9ff 100644 --- a/application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Bottom tee connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst rightLine = \"M100 100H200\";\nconst bottomLine = \"M 100,200 V 103\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('right', rightLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(``);\n}", "tags": [ { "tag": "line", @@ -15,23 +16,364 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "leftFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.fluid-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowDirection", + "name": "{i18n:scada.symbol.flow-direction}", + "hint": "{i18n:scada.symbol.flow-direction-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -42,12 +384,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -56,32 +397,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg index df708d47d3..ab6798b48b 100644 --- a/application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Cross connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst topLine = \"M100 97L100 0\";\nconst rightLine = \"M100 100H200\";\nconst bottomLine = \"M 100,200 V 103\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('right', rightLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(``);\n}", "tags": [ { "tag": "line", @@ -15,23 +16,480 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "leftFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.fluid-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowDirection", + "name": "{i18n:scada.symbol.flow-direction}", + "hint": "{i18n:scada.symbol.flow-direction-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -42,12 +500,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -56,32 +513,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg index 33b6c0a222..74d048e884 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Horizontal connector with an optional directional arrow to visually indicate flow.", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "arrow", @@ -85,6 +86,83 @@ }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null } ], "properties": [ @@ -93,16 +171,8 @@ "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -113,12 +183,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -127,48 +196,93 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { - "id": "arrowColor", - "name": "{i18n:scada.symbol.arrow-color}", + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", "type": "color", - "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "default": "#C8DFF7" + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg index bfc97fe928..fd14834d12 100644 --- a/application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Left bottom elbow connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "line", @@ -10,23 +11,132 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "animationDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -37,12 +147,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -51,32 +160,93 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7" + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg index eac3869a1a..af83a4abb3 100644 --- a/application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Left tee connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H97\";\nconst topLine = \"M100 100L100 0\";\nconst bottomLine = \"M 100,200 V 100\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(``);\n}", "tags": [ { "tag": "line", @@ -15,23 +16,364 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "leftFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.fluid-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowDirection", + "name": "{i18n:scada.symbol.flow-direction}", + "hint": "{i18n:scada.symbol.flow-direction-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -42,12 +384,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -56,32 +397,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg index 6dfbc9e265..5b6d30ab65 100644 --- a/application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Left top elbow connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "line", @@ -10,23 +11,132 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "animationDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -37,12 +147,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -51,32 +160,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg index 86bb07a520..3e65bd6b04 100644 --- a/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg @@ -4,6 +4,7 @@ "description": "Long horizontal connector with an optional directional arrow to visually indicate flow.", "widgetSizeX": 2, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "arrow", @@ -86,6 +87,83 @@ }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null } ], "properties": [ @@ -94,16 +172,8 @@ "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -114,12 +184,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -128,49 +197,93 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { - "id": "arrowColor", - "name": "{i18n:scada.symbol.arrow-color}", + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", "type": "color", - "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "default": "#C8DFF7" + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - - - + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg index d7a0aa8aa7..b5c9730842 100644 --- a/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Long vertical connector with an optional directional arrow to visually indicate flow.", "widgetSizeX": 1, "widgetSizeY": 2, + "stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "arrow", @@ -85,6 +86,83 @@ }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null } ], "properties": [ @@ -93,16 +171,8 @@ "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -113,12 +183,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -127,48 +196,93 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { - "id": "arrowColor", - "name": "{i18n:scada.symbol.arrow-color}", + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", "type": "color", - "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "default": "#C8DFF7" + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg index 17d8bf4e0c..62aecb065d 100644 --- a/application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Right tee connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst topLine = \"M100 100L100 0\";\nconst rightLine = \"M103 100H200\";\nconst bottomLine = \"M 100,200 V 100\";\n\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('right', rightLine);\nprepareFlowAnimation('bottom', bottomLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(``);\n}", "tags": [ { "tag": "line", @@ -15,23 +16,364 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "topFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.fluid-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowDirection", + "name": "{i18n:scada.symbol.flow-direction}", + "hint": "{i18n:scada.symbol.flow-direction-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "bottomFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.bottom-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -42,12 +384,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -56,32 +397,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg index f2d579c551..8ec4e3cc65 100644 --- a/application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Top right elbow connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimation,\n animationDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `-${dashWidth + (dashGap || dashWidth)}` : `${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "line", @@ -10,23 +11,132 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "animationDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -37,12 +147,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -51,32 +160,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg index 004096aaa7..e4561a8347 100644 --- a/application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Top tee connector", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\n\nconst leftLine = \"M0 100H100\";\nconst topLine = \"M100 97L100 0\";\nconst rightLine = \"M100 100H200\";\n\nprepareFlowAnimation('left', leftLine);\nprepareFlowAnimation('top', topLine);\nprepareFlowAnimation('right', rightLine);\n\nfunction prepareFlowAnimation(prefix, line) {\n const flowAnimation = ctx.values[prefix + 'Flow'];\n const flowDirection = ctx.values[prefix + 'FlowDirection'];\n const flowAnimationSpeed = ctx.values[prefix + 'FlowAnimationSpeed'];\n\n const animation = ctx.tags[prefix + 'Line'][0];\n const offset = Date.now() % 1000;\n const duration = 1 / flowAnimationSpeed;\n \n const prevFlowAnimation = animation.remember('flowAnimation');\n const prevFlowDirection = animation.remember('flowDirection');\n const prevFlowDuration = animation.remember('flowDuration');\n \n if (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(animation, offset, flowDirection, duration, line);\n } else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n } else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n }\n}\n\nfunction animateFlow(group, offset, flowDirection, duration, line) {\n group.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n group.add(``);\n}", "tags": [ { "tag": "line", @@ -15,23 +16,364 @@ "actions": null } ], - "behavior": [], + "behavior": [ + { + "id": "leftFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "leftFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.left-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.fluid-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowDirection", + "name": "{i18n:scada.symbol.animation-direction}", + "hint": "{i18n:scada.symbol.animation-direction-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "topFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.top-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlow", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowDirection", + "name": "{i18n:scada.symbol.flow-direction}", + "hint": "{i18n:scada.symbol.flow-direction-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.forward}", + "falseLabel": "{i18n:scada.symbol.reverse}", + "stateLabel": "{i18n:scada.symbol.forward}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": true, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "rightFlowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": "{i18n:scada.symbol.right-connector}", + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + } + ], "properties": [ { "id": "mainLine", "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -42,12 +384,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -56,32 +397,95 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "color", + "default": "#C8DFF7", + "disabled": false, + "visible": true + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg index 6ce9336fee..cfaf6793ea 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg @@ -3,6 +3,7 @@ "description": "Vertical connector with an optional directional arrow to visually indicate flow.", "widgetSizeX": 1, "widgetSizeY": 1, + "stateRenderFunction": "const {\n flowAnimation,\n arrowDirection: flowDirection,\n flowAnimationSpeed\n} = ctx.values;\nconst {\n flowAnimationWidth: lineWidth,\n flowAnimationColor: lineColor,\n flowStyleDash: dashWidth,\n flowStyleGap: dashGap,\n flowDashCap: dashCap\n} = ctx.properties;\nconst line = ctx.tags.line[0].attr('d');\nconst animation = ctx.tags.animationGroup[0];\nconst offset = Date.now() % 1000;\nconst duration = 1 / flowAnimationSpeed;\n\nconst prevFlowAnimation = animation.remember('flowAnimation');\nconst prevFlowDirection = animation.remember('flowDirection');\nconst prevFlowDuration = animation.remember('flowDuration');\n\nif (flowAnimation && flowAnimation !== prevFlowAnimation) {\n animation.remember('flowAnimation', flowAnimation);\n animation.remember('flowDuration', duration);\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && flowDirection !== prevFlowDirection) {\n animation.remember('flowDirection', flowDirection);\n animateFlow(offset, flowDirection);\n} else if (flowAnimation && duration !== prevFlowDuration) {\n animation.remember('flowDuration', duration);\n animation.findOne('animate').attr('dur', `${duration}s`) ;\n} else if (!flowAnimation && prevFlowAnimation) {\n animation.remember('flowAnimation', null);\n animation.clear();\n}\n\nfunction animateFlow(offset, flowDirection) {\n animation.clear();\n const dashArray = `${dashWidth}${dashGap ? ` ${dashGap}` : ''}`;\n const value = flowDirection ? `${dashWidth + (dashGap || dashWidth)}` : `-${dashWidth + (dashGap || dashWidth)}`;\n\n animation.add(``);\n}\n", "tags": [ { "tag": "arrow", @@ -85,6 +86,83 @@ }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimation", + "name": "{i18n:scada.symbol.flow-animation}", + "hint": "{i18n:scada.symbol.flow-animation-hint}", + "group": null, + "type": "value", + "valueType": "BOOLEAN", + "trueLabel": "{i18n:scada.symbol.present}", + "falseLabel": "{i18n:scada.symbol.absent}", + "stateLabel": "{i18n:scada.symbol.flow-present}", + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": false, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "key": "state", + "scope": null + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "compareToValue": true, + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null + }, + { + "id": "flowAnimationSpeed", + "name": "{i18n:scada.symbol.flow-animation-speed}", + "hint": "{i18n:scada.symbol.flow-animation-speed-hint}", + "group": null, + "type": "value", + "valueType": "DOUBLE", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": { + "action": "DO_NOTHING", + "defaultValue": 1, + "executeRpc": { + "method": "getState", + "requestTimeout": 5000, + "requestPersistent": false, + "persistentPollingInterval": 1000 + }, + "getAttribute": { + "scope": null, + "key": "state" + }, + "getTimeSeries": { + "key": "state" + }, + "getAlarmStatus": { + "severityList": null, + "typeList": null + }, + "dataToValue": { + "type": "NONE", + "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + } + }, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": null } ], "properties": [ @@ -93,16 +171,8 @@ "name": "{i18n:scada.symbol.main-line}", "type": "switch", "default": true, - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { "id": "mainLineSize", @@ -113,12 +183,11 @@ "subLabel": "Main", "divider": true, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "secondaryLineSize", @@ -127,48 +196,93 @@ "default": 2, "required": true, "subLabel": "Secondary", - "divider": null, "fieldSuffix": "px", - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", "min": 0, "max": 99, - "step": 1 + "step": 1, + "disabled": false, + "visible": true }, { "id": "lineColor", "name": "{i18n:scada.symbol.line-color}", "type": "color", "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "disabled": false, + "visible": true }, { - "id": "arrowColor", - "name": "{i18n:scada.symbol.arrow-color}", + "id": "flowAnimationWidth", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 4, + "subLabel": "Width", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowAnimationColor", + "name": "{i18n:scada.symbol.flow}", + "group": "{i18n:scada.symbol.animation}", "type": "color", - "default": "#1A1A1A", - "required": null, - "subLabel": null, - "divider": null, - "fieldSuffix": null, - "disableOnProperty": null, - "rowClass": "", - "fieldClass": "", - "min": null, - "max": null, - "step": null + "default": "#C8DFF7" + }, + { + "id": "flowStyleDash", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "required": true, + "subLabel": "{i18n:scada.symbol.dash}", + "divider": true, + "fieldSuffix": "px", + "min": 0, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowStyleGap", + "name": "{i18n:scada.symbol.flow-style}", + "group": "{i18n:scada.symbol.animation}", + "type": "number", + "default": 10, + "subLabel": "{i18n:scada.symbol.gap}", + "fieldSuffix": "px", + "min": 1, + "step": 1, + "disabled": false, + "visible": true + }, + { + "id": "flowDashCap", + "name": "{i18n:scada.symbol.flow-dash-cap}", + "group": "{i18n:scada.symbol.animation}", + "type": "select", + "default": "butt", + "items": [ + { + "value": "butt", + "label": "{i18n:scada.symbol.dash-cap-butt}" + }, + { + "value": "round", + "label": "{i18n:scada.symbol.dash-cap-round}" + }, + { + "value": "square", + "label": "{i18n:scada.symbol.dash-cap-square}" + } + ], + "disabled": false, + "visible": true } ] }]]> - + \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index 86754f0bd4..d0d0741e21 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -2,13 +2,17 @@ "widgetsBundle": { "alias": "maps_v2", "title": "Maps", - "image": "data:image/png;base64,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", + "image": "tb-image:bWFwc19zeXN0ZW1fYnVuZGxlX2ltYWdlLnBuZw==:Ik1hcHMiIHN5c3RlbSBidW5kbGUgaW1hZ2U=:SU1BR0U=;data:image/png;base64,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", + "scada": false, "description": "Visualize the latest location or trip of the devices or other entities on the indoor or outdoor maps.", "order": 6000, - "externalId": null, "name": "Maps" }, "widgetTypeFqns": [ + "map", + "image_map", + "trip_map", + "route_map", "maps_v2.openstreetmap", "maps_v2.google_maps", "maps_v2.image_map", diff --git a/application/src/main/data/json/system/widget_types/google_map.json b/application/src/main/data/json/system/widget_types/google_map.json index 9c7696d8db..cd69b304e9 100644 --- a/application/src/main/data/json/system/widget_types/google_map.json +++ b/application/src/main/data/json/system/widget_types/google_map.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.google_maps", "name": "Google Map", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/google_map_system_widget_image.png", "description": "Displays the location of the entities on Google Maps. Requires the Google map key to work properly. Highly customizable via custom markers, marker tooltips, and widget actions.", "descriptor": { @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", + "settingsDirective": "tb-map-widget-settings-legacy", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"google-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7568\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"Google Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" }, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/here_map.json b/application/src/main/data/json/system/widget_types/here_map.json index 9f5d7e9812..b49e1dcf41 100644 --- a/application/src/main/data/json/system/widget_types/here_map.json +++ b/application/src/main/data/json/system/widget_types/here_map.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.here_map", "name": "HERE Map", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/here_map_system_widget_image.png", "description": "Displays the location of the entities on HERE Maps. Requires the HERE map key to work properly. Highly customizable via custom markers, marker tooltips, and widget actions.", "descriptor": { @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('here', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", + "settingsDirective": "tb-map-widget-settings-legacy", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"here\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"HERE.normalDay\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"useV3\":true,\"apiKey\":\"kVXykxAfZ6LS4EbCTO02soFVfjA7HoBzNVVH9u7nzoE\"},\"mapImageUrl\":\"tb-image;/api/images/system/here_map_system_widget_map_image.svg\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"HERE Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" }, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/image_map.json b/application/src/main/data/json/system/widget_types/image_map.json index 02554e82f6..2b7d56957b 100644 --- a/application/src/main/data/json/system/widget_types/image_map.json +++ b/application/src/main/data/json/system/widget_types/image_map.json @@ -1,5 +1,5 @@ { - "fqn": "maps_v2.image_map", + "fqn": "image_map", "name": "Image Map", "deprecated": false, "image": "tb-image;/api/images/system/image_map_system_widget_image.png", @@ -7,30 +7,18 @@ "descriptor": { "type": "latest", "sizeX": 8.5, - "sizeY": 6.5, + "sizeY": 6, "resources": [], - "templateHtml": "", - "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.mapWidget.onInit();\n};\n\nself.typeParameters = function() {\n return {\n hideDataTab: true,\n hideDataSettings: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n datasourcesOptional: true,\n additionalWidgetActionTypes: ['placeMapItem']\n };\n}\n", + "settingsForm": [], + "dataKeySettingsForm": [], "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"image-map\",\"mapImageUrl\":\"tb-image;/api/images/system/image_map_system_widget_map_image.svg\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false},\"title\":\"Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + "hasBasicMode": true, + "basicModeDirective": "tb-map-basic-config", + "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"image\",\"imageSource\":{\"sourceType\":\"image\",\"url\":\"tb-image;/api/images/system/image_map_system_widget_map_image.svg\",\"entityAliasId\":null,\"entityKey\":null},\"markers\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8239425680406081,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details\",\"patternFunction\":null,\"trigger\":\"click\",\"autoclose\":true,\"offsetX\":0,\"offsetY\":-1},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var temperature = data.temperature;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\\n\"}},\"markerIcon\":{\"icon\":\"mdi:lightbulb-on\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xOTEuMzUgLTM1MS4xOCAxMDgzLjU4IDE3MzAuNDYiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjZmU3NTY5IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMzciIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgZD0iTTM1MS44MzMgMTM2MC43OGMtMzguNzY2LTE5MC4zLTEwNy4xMTYtMzQ4LjY2NS0xODkuOTAzLTQ5NS40NEMxMDAuNTIzIDc1Ni40NjkgMjkuMzg2IDY1NS45NzgtMzYuNDM0IDU1MC40MDRjLTIxLjk3Mi0zNS4yNDQtNDAuOTM0LTcyLjQ3Ny02Mi4wNDctMTA5LjA1NC00Mi4yMTYtNzMuMTM3LTc2LjQ0NC0xNTcuOTM1LTc0LjI2OS0yNjcuOTMyIDIuMTI1LTEwNy40NzMgMzMuMjA4LTE5My42ODUgNzguMDMtMjY0LjE3M0MtMjEtMjA2LjY5IDEwMi40ODEtMzAxLjc0NSAyNjguMTY0LTMyNi43MjRjMTM1LjQ2Ni0yMC40MjUgMjYyLjQ3NSAxNC4wODIgMzUyLjU0MyA2Ni43NDcgNzMuNiA0My4wMzggMTMwLjU5NiAxMDAuNTI4IDE3My45MiAxNjguMjggNDUuMjIgNzAuNzE2IDc2LjM2IDE1NC4yNiA3OC45NzEgMjYzLjIzMyAxLjMzNyA1NS44My03LjgwNSAxMDcuNTMyLTIwLjY4NCAxNTAuNDE3LTEzLjAzNCA0My40MS0zMy45OTYgNzkuNjk1LTUyLjY0NiAxMTguNDU1LTM2LjQwNiA3NS42NTktODIuMDQ5IDE0NC45ODEtMTI3Ljg1NSAyMTQuMzQ1LTEzNi40MzcgMjA2LjYwNi0yNjQuNDk2IDQxNy4zMS0zMjAuNTggNzA2LjAyOHoiLz48Y2lyY2xlIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBjeD0iMzUyLjg5MSIgY3k9IjIyNS43NzkiIHI9IjE4My4zMzIiLz48L3N2Zz4=\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}},{\"dsType\":\"function\",\"dsLabel\":\"Second point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7826299113906372,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1,\"patternFunction\":null},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"edit\":{\"enabledActions\":[],\"snappable\":false},\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"icon\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"size\":40,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var colors = ['#488bc7','#549c5d','#ed7546','#be2b29'];\\nvar temperature = data.temperature;\\nvar res = colors[0];\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120;\\n var index = Math.min(3, Math.floor(4 * percent));\\n res = colors[index];\\n}\\nreturn res;\"},\"icon\":\"thermostat\"},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/shape1.svg\",\"imageSize\":34,\"imageFunction\":\" \",\"images\":[]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}}],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[]},\"title\":\"Image Map\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\",\"titleIcon\":\"map\",\"iconColor\":\"#1F6BDD\",\"actions\":{}}" }, - "tags": [ - "building", - "interior", - "venue", - "inside", - "room", - "office", - "manufacturing", - "floor", - "plant", - "storage", - "warehouse", - "depot" - ], "resources": [ { "link": "/api/images/system/image_map_system_widget_image.png", @@ -38,7 +26,7 @@ "type": "IMAGE", "subType": "IMAGE", "fileName": "image_map_system_widget_image.png", - "publicResourceKey": "hDdSISQr6elribOYD6T3uePXZI5WvNtM", + "publicResourceKey": "otJxNhSbraXccAZhnPzmfOIdEXra5Hf5", "mediaType": "image/png", "data": "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", "public": true @@ -49,54 +37,25 @@ "type": "IMAGE", "subType": "IMAGE", "fileName": "image_map_system_widget_map_image.svg", - "publicResourceKey": "QKRIYhDeBGwjaeIS601VvNLSsvZ25DRj", + "publicResourceKey": "P4hZLRjmo2P1RjGPZ4CrCMxh6xOQFQr5", "mediaType": "image/svg+xml", "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTEzNC41IiBoZWlnaHQ9Ijc2Mi43OCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI3LjA3MSAtMzA3LjkpIj4KICA8ZyBmaWxsPSJub25lIj4KICAgPHBhdGggZD0ibTkwNi4wMyA3MDYuMTMgMy40MjkyIDE3Ljc5Nm0tODgwLjg5IDQxLjEyMWMxNTAuNDQgNi44MzM0IDE0Ni4zOS0yNi4zMzQgMTY2LjQzLTI5LjMyIDM2LjE0NC01LjM4NDggMTE0LjI5LTYuNTI1NCAxNDguMzMtOC42MjM1IDQzLjM3OC0yLjY3MzggMTQxLjc2LTExLjIzMSAxODguODYtMTkuODM0IDM5LjgxMS03LjI3MjggMjIxLjM3LTAuODYyMzUgMzE5LjA3LTAuODYyMzUgNzAuODI3IDAgMTQ2LjkyLTEuNzI0NyAyMTguMTgtMS43MjQ3LTMxLjYyIDAgMTE3Ljg2LTIuNTg3MSA4Ni4yMzYtMi41ODcxbS0yNS4wOTEtNjguMTI2Yy01Mi44IDM0Ljc4NS02NS44OTUgNTEuNzQ5LTk1LjYzOSA4MS40OTMtMjQuOTMxIDI0LjkzMS0xNDAuNC0xOS4xMzktMTc4Ljk0IDM2LjY1LTEyLjI4MSAxNy43NzctNDcuMDAzIDQ2LjU0Ny02NS4xMDggNTkuMDcxLTIwLjEwNSAxMy45MDgtNTYuMDM3IDQ0Ljk1Ny02Ny43NjkgNzMuMDc4LTQuODAxNSAxMS41MDktMTMuMzggMzUuOTkzLTIzLjQ0OSA0Ni4wNjItMTAuNDk3IDEwLjQ5Ny0zOC4zNzcgNi4zODU3LTQ0LjAyMyAxNy42NDgtMTkuMDA1IDM3LjkwOC0yNS40NjUgMTAwLjkyLTY3LjYxOCAxMDIuMDVtMTkuMjgyLTYyNC4wMWMzNC42NTktMS44NzM4IDg0LjAyNyA3LjM5MTMgMTA5LjktNC4yODU0IDEzLjI4Mi01Ljk5NDEgNDEuNDA3LTIuNDYxNCA2Ni44MjktMi4zMjA1IDM1LjMyMiAwLjE5NTc4IDY0LjM4MiAwLjYzNDc3IDEwMS45MiA1LjAyMzIgMjUuMDMgMi45MjY1IDQ0LjY2MyAzNC4yODcgNTguNTI3IDUwLjY0NCAxNy4wOTkgMjAuMTczIDYyLjc2NC0xLjcxNDcgNjYuMzA2IDMyLjEzNCA1LjEwMjcgNDguNzY2LTYuMzI4NCA3OC42MzcgNi4xNDExIDk3LjM0MiAxOS45NjkgMjkuOTU0IDUwLjQ4NiAxNy44NTYgNDQuNjE5IDgzLjk3MW0tNDcyLjQ1LTM3OC43OWM0LjY0MzUgMjMuNzI5IDE1LjA2OSA3Mi43NzYgMTkuMDYxIDEzMC42NCAwLjg3MjA2IDEyLjY0IDUuNDQ3MiAyNC45OTMgNC4yMjIzIDQ1LjI3OC0yLjUxNzIgNDEuNjg4LTE1LjcxNyA0My42NzctMTUuMDkxIDYwLjM2NSAxLjQzMiAzOC4xODIgMzAuNjE0IDkzLjgzNyAzMC42MTQgMTM5LjcgMCAyNC4xODEtMi42Njk2IDExNS4zOSA3LjMzIDEzNS4zOSAwLjE1OTExIDAuMzE4MjEgMTAuMDY1IDM1Ljg4MyAxMC43NzkgNDkuMTU0IDAuOTQzNzggMTcuNTI1LTI0LjQ3OCAzOS40Ny0yOC4wMjcgNDYuNTY3LTUuNDc3NyAxMC45NTUtMzYuOTczIDEwLjg4Mi00MC4xIDI0LjE0Ni0zLjg2ODggMTYuNDE1LTMuODY2MyA0My43OTcgNC4wNDY1IDU5LjQ0MW05Ny4zMzctNjkxLjAxYy01LjAxMzMgMzUuNTE2LTQzLjY1OSAxMS4zMTctNTguNTM5IDIzLjc4MS0yMS4zMyAxNy44NjktNjIuNSAzMS40MzItNzAuMTI0IDM1LjM2Ny0zNS4wODggMTguMTA4LTExMC40Ny0xNS4xNDItMTI1LjYxIDQuMjY4NC0xNS45NTEgMjAuNDQ3LTAuMDczNSA2MS40NjYtOS4xNDY3IDg0LjE0OS02LjAzNTcgMTUuMDg5LTE4Ljg3NyAyMy4wMTctMjcuNDQgMzIuOTI4LTE5Ljc0OCAyMi44NTYtNjkuOTc0IDY5LjgyNC04NC43NTkgMTAwLTcuNDk3NCAxNS4zMDQtMy4yODQzIDQ0LjQyLTMuNDcwNSA2My4zNDMtMC4xMjc5MyAxMi45OTQtMC44MTAxNSAyMy4xMDQgMi40MDM0IDI4LjI3NiA0Ljk2MTYgNy45ODU4IDIzLjcyIDI4LjExMiAyNC4yMzkgNTAuNjExIDAuMjk0MTEgMTIuNzcxIDAuMDEzMyA3OC41OTEgMy4wNDg5IDg3LjY1NSAyLjMxMjYgNi45MDU1IDQuMjIgMjYuNTY1IDEwLjIxNCAzNi41ODcgMTEuMzU0IDE4Ljk4NCA0LjM4NzQgNDAuMTU3IDI3Ljg5NyA1My41MDggMTkuMDUgMTAuODE5IDQ2Ljg3OCAxMi4yMTkgODEuOTI2IDE0LjQ2MSAzMy43MDMgMi4xNTU5IDYxLjUxMi0xLjQzMDQgNzYuOTIxIDYuMTQxMSAxMS41ODUgNS42OTI3IDguNTgxNSAxNy45MzMgMTQuMjk1IDI5LjM2MSA1LjY0MDQgMTEuMjgxIDMxLjUwMyAxMS4xNTYgNDEuODA0IDQzLjQ1NSA3LjYwNTkgMjMuODQ3IDMuMDg1OSA0NC4xNTcgNi43MDc2IDY1Ljg4NyIgc3Ryb2tlPSIjMzY0ZTU5IiBzdHJva2Utd2lkdGg9IjMiLz4KICAgPHBhdGggZD0ibTQzLjI3OCA1MTcuOTVzMjMwLjg1LTMuNjM4IDI1MC4wMS0zLjY1ODdjNy40ODIyLThlLTMgOC42MTk1IDUuMTUxOSAxNC4wMjEgMTEuNDU5IDI0LjU5NiAyOC43MTkgOTMuOTEgMTEyLjk0IDkzLjkxIDExMi45NCIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogICA8cGF0aCBkPSJtMzUuOTYxIDU3Ny43czE2NS41Mi0xLjY4NDUgMjQ4Ljc4LTEuNjg0NWM0Ljk0NzUgMCA3LjcyOTktMi44ODMzIDEwLjUzOC01LjcyOTggOS42NjExLTkuNzk0MiAyNS42MzItMjguNTkgMjUuNjMyLTI4LjU5IiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPC9nPgogIDxwYXRoIGQ9Im0zOC40IDY0MS43MyAzOTMuMzEtNC4yNjg0IiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0iIzMzNiIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDxwYXRoIGQ9Im0zOS4wMDkgNzA0LjU0IDQ4NC4xNi02LjcwNzYiIGNvbG9yPSIjMDAwMDAwIiBmaWxsPSIjMzM2IiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2Ij4KICAgPGcgc3Ryb2tlLXdpZHRoPSIxcHgiPgogICAgPHBhdGggZD0ibTMwMy45NiA2ODIuNTkgMTQ2LjggMS44MjkzYzEwLjUzNCAwLjEzMTI3IDE0LjM0NC0yLjYzNzQgMjUuNDg3LTYuMzcyOCAxMC40MTItMy40OTAzIDMxLjQyNC0yLjY5OSA0MS4zODUtMi43NzM4bDQwNS41Ni0zLjA0ODkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDI2LjIyIDMxNC44OWMyLjA2NzUgOS4wNTI3IDEuODQxOCA1MS43MjggNi41MDc5IDc0LjgzNSAxLjY3NDggOC4yOTM0IDguNjc1MSAxNC4wNjYgMTAuMDU1IDE0Ljg1OSA0LjkwMTUgMi44MTQ2IDEwLjgxNSA4LjE0OTggMTMuMDQ2IDE2LjA4OCA2Ljc1NzggMjQuMDQ2IDAuODc5NzIgNjguNDUyIDAuODc5NzIgMTEwLjY5IDAgNi4wOTc4IDEuNjYwMSAzMC4xNDctMi4xNTU5IDMzLjk2My0yLjU0MDggMi41NDA4LTAuMjgxNjMgMTIuOTkxLTMuNDM2OCAxNi4xNDRsLTkuODQ5NCA5Ljg0MzFjLTEwLjM2NyAxMC4zNi0xMS41OSA2LjUyNjEtMTcuNzM4IDE4LjgyMy0zLjU2NzcgNy4xMzU0IDUuNDAyNCAyMC42NzIgNy4zNTQzIDI0LjU3NiAxLjkzMjEgMy44NjQzLTEuODQyMiA0Ljc3NzctMS43OTI0IDcuNDQ2MyAwLjI1Mjg2IDEzLjU0NSAyLjI5NzUgMzczLjkzIDIuMjk3NSAzNzMuOTMiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMzY1LjI0IDUxOS43OCA0LjExNiA1MDIuMTUiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMTE2LjUzIDUwNC4xOSAzLjg4MDYgMzEwLjk2IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTMxNy42OCA1NzYuNDkgMTMwLjE5IDEuNTI0NGM0LjUxMDggMy4yNDE3IDIwLjM0NSA3Ljk2ODUgMjcuNzQ1IDQuMjY4NCAzLjE1NTUtMS41Nzc3IDkuNDE5LTUuMzg4MiAxNC4wMjUtMy45NjM2IDQuMjY3IDEuMzE5OCA2LjAxNjkgMy4xMTYzIDEwLjM2NiAzLjA0ODkgMTAuMzA0LTAuMTU5NzUgMjAuMjEyIDAuMzg3NDEgMzAuNDg5IDAuMzA0ODkgMTc3Ljg5LTEuNDI4MyAzNTYuNTktMi4xMzI1IDUzNC43Ny0zLjA0ODkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDc1LjMxIDU4Mi44OWMtMy40NDQyIDExLjM1MS0yLjEwMzQgMTIuNDM0IDMuNjU4NiAyMS4wMzcgMy43OTQ0IDUuNjY1NiA1MC44NjMgMTMuMDM4IDQxLjQ2NSAyNy4xMzUtMTAuNTM3IDE1LjgwNS0yMi44OTctNS40Nzc3LTMzLjg0My0xLjgyOTMtNS40NTI0IDEuODE3NC03LjM0OSA1LjQ1NjMtMy42NTg3IDkuMTQ2NiAyLjgwNjggMi44MDY4IDQuMDQ4IDEuODA0IDYuNTIwMyA1LjEwMDQiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDMyLjAxIDYzNi44NWM4LjMxOSAxMy4xMSAxOC44NDYgMTQuNjM1IDM1LjY3MiAxNC42MzUgMi45Mzg2IDAgNy44Ny0wLjkzMzcxIDEwLjY3MSAwIDExLjM1OSAzLjc4NjQgMjcuMTk0IDEwLjI3NiAzNi4yMDIgMjEuMTI5IDguMjggOS45NzY2IDEwLjI1MyAyMy44ODMgNy43MDIgMzcuMTA0LTYuMTY5OSAzMS45OC0xNi43MTQgNTYuOTg5LTE5LjA0NCA4Ni41NjktMS4zNDggMTcuMTE5IDQuNTA5NiAyMi41MzUgMTEuMDcxIDMzLjkyOSAxMC42NyAxOC41MjcgOC43MjQ1IDE0LjIgOC41NzE0IDM0LjI4Ni0wLjEzOTYzIDE4LjMxOSAwIDYwLjI2NCAwIDgwLjcxNCIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MjguNTEgNjU4Ljk2Yy0xMC42ODEgMC45MDQ1NC03LjEwOC01LjYwMjYtMTAuODI0LTguMDc5Ni00Ljc4NDUtMy4xODk3LTEyLjIyNy0xLjI1MS0xNi43NjktNS43OTI5LTAuNjY2MTItMC42NjYxMi04LjgwOTctNC4xMDg4LTEwLjE3NC0yLjc0NC04LjM2NDYgOC4zNjQ2LTMuMDQ4OSAyMC41NTItMy4wNDg5IDMzLjUzOGwzLjAyMiAzMzkuNyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MTcuOTkgNjUxLjAzYy0wLjIyMTcxLTIuNzAxOCAxLjkwMzUtNS41NjIxIDMuMzUzOC03LjAxMjQgMS43OTk0LTEuNzk5NCA2LjkyMjkgMS4wMDQyIDguODQxOC0wLjkxNDY2IDAuMjg3NjUtMC4yODc2NiAwLjg0MzI5LTExLjE2NCAwLjIyODY2LTEzLjU2OC0yLjA2NDgtOC4wNzQyLTIuMDU4LTI4LjY1Ny0yLjA1OC0zOC43MjF2LTczLjE3MyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MjguNjYgNjc1LjQyLTAuNDU3MzMtMzEuNTU2IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTc2Ni4zMiA1NzkuNjQgMC40MzExOCAxMy43OThjMy4xMzY0IDQuNjY5MiAzLjAxODIgOS42MDA3IDMuMDE4MiAxNi4zODV2MTU3LjM4IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTExMjIuOSA3NjUuOTFjLTIwMi4zMSA0LjY5MDUtNDAzLjc0LTEuMTEzOC02MDUuOTUgMy4zNTM5LTEwLjg2NCAwLjI0MDAyLTMuMzYxNS04LjU4NjMtMjguNTM3LTguNTg2MyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im04NjAuMDEgNzM3LjA3cy05Ny40NDggMC44NTgwNi0xNDcuNTcgMC44NTgwNmMtNS4yNjg2IDAtNC41MTU1LTguMzI5OS03LjMwMDktOC4zMjk5LTMuOTc0NCAwLTguNjI5MiAwLjAyMDEtMTAuNTA5IDAuMDM1OS0yLjMzNDggMC4wMTk3LTEuODEwOSA4LjM2Ni00LjE0NTggOC4zNjY5LTQ2LjE2OSAwLjAxODgtMTY3LjQxLTEuMzA4LTE3NS4wNS0xLjMwOC00LjQyOTYgMC04LjU3NjMtNi40Mzk3LTEzLjEzMi02LjQzOTdoLTE0LjM5NSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im02NzUuMDEgODMxLjE3LTAuNjA5NzgtNTIxLjc3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTc5OS40IDMxMy4wNiAxLjIxOTYgNDk1Ljg3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTczNi41OSAzMTIuNDUtMS4yMTk2IDcxNi40OSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MzAuMDMgNjQzLjQ2IDM5Mi4zNy0zLjAxODIiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtODU5LjQ1IDMxNC45IDEuMjkzNSA1MDcuOTgiIGNvbG9yPSIjMDAwMDAwIi8+CiAgIDwvZz4KICAgPHBhdGggZD0ibTkyMS41NCAzMTAuNTkgMS43MjQ3IDUzMS43NSIgY29sb3I9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgIDxnIHN0cm9rZS13aWR0aD0iMXB4Ij4KICAgIDxwYXRoIGQ9Im03MzYuMjkgNDUzLjMxIDE4NS42OC0wLjMwNDg5IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTEwNjAuOCA1MTQuOTdzLTM2My4yOC01LjYyNjItNTQ0LjY1IDIuNTIxOGMtNC4xNzc4IDAuMTg3NjktMTIuNSAxLjA2NzEtMTIuNSAxLjA2NzEtMS41NzEgMC4xMzQxLTIuMDAwOS0yLjMyNS0yLjU5MTYtMy41MDYyLTAuMDk2Ny0wLjE5MzQzLTcuMDYwOC0xLjkzMzQtNy42MjIyLTEuMzcyLTIuODkzMSAyLjg5MzEtNy42MzE3IDQuMjQ4Ny0xMi4xOTYgNC4xMTZsLTExMi4wNS0zLjI1NzgiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMzk5LjgyIDQ3OS42MSAxMS42NDIgNS42MDUzYzIuOTg0MSAxLjQzNjggNi41Mjg4LTAuNDc3MTIgOS45MTcxLTAuNDMxMThsMTI3LjIgMS43MjQ3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTUxOS4yNSA1MTcuMTItMC40MzExOS0yMDguNjkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDMyLjkzIDM4OS43MWMxMS4wNDUgMCAzNS41MzMgMC42MTkyNyA0Mi41OC0xLjAwNCA4LjQwNTItMS45MzYyIDcuMDY2LTYuOTUzOCAxNC4xOTctNi45NTM4IDcuODA5NSAwIDYuNTQyOSA4LjA2MjQgMjAuMTQyIDguMDYyNCAxMy45OTEgMCA0NC45NzcgMC4zNzg4NiA2My45NCAwLjM3ODg2IDEyLjA4NCAwIDgyLjAwMyAwLjMwNDg5IDkzLjYwMSAwLjMwNDg5IDguNzYwNSAwIDEzLjE2LTIuMjg4MyAyMS4zNDItNy4wMTI0IDcuMTk1Mi00LjE1NDEgMi4wNTQ2LTkuNDkxNCAyMC40MjgtOC44NDE4IDIzLjE0NSAwLjgxODMzIDEyLjY0MyAxNC4wMjUgMzIuMzE4IDE0LjAyNWgxNTAuOTJjMTQuMzMyIDAtNC4xMTkxLTEzLjExIDI5LjI2OS0xMy40MTUiIGNvbG9yPSIjMDAwMDAwIi8+CiAgIDwvZz4KICA8L2c+CiAgPGcgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4Ij4KICAgPHRleHQgeD0iNTg4LjY3OTU3IiB5PSI3MzUuODA0NjMiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU4OC42Nzk1NyIgeT0iNzM1LjgwNDYzIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TGluY29sbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI2ODYuMzk4NSIgeT0iNzY1LjYyODQyIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI2ODYuMzk4NSIgeT0iNzY1LjYyODQyIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI3MDkuODcxODMiIHk9Ii04MDIuMzc3MzgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjcwOS44NzE4MyIgeT0iLTgwMi4zNzczOCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldvb2RsYXduPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTYyLjExOTI2IiB5PSItNzcxLjk2ODE0IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1NjIuMTE5MjYiIHk9Ii03NzEuOTY4MTQiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5FZGdlbW9vcjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU5OC4zMDQ4NyIgeT0iLTczOC4zNjY0NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTk4LjMwNDg3IiB5PSItNzM4LjM2NjQ2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTkyLjEyMjg2IiB5PSItNjc3LjIwMzk4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1OTIuMTIyODYiIHk9Ii02NzcuMjAzOTgiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5IaWxsc2lkZTwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU5Ny4zMjcwOSIgeT0iLTg2Mi42MTQwNyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTk3LjMyNzA5IiB5PSItODYyLjYxNDA3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Um9jazwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU4Ny4zNzAxOCIgeT0iLTkyNi4xMzY2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1ODcuMzcwMTgiIHk9Ii05MjYuMTM2NiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iODcxLjE2MTAxIiB5PSI2MzcuNTc1MiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iODcxLjE2MTAxIiB5PSI2MzcuNTc1MiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkNlbnRyYWw8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iODczLjgzMjI4IiB5PSI1NzcuMDMyNDciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijg3My44MzIyOCIgeT0iNTc3LjAzMjQ3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI4NzUuOTY2NDkiIHk9IjUxMC4yNjE4MSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iODc1Ljk2NjQ5IiB5PSI1MTAuMjYxODEiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yMXN0PC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9Ijg4MS4zMTY1OSIgeT0iNDUwLjE5ODc2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI4ODEuMzE2NTkiIHk9IjQ1MC4xOTg3NiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjI5dGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNjE1Ljc5MjQ4IiB5PSIzODcuNzQ3MTYiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjYxNS43OTI0OCIgeT0iMzg3Ljc0NzE2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Mzd0aDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI0ODQuNjkwMzciIHk9IjQ4MS42NTI4NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNDg0LjY5MDM3IiB5PSI0ODEuNjUyODYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yNXRoPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjU2My4wNDY3NSIgeT0iNTEzLjM2MTMzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1NjMuMDQ2NzUiIHk9IjUxMy4zNjEzMyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNTY1Ljk3MTUiIHk9IjU3Ny44OTQ4NCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTY1Ljk3MTUiIHk9IjU3Ny44OTQ4NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI0MzMuNTgwNzUiIHk9Ii00NjAuNzMzMTIiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjQzMy41ODA3NSIgeT0iLTQ2MC43MzMxMiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkFtaWRvbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjQwNS41MzA5OCIgeT0iLTUyMy41NDAxNiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNDA1LjUzMDk4IiB5PSItNTIzLjU0MDE2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+QXJrYW5zYXM8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI3NDUuNDg0NjIiIHk9Ii0zNzIuNTg1OTQiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijc0NS40ODQ2MiIgeT0iLTM3Mi41ODU5NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldlc3Q8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTYuNzI4MzMiIHk9Ii01MzEuMjU5MjgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5Ni43MjgzMyIgeT0iLTUzMS4yNTkyOCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldhY288L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTUuNDM0ODEiIHk9Ii0xMjIuNTAyOTUiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5NS40MzQ4MSIgeT0iLTEyMi41MDI5NSIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk1hemllPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDQ1KSIgeD0iNjk1Ljc3Mjk1IiB5PSIxNjIuMDY4NzciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjY5NS43NzI5NSIgeT0iMTYyLjA2ODc3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Wm9vPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjI0MC41ODk5NyIgeT0iNTc0LjQ0NTQzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIyNDAuNTg5OTciIHk9IjU3NC40NDU0MyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMjA2LjAzMTc1IiB5PSI1MTEuNjM2NjMiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIwNi4wMzE3NSIgeT0iNTExLjYzNjYzIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjYyMC40NDMxMiIgeT0iLTUwNi42ODIxOSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNjIwLjQ0MzEyIiB5PSItNTA2LjY4MjE5IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TmltczwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSIzNzAuMjE2ODYiIHk9IjY5OC44NDAwOSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iMzcwLjIxNjg2IiB5PSI2OTguODQwMDkiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NYXBsZTwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSIzODQuMDg0MiIgeT0iNjgwLjg1MTM4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIzODQuMDg0MiIgeT0iNjgwLjg1MTM4IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogIDwvZz4KICA8cGF0aCBkPSJtMzY3LjkxIDEwMTBoMjYzLjAyIiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDxnIGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCI+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzM2LjI2NzQ2IiB5PSItNDMzLjEzNzc2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI3MzYuMjY3NDYiIHk9Ii00MzMuMTM3NzYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NZXJpZGlhbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI1NzIuODMyMTUiIHk9IjY0MC4yMDUyNiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTcyLjgzMjE1IiB5PSI2NDAuMjA1MjYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjU3NS4wODk2NiIgeT0iNjcwLjkwMzUiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU3NS4wODk2NiIgeT0iNjcwLjkwMzUiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5Eb3VnbGFzPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjQ5OS40ODk2MiIgeT0iMTAwOC42MDY5IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI0OTkuNDg5NjIiIHk9IjEwMDguNjA2OSIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjQ3dGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMjE2LjY0NTQzIiB5PSI3MjUuOTgyOTciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIxNi42NDU0MyIgeT0iNzI1Ljk4Mjk3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9Ijc3NC44NzU2MSIgeT0iLTUwOC4xODk3MyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNzc0Ljg3NTYxIiB5PSItNTA4LjE4OTczIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TWNDbGVhbjwvdHNwYW4+PC90ZXh0PgogIDwvZz4KICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDI4Ny4zNikiIGQ9Im0zNjQuMTYgNjU4LjQzIDI5OS41MS0xLjAxMDJjNi40OTg3LTAuMDIxOSA2Ljk3NzIgOS4yNTQxIDE2LjU5NiA5LjM5MjUgMTIuMDU0IDAuMTczMzkgMjkuMTExLTAuNTM1NzIgNTQuMTE0LTAuMzAxMSIgY29sb3I9IiMwMDAwMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzNiIgc3Ryb2tlLXdpZHRoPSIxcHgiLz4KICA8dGV4dCB4PSIzNzMuOTkzMDQiIHk9Ijk0NC4zNTc1NCIgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIzNzMuOTkzMDQiIHk9Ijk0NC4zNTc1NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk1hY0FydGh1cjwvdHNwYW4+PC90ZXh0PgogIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzgwLjg0NjA3IiB5PSItNDkwLjI0NTk3IiBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijc4MC44NDYwNyIgeT0iLTQ5MC4yNDU5NyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPlNlbmVjYTwvdHNwYW4+PC90ZXh0PgogIDxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMjg3LjM2KSIgZD0ibTM2Ny43IDUzNy4yMSAxNDEuMjgtMS4wMTAyYzYuNDktMC4wNDY0IDEyLjc4MSA3LjIzNTQgMTkuMTkzIDcuMzIzNiA1NS45MjQgMC43Njg5IDE1OC42OS0wLjE3MzMzIDIzNi41MS0xLjAxMDIgNy44Mzk2LTAuMDg0MyAyMi42MzEtMTkuODU0IDMwLjMwNS0yMC40NTYgMjIuMjY2LTEuMzUxOCA0NS4xNzktMC41MDUwNyA2Ny42OC0wLjUwNTA3IDE2LjE0Ny0wLjYzMjQxIDMuNjEwMiAyMC43MDggMjYuNzY5IDIwLjcwOGwyNDMuNDUtMS4wMTAyIiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDx0ZXh0IHg9IjY4NS4yMDgxMyIgeT0iODI3LjUzMDgyIiBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjY4NS4yMDgxMyIgeT0iODI3LjUzMDgyIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+UGF3bmVlPC90c3Bhbj48L3RleHQ+CiAgPHBhdGggdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAyODcuMzYpIiBkPSJtNTU0LjI5IDcyMS40My00LjI4NTctMTc4LjIxLTIuODU3MS00NDAuNzEtMC4zNTcxNC03OS4yODYiIGNvbG9yPSIjMDAwMDAwIiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1MjkuNjI1MzEiIHk9Ii01NTAuODQ3NzgiIGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTI5LjYyNTMxIiB5PSItNTUwLjg0Nzc4IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+QnJvYWR3YXk8L3RzcGFuPjwvdGV4dD4KIDwvZz4KPC9zdmc+Cg==", "public": true - }, - { - "link": "/api/images/system/map_marker_image_0.png", - "title": "Map marker image 0", - "type": "IMAGE", - "subType": "IMAGE", - "fileName": "map_marker_image_0.png", - "publicResourceKey": "CdCrVxsjA4EAiFaXK4a7K2MZFMeEuGeD", - "mediaType": "image/png", - "data": "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", - "public": true - }, - { - "link": "/api/images/system/map_marker_image_1.png", - "title": "Map marker image 1", - "type": "IMAGE", - "subType": "IMAGE", - "fileName": "map_marker_image_1.png", - "publicResourceKey": "DF3fuPXua9Vi3o3d9Nz2I1LXDTwEs2Tv", - "mediaType": "image/png", - "data": "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", - "public": true - }, - { - "link": "/api/images/system/map_marker_image_2.png", - "title": "Map marker image 2", - "type": "IMAGE", - "subType": "IMAGE", - "fileName": "map_marker_image_2.png", - "publicResourceKey": "rz5SFAw2Sg5T2EyXNdwLycoDwf4QbMiZ", - "mediaType": "image/png", - "data": "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", - "public": true - }, - { - "link": "/api/images/system/map_marker_image_3.png", - "title": "Map marker image 3", - "type": "IMAGE", - "subType": "IMAGE", - "fileName": "map_marker_image_3.png", - "publicResourceKey": "KfPfTuvKCeAnmTcKcrvZQHfdU0TPArWY", - "mediaType": "image/png", - "data": "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", - "public": true } + ], + "scada": false, + "tags": [ + "building", + "interior", + "venue", + "inside", + "room", + "office", + "manufacturing", + "floor", + "plant", + "storage", + "warehouse", + "depot" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/image_map_deprecated.json b/application/src/main/data/json/system/widget_types/image_map_deprecated.json new file mode 100644 index 0000000000..23cf7f7a14 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/image_map_deprecated.json @@ -0,0 +1,102 @@ +{ + "fqn": "maps_v2.image_map", + "name": "Image Map", + "deprecated": true, + "image": "tb-image;/api/images/system/image_map_system_widget_image.png", + "description": "Displays the indoor or relative location of the entities on the image map. Useful to display floor maps, smart parking, etc. Entity coordinates are expected to be in the range from 0 to 1. Highly customizable via custom markers, marker tooltips, and widget actions. ", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "", + "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings-legacy", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"image-map\",\"mapImageUrl\":\"tb-image;/api/images/system/image_map_system_widget_map_image.svg\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false},\"title\":\"Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "tags": [ + "building", + "interior", + "venue", + "inside", + "room", + "office", + "manufacturing", + "floor", + "plant", + "storage", + "warehouse", + "depot" + ], + "resources": [ + { + "link": "/api/images/system/image_map_system_widget_image.png", + "title": "\"Image Map\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "image_map_system_widget_image.png", + "publicResourceKey": "hDdSISQr6elribOYD6T3uePXZI5WvNtM", + "mediaType": "image/png", + "data": "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", + "public": true + }, + { + "link": "/api/images/system/image_map_system_widget_map_image.svg", + "title": "\"Image Map\" system widget map image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "image_map_system_widget_map_image.svg", + "publicResourceKey": "QKRIYhDeBGwjaeIS601VvNLSsvZ25DRj", + "mediaType": "image/svg+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTEzNC41IiBoZWlnaHQ9Ijc2Mi43OCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI3LjA3MSAtMzA3LjkpIj4KICA8ZyBmaWxsPSJub25lIj4KICAgPHBhdGggZD0ibTkwNi4wMyA3MDYuMTMgMy40MjkyIDE3Ljc5Nm0tODgwLjg5IDQxLjEyMWMxNTAuNDQgNi44MzM0IDE0Ni4zOS0yNi4zMzQgMTY2LjQzLTI5LjMyIDM2LjE0NC01LjM4NDggMTE0LjI5LTYuNTI1NCAxNDguMzMtOC42MjM1IDQzLjM3OC0yLjY3MzggMTQxLjc2LTExLjIzMSAxODguODYtMTkuODM0IDM5LjgxMS03LjI3MjggMjIxLjM3LTAuODYyMzUgMzE5LjA3LTAuODYyMzUgNzAuODI3IDAgMTQ2LjkyLTEuNzI0NyAyMTguMTgtMS43MjQ3LTMxLjYyIDAgMTE3Ljg2LTIuNTg3MSA4Ni4yMzYtMi41ODcxbS0yNS4wOTEtNjguMTI2Yy01Mi44IDM0Ljc4NS02NS44OTUgNTEuNzQ5LTk1LjYzOSA4MS40OTMtMjQuOTMxIDI0LjkzMS0xNDAuNC0xOS4xMzktMTc4Ljk0IDM2LjY1LTEyLjI4MSAxNy43NzctNDcuMDAzIDQ2LjU0Ny02NS4xMDggNTkuMDcxLTIwLjEwNSAxMy45MDgtNTYuMDM3IDQ0Ljk1Ny02Ny43NjkgNzMuMDc4LTQuODAxNSAxMS41MDktMTMuMzggMzUuOTkzLTIzLjQ0OSA0Ni4wNjItMTAuNDk3IDEwLjQ5Ny0zOC4zNzcgNi4zODU3LTQ0LjAyMyAxNy42NDgtMTkuMDA1IDM3LjkwOC0yNS40NjUgMTAwLjkyLTY3LjYxOCAxMDIuMDVtMTkuMjgyLTYyNC4wMWMzNC42NTktMS44NzM4IDg0LjAyNyA3LjM5MTMgMTA5LjktNC4yODU0IDEzLjI4Mi01Ljk5NDEgNDEuNDA3LTIuNDYxNCA2Ni44MjktMi4zMjA1IDM1LjMyMiAwLjE5NTc4IDY0LjM4MiAwLjYzNDc3IDEwMS45MiA1LjAyMzIgMjUuMDMgMi45MjY1IDQ0LjY2MyAzNC4yODcgNTguNTI3IDUwLjY0NCAxNy4wOTkgMjAuMTczIDYyLjc2NC0xLjcxNDcgNjYuMzA2IDMyLjEzNCA1LjEwMjcgNDguNzY2LTYuMzI4NCA3OC42MzcgNi4xNDExIDk3LjM0MiAxOS45NjkgMjkuOTU0IDUwLjQ4NiAxNy44NTYgNDQuNjE5IDgzLjk3MW0tNDcyLjQ1LTM3OC43OWM0LjY0MzUgMjMuNzI5IDE1LjA2OSA3Mi43NzYgMTkuMDYxIDEzMC42NCAwLjg3MjA2IDEyLjY0IDUuNDQ3MiAyNC45OTMgNC4yMjIzIDQ1LjI3OC0yLjUxNzIgNDEuNjg4LTE1LjcxNyA0My42NzctMTUuMDkxIDYwLjM2NSAxLjQzMiAzOC4xODIgMzAuNjE0IDkzLjgzNyAzMC42MTQgMTM5LjcgMCAyNC4xODEtMi42Njk2IDExNS4zOSA3LjMzIDEzNS4zOSAwLjE1OTExIDAuMzE4MjEgMTAuMDY1IDM1Ljg4MyAxMC43NzkgNDkuMTU0IDAuOTQzNzggMTcuNTI1LTI0LjQ3OCAzOS40Ny0yOC4wMjcgNDYuNTY3LTUuNDc3NyAxMC45NTUtMzYuOTczIDEwLjg4Mi00MC4xIDI0LjE0Ni0zLjg2ODggMTYuNDE1LTMuODY2MyA0My43OTcgNC4wNDY1IDU5LjQ0MW05Ny4zMzctNjkxLjAxYy01LjAxMzMgMzUuNTE2LTQzLjY1OSAxMS4zMTctNTguNTM5IDIzLjc4MS0yMS4zMyAxNy44NjktNjIuNSAzMS40MzItNzAuMTI0IDM1LjM2Ny0zNS4wODggMTguMTA4LTExMC40Ny0xNS4xNDItMTI1LjYxIDQuMjY4NC0xNS45NTEgMjAuNDQ3LTAuMDczNSA2MS40NjYtOS4xNDY3IDg0LjE0OS02LjAzNTcgMTUuMDg5LTE4Ljg3NyAyMy4wMTctMjcuNDQgMzIuOTI4LTE5Ljc0OCAyMi44NTYtNjkuOTc0IDY5LjgyNC04NC43NTkgMTAwLTcuNDk3NCAxNS4zMDQtMy4yODQzIDQ0LjQyLTMuNDcwNSA2My4zNDMtMC4xMjc5MyAxMi45OTQtMC44MTAxNSAyMy4xMDQgMi40MDM0IDI4LjI3NiA0Ljk2MTYgNy45ODU4IDIzLjcyIDI4LjExMiAyNC4yMzkgNTAuNjExIDAuMjk0MTEgMTIuNzcxIDAuMDEzMyA3OC41OTEgMy4wNDg5IDg3LjY1NSAyLjMxMjYgNi45MDU1IDQuMjIgMjYuNTY1IDEwLjIxNCAzNi41ODcgMTEuMzU0IDE4Ljk4NCA0LjM4NzQgNDAuMTU3IDI3Ljg5NyA1My41MDggMTkuMDUgMTAuODE5IDQ2Ljg3OCAxMi4yMTkgODEuOTI2IDE0LjQ2MSAzMy43MDMgMi4xNTU5IDYxLjUxMi0xLjQzMDQgNzYuOTIxIDYuMTQxMSAxMS41ODUgNS42OTI3IDguNTgxNSAxNy45MzMgMTQuMjk1IDI5LjM2MSA1LjY0MDQgMTEuMjgxIDMxLjUwMyAxMS4xNTYgNDEuODA0IDQzLjQ1NSA3LjYwNTkgMjMuODQ3IDMuMDg1OSA0NC4xNTcgNi43MDc2IDY1Ljg4NyIgc3Ryb2tlPSIjMzY0ZTU5IiBzdHJva2Utd2lkdGg9IjMiLz4KICAgPHBhdGggZD0ibTQzLjI3OCA1MTcuOTVzMjMwLjg1LTMuNjM4IDI1MC4wMS0zLjY1ODdjNy40ODIyLThlLTMgOC42MTk1IDUuMTUxOSAxNC4wMjEgMTEuNDU5IDI0LjU5NiAyOC43MTkgOTMuOTEgMTEyLjk0IDkzLjkxIDExMi45NCIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogICA8cGF0aCBkPSJtMzUuOTYxIDU3Ny43czE2NS41Mi0xLjY4NDUgMjQ4Ljc4LTEuNjg0NWM0Ljk0NzUgMCA3LjcyOTktMi44ODMzIDEwLjUzOC01LjcyOTggOS42NjExLTkuNzk0MiAyNS42MzItMjguNTkgMjUuNjMyLTI4LjU5IiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPC9nPgogIDxwYXRoIGQ9Im0zOC40IDY0MS43MyAzOTMuMzEtNC4yNjg0IiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0iIzMzNiIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDxwYXRoIGQ9Im0zOS4wMDkgNzA0LjU0IDQ4NC4xNi02LjcwNzYiIGNvbG9yPSIjMDAwMDAwIiBmaWxsPSIjMzM2IiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2Ij4KICAgPGcgc3Ryb2tlLXdpZHRoPSIxcHgiPgogICAgPHBhdGggZD0ibTMwMy45NiA2ODIuNTkgMTQ2LjggMS44MjkzYzEwLjUzNCAwLjEzMTI3IDE0LjM0NC0yLjYzNzQgMjUuNDg3LTYuMzcyOCAxMC40MTItMy40OTAzIDMxLjQyNC0yLjY5OSA0MS4zODUtMi43NzM4bDQwNS41Ni0zLjA0ODkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDI2LjIyIDMxNC44OWMyLjA2NzUgOS4wNTI3IDEuODQxOCA1MS43MjggNi41MDc5IDc0LjgzNSAxLjY3NDggOC4yOTM0IDguNjc1MSAxNC4wNjYgMTAuMDU1IDE0Ljg1OSA0LjkwMTUgMi44MTQ2IDEwLjgxNSA4LjE0OTggMTMuMDQ2IDE2LjA4OCA2Ljc1NzggMjQuMDQ2IDAuODc5NzIgNjguNDUyIDAuODc5NzIgMTEwLjY5IDAgNi4wOTc4IDEuNjYwMSAzMC4xNDctMi4xNTU5IDMzLjk2My0yLjU0MDggMi41NDA4LTAuMjgxNjMgMTIuOTkxLTMuNDM2OCAxNi4xNDRsLTkuODQ5NCA5Ljg0MzFjLTEwLjM2NyAxMC4zNi0xMS41OSA2LjUyNjEtMTcuNzM4IDE4LjgyMy0zLjU2NzcgNy4xMzU0IDUuNDAyNCAyMC42NzIgNy4zNTQzIDI0LjU3NiAxLjkzMjEgMy44NjQzLTEuODQyMiA0Ljc3NzctMS43OTI0IDcuNDQ2MyAwLjI1Mjg2IDEzLjU0NSAyLjI5NzUgMzczLjkzIDIuMjk3NSAzNzMuOTMiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMzY1LjI0IDUxOS43OCA0LjExNiA1MDIuMTUiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMTE2LjUzIDUwNC4xOSAzLjg4MDYgMzEwLjk2IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTMxNy42OCA1NzYuNDkgMTMwLjE5IDEuNTI0NGM0LjUxMDggMy4yNDE3IDIwLjM0NSA3Ljk2ODUgMjcuNzQ1IDQuMjY4NCAzLjE1NTUtMS41Nzc3IDkuNDE5LTUuMzg4MiAxNC4wMjUtMy45NjM2IDQuMjY3IDEuMzE5OCA2LjAxNjkgMy4xMTYzIDEwLjM2NiAzLjA0ODkgMTAuMzA0LTAuMTU5NzUgMjAuMjEyIDAuMzg3NDEgMzAuNDg5IDAuMzA0ODkgMTc3Ljg5LTEuNDI4MyAzNTYuNTktMi4xMzI1IDUzNC43Ny0zLjA0ODkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDc1LjMxIDU4Mi44OWMtMy40NDQyIDExLjM1MS0yLjEwMzQgMTIuNDM0IDMuNjU4NiAyMS4wMzcgMy43OTQ0IDUuNjY1NiA1MC44NjMgMTMuMDM4IDQxLjQ2NSAyNy4xMzUtMTAuNTM3IDE1LjgwNS0yMi44OTctNS40Nzc3LTMzLjg0My0xLjgyOTMtNS40NTI0IDEuODE3NC03LjM0OSA1LjQ1NjMtMy42NTg3IDkuMTQ2NiAyLjgwNjggMi44MDY4IDQuMDQ4IDEuODA0IDYuNTIwMyA1LjEwMDQiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDMyLjAxIDYzNi44NWM4LjMxOSAxMy4xMSAxOC44NDYgMTQuNjM1IDM1LjY3MiAxNC42MzUgMi45Mzg2IDAgNy44Ny0wLjkzMzcxIDEwLjY3MSAwIDExLjM1OSAzLjc4NjQgMjcuMTk0IDEwLjI3NiAzNi4yMDIgMjEuMTI5IDguMjggOS45NzY2IDEwLjI1MyAyMy44ODMgNy43MDIgMzcuMTA0LTYuMTY5OSAzMS45OC0xNi43MTQgNTYuOTg5LTE5LjA0NCA4Ni41NjktMS4zNDggMTcuMTE5IDQuNTA5NiAyMi41MzUgMTEuMDcxIDMzLjkyOSAxMC42NyAxOC41MjcgOC43MjQ1IDE0LjIgOC41NzE0IDM0LjI4Ni0wLjEzOTYzIDE4LjMxOSAwIDYwLjI2NCAwIDgwLjcxNCIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MjguNTEgNjU4Ljk2Yy0xMC42ODEgMC45MDQ1NC03LjEwOC01LjYwMjYtMTAuODI0LTguMDc5Ni00Ljc4NDUtMy4xODk3LTEyLjIyNy0xLjI1MS0xNi43NjktNS43OTI5LTAuNjY2MTItMC42NjYxMi04LjgwOTctNC4xMDg4LTEwLjE3NC0yLjc0NC04LjM2NDYgOC4zNjQ2LTMuMDQ4OSAyMC41NTItMy4wNDg5IDMzLjUzOGwzLjAyMiAzMzkuNyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MTcuOTkgNjUxLjAzYy0wLjIyMTcxLTIuNzAxOCAxLjkwMzUtNS41NjIxIDMuMzUzOC03LjAxMjQgMS43OTk0LTEuNzk5NCA2LjkyMjkgMS4wMDQyIDguODQxOC0wLjkxNDY2IDAuMjg3NjUtMC4yODc2NiAwLjg0MzI5LTExLjE2NCAwLjIyODY2LTEzLjU2OC0yLjA2NDgtOC4wNzQyLTIuMDU4LTI4LjY1Ny0yLjA1OC0zOC43MjF2LTczLjE3MyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MjguNjYgNjc1LjQyLTAuNDU3MzMtMzEuNTU2IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTc2Ni4zMiA1NzkuNjQgMC40MzExOCAxMy43OThjMy4xMzY0IDQuNjY5MiAzLjAxODIgOS42MDA3IDMuMDE4MiAxNi4zODV2MTU3LjM4IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTExMjIuOSA3NjUuOTFjLTIwMi4zMSA0LjY5MDUtNDAzLjc0LTEuMTEzOC02MDUuOTUgMy4zNTM5LTEwLjg2NCAwLjI0MDAyLTMuMzYxNS04LjU4NjMtMjguNTM3LTguNTg2MyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im04NjAuMDEgNzM3LjA3cy05Ny40NDggMC44NTgwNi0xNDcuNTcgMC44NTgwNmMtNS4yNjg2IDAtNC41MTU1LTguMzI5OS03LjMwMDktOC4zMjk5LTMuOTc0NCAwLTguNjI5MiAwLjAyMDEtMTAuNTA5IDAuMDM1OS0yLjMzNDggMC4wMTk3LTEuODEwOSA4LjM2Ni00LjE0NTggOC4zNjY5LTQ2LjE2OSAwLjAxODgtMTY3LjQxLTEuMzA4LTE3NS4wNS0xLjMwOC00LjQyOTYgMC04LjU3NjMtNi40Mzk3LTEzLjEzMi02LjQzOTdoLTE0LjM5NSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im02NzUuMDEgODMxLjE3LTAuNjA5NzgtNTIxLjc3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTc5OS40IDMxMy4wNiAxLjIxOTYgNDk1Ljg3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTczNi41OSAzMTIuNDUtMS4yMTk2IDcxNi40OSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MzAuMDMgNjQzLjQ2IDM5Mi4zNy0zLjAxODIiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtODU5LjQ1IDMxNC45IDEuMjkzNSA1MDcuOTgiIGNvbG9yPSIjMDAwMDAwIi8+CiAgIDwvZz4KICAgPHBhdGggZD0ibTkyMS41NCAzMTAuNTkgMS43MjQ3IDUzMS43NSIgY29sb3I9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgIDxnIHN0cm9rZS13aWR0aD0iMXB4Ij4KICAgIDxwYXRoIGQ9Im03MzYuMjkgNDUzLjMxIDE4NS42OC0wLjMwNDg5IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTEwNjAuOCA1MTQuOTdzLTM2My4yOC01LjYyNjItNTQ0LjY1IDIuNTIxOGMtNC4xNzc4IDAuMTg3NjktMTIuNSAxLjA2NzEtMTIuNSAxLjA2NzEtMS41NzEgMC4xMzQxLTIuMDAwOS0yLjMyNS0yLjU5MTYtMy41MDYyLTAuMDk2Ny0wLjE5MzQzLTcuMDYwOC0xLjkzMzQtNy42MjIyLTEuMzcyLTIuODkzMSAyLjg5MzEtNy42MzE3IDQuMjQ4Ny0xMi4xOTYgNC4xMTZsLTExMi4wNS0zLjI1NzgiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMzk5LjgyIDQ3OS42MSAxMS42NDIgNS42MDUzYzIuOTg0MSAxLjQzNjggNi41Mjg4LTAuNDc3MTIgOS45MTcxLTAuNDMxMThsMTI3LjIgMS43MjQ3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTUxOS4yNSA1MTcuMTItMC40MzExOS0yMDguNjkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDMyLjkzIDM4OS43MWMxMS4wNDUgMCAzNS41MzMgMC42MTkyNyA0Mi41OC0xLjAwNCA4LjQwNTItMS45MzYyIDcuMDY2LTYuOTUzOCAxNC4xOTctNi45NTM4IDcuODA5NSAwIDYuNTQyOSA4LjA2MjQgMjAuMTQyIDguMDYyNCAxMy45OTEgMCA0NC45NzcgMC4zNzg4NiA2My45NCAwLjM3ODg2IDEyLjA4NCAwIDgyLjAwMyAwLjMwNDg5IDkzLjYwMSAwLjMwNDg5IDguNzYwNSAwIDEzLjE2LTIuMjg4MyAyMS4zNDItNy4wMTI0IDcuMTk1Mi00LjE1NDEgMi4wNTQ2LTkuNDkxNCAyMC40MjgtOC44NDE4IDIzLjE0NSAwLjgxODMzIDEyLjY0MyAxNC4wMjUgMzIuMzE4IDE0LjAyNWgxNTAuOTJjMTQuMzMyIDAtNC4xMTkxLTEzLjExIDI5LjI2OS0xMy40MTUiIGNvbG9yPSIjMDAwMDAwIi8+CiAgIDwvZz4KICA8L2c+CiAgPGcgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4Ij4KICAgPHRleHQgeD0iNTg4LjY3OTU3IiB5PSI3MzUuODA0NjMiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU4OC42Nzk1NyIgeT0iNzM1LjgwNDYzIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TGluY29sbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI2ODYuMzk4NSIgeT0iNzY1LjYyODQyIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI2ODYuMzk4NSIgeT0iNzY1LjYyODQyIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI3MDkuODcxODMiIHk9Ii04MDIuMzc3MzgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjcwOS44NzE4MyIgeT0iLTgwMi4zNzczOCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldvb2RsYXduPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTYyLjExOTI2IiB5PSItNzcxLjk2ODE0IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1NjIuMTE5MjYiIHk9Ii03NzEuOTY4MTQiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5FZGdlbW9vcjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU5OC4zMDQ4NyIgeT0iLTczOC4zNjY0NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTk4LjMwNDg3IiB5PSItNzM4LjM2NjQ2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTkyLjEyMjg2IiB5PSItNjc3LjIwMzk4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1OTIuMTIyODYiIHk9Ii02NzcuMjAzOTgiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5IaWxsc2lkZTwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU5Ny4zMjcwOSIgeT0iLTg2Mi42MTQwNyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTk3LjMyNzA5IiB5PSItODYyLjYxNDA3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Um9jazwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU4Ny4zNzAxOCIgeT0iLTkyNi4xMzY2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1ODcuMzcwMTgiIHk9Ii05MjYuMTM2NiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iODcxLjE2MTAxIiB5PSI2MzcuNTc1MiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iODcxLjE2MTAxIiB5PSI2MzcuNTc1MiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkNlbnRyYWw8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iODczLjgzMjI4IiB5PSI1NzcuMDMyNDciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijg3My44MzIyOCIgeT0iNTc3LjAzMjQ3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI4NzUuOTY2NDkiIHk9IjUxMC4yNjE4MSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iODc1Ljk2NjQ5IiB5PSI1MTAuMjYxODEiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yMXN0PC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9Ijg4MS4zMTY1OSIgeT0iNDUwLjE5ODc2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI4ODEuMzE2NTkiIHk9IjQ1MC4xOTg3NiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjI5dGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNjE1Ljc5MjQ4IiB5PSIzODcuNzQ3MTYiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjYxNS43OTI0OCIgeT0iMzg3Ljc0NzE2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Mzd0aDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI0ODQuNjkwMzciIHk9IjQ4MS42NTI4NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNDg0LjY5MDM3IiB5PSI0ODEuNjUyODYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yNXRoPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjU2My4wNDY3NSIgeT0iNTEzLjM2MTMzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1NjMuMDQ2NzUiIHk9IjUxMy4zNjEzMyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNTY1Ljk3MTUiIHk9IjU3Ny44OTQ4NCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTY1Ljk3MTUiIHk9IjU3Ny44OTQ4NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI0MzMuNTgwNzUiIHk9Ii00NjAuNzMzMTIiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjQzMy41ODA3NSIgeT0iLTQ2MC43MzMxMiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkFtaWRvbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjQwNS41MzA5OCIgeT0iLTUyMy41NDAxNiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNDA1LjUzMDk4IiB5PSItNTIzLjU0MDE2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+QXJrYW5zYXM8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI3NDUuNDg0NjIiIHk9Ii0zNzIuNTg1OTQiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijc0NS40ODQ2MiIgeT0iLTM3Mi41ODU5NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldlc3Q8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTYuNzI4MzMiIHk9Ii01MzEuMjU5MjgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5Ni43MjgzMyIgeT0iLTUzMS4yNTkyOCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldhY288L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTUuNDM0ODEiIHk9Ii0xMjIuNTAyOTUiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5NS40MzQ4MSIgeT0iLTEyMi41MDI5NSIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk1hemllPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDQ1KSIgeD0iNjk1Ljc3Mjk1IiB5PSIxNjIuMDY4NzciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjY5NS43NzI5NSIgeT0iMTYyLjA2ODc3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Wm9vPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjI0MC41ODk5NyIgeT0iNTc0LjQ0NTQzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIyNDAuNTg5OTciIHk9IjU3NC40NDU0MyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMjA2LjAzMTc1IiB5PSI1MTEuNjM2NjMiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIwNi4wMzE3NSIgeT0iNTExLjYzNjYzIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjYyMC40NDMxMiIgeT0iLTUwNi42ODIxOSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNjIwLjQ0MzEyIiB5PSItNTA2LjY4MjE5IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TmltczwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSIzNzAuMjE2ODYiIHk9IjY5OC44NDAwOSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iMzcwLjIxNjg2IiB5PSI2OTguODQwMDkiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NYXBsZTwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSIzODQuMDg0MiIgeT0iNjgwLjg1MTM4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIzODQuMDg0MiIgeT0iNjgwLjg1MTM4IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogIDwvZz4KICA8cGF0aCBkPSJtMzY3LjkxIDEwMTBoMjYzLjAyIiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDxnIGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCI+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzM2LjI2NzQ2IiB5PSItNDMzLjEzNzc2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI3MzYuMjY3NDYiIHk9Ii00MzMuMTM3NzYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NZXJpZGlhbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI1NzIuODMyMTUiIHk9IjY0MC4yMDUyNiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTcyLjgzMjE1IiB5PSI2NDAuMjA1MjYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjU3NS4wODk2NiIgeT0iNjcwLjkwMzUiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU3NS4wODk2NiIgeT0iNjcwLjkwMzUiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5Eb3VnbGFzPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjQ5OS40ODk2MiIgeT0iMTAwOC42MDY5IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI0OTkuNDg5NjIiIHk9IjEwMDguNjA2OSIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjQ3dGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMjE2LjY0NTQzIiB5PSI3MjUuOTgyOTciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIxNi42NDU0MyIgeT0iNzI1Ljk4Mjk3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9Ijc3NC44NzU2MSIgeT0iLTUwOC4xODk3MyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNzc0Ljg3NTYxIiB5PSItNTA4LjE4OTczIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TWNDbGVhbjwvdHNwYW4+PC90ZXh0PgogIDwvZz4KICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDI4Ny4zNikiIGQ9Im0zNjQuMTYgNjU4LjQzIDI5OS41MS0xLjAxMDJjNi40OTg3LTAuMDIxOSA2Ljk3NzIgOS4yNTQxIDE2LjU5NiA5LjM5MjUgMTIuMDU0IDAuMTczMzkgMjkuMTExLTAuNTM1NzIgNTQuMTE0LTAuMzAxMSIgY29sb3I9IiMwMDAwMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzNiIgc3Ryb2tlLXdpZHRoPSIxcHgiLz4KICA8dGV4dCB4PSIzNzMuOTkzMDQiIHk9Ijk0NC4zNTc1NCIgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIzNzMuOTkzMDQiIHk9Ijk0NC4zNTc1NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk1hY0FydGh1cjwvdHNwYW4+PC90ZXh0PgogIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzgwLjg0NjA3IiB5PSItNDkwLjI0NTk3IiBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijc4MC44NDYwNyIgeT0iLTQ5MC4yNDU5NyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPlNlbmVjYTwvdHNwYW4+PC90ZXh0PgogIDxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMjg3LjM2KSIgZD0ibTM2Ny43IDUzNy4yMSAxNDEuMjgtMS4wMTAyYzYuNDktMC4wNDY0IDEyLjc4MSA3LjIzNTQgMTkuMTkzIDcuMzIzNiA1NS45MjQgMC43Njg5IDE1OC42OS0wLjE3MzMzIDIzNi41MS0xLjAxMDIgNy44Mzk2LTAuMDg0MyAyMi42MzEtMTkuODU0IDMwLjMwNS0yMC40NTYgMjIuMjY2LTEuMzUxOCA0NS4xNzktMC41MDUwNyA2Ny42OC0wLjUwNTA3IDE2LjE0Ny0wLjYzMjQxIDMuNjEwMiAyMC43MDggMjYuNzY5IDIwLjcwOGwyNDMuNDUtMS4wMTAyIiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDx0ZXh0IHg9IjY4NS4yMDgxMyIgeT0iODI3LjUzMDgyIiBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjY4NS4yMDgxMyIgeT0iODI3LjUzMDgyIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+UGF3bmVlPC90c3Bhbj48L3RleHQ+CiAgPHBhdGggdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAyODcuMzYpIiBkPSJtNTU0LjI5IDcyMS40My00LjI4NTctMTc4LjIxLTIuODU3MS00NDAuNzEtMC4zNTcxNC03OS4yODYiIGNvbG9yPSIjMDAwMDAwIiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1MjkuNjI1MzEiIHk9Ii01NTAuODQ3NzgiIGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTI5LjYyNTMxIiB5PSItNTUwLjg0Nzc4IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+QnJvYWR3YXk8L3RzcGFuPjwvdGV4dD4KIDwvZz4KPC9zdmc+Cg==", + "public": true + }, + { + "link": "/api/images/system/map_marker_image_0.png", + "title": "Map marker image 0", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "map_marker_image_0.png", + "publicResourceKey": "CdCrVxsjA4EAiFaXK4a7K2MZFMeEuGeD", + "mediaType": "image/png", + "data": "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", + "public": true + }, + { + "link": "/api/images/system/map_marker_image_1.png", + "title": "Map marker image 1", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "map_marker_image_1.png", + "publicResourceKey": "DF3fuPXua9Vi3o3d9Nz2I1LXDTwEs2Tv", + "mediaType": "image/png", + "data": "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", + "public": true + }, + { + "link": "/api/images/system/map_marker_image_2.png", + "title": "Map marker image 2", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "map_marker_image_2.png", + "publicResourceKey": "rz5SFAw2Sg5T2EyXNdwLycoDwf4QbMiZ", + "mediaType": "image/png", + "data": "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", + "public": true + }, + { + "link": "/api/images/system/map_marker_image_3.png", + "title": "Map marker image 3", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "map_marker_image_3.png", + "publicResourceKey": "KfPfTuvKCeAnmTcKcrvZQHfdU0TPArWY", + "mediaType": "image/png", + "data": "iVBORw0KGgoAAAANSUhEUgAAAB4AAAB/CAYAAAD4mHJdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAACWAAAAlgB7MGOJQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAxNSURBVGiB7Zp7kFTllcB/5/a93dMz3T0PemYIDgoCPhZ5iaD4wNkFjQjRRMlLTNbSlKlyzZpobSVbFRPUbNVWSRCWuKvlxqybtbIrukp4SATZCAgospEBgeElj4EZ5t3d0+++37d/9O2ZnqEHQZzZSlXfqlMz/c253+875zvfOefeHuH8L6u83P+AwH0lJZ4pbrenEiCVSnYmEsndGl4NhSKvAJkLmPPcV0VFxZjKivKPv77wXr274WN9uvm0PnHihD5y9IhuPNioN216Vy+Yf6eurAj8b2Vl5aXnM6d8loLf7w9apvHhyy//29jZ9fW0t7fpdWtWN7Wdao4qpaiqDpbdXF9fV1paKpu3bGbxk08eSWXU9ZFIpOPirC33v7xs+TIdiUT0Pz239NjeaTOTHXXjdb4cuP6W5DOLFx/7aNdH+oknfqQryv0vXZTFfr8/GKyqaN7XeMhc//ba6NSfPFXqS6fESJ29jdGAX69+9KHY9OnTyxbec08mHInWhsPhzsHmNs4FNgxdf+NNN5sAh3/7n40dCxeKedUsOr6x8CzdsnBEQu9sPABwzTWTTMNQ9eec+1x/FDEuGTduHABXtreOKutJYyiFqq4tqD+5O3wJQF1dHSij7nODtdZuj9cLgMfGOpcuQInSFoDldqNFez43eCivIrgILoKL4CK4CC6Ci+AiuAgugovgIrgILoKL4CK4CC6Ci+A/B7B5vor6Mz4PNnbRYAAtoCQLUMMFVobuBWOALWdjVIGxiwbbZC3WkrXWLqAzJBZrR5T0LWTgdSHfdF1YcIlG57t8oM5nfov1OcCKPmDW1Rfi2IsA5yI5F9WFXF0o0i8arARwggsBu4BbhwaM6g0ujXY+9b+GLqrzLR5E5wsH2ziB5QRXoW8lCy3mosH553iwlDlEe9znai2DpMyhAJ+PxUNTJMhZm51+WM9xvsWFXD2kx0nl9rjQ4oYC3C+4BoEMnasl39Vn6wxRdcqbXApXpwupWBcEVgLKGLw6DU1w5bkaCjcChcYuHozuLYtqEFfroXC1TZ67GcbjlEuZWjSIHr6ozjZ7/y/VSWOLdgJIF9zjQl3JFwDOXn1lsYDOULm6X+YaROcLB6s8+LC2tzqvoc+Wx0L2nT/6wlIm5y6LQ9bs5TLXsO5x7jG192lxuJq9bCOg0aIRGcYEkt9lCsPp6lxlMsBlFE4ghcYuGoxznHKFYNjKYq7Zy5XFYW32lMtCBGzbLlwWLwB83m/2NNC44R0iFaP503+8jO1UqHz5wiwW0aNzvysgdPJTQr/7dFD9fHD+vecN9vl8NaYpv546ZeqCBx98CMhGbPXEqZRfcTWmyySTjuO2TMora/B4Sji+832OnWoGYMGCBez88IMfHD50eExG6Yd6enraBjJcAwf8fv+Vbsv1Pz9f/NT1y1esQCnNPz6zeGuy6WBN+MRRrwp1YMR6MOIJMqEuOj49xNFd2zh5aD9SVpr44PCJXVOmXXvpHfPm4fP7rtz98Z/usSz3+lQq1e/fnvuFSHl5+VjTNLb96lfPj6yv/0t2bN/eufJnj+37Uql1c/1Xv8WM279CaZn/rJcBGoj1hNm+7k22rF5JcyK1edp3Hps0bfq0yj0Ne/jFL55pVopZ3d3dx88C19bWlqVS8Z2Lf/7U1XNvu51Vb72x7/irz9fUBEcEv/03PyFYPRJDgZHt9XpvzG8QlAFnWppY+S9LaOnsaPPOWdhxx7z5V320cydLl/7yE2+pb+bp06dj/VxtWbJ03h13zr/r7rtZu2bNwVP/9cKYMiHwtW8+QNAbwOiOIN09SCiChCKQL+EIKhxBhcN4EGpGjuJww66yxNH9gePac+zGm26sikQiNY379/kSydT63uCqrCybXB6oeuS+RYvYv29f/OTKFz1+dIlXXFQrCznRjNhkRfdJzmIMEAExsqbUmh68holWGXf43deMg6NHJ+5btKjkgw8//IFh8lJnZ88nBoBWxpPf+e53DYC1Ly5bVSb6Mo8WSrQgx5uRY6cHSDMcz0q/vx/PSTNeJXi04EOPfe93L70JcP/99xu2bfwUwPT5fNU+n++rM2fO5P3332+uS3V9y9KCG8FSmtjRo3iN0uz+qqylemDnLhpDQDsFJGrHMG2F2xAyGi5Nhr65Y8f21unTZ9T4yrz3KqVHGC4X91x33XUmwN7N775nApbuk90nD5BpbUbaWqG9Dd3eju5o6y/t7dDehrS1kmltYffJ/ViA25nDBcbeLZs2AUyaNNkyDL5minDL7Nm3opSiNtQ0yUQwESydlXg6xc70Sf5CewliYSD9TqHu/anpIMUnJIiLjSVCGjAFTA21odNTlFLMunEWO7bvuMUUjKkTrriCvXv3RDyiJxpacGVXSc56W2uO6DhtKkmFFsocHchmtKhoukURNrJPG5YDdAEuDYaAV/TVjY0HesaNG+8Tw5hmuC1zFEBLS0urkQ3QPtFgILgQTC0IkAZSgEJQCClnTBwdF4KBOPf2iQBnzrS2GYaBZblGmWK4/ADxWCzqoS85iDOZDFiMS2ddV5Kz2EkGhgwECYLOzqOzxy0W7YkAiBgBw7btFIC3tMw/2JsrnS9OI5B2pPdt0AC9gdVZZxkBANu2k0Ymk2kDCI6oqsw1c/nNu8rVW8l+2ZFCkxRNzMhKUjQpNBlnv23nXfbAeTRQHayudMBtBlod6OrqZNz4CeVprcKqd4KsZBxgGk1KNEmBmGiijsScsZRo0s4CMnn3284CMqJCY8aOCXR2dqK0PmBokQ3r1q7D7/dLq7tyY8axMCOatDNZFqhJiCbuWNsLNrJjCUcnt4C0ZOew0WTQnDYr3/X5fLJx4wZE5B1DKVm1a9dHAIyYesPYjEBa+vYwJZAUSAgkHAtjookaWcl9Togm4eim8u5PS9YDNVNmXg7QsLsBreX3RjgcPtzW1rarubmZ+QvumtahXJvzrUzmWRvrZ61yxNnvPKuTA6xvt13bvjxv/tSW5mY6Ozt3hkKhoy4Ar6ek6dChg4vm3nY7oZJAJnG4oUIQESdD5Ud0v30XSBlZC1OGdjyTA/darwK3LcxcPm585ZJnl9ATinwvnkweNgC6wuF1x44d27R3714WfOWucZGrb3g7kee+eJ6LewPLcXU0bzwuuf2G3P3NoyevnzP3tsv3NOylqenkHzvD4fWQ197aikeW/nJJd1dnJ4//9On57V+a8Hoib7K4kQeUAWL0D7RcsJ2oqHv9wUcfu7Orq5MVK5Z3KS0P53j96lsgEPjyiKqKtW/891uu2tpalvzDMxsTW96s9yhMC8HUOCkxm07JO/fZk5A9dkmDTOSqWe/99fcfmRPtifHY3z6a6Q5F7gyFQhsKggFGjKh4wFviffG11153T59xHVu3bg3968/+7g9V3ae+0Zv0kX49l3ISjA2ccpe/NXvR9+uvnX5tRdOpJv7+xz9OxnpiD3d0d/97PqcXrLWeBcwGLnv11d96n3j88QVPPf108KHvPUwmk+HttWu71q96Y0dozzajJBUfXyqMA4gpfShmeY54JkzX19/6VzfMmDmjMpPOsOqtVbzyym9alz23fM23Fy1KACeAP4rIBwCitb4MeAQY5SxEt7a2qIaGBn70wx+OTKXTc5Y+t8w1d85cdN5KtdbYSqGVImPbJOIxotEo6/+wniXPPmsH/L4Ny5etaJk46Rqprq7JPTgooBn4Z9FaPw9UAHR1dSnbTsuZMy1GMpnItLZ2GFu3bq5d/fvVc0ZUjZB7F36d2fW3MmHCFZguF0pr0uk0Bxsb2bL5PV5fuZLuUEjfdffdG2+66ebW6mCVLvP5qa4OAoYEg8Gcg7tNIAIEADHdJnbcxmNZ6UQ05nK7TT1x4sRYRVV1/FTTqdLVa9bywgsvEImESKfSAFiWhT9QzqhL6rh25g3UjbokPnJkTaKkxFRaa8NtGbaIy+Up8eS2VgEx0VpXO66+HKfdbW9vV93d7RKNJl3xeNQOd4d1Mp0i3B3yRCKRsmgiYSVTaa9orS23lfR5vany8vKYLxCIeyxLKqoqtddbKh6PSVVVtQ4Gg5IHPQI8nx9ck4CbgSuBarJnvARsiUai4XBPmGQyqbWGRCxh2VrZAKYYLtNjZUyXSxsuU6oqyg1fwO91nhUSzvQdwB5gm4h8UvA4OYsoByYDY4EaoBLwAN7sYiDvZ4LsqUo60uNIK3AY2CMioYGM/wPREY0iGUY58wAAAABJRU5ErkJggg==", + "public": true + } + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/map.json b/application/src/main/data/json/system/widget_types/map.json new file mode 100644 index 0000000000..9f246a215a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/map.json @@ -0,0 +1,44 @@ +{ + "fqn": "map", + "name": "Map", + "deprecated": false, + "image": "tb-image;/api/images/system/openstreet_map_system_widget_image.png", + "description": "Displays the location of the entities on Map. Allows to choose among existing tile providers or configure own server. Some providers require additional licenses. Highly customizable via custom markers, marker tooltips, and widget actions. ", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.mapWidget.onInit();\n};\n\nself.typeParameters = function() {\n return {\n hideDataTab: true,\n hideDataSettings: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n datasourcesOptional: true,\n additionalWidgetActionTypes: ['placeMapItem']\n };\n}\n", + "settingsForm": [], + "dataKeySettingsForm": [], + "settingsDirective": "tb-map-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-map-basic-config", + "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"markers\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8239425680406081,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\"},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\"},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var temperature = data.temperature;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\\n\"}},\"markerIcon\":{\"icon\":\"mdi:lightbulb-on\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xOTEuMzUgLTM1MS4xOCAxMDgzLjU4IDE3MzAuNDYiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjZmU3NTY5IiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iMzciIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgZD0iTTM1MS44MzMgMTM2MC43OGMtMzguNzY2LTE5MC4zLTEwNy4xMTYtMzQ4LjY2NS0xODkuOTAzLTQ5NS40NEMxMDAuNTIzIDc1Ni40NjkgMjkuMzg2IDY1NS45NzgtMzYuNDM0IDU1MC40MDRjLTIxLjk3Mi0zNS4yNDQtNDAuOTM0LTcyLjQ3Ny02Mi4wNDctMTA5LjA1NC00Mi4yMTYtNzMuMTM3LTc2LjQ0NC0xNTcuOTM1LTc0LjI2OS0yNjcuOTMyIDIuMTI1LTEwNy40NzMgMzMuMjA4LTE5My42ODUgNzguMDMtMjY0LjE3M0MtMjEtMjA2LjY5IDEwMi40ODEtMzAxLjc0NSAyNjguMTY0LTMyNi43MjRjMTM1LjQ2Ni0yMC40MjUgMjYyLjQ3NSAxNC4wODIgMzUyLjU0MyA2Ni43NDcgNzMuNiA0My4wMzggMTMwLjU5NiAxMDAuNTI4IDE3My45MiAxNjguMjggNDUuMjIgNzAuNzE2IDc2LjM2IDE1NC4yNiA3OC45NzEgMjYzLjIzMyAxLjMzNyA1NS44My03LjgwNSAxMDcuNTMyLTIwLjY4NCAxNTAuNDE3LTEzLjAzNCA0My40MS0zMy45OTYgNzkuNjk1LTUyLjY0NiAxMTguNDU1LTM2LjQwNiA3NS42NTktODIuMDQ5IDE0NC45ODEtMTI3Ljg1NSAyMTQuMzQ1LTEzNi40MzcgMjA2LjYwNi0yNjQuNDk2IDQxNy4zMS0zMjAuNTggNzA2LjAyOHoiLz48Y2lyY2xlIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBjeD0iMzUyLjg5MSIgY3k9IjIyNS43NzkiIHI9IjE4My4zMzIiLz48L3N2Zz4=\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":1},{\"dsType\":\"function\",\"dsLabel\":\"Second point\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7826299113906372,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details\",\"offsetX\":0,\"offsetY\":-1},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"edit\":{\"enabledActions\":[],\"snappable\":false},\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 500 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"icon\",\"markerShape\":{\"shape\":\"markerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"size\":40,\"color\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var colors = ['#488bc7','#549c5d','#ed7546','#be2b29'];\\nvar temperature = data.temperature;\\nvar res = colors[0];\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120;\\n var index = Math.min(3, Math.floor(4 * percent));\\n res = colors[index];\\n}\\nreturn res;\"},\"icon\":\"thermostat\"},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/shape1.svg\",\"imageSize\":34,\"imageFunction\":\"\\n\",\"images\":[]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null}}],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[]},\"title\":\"Map\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\",\"titleIcon\":\"map\",\"iconColor\":\"#1F6BDD\",\"actions\":{}}" + }, + "resources": [ + { + "link": "/api/images/system/openstreet_map_system_widget_image.png", + "title": "\"OpenStreet Map\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "openstreet_map_system_widget_image.png", + "publicResourceKey": "Uyd9JmVKUcCF6PchzgNnAOjx9WvVN5ZE", + "mediaType": "image/png", + "data": "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", + "public": true + } + ], + "scada": false, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/markers_placement___google_maps.json b/application/src/main/data/json/system/widget_types/markers_placement___google_maps.json index e9f492b7fc..bf721b05b7 100644 --- a/application/src/main/data/json/system/widget_types/markers_placement___google_maps.json +++ b/application/src/main/data/json/system/widget_types/markers_placement___google_maps.json @@ -1,7 +1,7 @@ { "fqn": "input_widgets.markers_placement_google_maps", "name": "Markers Placement - Google Maps", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/markers_placement_google_maps_system_widget_image.png", "description": "Allows configuring the location of the selected entities on Google Maps. By default, store the location using 'latitude' and 'longitude' server-side attributes.", "descriptor": { @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", + "settingsDirective": "tb-map-widget-settings-legacy", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete\",\"markerImageSize\":34,\"gmDefaultMapType\":\"roadmap\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"colorFunction\":\"\\n\",\"color\":\"#fe7569\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"defaultZoomLevel\":5,\"provider\":\"google-map\",\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\",\"showPolygonTooltip\":false},\"title\":\"Markers Placement - Google Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"8d3c0156-0a14-7a6f-0ddd-0ec16b9ffc91\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"46bf69cd-8906-234c-a879-e2e4c92f5b67\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" }, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/markers_placement___image_map.json b/application/src/main/data/json/system/widget_types/markers_placement___image_map.json index 5544471bc3..c4bddab86d 100644 --- a/application/src/main/data/json/system/widget_types/markers_placement___image_map.json +++ b/application/src/main/data/json/system/widget_types/markers_placement___image_map.json @@ -1,7 +1,7 @@ { "fqn": "input_widgets.markers_placement_image_map", "name": "Markers Placement - Image Map", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/markers_placement_image_map_system_widget_image.png", "description": "Allows configuring the location of the selected entities on the Image map. By default, store the location using 'xPos' and 'yPos' server-side attributes with values of 0.0 to 1.0.", "descriptor": { @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", + "settingsDirective": "tb-map-widget-settings-legacy", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}

Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapImageUrl\":\"tb-image;/api/images/system/markers_placement_image_map_system_widget_map_image.svg\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"showTooltip\":true,\"autocloseTooltip\":true,\"showTooltipAction\":\"click\",\"defaultCenterPosition\":\"0,0\",\"provider\":\"image-map\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\"},\"title\":\"Markers Placement - Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"c39f512a-21c6-6b06-3aa1-715262c6553d\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"94bf5ffd-b526-c6c3-ae3b-ab42191217d9\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" }, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/markers_placement___openstreetmap.json b/application/src/main/data/json/system/widget_types/markers_placement___openstreetmap.json index a351a75564..da96abb49e 100644 --- a/application/src/main/data/json/system/widget_types/markers_placement___openstreetmap.json +++ b/application/src/main/data/json/system/widget_types/markers_placement___openstreetmap.json @@ -1,7 +1,7 @@ { "fqn": "input_widgets.markers_placement_openstreetmap", "name": "Markers Placement - OpenStreetMap", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/markers_placement_openstreetmap_system_widget_image.png", "description": "Allows configuring the location of the selected entities on OpenStreetMap. By default, store the location using 'latitude' and 'longitude' server-side attributes.", "descriptor": { @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", + "settingsDirective": "tb-map-widget-settings-legacy", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7867521952070078,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7040053227577256,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"defaultZoomLevel\":5,\"provider\":\"openstreet-map\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\"},\"title\":\"Markers Placement - OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"54c293c4-9ca6-e34f-dc6a-0271944c1c66\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"6beb7bed-dfd8-388d-b60c-82988ab52f06\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" }, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/openstreet_map.json b/application/src/main/data/json/system/widget_types/openstreet_map.json index 15454743f3..b0294e0988 100644 --- a/application/src/main/data/json/system/widget_types/openstreet_map.json +++ b/application/src/main/data/json/system/widget_types/openstreet_map.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.openstreetmap", "name": "OpenStreet Map", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/openstreet_map_system_widget_image.png", "description": "Displays the location of the entities on OpenStreetMap. Allows to choose among existing tile providers or configure own server. Some providers require additional licenses. Highly customizable via custom markers, marker tooltips, and widget actions. ", "descriptor": { @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", + "settingsDirective": "tb-map-widget-settings-legacy", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"openstreet-map\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"OpenStreet Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" }, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/route_map.json b/application/src/main/data/json/system/widget_types/route_map.json new file mode 100644 index 0000000000..6d9590fe90 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/route_map.json @@ -0,0 +1,80 @@ +{ + "fqn": "route_map", + "name": "Route Map", + "deprecated": false, + "image": "tb-image;/api/images/system/route_map_openstreet_system_widget_image.png", + "description": "Displays the trip of the entity on the OpenStreetMap or other map providers. Highly customizable via custom markers, marker tooltips, and widget actions.", + "descriptor": { + "type": "timeseries", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.mapWidget.onInit();\n};\n\nself.typeParameters = function() {\n return {\n trip: true,\n hideDataTab: true,\n hideDataSettings: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n datasourcesOptional: true,\n additionalWidgetActionTypes: ['placeMapItem']\n };\n}", + "settingsForm": [], + "dataKeySettingsForm": [], + "latestDataKeySettingsForm": [], + "settingsDirective": "tb-map-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-map-basic-config", + "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"markers\":[],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[],\"trips\":[{\"dsType\":\"function\",\"dsLabel\":\"First route\",\"dsDeviceId\":null,\"dsEntityAliasId\":null,\"dsFilterId\":null,\"additionalDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.17490048149347315,\"funcBody\":\"var value = prevValue;\\nif (!value) {\\n value = 45;\\n}\\nif (time % 500 < 500) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH\",\"offsetX\":0,\"offsetY\":-1,\"patternFunction\":null,\"tagActions\":null},\"click\":{\"type\":\"doNothing\"},\"groups\":null,\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"image\",\"markerShape\":{\"shape\":\"tripMarkerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"icon\":\"arrow_forward\",\"size\":48,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"function\",\"image\":\"/assets/markers/tripShape1.svg\",\"imageSize\":34,\"imageFunction\":\"var speed = data.Speed;\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"images\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\"]},\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"rotateMarker\":true,\"offsetAngle\":0,\"showPath\":true,\"pathStrokeWeight\":4,\"pathStrokeColor\":{\"type\":\"function\",\"color\":\"#307FE5\",\"colorFunction\":\"var speed = data.Speed;\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).setAlpha(0.65).toRgbString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).setAlpha(0.65).toRgbString();\\n }\\n}\"},\"usePathDecorator\":false,\"pathDecoratorSymbol\":\"arrowHead\",\"pathDecoratorSymbolSize\":10,\"pathDecoratorSymbolColor\":\"#307FE5\",\"pathDecoratorOffset\":20,\"pathEndDecoratorOffset\":20,\"pathDecoratorRepeat\":20,\"showPoints\":false,\"pointSize\":10,\"pointColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"pointTooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-1}}],\"tripTimeline\":{\"showTimelineControl\":false}},\"title\":\"Route Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"assistant_navigation\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"titleFont\":{\"size\":null,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":null},\"titleColor\":null,\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\"}" + }, + "resources": [ + { + "link": "/api/images/system/map_marker_image_0.png", + "title": "Map marker image 0", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "map_marker_image_0.png", + "publicResourceKey": "LPbcriZ2v053mkWb33T5JdK7Agkt1jGg", + "mediaType": "image/png", + "data": "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", + "public": true + }, + { + "link": "/api/images/system/map_marker_image_1.png", + "title": "Map marker image 1", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "map_marker_image_1.png", + "publicResourceKey": "TwKYnwJfaCIgDbJsetgcj3q7AYK4HUSA", + "mediaType": "image/png", + "data": "iVBORw0KGgoAAAANSUhEUgAAAFAAAABdCAYAAAAyj+FzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAH3gAAB94BHQKrYQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic3bx5uF1Flff/WVV77zPeIQMJIYQxYRRBpBGcQFEEbVQQUXB6xW5tWx9+Cm07IYIitiJog2P7qu3UCN22aDs0KIIyg0CYyUhCyHiTmzucce+qtd4/zrkhQIIogz6/9Tz1nHP22buG715D1VpVS/gLktnZjg3P2wGz3RC/N9hBCHtjMgOxGjDZv3UAkyZim4EHQO4i2j3UkxXUb9kkcrb+pcYgz3aDNvLjOah7BSZvRnwLX/8D2axILM0Dtx9ODgGGt/P4GNgtqD6A764i3+iJ44dC9CD/jaRXyqzXrHs2x/OsAGhrL9sB8W/B7ARKwz/H7TAE2btAZj89DbAW6X4HHRknnzgW7KdU5fsyeMKmp6X+J6BnDEAzhLWXHYz4c3GlG8l2HgL3frDsmWqzR5KDfpnOykkIL8D4OHPecIcI9oy09kxUaut//CKCfp7qtMuwwb8DnrOd5nOcXIfJCryAOgEpASUwh5HiBMwKEAW6YF1chIghthtqL97uSxG5C8a/TXvsBLx9VGa/8Yane6xPK4C2/kd7EuWblIZ+itU+BMx93E1OFmLchqQpTmaDA0kAlyDSwSwizkAFfN84RAfOQASLCVACDVgAESOGDZjmSDwEtYO20bVVaPMCionjiPoe2eXNy56uMT8tAJp9I2X10GfxyQTJ4GtADn3UDd6NYv5niKtgyQxcYjinkCSYi3gHikeSLhDwXjHzTNlWB4hEYnRAgoUSmIIqxAQsYEFQA/JRNHZw+lqiTn90R/VGYuNKQl5j/cTH5JD3FE917E8ZQFv23b0oJf9GNnQd8PFH1y7rkORKLJ2BTxJcAqQOSQyXGEiC+IB4wZxHXMABeIeZQ3q/MBQRhdiDNGqCaMSiYTFBKIiF64FYKBbAigKLD6PFsYjt+uhOcwHdiUMRe5fMe8uSpzL+pwSgrfje+/CyK1n1dcBeW/7wMoZll4Kfhy95SARXMsSDSxxkIInifIK4gHpH6kAlggjOOwTBJEFV8FIQDcQCGIh6ighOFdMELQKYYF1Bg0KgB2ZuxG4kFqvw+cmoDG7V/cXk7Z9iulx2efvX/1wM/iwA7bLLPIe1v4m4RSTJuWDJI/+675OUB3ClCmSC9yA1cKn1dF3mSFLDRJEsAYk9wJxSTEzQGW3TXlEQC6G7sde/0gzDZ0Zlt5Ty9Arp4GBfnBWCgxghOkIhkBsxGK4QYgs0gHZAQxPtNLH41q2GH4jhNFRfwrzK20ROis84gLbkohLp4C/IuAXso1v+UFmLlK4gLc/Bl4AMfEWQDHzZIAFJhKQMLgWzhMayjTz0kyYbrt2T2Nq3a5WFE3HWqgYzNxeU81ao5ADVpJ2ldLI6G6YN+o3zStI+CF+9n1kvWcYub6hR330mIgEtIHSAYFgOVkDREegYmoO2QfOHCJ3X4thqDirn0rUX0Kr9rex/Uv6MAWhLLiqRDPwPafdBxN79SC3yS1y9S5JVoQpSEnylB5wrgSvTAzCt0Vqzmfs/32By8cs2xZ2vXGJHjo/KnlVz1aEsLZsXKVQkpIlXTHo6T8wFjU5iTELULFpEQmN8Gsub87lq2gy/5pUM7ftb9jtjgNJO07DQQHPBerMeYqsnztaGmBvabhNbVYivemRwfJWitADktbL7OztPO4C25KISvnolWWMN8OZHasj+L5Luih9QfAWSmvVEtwquAi4DyWay6aYHuO/8A1phYOkd9sbVTbdgVlYqJVk5wycpWZaRJY7E+2i44L2Y0Zv8CkgMCMQ0xOCKGAndnCJG8m5ueacba7pkw/Pksp2rvrkH+3/kXqY9fx+cbiA0HbEDdIzYBmvRE+12l7y1GRffsRUj/JBubR6xdbQsOK37tAFol13mOXjzzymNrQL+/pGnS1/FZXvg64IfAKkqSTnBVRRXEaQ8g7HFK7jnU/NHde7tC5N3RpKB4WqpKuVq2cpZSpJlkvrMSqVEvE8ty9JClSJJJIr44BwWY0xjNC9iWZ4HH2OUdrsjzpm1Wi3X6RTW7XZpdNviwsToQfodP92tPpj9z1nG8Pxd0M4mYtugEygaGdpWQgO05aCxFI3/+Mhg5WsUQ3vx0Npj5GVnh6cHwCVfv4x0ZAViH9py0WcXIpUDcPVIOiBIFZIauKrgKg6z2Sw8c0m72XK3uA+MuMq06dVqzSqlTGq1uqVp6iuVsqRpGtIsM++ceO8NEQQi0AGmuKAElAEfYxTAQowUeZBu0U3zTkc7nVzzvGOtVpdGqyHa3rTpUPvSjpVyreDgc/dGZB3aVrRlFE3QlmBtoxhXtPUQlr/nEVTceRQzd5c9/+GUpwygLf7Ke0jHykjrS1suavpV0vqeuDqkdUEGlKTsSQYUqcxmcvky7v38c+6yt/xqvHTwvEp90OqVTKrVitVqNcrVasiyTBLnUhEBGAXW9ssqYD2QA1MT3bQP4g7APGBOv0w3M4kxxm5RxG67nUxOTkq7ndPO29YYb9pQ55a1z3U/Opb9P3wXA7vtgbbXow1HbBk6aYSGoA2hmFgK+ggnxoEPYENR5r/3y382gHb/xQeQFm/Gr/0ImOuD9zXS6p4kg4ofBKkJyYD1gCzty9IfXlVsumuXm7NPrC6Xh6sDA1XJyhWmDQ1ampa0XM689z4FNgP3A0uBdr8vSk/veXpceFj/9y39/6ccAq7/vQLsCewHDMUYQ7fbtWaz6VqtXJutCWt3OtJubBo7rPjMXsmMA5cz/60vJ3TuJzQEm1TiREJsRsKEElqrcEWfE0UJcz4P2Q9kwfvv/ZMBtD98I6XW+QXZkj0Q9uzdLZcgA8OkdYdMF5KakgwK1AWfPof7Lr52vJk27qufVq0PDKTltOoGBqo2OFi3crmszrkB4CHgAXpc5vpgSf/7MFAD9gVe0AcHYAlwK3Af0AQm+gDrVmV2H8i5ZtZpt9s2MdGwRqNJq9WUVqsZ9m1f1BqqxAr7v++laH43sWHEhhAaRhjzMBkJExNgJ/XhWUK+YDVx9FWy/9nbnN4k27oIQK39FUqLFmLhlX1beB+U67jMIRlIGiF1kApen8PCzy0cYZ91Dw2/dadp9ZqrVWtFrVZLa7UyWZaVRGQc+D2wiR73zARKqhqccwqIquKcmzSz14rImUC135uWmZ0nIrf0fw+oqjjnhJ54d4AGPU6dJSL7VqvVoSRJOlmWSKmU+KxSssVjH6zOa/5g8453nn8bzz3tEHzpDrTrECeQKnjBJSmhdQ+izwEWkNx/Oez9ReB924Jpmxxo91+wl0rjjc4vO7d3wQKu8kP84CyyIUHqgh82/ICQVA7k3m9evybstXb98FtnD9Xrrj5Qp5RlWq/Xnfd+AFgILANSVfVAHZjVf4E1EZlhZs8VEWdm3xCRD/QBfqSjIhtU9SIR+XszUxG5T0Q2quokPV25EZjsv4wA7AI838wajUaDZrujk+NjyWSzyZzJ/3pwTnrffPb5u79B2wsJEylxUonjkTAhxPENaOcURBIADfM/6bT+H7L/6Uv/KIBmiN1z/tWS3TqESM81JMlXSQb3QIYgGwQ/CH5QoDaflT+7cXNjYGLl4Lt3qA/W3WC9rtVqxdfr9QxIVPVeYKNzzoCOqjpVLSdJUg0huCRJusC4qp5FT7wPAlYCP1XVkf5zs4DXAbsCtwN7OOfO7nNiRk+EWzHGwntvzrlSn0N3APYzszjZbE50Wu1sfGKcZqNtu05+ffO0aitlj+NeSphcTGyATkCcgDhmhInlWJziutst/E1D9v2nIx/rmH08gPd+7ijSpc/BRnpW1+RWXGUd6WCGH+5xXjJo+MFhRpcu6q6+a2jJzHO65UpdBgeqbmBgwMrlciYiqqqLnXOFqk7vc4WPMRpQTZJkB1U9FEhF5Fwzu9DMbnfO/VBEXqyqrwGmHKW5c+4XZnatqr5dRA7y3p+uqh8DVFVvE5H1QAtwIhLo6dSNzrkKsEeMMQ0h5GNjY9rpFH5iYizutemTrjTvuS0G91iANcYJE544HoljQpyMxOZcsAN7SM3+AHHPhbLvP/9ua7zc47jP7OPEhz+KdcA64MIdSJKBBxFFpPcGigmx1Tc/f9G0syZrtZofHKi5en3AyuVy0gfveufc5qIo6mZWAWaZ2Rzvfdl7/3AI4SozGzCztqqeG0L4ELCPql4QYzzezFRVH1DVB8xMY4zHq+qFwHwzOyOEcF6Msamqg865K2KMK0XE05vaTDOzUgihqqojqnqD9z5kWZYMDAwmpVJGvT4gS6af1baHbzyY0FQwD67nzBVngMNz4xYcbOXHLMRzzEy2CyB3nfdicQ/8FOvMRrsQu78A2xWJYCaY6zGwxf1Y+quwbNrp19QHB6u1Wo16vUalUk5EZGdVXaiqQ3meO+/9BhFZrqqr6OnAE83sH/ttV4HvAB3n3Plmttg5d6Zz7sNm9i0zu69fvqWqH3bOnWlmy83sAhFpiMh/0JtgO+/9e1X1TTHGATNbb2YPOefGVbUEDKvqQmBOqZS5wcE6lXpdqgODlWXTP/Iblv48RePeOOt5wrUvpZrvDt3/7WMxS9zi/+aezx2+NWSPssImeqbY4j23XJDSeszvBAJODFHBWcrIA1c1sr1zN7DHjqVKhUqlQqlUcsA8M3tQVV8MNEVkjqoGEVkLPKCqVwEvA3Iz+yDwG+BMEfmtmV0hIifHGM81e3T8Z+p3jBHgTu/9h4qiOM4591ERuTKEcIb0JCMTkSuAGWa2v3Nujpk5MxsFEhFZ7ZybWyqVHq6pOrQaJuPOcxvNve6sjy2+l+Gdd8EkIAJ9LFHWYFMLokXvN9vzQWCLE2ILgPbA53bS7qrfi3WP7l+6GvxcJPRG4Kwn5LE1l/FVu63e4fM31CsV6pWKZVlm3vshVb0S2BBjTAG890PAAar6ahE5EjjPzOohhLO89xeaWQ6caWZnAS/vA3Wlqv5eRFqPAbEmIkeIyCtCCAeKSEdEPqaqfw/MVdXTReRCEXHAe4B6COEqEbk7y7LNIQRCCEWaprO894eWsmyTxuhDCG7NzH8s77X+jBcyML2AsBIxQ0xwKgTdCcl/h9kRwAJh/fds4blz5aAzVz+aA7vtE527fi7WXz87/wCwO+ZAVIgFuODZuOqmkfrrJirV+k5ZkkiWZWRZtqOZbYgxHglUnXOJiKwMIdyRZdnVIYTXmdl6EXm7qq52zn1BRD6gqv8CnAtc65z7sarua2avF5GjeQyJCH3R/IaZLXLOvSHGeB498f+AmV2oqqu89/8nxjgmIrPSNL0qhDAjhPBiM9sdiCGEpvd+JE3TOTHGDZVyOSpUNtVe/fMZI9cNMn32LliMmBnmIs4ghnshHtHrybU7Iq9/A3DRFgDNEL1l/U6uUhzRN8wbUD+vF8PAepecoHEandburbkvv7mSpZTLFSuXywnQCSFc1xezIefcc83sOOCQoig+18fgy2Z2gZl92cyON7MvAb9wzl2vqqfHGKfW2rmqLnTOPaiqK1XVJUkyD9iD3grlPX0wNwIfU9WXmNmXzGyVmf1cRN7rnDtdVS+MMTpVfTcwA/gVsFBExsyscM4dVy6XnRmxiGqTw6/cYcbqKw9nmo6CbEREURFIDPW7IGETyAwIL9PO+oZZ7516gLOP/budTZfuIXp3FT89Q9yvcaUhpNTzKLuy4TJlcmLZWPqi+2P9wIFqtUq5XDLv/d5mdqeqntLXQaNm9gBwrIisMbN5fZ10sHPuJ8C7gbu9919X1XeZ2dH0ph8Xq+p/A8tFZJr1RObFIvICM9vVzB4Efq6ql5rZXSLyfOBvRWRXVf2EiMxxzh3vnPt2jPEdgJjZcF+kZznnvuGcK6vqwar6ehG5XUSeZ6YbBcOMxIrx20v5is2UXA0NPYesRgfqIf4Bs5l0H/yDWHUZq45eec63/jDpALTg1c7dNouox9Nafh1KgKRnrhWPakKINZrtlzSnv7ZWq5UlTb1kWVYG1hZFsczMFpnZ61X1VBFJrfeKvm1mL+nruLkhhKPN7MNmdngI4Twzu8Y59wHgWjP7sIhcaGbvV9WXqGpZVVv9UlXVl6rqaX0996GpZ/v68jwze4GZfTiE8BpgjpmdZWYvCSF818wwszSE8E4zO9HMVqnqMhFZm2VZ4n0qWVay1ow31Gg0DydaBTMH3qHiEGeoy2kuv4YY3wy3zUL1WOjLq133ritxP3w+2HSgQTrwc8p7DeBqDl/ueVxi1o7Nimza5VNFqVqlkmWSpulBqvrrT3/603bTTTed6pwrPVZ3/TWRqnZf/epX3/ye97znZzHGwVKpNEtEXhVCuL3b7dLpdGzmio9XZKBb4PIKoQXSNEKrS3dJh2LytfRiFqMWTvmDe+m3X5WYne30+kUbnFkvCC38L0U+HdZ2KO9uSMxw0Wh3xyanvXHCe79TImLee8ys1O125998881HOec46aSTfrd06dIlCxYsWHDvvfcu2n///fdutVqNPM8LYNqvf/3rI733d5xwwgnjU4MaGRnZuHHjxk2zZ8/eYfr06dMvvfTS/VV12pve9KbrZWrSvhWJCHfeeWdz8eLFr5kxY8YtL3/5y1t9cOyBBx5YMn/+/N2yLMtGR0c3XXHFFUclSTI8MDBw1THHHON/+ctf2hVXXPGyer1+/ymnnHJkURSrsiwrpWlqeZ6Lc07Gh49bOty4ZJB6UcXFnh+gvcbQfAbGlcDrwaabdcfMznYJt66Yha2+A3hLr4tuBGQIbWe0V7Yp7xlJY5mQPjfUD16YJok453DOzVTVmycnJ1/kvXdFUXROOumkF8cYJ0XkkBNPPPEgM/secBpwu4hc8etf//rI4447bvyEE07YDNwAvA04cqt535L/+Z//WTk5OZmedNJJP6PnsgJIY4xVYHfgqGOPPbZ26qmn8sY3vjE/4ogjqsB/xhhf6r1/N4D3/lxVPfG6667b0O12hy+++OJ9yuXywAknnHD9qaeeuujHP/7x604++eQlIvJKVf2ZiMzy3m/IshJh2iE1Ri/dn8I2o/lGOg8NQLeKCuDWYr04l0tX38rNuoOjU+zpdHHSW2EAKiWE0PO2FTW6K0qE8fWmyXBWqdfTNLM0TUVV91fVDVtzhqp+xjl3HHC+mVXN7FQz+5CZHaSqHwVYt27dGjN7jZmdr6r7hBC+r6qnq+q5ZrYsy7LEOSchhAtCCF/ql/PN7BwzO8HM/tBut7/Sr2uVqh5iZuc7514FnA98N8Z4ppnNz/M8AmRZNg04X0SOPeWUU64WkTkrVqy4sq8bVwP7eO/FewdpdQjxMwgTG2g9tAMxr6BqGBGTdAtO4QFH7ua7qLoXrjHvEQBtGmopph6LghZCc023M/yCr5mZgk2tCBpm1th61aCqJ5vZ9WZ2DvAZoGlm58cY/11V/y/ATjvtNBe4S1VPB74FvBG4EDhTVY9xzomqmpn9j6r+SFV/FGP8jZnd1Tcmx1er1dMAhoaGZqvqN/ov4D5V/ZCqvkNVvxljvDTLMm9mOOcuNLNPm9mNr3zlK1+oqvHKK698oZmpmY2LyIRzzkTEvEinW33ORbTXdqEbUQUjwUiINn0LTtaYi9meiWg8ACkO6GPQQaRCtBwfCyIlEEOHO6jf1bmkSJIkAENmtjaE8KrR0dHvAh/pc+FewMMxxt+IyDkicq2q/peIvK//tlm+fPnyGOMxwIV9Sz1mZpc75xYCRbPZfDO9KcjuZlYDMLMNZrZSRH6rqqsnJiYAzldVAd7br2d1COHMNE33VtW/FxFijIv6n2c6537rnFNVPRxYd9ddd80xs/NDCMc659aratk5twFI1dUXEJM2lnchRlwUMI+TKpCjZEjYT0PsJBJ1fxwHA2ByD6KG8wENAWcZBYKUM4b2ayeJK8eolqZJbma5qh5YrVbX98FLb7jhhi8fcsghpyZJsjbG+Enn3Dn9acyXVRURef+ee+65B70A0b/EGDc5514A/G0I4c0A1Wr1rna7japuvadwd9VHtkEPDAxM9EV4tYicEULY0Xv/9yJyboyRGOMX77vvPpfn+SnOObrd7qdKpdJbzGxP4JMzZsw4ft26dTNijEc65w4ErnbOdfO8GEqSxJLpB25ktFzDaUSDoNERDEQdjvsxDgQ7VFRDglgZo95TZPogJlUsRpCIRkEAqQ672ryuiQQRSVV1DzObEJH/k2XZJ/uK21988cXv74vyHqr6MTMbT9O0VSqVjqHn9OTyyy/fddWqVfckSXJ4lmU2PDxMjPFmVV3TbDZHNm3a9Gag+pOf/OSHSZL4iYmJep7n3YmJCdm8eXPsdDph06ZNOwMvufLKK1/5+9///tA8z2tFUQB0RCTz3n8QwLmesynP83enaTrovf+AmV04f/7862666aY9RWSWiLwjxniqiOyRJH5pURQastkxteowRW6g1tsVZgZiRHsIOBBjEI2VBNXe3kUAtSaQoma4QsEJFoQ0rbq0hjmX9yTKhoHRWq32yRUrVnyn2Wx+qlqt0g9R4pyT/pywBAx1u48E+WOMu91yyy27sR1Kkt7y/PLLL3/L9u6ZIhGZ3e12Z2/93LZo2bJl//2c5zxnLvAl4L4sy+7tdDoHxxg/MzAw8KlOp7PUzIaT3to+NwYMyUpYXiBqRAOHEdWDTiJTLkHFoYVsUYxCCwgQlRiFEAQtDEmHVBLnvS9UNdCLV7SA3efOnfuqkZER1qxZQ6PR2OJ6+muiJEkOFZHXmdmFIvK5I488UkZGRkoDAwOvA3YVkWY/LlOYmSVpDciGevtrQi/Or7FvPGg/YnCDc72NnvRKdEqkidDEaKK2DmMN4hLvk2az2eyISINe7GIDgPd+N4CiKNi4cSMPP/wwo6Oj9EXqr4IWLVp0D/B3RVG8Kc/z7+y9995Lgc7atWt9/5YNwKoYY8PMmnnezXGuRKBFpAk0UBqYTqDoFrxMSTDskTCJ1VCGCQgmZg4vZoZnMoa8UqkMls0siTHuY2YPbauzMUYmJiaYmJggyzJqtRq1Wu0JReyZoKIoaLVaNJtNfvCDH5RGRkZeOmfOnN/ssMMOyxcsWPBGEVl37bXXPnDyySc/L8Y4R0T2EZGFIhLE2ySiE5jMwBCi9QL+Ig7byotvWALxkXi/yRCQRMU5jFhYCXGaxDgeuk2DctL39TXpBcCfkPI8J89zNm/eTJqmU55rSqXS0w5oCIH+epZOp/MoCdiwYcPJ3//+9x/3zNe//vWFJ598MmY2ADRU1RVF4V0+TmahEYxpBBVBAog6ByJWe4ThIglqG3CyCmwepkNmKIZEwUV1DlTF8gZx3GBGGmN0fZ3x+B34j9Bm59xD9BjdqequRVEMbz2oJElI05QkSciyjCRJcM6RJAkissWCAqgqU/NIVSWEQFEU9L3M5Hk+NbnfFq1yzo1OratjjPvTC8azadOmV/bvmWNmDeecxBgTjZMSi9CIQYcR5zykeFUiINQQAZEHzeLaxDTeLUYKzEPcAWLcY6ZmipppjMFI8tEGzeXW9TtnWZYCrFfVA+jtBngUOefuEhEdHh5ePnPmTN2wYYMbHx8HWNV/BmDL4J9BUu/9LWaWzJ49e/nAwIB7+OGHa51OR60XtdsdyNevX39zrVY72Dl3dwghiTGaH1+UabGpFQszEg3OgSgizjnE9u8bkdscdqczC/ejyeL+Mm6WYQ3MopmoBefMJOk21laTxuKKmbrY83aPmlkSQrgeoF6v/wxARO4WkearXvWqHdI0PcE5d2KpVDrhqKOOmiEiLefcfc8kYluT9/7mUqk0/qIXvWh/7/1JaZqeOG3atGOPPfbYrohMAmumTZt23ZIlSxaaWaqqm83MVFVKnQdrne66ajRJXHSiEcQkGjaO0lvOkSxB7QHnE1lCHFyLWc+3H2l5xKtapqYuRpNue6ySFSsXhKCxv05tAHS73dWPEZ3k2GOPnT4yMjL3C1/4AqVSic9+9rOMjIzsfNRRRw3S24X1bJysXGlmpec973kvnT59euUTn/gE8+bN4/TTT+fee+89/MADD1zhnFvfaDRmttvtrK8eGj3VECzLVy0IjfGKRiOqOtRSU0vFaE3hRJi2nsKWJ2xuL9fa7Nc7t7HXtLNGjOwgaoWpYEoSY3emaPeAPG//CkoVEcmdc4QQ5l166aVXNxqNI5MkuQ6we++99yWHHXYYt912G7vvvjs33XQTCxYs4NZbb91XRK53zt1mZjOfcPhPkURk0+zZsx9cuXLlwcPDw6xcuZKhoSHuuusuDj30UFqt1jELFy5cVhTFXO990teteVEUxJi3he4+MXYjgDnUjAg4orWm9nJo2GGmm2bLEnnrzRP6k8MPe8QS41EwkwTFFIsaRIr25qt8vmY8uHlV770lSbIaOOKFL3zh/y5evPhKgOc+97nV733vexx++OEsXbqUiYkJ5s2bx2GHHcbvfvc7jj322MV33nnnI6HUp2nSLfL4PVJHH3307BtuuIHjjz+eK664gvHxcV7xilcwOTnJ9ddfnx1wwAE3NZtNt+uuu74GeKgoCosxGq1VY3lr9CoN7CjO1KI4ExEUxZNN4SSUXiwvu+YT/bCbbRLldoSDMTnSO1seogU1cTEXb2YyvvaOscHB31dG0zdrjFG893cDx+yyyy4bP/OZz5yqqqgqt9xyCz/72c94xzveQQiBgw46iO9973ssWLCA973vfe+cum/Kom5tXacAfeznFEBbfzrnEJFHfe87erdY8F/96lf86Ec/4rTTTuOaa66h0WhwzTXXcPDBB/O2t73tnd1u99uqOlNVfwVYu92VOe3rapMjCyei2lwfxXDOnMTgRQSTl4OB8QczVkJvcyPnvGnuJDI+ihWvAKaJ2kIzqmoiqhBUpNMZr8/ace8j1snzljrnvHMud87tF2Ospmk6bGZOVVmwYAGXXHIJeZ6zdOlS1q9fzx133MEZZ5xBrVZ7FHjbA3Lr348t2+Pex3KhiDBnzhyuuOIKGo0Gq1atYs2aNaxfv54PV+O33AAAECZJREFUfehDJEkSut1uDZihqre3Wm2X5+3unPy3x2548Kpu6sV7QROPpIL3XkYxDu8teev/Kjrjl+dctnpF71W1uFnjzvVHnIV+rSBCRDDBDDWl3GmM/LrUWTbS6XQkxqiqehuwT7fb/Y+pAe68885cfPHFpGmKc45SqcQFF1zAnDlzHgfSE5W+W2pLeTLPPBbsww47jLPOOot2u02e5+y1115cdNFFZFlGv8/7qOqt3W5Xut3cyvmDGzutkSs0UlLFMHEoiIlhbJjCR8PcIWLj1p4o90kvPegaSe/bC2wOcLsZY50CaRdYNzfJI6gbWL3LIe+euyh9+4ZSqSKDg3UnIieq6n3lcnm3vsf4cQP+YwA8lgufSISfSGwfW7z32/s93ul0NojIfFX9z0ajRbPZtP35wZyVt/zbKomTcyspVkqFSoaWUkSEGcCBmKy2uN9id9LCl8NWu7NU9UKsdHEf5YMFNgjgpLcBFpCiPbkTYbKgvXp9nnes3W6rmd0I7Nduty/dlsg9FpjtgbctDvxjL2B79W2v7anS6XQuN7MFwPV5nlur1RLXfXi9FRPtojM5B3D9bXwighNYh3Fgb65culgtfmEKty0A+qHWFZrvNG/LPEddI3WGiLkE8JiKt/TB2340viD9/X7tdtva7bZ18/xhM5swszfGGK/bnu7a3oC2J7pP9P2JxPaJ9KeZEUL4dYzxFBEZy/N89cTEpO902rpP6boDlt98adNjPsE0wSQRIxEDle4ULhp3nu/Xt696HIDy6qVd1Asql/ZMdXy7F5ssiUURvDcRr1i3NT5DWxsmKvnitZONhms1m1oUxdVmVu92u0NmNj4lftsb2JPVgU9WBTwWzG2B2O/ThjzPZ5tZmuf5NZONhmu22jpkS9fE9sho0R4fRhDvRRMxSxLDY2OIvq0nmXIJKm05bWn3cQACOHFna5hzX1+MM8ytS5yId+AEBMErsui671b3Gbjn2G6n02w0GrRarY6ZXQUc0Ol0rrZetOsJOWFbYG5Ld/4xffpk2zGzTp7nd5rZc4HfdDqdvNloWrc12dqrfs+rF1373YokvU2ECeC9+FTEsGQjPbcfxLlLXJJ/+lGYPcr0n3LPerS8D/DbHhfaWxOxsVKCeYdkHhMvRAvDS2/58Y0H1X9fGZ9o0Gw26Xa7m+htAH99nuc/2NoIbP35ZET6sRZ4ayCfTB3barvb7f48xvhK4A+NRmt0YmLSJpsNe/70m+tLbrrsRrUwLe0fB08F6Z2rtzGI7+jpPq6MoTRfTlo6sl0AATq+/U+az76h/1AVlZg6o5xg3uNSj6WefHz9kj1orrCdSsvWTUxMyOjoZtrt9lLgTjN7ewjhB8CfxYl/zpTlCUpeFMUlZnYicHur1Vo+OTnpGo0GOyVLVkvzIR1bt3yPzBNTJ5Y5LHGQelPE5T1JBHTObb7onvFYvB4HYO3kVWuIpRSTb/aXLSd6WJQ6c5nHSg4p9xqS+67+9tCe9QcPk+66NZOTEzI6Okqz1VpsZjer6luLovjZ1jrxyXLlE80Dnwy3TX0C62KMv1PVk4GbGo32srGxMRkd22x0143sOfTQYXf+5t/qpQQyJ5qmWOKQLDHxxiLM3tTXfV/TIknlnSselxXpcQACuEp+Tsx3HERp9Pz/7kWZp1tySEkg8bjUiROscsvln1v7kl2Wvrzb2LhhbGxSxsfGtNlsPqSqV5jZ60IIK1X1uq3r/1PB/BNBm6Lr8zzfqKqvUNX/nZxsPjw2ttmNj08Smps2vnju4iNv+cnn1qVi1VRESg5KhpQTXObIUXdEP/bRiPnsHZzaJ7aJ1bYuykkPt73xbbR+Zu8N2P6ibqycQrmMlZ1ZKYVyIi6VOHzzjz9/99ELlh8dWhvXjI6OsmnTJhsbG5sIIfwXsIOqHhRj/Da9I1mPom0BsD0An+iZrWg8hPDdEMLzgel5nv98fHx8cnRs1MbHN2unuWHkmL0ffMXNl3/+LtEwVErFlRJcmpiVykgpRcXcJrB9eqJbP9OL/5q8c8U2T7E/4WnN8J2df+iTtQKc3If7KyGyf7ONNbqUOoXQbFtoBaJKZc3hJ33igF89sOPVOcNzhoYGQpqWy/V6RUul8qCZHqGqS4CFMcZTVHvO2a0t67asLvC41cTUiuIxn+qc+w/ghc65nUTkd3mej7dardhqtbLJZlMzHXv41c/Z8MqbL/nMIrHG9EqCr1TEVVOjVibUM0g89wFTx14viWHHkLxz9du3h5Hf3h8A57x++i9jrLzV+ZZgzACe6+B3HqYhaIw4J+JMkbwIQyvuvHryZS9//i6tXB9YuSFMC0Xs5W2KoeVElgCY2dFmttjMfk7v8M1g//qWds1sm56XKRAfc20N8J/0gvgvBO4piuKOZrMZx8YmmZycYPPmUXYb3LT+pXtu+ptrf/iJEbHujEpKUstEaplRKxNrKaTCCoR3AB6RJTGfPekle/s5Px3bbuzhjx64bn9tx92yrPigS8b+AcgQNqP8ohuZ28zxrQ7SynHNDtIulE5wxaEn/PP6WJnDT24faJfSSlKtlsvV6oCVKxmlXgCpDOypqrur6m/NbF2Mcb6qvlh7Z+keJbZbr3+dc8E5d4OILPXe7ygiR3rvV5jZgzHGVp7ntNtt2p3cmq2m73ZbjeOfN1nz3TXx1h+fv2PJaVYtOStnSK2C1lJirUxeSlmPcQwwo7e9b+gifPlf5e1rthm+fdIAAoRvzXytSHcv51rn959aCdzcDcxsdPCtHNfuIO0ca+UaWwEGZu+16pDj3vc3Ny1Nfr1oXW1WuVz2pSyxUqki5XK2dSQuU9VhM9vFzKap6gozW2NmDVUt+qcvMxGpi8iOwK5JkkyIyEOqOm5mRVC1kOd0Ojndbod2u0On0427zypWHLlv53X3XfWD69ctv3V+NcPXMmeVBKplQqWEq5eQUsZalBfSOw2PhuqHTct3J+8e+dUfw+bJZ+345vR3kbTrkE8dR1iF8LsisGujQ2xFrNUmtrvUmwXdboHP1RUvPOmfNyYDO02//BbuGu9k8yqlMlk5w7uEUlYy55A0TcQliTkRw0xxHouxd2Cot3PT+kcbxEzEQIIG0SISo1qet6UoAt08aLvb1uGyrj/hMD0gTK7ZeP2PPj+zlGhazoiVBK1lZJUSRb2CVlMk9ayldzJ+DwBi9gG01pV3b3xS2Yz+pLwx8RvDn3RZdwzrgyhsxvhJMPZq52izLVmzwFq5SbdLaEeNndyZlOutw97wwU5anz39ZzeHO9dPMCvxaZp4j08z0iQlSz1Kz/XhxHdxRFR7ESvnPIqPUTPV6LwX63aDKLEXEy6ChVDkc4Z15G8Pyw4qJtZvvOm/v1ixTqNaTpRSySXVFF/NROslpJwY1RKWeBZjnIAw1BtP9gGKclXevfmzTxaTPzlzUfzawBnOhwSXn9dPDpZj9i1DDmp2oR0Iza5qJzhrFfhuR5NOQdENTpPKwNiBx5w6MXOXfY9YtiZcef097e7IhE2XLMlSvDjn8IngncfMgllvQ7JzIoqkGsRUC4kWpCiCodadPuw2vXi/cmXBTslRIyvvvfauX/37YNGdnFbNVDJHUi67WMmIlVSpJs5XMqRWwouzuzB5J5BhqMbkDGflivzD+JMG788CECB8tfZO8cVOzsd/4pF8pz9CGOjm1DoB1+yStgq1dk6RF851g/puTtE10iLSGNpx95EDjjrFTZu928taOXcvebizfMWabvfhTV3f6UI3qqC9o6WC0ywVyiXYeUYp7rZTWpq/c3WPWsYBm9c9ePU9v7lEx9Y/uEOaUMs8oZSQVTJXZF6tZ22dK2eESkYsJf2NU0I/LwKjhPSLEb8i+YfmD/5ULP7s7G321cphEXuv98XeiL2gf3kxwu8N269VENtdF9sFaadQ6/aSDaXdSMgLfKFoEUhiJCcpjw7OnDu+497P1xk77pqV6tNr5drAoE/THUSchLy7odOcnOg2Rpub1q3M1y26zU1sXD1E6ExPElLv0MzjspRQ8iSlhKKSkpRTJ6WUolZSygmZiNyH8VKmMs2Z3BhDtlyRf83e17r1z8HhKaW/m/jywIy65N+XJP4B9JGljsiPMTSiO3cCRTt32im01A3keeF8oap57nxhWsRA0lEwJQQlMcNCz502ldqk109BEwERJHEEcSQlB6knJN6lmdOYlpzLnMZKQpZlrltJ1ZVLpB63CiPF7PgtfTT3KYv+b0TKb5F/HN/852Lw1BMwXobX9dmZmJrL9K3Agv5fOSr/jmdGVJ2bF3Q76nxRqHZy52LUmCsuj06jqu8qgjmiEtTUmEqF0vumgDlx4h2Jd2qJgHcuJk592ROT1PlSopomzpW9xiwl896tQumAvZlH0gcs1sJd4oTAxnCenP3Udko8bTlU7SvMN/NfFW9XAJ9iKmVJL/vkDzEM0V0Ldb5bqObRuagaikBWmLMQCKAuGAFDowKO3gpASbwDBJcICeI08SSJU8kceZKSpGCl1EnqNWJuBYLD7C1bsmBCC+MTMcqrfIjvlQ+y/OkY99ObhNaQ+K8ch+OdPnG3KXwco7xVY/eqyY3iqRk6xyJZUFeEqC4oFOYExaKh9JzaPSMiGOLECw6HpKKWeMyLI/XqxVMIbq1Fmk7scIP9txphxymfiUGfD3zL/3/84ulMifzMpEH+Bmls8yaMU8y535rnNOnP8rdqeA3I1eKsbSolB4NRdFgMb0Y0wcR64mXSSzggggeCw40rTIizrqlUwF5msNOj+gArPVykhR6t8P27q1x2yHt42vcdP6OZzO1sXBjmKODDqvxeEjfN4ANP0JlRRG43GBdljN5OWDCrmWNYYAizgw2mP0EdXzJ0s1NeivDZZDNXP1U990T0rOXSty8wsyuchXMrTLjgmWhDjDNQ3a1kfEr+iY3PRBuPa/PZaGSKDKR9AZc47x6KulUuwqelbrnIqe5c+SdOFJ4+HffHaJse6WeKBKwyyVtDoQca/Eb7qbSfarHItVbovpUB3vxsggfPMoAAcjahW+KNVlhDlZVPGcDIWjFbF5U3yTNgJP7oeJ7tBqdo9F84wMOpivwjsmWS+6dSMLUvpsJ3Bz7CdpMkPpP0FwMQYPyznByUHXFy4Z9VgdrpzjE+7aN8+2nu2pOmvyiAAJvO44tmboNh5/1pT8qnPTpj+se3nRjx2aK/OIBmyKbP8FMzN6bY257MM+LkMjGtzQy89pmc4z0ZetaNyGNJBGtv4k2gczVy+5MwHHcRdVoROOkvDR78FQAIMO+LtIm8zYndr8bodqcrRkuwG73jXTudTeuP1/zM019chLemtWdzpCkvir2EZI8jBx9xCTfNOYvfbev/vwT9VXDgFM05m2sMgiinP477lNMd6F8TePBXxoHQW+6tOYsfFsrDpnyof/GiJGHmzp/mrc/2SuOP0bN7CvpJkICZ4+2rjF8G5RcmDDrHvjt7Xv3XBh78lYnwFMnZhOg5SRxF6hmhyUlyNs/o2dj/X9LKT7D/yo+y31+6H09E/w/wHJVcjfUH5AAAAABJRU5ErkJggg==", + "public": true + }, + { + "link": "/api/images/system/map_marker_image_2.png", + "title": "Map marker image 2", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "map_marker_image_2.png", + "publicResourceKey": "FazBQsEp1uSeIsT1XL31o2npLAx5s3zJ", + "mediaType": "image/png", + "data": "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", + "public": true + }, + { + "link": "/api/images/system/route_map_openstreet_system_widget_image.png", + "title": "\"Route Map - OpenStreet\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "route_map_openstreet_system_widget_image.png", + "publicResourceKey": "VTTAajdw2NRpCUbQLGVFCMBybEtJfwFK", + "mediaType": "image/png", + "data": "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", + "public": true + } + ], + "scada": false, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/route_map___google.json b/application/src/main/data/json/system/widget_types/route_map___google.json index ac7f511687..6eda23ef00 100644 --- a/application/src/main/data/json/system/widget_types/route_map___google.json +++ b/application/src/main/data/json/system/widget_types/route_map___google.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.route_map", "name": "Route Map - Google", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/route_map_google_system_widget_image.png", "description": "Visualize the entity trip on Google Maps. Allows to visualize location history. Use the Trip Animation widget for advanced features.", "descriptor": { diff --git a/application/src/main/data/json/system/widget_types/route_map___openstreet.json b/application/src/main/data/json/system/widget_types/route_map___openstreet.json index 9021fac8aa..92bb2516a9 100644 --- a/application/src/main/data/json/system/widget_types/route_map___openstreet.json +++ b/application/src/main/data/json/system/widget_types/route_map___openstreet.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.route_map_openstreetmap", "name": "Route Map - OpenStreet", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/route_map_openstreet_system_widget_image.png", "description": "Visualize the entity trip on OpenStreetMap. Allows to visualize location history. Use the Trip Animation widget for advanced features.", "descriptor": { diff --git a/application/src/main/data/json/system/widget_types/route_map___tencent.json b/application/src/main/data/json/system/widget_types/route_map___tencent.json index f2f01a5b57..3de304c8fc 100644 --- a/application/src/main/data/json/system/widget_types/route_map___tencent.json +++ b/application/src/main/data/json/system/widget_types/route_map___tencent.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.route_map_tencent_maps", "name": "Route Map - Tencent", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/route_map_tencent_system_widget_image.png", "description": "Visualize the entity trip on Tencent Maps. Allows to visualize location history. Use the Trip Animation widget for advanced features.", "descriptor": { diff --git a/application/src/main/data/json/system/widget_types/tencent_map.json b/application/src/main/data/json/system/widget_types/tencent_map.json index a3e480f836..ec41717ca4 100644 --- a/application/src/main/data/json/system/widget_types/tencent_map.json +++ b/application/src/main/data/json/system/widget_types/tencent_map.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.tencent_maps", "name": "Tencent Map", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/tencent_map_system_widget_image.png", "description": "Displays the location of the entities on Tencent maps. Requires the Tencent map key to work properly. Highly customizable via custom markers, marker tooltips, and widget actions.", "descriptor": { @@ -14,7 +14,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('tencent-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", "settingsSchema": "", "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", + "settingsDirective": "tb-map-widget-settings-legacy", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.24727730589425012,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.8437014651129422,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7558240907832925,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second Point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.19266205227372524,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7995830793603149,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.04902495467943502,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.44120841439482095,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"tencent-map\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"
${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details
\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"tb-image;/api/images/system/map_marker_image_0.png\",\"tb-image;/api/images/system/map_marker_image_1.png\",\"tb-image;/api/images/system/map_marker_image_2.png\",\"tb-image;/api/images/system/map_marker_image_3.png\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"Tencent Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" }, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/trip_animation.json b/application/src/main/data/json/system/widget_types/trip_animation.json index e4429dd0b5..52eb7b8503 100644 --- a/application/src/main/data/json/system/widget_types/trip_animation.json +++ b/application/src/main/data/json/system/widget_types/trip_animation.json @@ -1,7 +1,7 @@ { "fqn": "maps_v2.test", "name": "Trip Animation", - "deprecated": false, + "deprecated": true, "image": "tb-image;/api/images/system/trip_animation_system_widget_image.png", "description": "Displays the trip of the entity on the OpenStreetMap or other map providers. Allows to scroll and animate the movement of the entity. Highly customizable via custom markers, marker tooltips, and widget actions.", "descriptor": { diff --git a/application/src/main/data/json/system/widget_types/trip_map.json b/application/src/main/data/json/system/widget_types/trip_map.json new file mode 100644 index 0000000000..09eba54978 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/trip_map.json @@ -0,0 +1,47 @@ +{ + "fqn": "trip_map", + "name": "Trip Map", + "deprecated": false, + "image": "tb-image;/api/images/system/trip_animation_system_widget_image.png", + "description": "Displays the trip of the entity on the OpenStreetMap or other map providers. Allows to scroll and animate the movement of the entity. Highly customizable via custom markers, marker tooltips, and widget actions.", + "descriptor": { + "type": "timeseries", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.mapWidget.onInit();\n};\n\nself.typeParameters = function() {\n return {\n trip: true,\n hideDataTab: true,\n hideDataSettings: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n datasourcesOptional: true,\n additionalWidgetActionTypes: ['placeMapItem']\n };\n}", + "settingsForm": [], + "dataKeySettingsForm": [], + "latestDataKeySettingsForm": [], + "settingsDirective": "tb-map-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-map-basic-config", + "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"mapType\":\"geoMap\",\"markers\":[],\"polygons\":[],\"circles\":[],\"additionalDataSources\":[],\"trips\":[{\"dsType\":\"function\",\"dsLabel\":\"First point\",\"xKey\":{\"name\":\"f(x)\",\"label\":\"latitude\",\"type\":\"function\",\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : (value + 2) % gpsData.length)];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"yKey\":{\"name\":\"f(x)\",\"label\":\"longitude\",\"type\":\"function\",\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : (value + 2) % gpsData.length)];\",\"settings\":{},\"color\":\"#2196f3\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},\"markerType\":\"shape\",\"markerShape\":{\"shape\":\"tripMarkerShape1\",\"size\":34,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerIcon\":{\"icon\":\"arrow_forward\",\"size\":48,\"color\":{\"type\":\"constant\",\"color\":\"#307FE5\"}},\"markerImage\":{\"type\":\"image\",\"image\":\"/assets/markers/tripShape1.svg\",\"imageSize\":34},\"markerOffsetX\":0.5,\"markerOffsetY\":0.5,\"positionFunction\":\"return {x: origXPos, y: origYPos};\",\"markerClustering\":{\"enable\":false,\"zoomOnClick\":true,\"maxZoom\":null,\"maxClusterRadius\":80,\"zoomAnimation\":true,\"showCoverageOnHover\":true,\"spiderfyOnMaxZoom\":false,\"chunkedLoad\":false,\"lazyLoad\":true,\"useClusterMarkerColorFunction\":false,\"clusterMarkerColorFunction\":null},\"label\":{\"show\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}\"},\"tooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-0.5},\"click\":{\"type\":\"doNothing\"},\"edit\":{\"enabledActions\":[],\"snappable\":false},\"rotateMarker\":true,\"offsetAngle\":0,\"showPath\":true,\"pathStrokeWeight\":2,\"pathStrokeColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"usePathDecorator\":false,\"pathDecoratorSymbol\":\"arrowHead\",\"pathDecoratorSymbolSize\":10,\"pathDecoratorSymbolColor\":\"#307FE5\",\"pathDecoratorOffset\":20,\"pathEndDecoratorOffset\":20,\"pathDecoratorRepeat\":20,\"showPoints\":false,\"pointSize\":10,\"pointColor\":{\"type\":\"constant\",\"color\":\"#307FE5\"},\"pointTooltip\":{\"show\":true,\"trigger\":\"click\",\"autoclose\":true,\"type\":\"pattern\",\"pattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"offsetX\":0,\"offsetY\":-1}}],\"tripTimeline\":{\"showTimelineControl\":true}},\"title\":\"Trip Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"assistant_navigation\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":null,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":null},\"titleColor\":null,\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\"}" + }, + "resources": [ + { + "link": "/api/images/system/trip_animation_system_widget_image.png", + "title": "\"Trip Animation\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "trip_animation_system_widget_image.png", + "publicResourceKey": "3UKAE6mZvW6bnIhr7NAtYTG8FbIDFY1e", + "mediaType": "image/png", + "data": "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", + "public": true + } + ], + "scada": false, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 2c22941c2f..9df613353d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.MailService; @@ -432,10 +431,6 @@ public class ActorSystemContext { @Getter private ClaimDevicesService claimDevicesService; - @Autowired - @Getter - private JsInvokeStats jsInvokeStats; - //TODO: separate context for TbCore and TbRuleEngine @Autowired(required = false) @Getter @@ -595,17 +590,6 @@ public class ActorSystemContext { this.localCacheType = "caffeine".equals(cacheType); } - @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}") - public void printStats() { - if (statisticsEnabled) { - if (jsInvokeStats.getRequests() > 0 || jsInvokeStats.getResponses() > 0 || jsInvokeStats.getFailures() > 0) { - log.info("Rule Engine JS Invoke Stats: requests [{}] responses [{}] failures [{}]", - jsInvokeStats.getRequests(), jsInvokeStats.getResponses(), jsInvokeStats.getFailures()); - jsInvokeStats.reset(); - } - } - } - @Value("${actors.tenant.create_components_on_init:true}") @Getter private boolean tenantComponentsInitEnabled; diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java index 9ab019097b..a185b71d56 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java @@ -24,6 +24,7 @@ import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; @@ -34,6 +35,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg; +import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.gen.transport.TransportProtos.AttributeScopeProto; import org.thingsboard.server.gen.transport.TransportProtos.AttributeValueProto; @@ -74,7 +76,6 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM final EntityId entityId; final CalculatedFieldProcessingService cfService; final CalculatedFieldStateService cfStateService; - final int partition; TbActorCtx ctx; Map states = new HashMap<>(); @@ -85,7 +86,6 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM this.entityId = entityId; this.cfService = systemContext.getCalculatedFieldProcessingService(); this.cfStateService = systemContext.getCalculatedFieldStateService(); - this.partition = systemContext.getCalculatedFieldEntityProfileCache().getEntityIdPartition(tenantId, entityId); } void init(TbActorCtx ctx) { @@ -93,8 +93,8 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } public void process(CalculatedFieldPartitionChangeMsg msg) { - if (!msg.getPartitions()[partition]) { - log.info("[{}][{}] Stopping entity actor due to change partition event.", partition, entityId); + if (!systemContext.getPartitionService().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, tenantId, entityId).isMyPartition()) { + log.info("[{}] Stopping entity actor due to change partition event.", entityId); ctx.stop(ctx.getSelf()); } } @@ -153,7 +153,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } public void process(EntityCalculatedFieldTelemetryMsg msg) throws CalculatedFieldException { - log.info("[{}] Processing CF telemetry msg.", msg.getEntityId()); + log.debug("[{}] Processing CF telemetry msg.", msg.getEntityId()); var proto = msg.getProto(); var numberOfCallbacks = CALLBACKS_PER_CF * (msg.getEntityIdFields().size() + msg.getProfileIdFields().size()); MultipleTbCallback callback = new MultipleTbCallback(numberOfCallbacks, msg.getCallback()); @@ -168,7 +168,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } public void process(EntityCalculatedFieldLinkedTelemetryMsg msg) throws CalculatedFieldException { - log.info("[{}] Processing CF link telemetry msg.", msg.getEntityId()); + log.debug("[{}] Processing CF link telemetry msg.", msg.getEntityId()); var proto = msg.getProto(); var ctx = msg.getCtx(); var callback = new MultipleTbCallback(CALLBACKS_PER_CF, msg.getCallback()); @@ -287,7 +287,11 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM state.checkStateSize(ctxId, ctx.getMaxStateSize()); stateSizeChecked = true; if (state.isSizeOk()) { - cfService.pushMsgToRuleEngine(tenantId, entityId, calculationResult, cfIdList, callback); + if (!calculationResult.isEmpty()) { + cfService.pushMsgToRuleEngine(tenantId, entityId, calculationResult, cfIdList, callback); + } else { + callback.onSuccess(); + } if (DebugModeUtil.isDebugAllAvailable(ctx.getCalculatedField())) { systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArguments(), tbMsgId, tbMsgType, JacksonUtil.writeValueAsString(calculationResult.getResult()), null); } diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java index 1418821081..b0185fd555 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java @@ -318,14 +318,14 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware public void onTelemetryMsg(CalculatedFieldTelemetryMsg msg) { EntityId entityId = msg.getEntityId(); - log.info("Received telemetry msg from entity [{}]", entityId); + log.debug("Received telemetry msg from entity [{}]", entityId); // 2 = 1 for CF processing + 1 for links processing MultipleTbCallback callback = new MultipleTbCallback(2, msg.getCallback()); // process all cfs related to entity, or it's profile; var entityIdFields = getCalculatedFieldsByEntityId(entityId); var profileIdFields = getCalculatedFieldsByEntityId(getProfileId(tenantId, entityId)); if (!entityIdFields.isEmpty() || !profileIdFields.isEmpty()) { - log.info("Pushing telemetry msg to specific actor [{}]", entityId); + log.debug("Pushing telemetry msg to specific actor [{}]", entityId); getOrCreateActor(entityId).tell(new EntityCalculatedFieldTelemetryMsg(msg, entityIdFields, profileIdFields, callback)); } else { callback.onSuccess(); @@ -342,7 +342,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware public void onLinkedTelemetryMsg(CalculatedFieldLinkedTelemetryMsg msg) { EntityId sourceEntityId = msg.getEntityId(); - log.info("Received linked telemetry msg from entity [{}]", sourceEntityId); + log.debug("Received linked telemetry msg from entity [{}]", sourceEntityId); var proto = msg.getProto(); var linksList = proto.getLinksList(); for (var linkProto : linksList) { @@ -357,14 +357,14 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware MultipleTbCallback callback = new MultipleTbCallback(entityIds.size(), msg.getCallback()); var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg(tenantId, sourceEntityId, proto.getMsg(), cf, callback); entityIds.forEach(entityId -> { - log.info("Pushing linked telemetry msg to specific actor [{}]", entityId); + log.debug("Pushing linked telemetry msg to specific actor [{}]", entityId); getOrCreateActor(entityId).tell(newMsg); }); } else { msg.getCallback().onSuccess(); } } else { - log.info("Pushing linked telemetry msg to specific actor [{}]", targetEntityId); + log.debug("Pushing linked telemetry msg to specific actor [{}]", targetEntityId); var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg(tenantId, sourceEntityId, proto.getMsg(), cf, msg.getCallback()); getOrCreateActor(targetEntityId).tell(newMsg); } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index ee6758df6a..033e10ca9a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -654,27 +654,6 @@ public class DefaultTbContext implements TbContext { } } - @Override - public void logJsEvalRequest() { - if (mainCtx.isStatisticsEnabled()) { - mainCtx.getJsInvokeStats().incrementRequests(); - } - } - - @Override - public void logJsEvalResponse() { - if (mainCtx.isStatisticsEnabled()) { - mainCtx.getJsInvokeStats().incrementResponses(); - } - } - - @Override - public void logJsEvalFailure() { - if (mainCtx.isStatisticsEnabled()) { - mainCtx.getJsInvokeStats().incrementFailures(); - } - } - @Override public String getServiceId() { return mainCtx.getServiceInfoProvider().getServiceId(); diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index 4be2024680..f9eec324fc 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -91,6 +91,15 @@ public class TenantActor extends RuleChainManagerActor { isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); if (isRuleEngine) { if (systemContext.getPartitionService().isManagedByCurrentService(tenantId)) { + try { + //TODO: IM - extend API usage to have CF Exec Enabled? Not in 4.0; + cfActor = ctx.getOrCreateChildActor(new TbStringActorId("CFM|" + tenantId), + () -> DefaultActorService.CF_MANAGER_DISPATCHER_NAME, + () -> new CalculatedFieldManagerActorCreator(systemContext, tenantId), + () -> true); + } catch (Exception e) { + log.info("[{}] Failed to init CF Actor.", tenantId, e); + } try { if (getApiUsageState().isReExecEnabled()) { log.debug("[{}] Going to init rule chains", tenantId); @@ -98,11 +107,6 @@ public class TenantActor extends RuleChainManagerActor { } else { log.info("[{}] Skip init of the rule chains due to API limits", tenantId); } - //TODO: IM - extend API usage to have CF Exec Enabled? Not in 4.0; - cfActor = ctx.getOrCreateChildActor(new TbStringActorId("CFM|" + tenantId), - () -> DefaultActorService.CF_MANAGER_DISPATCHER_NAME, - () -> new CalculatedFieldManagerActorCreator(systemContext, tenantId), - () -> true); } catch (Exception e) { log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e); cantFindTenant = true; @@ -185,6 +189,10 @@ public class TenantActor extends RuleChainManagerActor { } private void onToCalculatedFieldSystemActorMsg(ToCalculatedFieldSystemMsg msg, boolean priority) { + if (cfActor == null) { + log.warn("[{}] CF Actor is not initialized.", tenantId); + return; + } if (priority) { cfActor.tellWithHighPriority(msg); } else { @@ -251,11 +259,25 @@ public class TenantActor extends RuleChainManagerActor { ServiceType serviceType = msg.getServiceType(); if (ServiceType.TB_RULE_ENGINE.equals(serviceType)) { if (systemContext.getPartitionService().isManagedByCurrentService(tenantId)) { + if (cfActor == null) { + try { + //TODO: IM - extend API usage to have CF Exec Enabled? Not in 4.0; + cfActor = ctx.getOrCreateChildActor(new TbStringActorId("CFM|" + tenantId), + () -> DefaultActorService.CF_MANAGER_DISPATCHER_NAME, + () -> new CalculatedFieldManagerActorCreator(systemContext, tenantId), + () -> true); + } catch (Exception e) { + log.info("[{}] Failed to init CF Actor.", tenantId, e); + } + } if (!ruleChainsInitialized) { log.info("Tenant {} is now managed by this service, initializing rule chains", tenantId); initRuleChains(); } } else { + if (cfActor != null) { + ctx.stop(cfActor.getActorId()); + } if (ruleChainsInitialized) { log.info("Tenant {} is no longer managed by this service, stopping rule chains", tenantId); destroyRuleChains(); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java index 8eb27395c1..49acf6917c 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java @@ -27,4 +27,11 @@ public final class CalculatedFieldResult { private final AttributeScope scope; private final JsonNode result; + public boolean isEmpty() { + return result == null || result.isMissingNode() || result.isNull() || + (result.isObject() && result.isEmpty()) || + (result.isArray() && result.isEmpty()) || + (result.isTextual() && result.asText().isEmpty()); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldInitService.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldInitService.java index 9442329edb..cc3022fa29 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldInitService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldInitService.java @@ -47,12 +47,20 @@ public class DefaultCalculatedFieldInitService implements CalculatedFieldInitSer PageDataIterable deviceIdInfos = new PageDataIterable<>(deviceService::findProfileEntityIdInfos, initFetchPackSize); for (ProfileEntityIdInfo idInfo : deviceIdInfos) { log.trace("Processing device record: {}", idInfo); - entityProfileCache.add(idInfo.getTenantId(), idInfo.getProfileId(), idInfo.getEntityId()); + try { + entityProfileCache.add(idInfo.getTenantId(), idInfo.getProfileId(), idInfo.getEntityId()); + } catch (Exception e) { + log.error("Failed to process device record: {}", idInfo, e); + } } PageDataIterable assetIdInfos = new PageDataIterable<>(assetService::findProfileEntityIdInfos, initFetchPackSize); for (ProfileEntityIdInfo idInfo : assetIdInfos) { log.trace("Processing asset record: {}", idInfo); - entityProfileCache.add(idInfo.getTenantId(), idInfo.getProfileId(), idInfo.getEntityId()); + try { + entityProfileCache.add(idInfo.getTenantId(), idInfo.getProfileId(), idInfo.getEntityId()); + } catch (Exception e) { + log.error("Failed to process asset record: {}", idInfo, e); + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java index 27bc0120c4..e9a6cb09aa 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldProcessingService.java @@ -30,6 +30,7 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.actors.calculatedField.CalculatedFieldTelemetryMsg; import org.thingsboard.server.actors.calculatedField.MultipleTbCallback; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.cf.configuration.Argument; @@ -51,6 +52,7 @@ import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.attributes.AttributesService; @@ -200,7 +202,7 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP if (broadcast) { broadcasts.add(link); } else { - TopicPartitionInfo tpi = partitionService.resolve(QueueKey.CF, link.entityId()); + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, link.tenantId(), link.entityId()); unicasts.computeIfAbsent(tpi, k -> new ArrayList<>()).add(link); } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/cache/DefaultCalculatedFieldEntityProfileCache.java b/application/src/main/java/org/thingsboard/server/service/cf/cache/DefaultCalculatedFieldEntityProfileCache.java index 4cf62c01b3..2f5772ae50 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/cache/DefaultCalculatedFieldEntityProfileCache.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/cache/DefaultCalculatedFieldEntityProfileCache.java @@ -18,8 +18,10 @@ package org.thingsboard.server.service.cf.cache; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.QueueKey; @@ -57,7 +59,7 @@ public class DefaultCalculatedFieldEntityProfileCache extends TbApplicationEvent @Override public void add(TenantId tenantId, EntityId profileId, EntityId entityId) { - var tpi = partitionService.resolve(QueueKey.CF, entityId); + var tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, tenantId, entityId); var partition = tpi.getPartition().orElse(UNKNOWN); tenantCache.computeIfAbsent(tenantId, id -> new TenantEntityProfileCache()) .add(profileId, entityId, partition, tpi.isMyPartition()); @@ -65,7 +67,7 @@ public class DefaultCalculatedFieldEntityProfileCache extends TbApplicationEvent @Override public void update(TenantId tenantId, EntityId oldProfileId, EntityId newProfileId, EntityId entityId) { - var tpi = partitionService.resolve(QueueKey.CF, entityId); + var tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, tenantId, entityId); var partition = tpi.getPartition().orElse(UNKNOWN); var cache = tenantCache.computeIfAbsent(tenantId, id -> new TenantEntityProfileCache()); //TODO: make this method atomic; @@ -86,7 +88,7 @@ public class DefaultCalculatedFieldEntityProfileCache extends TbApplicationEvent @Override public int getEntityIdPartition(TenantId tenantId, EntityId entityId) { - var tpi = partitionService.resolve(QueueKey.CF, entityId); + var tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, tenantId, entityId); return tpi.getPartition().orElse(UNKNOWN); } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java index e5a3d0e05e..0c4352dcea 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java @@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto; @@ -44,7 +43,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @Data public class CalculatedFieldCtx { @@ -58,8 +56,6 @@ public class CalculatedFieldCtx { private final Map arguments; private final Map mainEntityArguments; private final Map> linkedEntityArguments; - - private final Map, String> referencedEntityKeys; private final List argNames; private Output output; private String expression; @@ -87,17 +83,12 @@ public class CalculatedFieldCtx { for (Map.Entry entry : arguments.entrySet()) { var refId = entry.getValue().getRefEntityId(); var refKey = entry.getValue().getRefEntityKey(); - if (refId == null) { + if (refId == null || refId.equals(calculatedField.getEntityId())) { mainEntityArguments.put(refKey, entry.getKey()); } else { linkedEntityArguments.computeIfAbsent(refId, key -> new HashMap<>()).put(refKey, entry.getKey()); } } - this.referencedEntityKeys = arguments.entrySet().stream() - .collect(Collectors.toMap( - entry -> new TbPair<>(entry.getValue().getRefEntityId() == null ? entityId : entry.getValue().getRefEntityId(), entry.getValue().getRefEntityKey()), - Map.Entry::getKey - )); this.argNames = new ArrayList<>(arguments.keySet()); this.output = configuration.getOutput(); this.expression = configuration.getExpression(); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java index e81fa4d1dc..557768e9c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/KafkaCalculatedFieldStateService.java @@ -20,10 +20,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; @@ -67,9 +69,11 @@ public class KafkaCalculatedFieldStateService extends AbstractCalculatedFieldSta @Override public void init(PartitionedQueueConsumerManager> eventConsumer) { super.init(eventConsumer); + + var queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.CF_STATES_QUEUE_NAME); this.stateConsumer = PartitionedQueueConsumerManager.>create() - .queueKey(QueueKey.CF_STATES) - .topic(partitionService.getTopic(QueueKey.CF_STATES)) + .queueKey(queueKey) + .topic(partitionService.getTopic(queueKey)) .pollInterval(pollInterval) .msgPackProcessor((msgs, consumer, config) -> { for (TbProtoQueueMsg msg : msgs) { @@ -101,7 +105,7 @@ public class KafkaCalculatedFieldStateService extends AbstractCalculatedFieldSta @Override protected void doPersist(CalculatedFieldEntityCtxId stateId, CalculatedFieldStateProto stateMsgProto, TbCallback callback) { - TopicPartitionInfo tpi = partitionService.resolve(QueueKey.CF_STATES, stateId.entityId()); + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_STATES_QUEUE_NAME, stateId.tenantId(), stateId.entityId()); TbProtoQueueMsg msg = new TbProtoQueueMsg<>(stateId.entityId().getId(), stateMsgProto); if (stateMsgProto == null) { putStateId(msg.getHeaders(), stateId); diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java index 0f4cf30fac..a0012a6fb3 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java @@ -32,7 +32,7 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti // This list should include all versions which are compatible for the upgrade. // The compatibility cycle usually breaks when we have some scripts written in Java that may not work after new release. - private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("3.9.0"); + private static final List SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("3.9.0", "3.9.1"); private final ProjectInfo projectInfo; private final JdbcTemplate jdbcTemplate; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java index e76a2be9be..ad248cc3d4 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCalculatedFieldConsumerService.java @@ -27,6 +27,7 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.calculatedField.CalculatedFieldLinkedTelemetryMsg; import org.thingsboard.server.actors.calculatedField.CalculatedFieldTelemetryMsg; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.queue.QueueConfig; @@ -79,8 +80,6 @@ public class DefaultTbCalculatedFieldConsumerService extends AbstractConsumerSer private long pollInterval; @Value("${queue.calculated_fields.pack_processing_timeout:60000}") private long packProcessingTimeout; - @Value("${queue.calculated_fields.pool_size:8}") - private int poolSize; private final TbRuleEngineQueueFactory queueFactory; private final CalculatedFieldStateService stateService; @@ -108,9 +107,10 @@ public class DefaultTbCalculatedFieldConsumerService extends AbstractConsumerSer public void init() { super.init("tb-cf"); + var queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME); this.eventConsumer = PartitionedQueueConsumerManager.>create() - .queueKey(QueueKey.CF) - .topic(partitionService.getTopic(QueueKey.CF)) + .queueKey(queueKey) + .topic(partitionService.getTopic(queueKey)) .pollInterval(pollInterval) .msgPackProcessor(this::processMsgs) .consumerCreator((config, partitionId) -> queueFactory.createToCalculatedFieldMsgConsumer()) @@ -140,20 +140,12 @@ public class DefaultTbCalculatedFieldConsumerService extends AbstractConsumerSer // Cleanup old entities after corresponding consumers are stopped. // Any periodic tasks need to check that the entity is still managed by the current server before processing. - actorContext.tell(new CalculatedFieldPartitionChangeMsg(partitionsToBooleanIndexArray(partitions))); + actorContext.tell(new CalculatedFieldPartitionChangeMsg()); } catch (Throwable t) { log.error("Failed to process partition change event: {}", event, t); } } - private boolean[] partitionsToBooleanIndexArray(Set partitions) { - boolean[] myPartitions = new boolean[partitionService.getTotalCalculatedFieldPartitions()]; - for (var tpi : partitions) { - tpi.getPartition().ifPresent(partition -> myPartitions[partition] = true); - } - return myPartitions; - } - private void processMsgs(List> msgs, TbQueueConsumer> consumer, QueueConfig config) throws Exception { List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).toList(); ConcurrentMap> pendingMap = orderedMsgList.stream().collect( diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 800578ace4..c7174469b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -358,7 +358,7 @@ public class DefaultTbClusterService implements TbClusterService { @Override public void pushMsgToCalculatedFields(TenantId tenantId, EntityId entityId, ToCalculatedFieldMsg msg, TbQueueCallback callback) { - TopicPartitionInfo tpi = partitionService.resolve(QueueKey.CF, entityId); + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, tenantId, entityId); pushMsgToCalculatedFields(tpi, UUID.randomUUID(), msg, callback); } @@ -371,7 +371,7 @@ public class DefaultTbClusterService implements TbClusterService { @Override public void pushNotificationToCalculatedFields(TenantId tenantId, EntityId entityId, ToCalculatedFieldNotificationMsg msg, TbQueueCallback callback) { - TopicPartitionInfo tpi = partitionService.resolve(QueueKey.CF, entityId); + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME, tenantId, entityId); producerProvider.getCalculatedFieldsNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), callback); toRuleEngineNfs.incrementAndGet(); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index 6391beecf8..d522f11f7b 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -22,6 +22,7 @@ import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; @@ -108,7 +109,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @Override protected void onTbApplicationEvent(PartitionChangeEvent event) { event.getNewPartitions().forEach((queueKey, partitions) -> { - if (CollectionsUtil.isOneOf(queueKey, QueueKey.CF, QueueKey.CF_STATES)) { + if (DataConstants.CF_QUEUE_NAME.equals(queueKey.getQueueName()) || DataConstants.CF_STATES_QUEUE_NAME.equals(queueKey.getQueueName())) { return; } if (partitionService.isManagedByCurrentService(queueKey.getTenantId())) { diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java deleted file mode 100644 index e160e0e5b5..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright © 2016-2025 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.stats; - -import jakarta.annotation.PostConstruct; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.thingsboard.server.actors.JsInvokeStats; -import org.thingsboard.server.common.stats.StatsCounter; -import org.thingsboard.server.common.stats.StatsFactory; -import org.thingsboard.server.common.stats.StatsType; - -@Service -public class DefaultJsInvokeStats implements JsInvokeStats { - private static final String REQUESTS = "requests"; - private static final String RESPONSES = "responses"; - private static final String FAILURES = "failures"; - - private StatsCounter requestsCounter; - private StatsCounter responsesCounter; - private StatsCounter failuresCounter; - - @Autowired - private StatsFactory statsFactory; - - @PostConstruct - public void init() { - String key = StatsType.JS_INVOKE.getName(); - this.requestsCounter = statsFactory.createStatsCounter(key, REQUESTS); - this.responsesCounter = statsFactory.createStatsCounter(key, RESPONSES); - this.failuresCounter = statsFactory.createStatsCounter(key, FAILURES); - } - - @Override - public void incrementRequests(int amount) { - requestsCounter.add(amount); - } - - @Override - public void incrementResponses(int amount) { - responsesCounter.add(amount); - } - - @Override - public void incrementFailures(int amount) { - failuresCounter.add(amount); - } - - @Override - public int getRequests() { - return requestsCounter.get(); - } - - @Override - public int getResponses() { - return responsesCounter.get(); - } - - @Override - public int getFailures() { - return failuresCounter.get(); - } - - @Override - public void reset() { - requestsCounter.clear(); - responsesCounter.clear(); - failuresCounter.clear(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 89b51ce84a..8ef6465d4b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -529,6 +529,8 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @Override public ListenableFuture deleteVersionControlSettings(TenantId tenantId) { + log.debug("[{}] Deleting version control settings", tenantId); + repositorySettingsService.delete(tenantId); return gitServiceQueue.clearRepository(tenantId); } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/KafkaEdgeTopicsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/KafkaEdgeTopicsCleanUpService.java index 5a74f9f69a..73712542fd 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/KafkaEdgeTopicsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/KafkaEdgeTopicsCleanUpService.java @@ -53,8 +53,6 @@ import static org.thingsboard.server.service.state.DefaultDeviceStateService.LAS @ConditionalOnExpression("'${queue.type:null}'=='kafka' && ${edges.enabled:true} && ${sql.ttl.edge_events.edge_events_ttl:0} > 0") public class KafkaEdgeTopicsCleanUpService extends AbstractCleanUpService { - private static final String EDGE_EVENT_TOPIC_NAME = "tb_edge_event.notifications."; - private final TopicService topicService; private final TenantService tenantService; private final EdgeService edgeService; @@ -64,6 +62,9 @@ public class KafkaEdgeTopicsCleanUpService extends AbstractCleanUpService { @Value("${sql.ttl.edge_events.edge_events_ttl:2628000}") private long ttlSeconds; + @Value("${queue.edge.event-notifications-topic:tb_edge_event.notifications}") + private String tbEdgeEventNotificationsTopic; + public KafkaEdgeTopicsCleanUpService(PartitionService partitionService, EdgeService edgeService, TenantService tenantService, AttributesService attributesService, TopicService topicService, TbKafkaSettings kafkaSettings, TbKafkaTopicConfigs kafkaTopicConfigs) { @@ -86,7 +87,7 @@ public class KafkaEdgeTopicsCleanUpService extends AbstractCleanUpService { return; } - String edgeTopicPrefix = topicService.buildTopicName(EDGE_EVENT_TOPIC_NAME); + String edgeTopicPrefix = topicService.buildTopicName(tbEdgeEventNotificationsTopic); List matchingTopics = topics.stream().filter(topic -> topic.startsWith(edgeTopicPrefix)).toList(); if (matchingTopics.isEmpty()) { log.debug("No matching topics found with prefix [{}]. Skipping cleanup.", edgeTopicPrefix); @@ -147,7 +148,7 @@ public class KafkaEdgeTopicsCleanUpService extends AbstractCleanUpService { try { String remaining = topic.substring(prefix.length()); String[] parts = remaining.split("\\."); - TenantId tenantId = new TenantId(UUID.fromString(parts[0])); + TenantId tenantId = TenantId.fromUUID(UUID.fromString(parts[0])); EdgeId edgeId = new EdgeId(UUID.fromString(parts[1])); tenantEdgeMap.computeIfAbsent(tenantId, id -> new ArrayList<>()).add(edgeId); } catch (Exception e) { diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 2ee3c439c1..b3022fb9d3 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -502,8 +502,6 @@ actors: statistics: # Enable/disable actor statistics enabled: "${ACTORS_STATISTICS_ENABLED:true}" - # Frequency of printing the JS executor statistics - js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}" # Actors statistic persistence frequency in milliseconds persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" calculated_fields: @@ -1284,7 +1282,7 @@ transport: # URL of gateways dashboard repository repository_url: "${TB_GATEWAY_DASHBOARD_SYNC_REPOSITORY_URL:https://github.com/thingsboard/gateway-management-extensions-dist.git}" # Branch of gateways dashboard repository to work with - branch: "${TB_GATEWAY_DASHBOARD_SYNC_BRANCH:}" + branch: "${TB_GATEWAY_DASHBOARD_SYNC_BRANCH:release/4.0.0}" # Fetch frequency in hours for gateways dashboard repository fetch_frequency: "${TB_GATEWAY_DASHBOARD_SYNC_FETCH_FREQUENCY:24}" @@ -1759,9 +1757,6 @@ queue: stats: # Enable/disable statistics for EDQS enabled: "${TB_EDQS_STATS_ENABLED:true}" - # Statistics printing interval for EDQS - print-interval-ms: "${TB_EDQS_STATS_PRINT_INTERVAL_MS:300000}" - vc: # Default topic name topic: "${TB_QUEUE_VC_TOPIC:tb_version_control}" @@ -1818,9 +1813,7 @@ queue: # For high-priority notifications that require minimum latency and processing time notifications_topic: "${TB_QUEUE_CF_NOTIFICATIONS_TOPIC:calculated_field.notifications}" # Interval in milliseconds to poll messages by CF (Rule Engine) microservices - poll_interval: "${TB_QUEUE_CF_POLL_INTERVAL_MS:25}" - # Amount of partitions used by CF microservices - partitions: "${TB_QUEUE_CF_PARTITIONS:10}" + poll_interval: "${TB_QUEUE_CF_POLL_INTERVAL_MS:1000}" # Timeout for processing a message pack by CF microservices pack_processing_timeout: "${TB_QUEUE_CF_PACK_PROCESSING_TIMEOUT_MS:60000}" # Thread pool size for processing of the incoming messages diff --git a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java index 9ddf0f0a04..002f668fdc 100644 --- a/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/cf/CalculatedFieldIntegrationTest.java @@ -48,6 +48,9 @@ import static org.awaitility.Awaitility.await; @DaoSqlTest public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTest { + public static final int TIMEOUT = 60; + public static final int POLL_INTERVAL = 1; + @BeforeEach void setUp() throws Exception { loginTenantAdmin(); @@ -86,6 +89,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); await().alias("create CF -> perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); @@ -95,6 +99,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); await().alias("update telemetry -> recalculate state").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); @@ -108,6 +113,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes savedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); await().alias("update CF output -> perform calculation with updated output").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ArrayNode temperatureF = getServerAttributes(testDevice.getId(), "temperatureF"); assertThat(temperatureF).isNotNull(); @@ -119,6 +125,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes savedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); await().alias("update CF argument -> perform calculation with new argument").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ArrayNode temperatureF = getServerAttributes(testDevice.getId(), "temperatureF"); assertThat(temperatureF).isNotNull(); @@ -129,6 +136,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes savedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class); await().alias("update CF expression -> perform calculation with new expression").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ArrayNode temperatureF = getServerAttributes(testDevice.getId(), "temperatureF"); assertThat(temperatureF).isNotNull(); @@ -166,6 +174,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); await().alias("create CF -> state is not ready -> no calculation performed").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); @@ -175,6 +184,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); await().alias("update telemetry -> perform calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); @@ -213,6 +223,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); await().alias("create CF -> perform initial calculation with default value").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); @@ -222,6 +233,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); await().alias("update telemetry -> recalculate state").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); @@ -277,6 +289,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/calculatedField", calculatedField, CalculatedField.class); await().alias("create CF and perform initial calculation").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // result of asset 1 ArrayNode z1 = getServerAttributes(asset1.getId(), "z"); @@ -292,6 +305,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"x\":25}")); await().alias("update device telemetry -> recalculate state for all assets").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // result of asset 1 ArrayNode z1 = getServerAttributes(asset1.getId(), "z"); @@ -307,6 +321,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/ASSET/" + asset1.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"y\":15}")); await().alias("update asset 1 telemetry -> recalculate state only for asset 1").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // result of asset 1 ArrayNode z1 = getServerAttributes(asset1.getId(), "z"); @@ -322,6 +337,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/ASSET/" + asset2.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"y\":5}")); await().alias("update asset 2 telemetry -> recalculate state only for asset 2").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // result of asset 1 (no changes) ArrayNode z1 = getServerAttributes(asset1.getId(), "z"); @@ -339,6 +355,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes Asset finalAsset3 = asset3; await().alias("add new entity to profile -> calculate state for new entity").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // result of asset 3 ArrayNode z3 = getServerAttributes(finalAsset3.getId(), "z"); @@ -349,6 +366,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/attributes/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"x\":20}")); await().alias("update device telemetry -> recalculate state for all assets").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // result of asset 1 ArrayNode z1 = getServerAttributes(asset1.getId(), "z"); @@ -375,6 +393,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes Asset updatedAsset3 = asset3; await().alias("update device telemetry -> recalculate state for asset 1 and asset 2").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { // result of asset 1 ArrayNode z1 = getServerAttributes(asset1.getId(), "z"); @@ -425,6 +444,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class); await().alias("create CF -> ctx is not initialized -> no calculation perform").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); @@ -434,6 +454,7 @@ public class CalculatedFieldIntegrationTest extends CalculatedFieldControllerTes doPost("/api/plugins/telemetry/DEVICE/" + testDevice.getUuidId() + "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode("{\"temperature\":30}")); await().alias("update telemetry -> ctx is not initialized -> no calculation perform").atMost(TIMEOUT, TimeUnit.SECONDS) + .pollInterval(POLL_INTERVAL, TimeUnit.SECONDS) .untilAsserted(() -> { ObjectNode fahrenheitTemp = getLatestTelemetry(testDevice.getId(), "fahrenheitTemp"); assertThat(fahrenheitTemp).isNotNull(); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java index 77002cef8e..b0a3518e60 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java @@ -28,6 +28,7 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -73,6 +74,7 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.containsString; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; @@ -325,6 +327,59 @@ public class BaseQueueControllerTest extends AbstractControllerTest { doDelete("/api/queues/" + queue.getUuidId()).andExpect(status().isOk()); } + @Test + public void testQueueWithReservedName() throws Exception { + loginSysAdmin(); + + // create queue + Queue queue = new Queue(); + queue.setName(DataConstants.CF_QUEUE_NAME); + queue.setTopic("tb_rule_engine.calculated_fields"); + queue.setPollInterval(25); + queue.setPartitions(10); + queue.setTenantId(TenantId.SYS_TENANT_ID); + queue.setConsumerPerPartition(false); + queue.setPackProcessingTimeout(2000); + SubmitStrategy submitStrategy = new SubmitStrategy(); + submitStrategy.setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); + queue.setSubmitStrategy(submitStrategy); + ProcessingStrategy processingStrategy = new ProcessingStrategy(); + processingStrategy.setType(ProcessingStrategyType.RETRY_ALL); + processingStrategy.setRetries(3); + processingStrategy.setFailurePercentage(0.7); + processingStrategy.setPauseBetweenRetries(3); + processingStrategy.setMaxPauseBetweenRetries(5); + queue.setProcessingStrategy(processingStrategy); + + doPost("/api/queues?serviceType=" + "TB-RULE-ENGINE", queue) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(String.format("The queue name '%s' is not allowed. This name is reserved for internal use. Please choose a different name.", DataConstants.CF_QUEUE_NAME)))); + + // create queue + Queue queue2 = new Queue(); + queue2.setName(DataConstants.CF_STATES_QUEUE_NAME); + queue2.setTopic("tb_rule_engine.calculated_fields"); + queue2.setPollInterval(25); + queue2.setPartitions(10); + queue2.setTenantId(TenantId.SYS_TENANT_ID); + queue2.setConsumerPerPartition(false); + queue2.setPackProcessingTimeout(2000); + SubmitStrategy submitStrategy2 = new SubmitStrategy(); + submitStrategy2.setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); + queue2.setSubmitStrategy(submitStrategy); + ProcessingStrategy processingStrategy2 = new ProcessingStrategy(); + processingStrategy2.setType(ProcessingStrategyType.RETRY_ALL); + processingStrategy2.setRetries(3); + processingStrategy2.setFailurePercentage(0.7); + processingStrategy2.setPauseBetweenRetries(3); + processingStrategy2.setMaxPauseBetweenRetries(5); + queue2.setProcessingStrategy(processingStrategy); + + doPost("/api/queues?serviceType=" + "TB-RULE-ENGINE", queue2) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(String.format("The queue name '%s' is not allowed. This name is reserved for internal use. Please choose a different name.", DataConstants.CF_STATES_QUEUE_NAME)))); + } + private Queue saveQueue(Queue queue) { return doPost("/api/queues?serviceType=TB_RULE_ENGINE", queue, Queue.class); } diff --git a/application/src/test/java/org/thingsboard/server/controller/RepositorySettingsTest.java b/application/src/test/java/org/thingsboard/server/controller/RepositorySettingsTest.java new file mode 100644 index 0000000000..9cd75e9a3c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/RepositorySettingsTest.java @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import com.google.common.util.concurrent.SettableFuture; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.thingsboard.server.common.data.sync.vc.RepositoryAuthMethod; +import org.thingsboard.server.common.data.sync.vc.RepositorySettings; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.sync.vc.GitVersionControlQueueService; + +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Slf4j +@DaoSqlTest +public class RepositorySettingsTest extends AbstractControllerTest { + + @MockBean + private GitVersionControlQueueService gitVersionControlQueueService; + + @Test + public void testFindRepositorySettings() throws Exception { + loginTenantAdmin(); + doGet("/api/admin/repositorySettings") + .andExpect(status().isNotFound()); + + String testRepositoryUri = "https://github.com/test/version-control-test-repository.git"; + + SettableFuture successFuture = SettableFuture.create(); + successFuture.set(null); + when(gitVersionControlQueueService.initRepository(any(), any())) + .thenReturn(successFuture); + + RepositorySettings repositorySettings = new RepositorySettings(); + repositorySettings.setPassword("test"); + repositorySettings.setAuthMethod(RepositoryAuthMethod.USERNAME_PASSWORD); + repositorySettings.setRepositoryUri(testRepositoryUri); + repositorySettings.setDefaultBranch("main"); + doPost("/api/admin/repositorySettings", repositorySettings) + .andExpect(status().isOk()); + + // check repository settings + doGet("/api/admin/repositorySettings") + .andExpect(status().isOk()) + .andExpect(content().contentType(contentType)) + .andExpect(jsonPath("$.repositoryUri", is(testRepositoryUri))); + + // delete settings + when(gitVersionControlQueueService.clearRepository(any())) + .thenReturn(successFuture); + doDelete("/api/admin/repositorySettings") + .andExpect(status().isOk()); + + // check repository settings + doGet("/api/admin/repositorySettings") + .andExpect(status().isNotFound()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index 4a303e5253..dace159774 100644 --- a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java @@ -428,7 +428,6 @@ public class HashPartitionServiceTest { ReflectionTestUtils.setField(partitionService, "corePartitions", 10); ReflectionTestUtils.setField(partitionService, "cfEventTopic", "tb_cf_event"); ReflectionTestUtils.setField(partitionService, "cfStateTopic", "tb_cf_state"); - ReflectionTestUtils.setField(partitionService, "cfPartitions", 10); ReflectionTestUtils.setField(partitionService, "vcTopic", "tb.vc"); ReflectionTestUtils.setField(partitionService, "vcPartitions", 10); ReflectionTestUtils.setField(partitionService, "hashFunctionName", hashFunctionName); diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java index 320093e8f4..91eced64f6 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.cf.ctx.state; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.stats.DefaultStatsFactory; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.service.cf.CalculatedFieldResult; @@ -51,7 +53,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -@SpringBootTest(classes = DefaultTbelInvokeService.class) +@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class}) public class ScriptCalculatedFieldStateTest { private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("5b18e321-3327-4290-b996-d72a65e90382")); diff --git a/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java b/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java index f8563e5e2a..e3383e32a8 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/AbstractTbelInvokeTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.script; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.thingsboard.common.util.JacksonUtil; @@ -22,7 +23,7 @@ import org.thingsboard.script.api.ScriptType; import org.thingsboard.script.api.tbel.DefaultTbelInvokeService; import org.thingsboard.script.api.tbel.TbelInvokeService; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.common.stats.DefaultStatsFactory; import java.util.Map; import java.util.UUID; @@ -30,7 +31,7 @@ import java.util.concurrent.ExecutionException; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; -@SpringBootTest(classes = DefaultTbelInvokeService.class) +@SpringBootTest(classes = {SimpleMeterRegistry.class, DefaultStatsFactory.class, DefaultTbelInvokeService.class }) public abstract class AbstractTbelInvokeTest { @Autowired diff --git a/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java index 7404427f92..363f21fa10 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/RemoteJsInvokeServiceTest.java @@ -21,9 +21,13 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.script.api.ScriptType; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.DefaultStatsFactory; +import org.thingsboard.server.common.stats.StatsCounter; +import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; import org.thingsboard.server.gen.js.JsInvokeProtos; @@ -68,6 +72,10 @@ class RemoteJsInvokeServiceTest { remoteJsInvokeService = new RemoteJsInvokeService(Optional.of(apiUsageStateClient), Optional.of(apiUsageReportClient)); jsRequestTemplate = mock(TbQueueRequestTemplate.class); remoteJsInvokeService.requestTemplate = jsRequestTemplate; + StatsFactory statsFactory = mock(StatsFactory.class); + when(statsFactory.createStatsCounter(any(), any())).thenReturn(mock(StatsCounter.class)); + ReflectionTestUtils.setField(remoteJsInvokeService, "statsFactory",statsFactory); + remoteJsInvokeService.init(); } @AfterEach diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/EntityViewFields.java b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/EntityViewFields.java index 7674fba200..ba3c105a87 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/EntityViewFields.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/EntityViewFields.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.common.data.edqs.fields; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import java.util.UUID; +import static org.thingsboard.server.common.data.edqs.fields.FieldsUtil.getText; + @Data @NoArgsConstructor @SuperBuilder @@ -29,9 +32,9 @@ public class EntityViewFields extends AbstractEntityFields { private String type; private String additionalInfo; - public EntityViewFields(UUID id, long createdTime, UUID tenantId, UUID customerId, String name, String type, String additionalInfo, Long version) { + public EntityViewFields(UUID id, long createdTime, UUID tenantId, UUID customerId, String name, String type, JsonNode additionalInfo, Long version) { super(id, createdTime, tenantId, customerId, name, version); this.type = type; - this.additionalInfo = additionalInfo; + this.additionalInfo = getText(additionalInfo); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/FieldsUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/FieldsUtil.java index a36514248c..9ba6c20188 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/FieldsUtil.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edqs/fields/FieldsUtil.java @@ -289,7 +289,7 @@ public class FieldsUtil { } public static String getText(JsonNode node) { - return node != null ? node.asText() : ""; + return node != null ? node.toString() : ""; } private static UUID getCustomerId(CustomerId customerId) { diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java index 0c55bc50dd..ab7fb3acff 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/repo/TenantRepo.java @@ -422,16 +422,19 @@ public class TenantRepo { } public String getOwnerName(EntityId ownerId) { - if (ownerId == null || (EntityType.CUSTOMER.equals(ownerId.getEntityType()) && CustomerId.NULL_UUID.equals(ownerId.getId()))) { - ownerId = tenantId; + if (ownerId == null || (ownerId.getEntityType() == EntityType.CUSTOMER && ownerId.isNullUid())) { + return getOwnerEntityName(tenantId); } - return getEntityName(ownerId); + return getOwnerEntityName(ownerId); } - private String getEntityName(EntityId entityId) { + private String getOwnerEntityName(EntityId entityId) { EntityType entityType = entityId.getEntityType(); return switch (entityType) { - case CUSTOMER, TENANT -> getEntityMap(entityType).get(entityId.getId()).getFields().getName(); + case CUSTOMER, TENANT -> { + EntityFields fields = getEntityMap(entityType).get(entityId.getId()).getFields(); + yield fields != null ? fields.getName() : ""; + } default -> throw new RuntimeException("Unsupported entity type: " + entityType); }; } diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/stats/EdqsStatsService.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/stats/EdqsStatsService.java index a12a12dbe3..442453fc93 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/stats/EdqsStatsService.java +++ b/common/edqs/src/main/java/org/thingsboard/server/edqs/stats/EdqsStatsService.java @@ -20,7 +20,6 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.ObjectType; import org.thingsboard.server.common.data.edqs.EdqsEventType; @@ -43,18 +42,6 @@ public class EdqsStatsService { private final ConcurrentHashMap statsMap = new ConcurrentHashMap<>(); private final StatsFactory statsFactory; - @Scheduled(initialDelayString = "${queue.edqs.stats.print-interval-ms:300000}", - fixedDelayString = "${queue.edqs.stats.print-interval-ms:300000}") - private void reportStats() { - if (statsMap.isEmpty()) { - return; - } - String values = statsMap.entrySet().stream() - .map(kv -> "TenantId [" + kv.getKey() + "] stats [" + kv.getValue() + "]") - .collect(Collectors.joining(System.lineSeparator())); - log.info("EDQS Stats: {}", values); - } - public void reportEvent(TenantId tenantId, ObjectType objectType, EdqsEventType eventType) { statsMap.computeIfAbsent(tenantId, id -> new EdqsStats(tenantId, statsFactory)) .reportEvent(objectType, eventType); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/cf/CalculatedFieldPartitionChangeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/cf/CalculatedFieldPartitionChangeMsg.java index 38a4853219..44756013ca 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/cf/CalculatedFieldPartitionChangeMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/cf/CalculatedFieldPartitionChangeMsg.java @@ -26,8 +26,6 @@ import java.util.Set; @Data public class CalculatedFieldPartitionChangeMsg implements ToCalculatedFieldSystemMsg { - private final boolean[] partitions; - @Override public TenantId getTenantId() { return TenantId.SYS_TENANT_ID; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index b42f8cc380..345b44e764 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.exception.TenantNotFoundException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -67,8 +68,6 @@ public class HashPartitionService implements PartitionService { private String cfEventTopic; @Value("${queue.calculated_fields.state_topic:tb_cf_state}") private String cfStateTopic; - @Value("${queue.calculated_fields.partitions:10}") - private Integer cfPartitions; @Value("${queue.vc.topic:tb_version_control}") private String vcTopic; @Value("${queue.vc.partitions:10}") @@ -122,11 +121,6 @@ public class HashPartitionService implements PartitionService { partitionSizesMap.put(coreKey, corePartitions); partitionTopicsMap.put(coreKey, coreTopic); - partitionSizesMap.put(QueueKey.CF, cfPartitions); - partitionTopicsMap.put(QueueKey.CF, cfEventTopic); - partitionSizesMap.put(QueueKey.CF_STATES, cfPartitions); - partitionTopicsMap.put(QueueKey.CF_STATES, cfStateTopic); - QueueKey vcKey = new QueueKey(ServiceType.TB_VC_EXECUTOR); partitionSizesMap.put(vcKey, vcPartitions); partitionTopicsMap.put(vcKey, vcTopic); @@ -165,6 +159,14 @@ public class HashPartitionService implements PartitionService { List queueRoutingInfoList = getQueueRoutingInfos(); queueRoutingInfoList.forEach(queue -> { QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); + if (DataConstants.MAIN_QUEUE_NAME.equals(queueKey.getQueueName())) { + QueueKey cfQueueKey = queueKey.withQueueName(DataConstants.CF_QUEUE_NAME); + partitionSizesMap.put(cfQueueKey, queue.getPartitions()); + partitionTopicsMap.put(cfQueueKey, cfEventTopic); + QueueKey cfQueueStatesKey = queueKey.withQueueName(DataConstants.CF_STATES_QUEUE_NAME); + partitionSizesMap.put(cfQueueStatesKey, queue.getPartitions()); + partitionTopicsMap.put(cfQueueStatesKey, cfStateTopic); + } partitionTopicsMap.put(queueKey, queue.getQueueTopic()); partitionSizesMap.put(queueKey, queue.getPartitions()); queueConfigs.put(queueKey, new QueueConfig(queue)); @@ -213,6 +215,14 @@ public class HashPartitionService implements PartitionService { QueueRoutingInfo queueRoutingInfo = new QueueRoutingInfo(queueUpdateMsg); TenantId tenantId = queueRoutingInfo.getTenantId(); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueRoutingInfo.getQueueName(), tenantId); + if (DataConstants.MAIN_QUEUE_NAME.equals(queueKey.getQueueName())) { + QueueKey cfQueueKey = queueKey.withQueueName(DataConstants.CF_QUEUE_NAME); + partitionSizesMap.put(cfQueueKey, queueRoutingInfo.getPartitions()); + partitionTopicsMap.put(cfQueueKey, cfEventTopic); + QueueKey cfQueueStatesKey = queueKey.withQueueName(DataConstants.CF_STATES_QUEUE_NAME); + partitionSizesMap.put(cfQueueStatesKey, queueRoutingInfo.getPartitions()); + partitionTopicsMap.put(cfQueueStatesKey, cfStateTopic); + } partitionTopicsMap.put(queueKey, queueRoutingInfo.getQueueTopic()); partitionSizesMap.put(queueKey, queueRoutingInfo.getPartitions()); queueConfigs.put(queueKey, new QueueConfig(queueRoutingInfo)); @@ -252,6 +262,15 @@ public class HashPartitionService implements PartitionService { partitionTopicsMap.remove(queueKey); partitionSizesMap.remove(queueKey); queueConfigs.remove(queueKey); + + if (DataConstants.MAIN_QUEUE_NAME.equals(queueKey.getQueueName())) { + QueueKey cfQueueKey = queueKey.withQueueName(DataConstants.CF_QUEUE_NAME); + partitionSizesMap.remove(cfQueueKey); + partitionTopicsMap.remove(cfQueueKey); + QueueKey cfQueueStatesKey = queueKey.withQueueName(DataConstants.CF_STATES_QUEUE_NAME); + partitionSizesMap.remove(cfQueueStatesKey); + partitionTopicsMap.remove(cfQueueStatesKey); + } } @Override @@ -336,8 +355,7 @@ public class HashPartitionService implements PartitionService { } } - @Override - public TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) { + private TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) { Integer partitionSize = partitionSizesMap.get(queueKey); if (partitionSize == null) { throw new IllegalStateException("Partitions info for queue " + queueKey + " is missing"); @@ -552,11 +570,6 @@ public class HashPartitionService implements PartitionService { return list == null ? 0 : list.size(); } - @Override - public int getTotalCalculatedFieldPartitions() { - return cfPartitions; - } - private Map> getServiceKeyListMap(List services) { final Map> currentMap = new HashMap<>(); services.forEach(serviceInfo -> { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java index 404b0258c0..7abd68e25f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java @@ -37,8 +37,6 @@ public interface PartitionService { TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId); - TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId); - List resolveAll(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId); boolean isMyPartition(ServiceType serviceType, TenantId tenantId, EntityId entityId); @@ -81,6 +79,4 @@ public interface PartitionService { int resolvePartitionIndex(UUID entityId, int partitions); - int getTotalCalculatedFieldPartitions(); - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueKey.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueKey.java index ca38959fdd..6720a9d71e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueKey.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/QueueKey.java @@ -35,9 +35,6 @@ public class QueueKey { private final String queueName; private final TenantId tenantId; - public static final QueueKey CF = new QueueKey(ServiceType.TB_RULE_ENGINE).withQueueName(CF_QUEUE_NAME); - public static final QueueKey CF_STATES = new QueueKey(ServiceType.TB_RULE_ENGINE).withQueueName(CF_STATES_QUEUE_NAME); - public QueueKey(ServiceType type, Queue queue) { this.type = type; this.queueName = queue.getName(); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java index 597463300a..f165f60be7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java @@ -64,10 +64,10 @@ public class PartitionChangeEvent extends TbApplicationEvent { } public Set getCfPartitions() { - return newPartitions.getOrDefault(QueueKey.CF, Collections.emptySet()); + return getPartitionsByServiceTypeAndQueueName(ServiceType.TB_RULE_ENGINE, DataConstants.CF_QUEUE_NAME); } - private Set getPartitionsByServiceTypeAndQueueName(ServiceType serviceType, String queueName) { + public Set getPartitionsByServiceTypeAndQueueName(ServiceType serviceType, String queueName) { return newPartitions.entrySet() .stream() .filter(entry -> serviceType.equals(entry.getKey().getType()) && queueName.equals(entry.getKey().getQueueName())) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index bb9c1a028e..f3d1e2d158 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -494,7 +494,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createEdgeEventMsgConsumer(TenantId tenantId, EdgeId edgeId) { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(topicService.buildTopicName("tb_edge_event.notifications." + tenantId + "." + edgeId)); + consumerBuilder.topic(topicService.buildEdgeEventNotificationsTopicPartitionInfo(tenantId, edgeId).getTopic()); consumerBuilder.clientId("monolith-to-edge-event-consumer-" + serviceInfoProvider.getServiceId() + "-" + edgeConsumerCount.incrementAndGet()); consumerBuilder.groupId(topicService.buildTopicName("monolith-edge-event-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeEventNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index f3adb266a4..3c6d144a0c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -440,7 +440,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { public TbQueueConsumer> createEdgeEventMsgConsumer(TenantId tenantId, EdgeId edgeId) { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(topicService.buildTopicName("tb_edge_event.notifications." + tenantId + "." + edgeId)); + consumerBuilder.topic(topicService.buildEdgeEventNotificationsTopicPartitionInfo(tenantId, edgeId).getTopic()); consumerBuilder.clientId("tb-core-edge-event-consumer-" + serviceInfoProvider.getServiceId() + "-" + edgeConsumerCount.incrementAndGet()); consumerBuilder.groupId(topicService.buildTopicName("tb-core-edge-event-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToEdgeEventNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java index 7cbac0401d..3d9b855064 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java @@ -20,12 +20,16 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfObject; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.StatsCounter; +import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.common.stats.StatsType; import java.util.Map; import java.util.UUID; @@ -34,22 +38,31 @@ import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; import static java.lang.String.format; @Slf4j public abstract class AbstractScriptInvokeService implements ScriptInvokeService { + private static final String REQUESTS = "requests"; + private static final String INVOKE_RESPONSES = "invoke_responses"; + private static final String EVAL_RESPONSES = "eval_responses"; + private static final String FAILURES = "failures"; + private static final String TIMEOUTS = "timeouts"; + protected final Map disabledScripts = new ConcurrentHashMap<>(); - private final AtomicInteger pushedMsgs = new AtomicInteger(0); - private final AtomicInteger invokeMsgs = new AtomicInteger(0); - private final AtomicInteger evalMsgs = new AtomicInteger(0); - protected final AtomicInteger failedMsgs = new AtomicInteger(0); - protected final AtomicInteger timeoutMsgs = new AtomicInteger(0); - private final FutureCallback evalCallback = new ScriptStatCallback<>(evalMsgs, timeoutMsgs, failedMsgs); - private final FutureCallback invokeCallback = new ScriptStatCallback<>(invokeMsgs, timeoutMsgs, failedMsgs); + private StatsCounter requestsCounter; + private StatsCounter invokeResponsesCounter; + private StatsCounter evalResponsesCounter; + private StatsCounter failuresCounter; + private StatsCounter timeoutsCounter; + + private FutureCallback evalCallback; + private FutureCallback invokeCallback; + + @Autowired + private StatsFactory statsFactory; protected ScheduledExecutorService timeoutExecutorService; @@ -78,6 +91,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService protected abstract boolean isScriptPresent(UUID scriptId); protected abstract boolean isExecEnabled(TenantId tenantId); + protected abstract void reportExecution(TenantId tenantId, CustomerId customerId); protected abstract ListenableFuture doEvalScript(TenantId tenantId, ScriptType scriptType, String scriptBody, UUID scriptId, String[] argNames); @@ -87,6 +101,14 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService protected abstract void doRelease(UUID scriptId) throws Exception; public void init() { + String key = getStatsType().getName(); + this.requestsCounter = statsFactory.createStatsCounter(key, REQUESTS); + this.invokeResponsesCounter = statsFactory.createStatsCounter(key, INVOKE_RESPONSES); + this.evalResponsesCounter = statsFactory.createStatsCounter(key, EVAL_RESPONSES); + this.failuresCounter = statsFactory.createStatsCounter(key, FAILURES); + this.timeoutsCounter = statsFactory.createStatsCounter(key, TIMEOUTS); + this.evalCallback = new ScriptStatCallback<>(evalResponsesCounter, timeoutsCounter, failuresCounter); + this.invokeCallback = new ScriptStatCallback<>(invokeResponsesCounter, timeoutsCounter, failuresCounter); if (getMaxEvalRequestsTimeout() > 0 || getMaxInvokeRequestsTimeout() > 0) { timeoutExecutorService = ThingsBoardExecutors.newSingleThreadScheduledExecutor("script-timeout"); } @@ -100,11 +122,11 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService public void printStats() { if (isStatsEnabled()) { - int pushed = pushedMsgs.getAndSet(0); - int invoked = invokeMsgs.getAndSet(0); - int evaluated = evalMsgs.getAndSet(0); - int failed = failedMsgs.getAndSet(0); - int timedOut = timeoutMsgs.getAndSet(0); + int pushed = requestsCounter.getAndClear(); + int invoked = invokeResponsesCounter.getAndClear(); + int evaluated = evalResponsesCounter.getAndClear(); + int failed = failuresCounter.getAndClear(); + int timedOut = timeoutsCounter.getAndClear(); if (pushed > 0 || invoked > 0 || evaluated > 0 || failed > 0 || timedOut > 0) { log.info("{}: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]", getStatsName(), pushed, invoked + evaluated, invoked, evaluated, failed, timedOut); @@ -119,7 +141,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService return error(format("Script body exceeds maximum allowed size of %s symbols", getMaxScriptBodySize())); } UUID scriptId = UUID.randomUUID(); - pushedMsgs.incrementAndGet(); + requestsCounter.increment(); return withTimeoutAndStatsCallback(scriptId, null, doEvalScript(tenantId, scriptType, scriptBody, scriptId, argNames), evalCallback, getMaxEvalRequestsTimeout()); } else { @@ -141,7 +163,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService return Futures.immediateFailedFuture(handleScriptException(scriptId, null, t)); } reportExecution(tenantId, customerId); - pushedMsgs.incrementAndGet(); + requestsCounter.increment(); log.trace("[{}] InvokeScript uuid {} with timeout {}ms", tenantId, scriptId, getMaxInvokeRequestsTimeout()); var task = doInvokeFunction(scriptId, args); @@ -278,4 +300,6 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService private ListenableFuture error(String message) { return Futures.immediateFailedFuture(new RuntimeException(message)); } + + protected abstract StatsType getStatsType(); } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java index 33812b7852..fd6f5bf7d7 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/ScriptStatCallback.java @@ -19,29 +19,29 @@ import com.google.common.util.concurrent.FutureCallback; import jakarta.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.stats.StatsCounter; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; @Slf4j @AllArgsConstructor public class ScriptStatCallback implements FutureCallback { - private final AtomicInteger successMsgs; - private final AtomicInteger timeoutMsgs; - private final AtomicInteger failedMsgs; + private final StatsCounter successMsgs; + private final StatsCounter timeoutMsgs; + private final StatsCounter failedMsgs; @Override public void onSuccess(@Nullable T result) { - successMsgs.incrementAndGet(); + successMsgs.increment(); } @Override public void onFailure(Throwable t) { if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { - timeoutMsgs.incrementAndGet(); + timeoutMsgs.increment(); } else { - failedMsgs.incrementAndGet(); + failedMsgs.increment(); } } } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java index 4049040c94..b6ddf16e66 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java @@ -26,6 +26,7 @@ import org.thingsboard.script.api.ScriptType; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; @@ -117,4 +118,8 @@ public abstract class AbstractJsInvokeService extends AbstractScriptInvokeServic .hash().toString(); } + @Override + protected StatsType getStatsType() { + return StatsType.JS_INVOKE; + } } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java index 3680e1bfb2..25a7ede547 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java @@ -44,6 +44,7 @@ import org.thingsboard.script.api.TbScriptException; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; @@ -263,4 +264,9 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem protected long getMaxEvalRequestsTimeout() { return maxInvokeRequestsTimeout * 2; } + + @Override + protected StatsType getStatsType() { + return StatsType.TBEL_INVOKE; + } } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index ca3a8db02a..95fff48883 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -18,8 +18,6 @@ package org.thingsboard.script.api.tbel; import com.google.common.primitives.Bytes; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; -import org.mvel2.ConversionHandler; -import org.mvel2.DataConversion; import org.mvel2.ExecutionContext; import org.mvel2.ParserConfiguration; import org.mvel2.execution.ExecutionArrayList; @@ -259,6 +257,8 @@ public class TbUtils { float.class, int.class))); parserConfig.addImport("toInt", new MethodStub(TbUtils.class.getMethod("toInt", double.class))); + parserConfig.addImport("isNaN", new MethodStub(TbUtils.class.getMethod("isNaN", + double.class))); parserConfig.addImport("hexToBytes", new MethodStub(TbUtils.class.getMethod("hexToBytes", ExecutionContext.class, String.class))); parserConfig.addImport("hexToBytesArray", new MethodStub(TbUtils.class.getMethod("hexToBytesArray", @@ -1163,6 +1163,10 @@ public class TbUtils { return BigDecimal.valueOf(value).setScale(0, RoundingMode.HALF_UP).intValue(); } + public static boolean isNaN(double value) { + return Double.isNaN(value); + } + public static ExecutionHashMap toFlatMap(ExecutionContext ctx, Map json) { return toFlatMap(ctx, json, new ArrayList<>(), true); } diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index 69987d3132..e3860ed89a 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -1138,12 +1138,18 @@ public class TbUtilsTest { } @Test - void toInt() { + public void toInt() { Assertions.assertEquals(1729, TbUtils.toInt(doubleVal)); Assertions.assertEquals(13, TbUtils.toInt(12.8)); Assertions.assertEquals(28, TbUtils.toInt(28.0)); } + @Test + public void isNaN() { + Assertions.assertFalse(TbUtils.isNaN(doubleVal)); + Assertions.assertTrue(TbUtils.isNaN(Double.NaN)); + } + private static List toList(byte[] data) { List result = new ArrayList<>(data.length); for (Byte b : data) { diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java index ef9041fab5..126d4dac95 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultCounter.java @@ -41,6 +41,10 @@ public class DefaultCounter { return aiCounter.get(); } + public int getAndClear() { + return aiCounter.getAndSet(0); + } + public void add(int delta){ aiCounter.addAndGet(delta); micrometerCounter.increment(delta); diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java index de64d4fe54..90bf1bec32 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsType.java @@ -20,6 +20,7 @@ public enum StatsType { CORE("core"), TRANSPORT("transport"), JS_INVOKE("jsInvoke"), + TBEL_INVOKE("tbelInvoke"), RATE_EXECUTOR("rateExecutor"), HOUSEKEEPER("housekeeper"), EDGE("edge"), diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index e91c1939f6..3be7f5b91c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -379,6 +379,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig(); if (defaultConfig != null) { updated |= convertToImageUrlsByMapping(tenantId, WIDGET_TYPE_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), defaultConfig, imagesLinks); + updated |= convertToImageUrls(tenantId, prefix, defaultConfig, imagesLinks); widgetTypeDetails.setDefaultConfig(defaultConfig); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index ba88d446d9..9f1f583108 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -21,6 +21,7 @@ import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.EntityId; @@ -158,6 +159,9 @@ public abstract class DataValidator> { protected static void validateQueueName(String name) { validateQueueNameOrTopic(name, NAME); + if (DataConstants.CF_QUEUE_NAME.equals(name) || DataConstants.CF_STATES_QUEUE_NAME.equals(name)) { + throw new DataValidationException(String.format("The queue name '%s' is not allowed. This name is reserved for internal use. Please choose a different name.", name)); + } } protected static void validateQueueTopic(String topic) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java index 71d2f04b81..6094e9b171 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java @@ -147,7 +147,8 @@ public interface EntityViewRepository extends JpaRepository :id ORDER BY e.id") + @Query("SELECT new org.thingsboard.server.common.data.edqs.fields.EntityViewFields(e.id, e.createdTime, e.tenantId, " + + "e.customerId, e.name, e.type, e.additionalInfo, e.version) " + + "FROM EntityViewEntity e WHERE e.id > :id ORDER BY e.id") List findNextBatch(@Param("id") UUID id, Limit limit); } diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index 5767026b11..3862024786 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -143,7 +143,6 @@ function additionalComposeEdqsArgs() { function permissionList() { PERMISSION_LIST=" 799 799 tb-node/log - 799 799 tb-transports/coap/log 799 799 tb-transports/lwm2m/log 799 799 tb-transports/http/log 799 799 tb-transports/mqtt/log @@ -200,29 +199,77 @@ function permissionList() { } function checkFolders() { + CREATE=false + SKIP_CHOWN=false + for i in "$@" + do + case $i in + --create) + CREATE=true + shift + ;; + --skipChown) + SKIP_CHOWN=true + shift + ;; + *) + # unknown option + ;; + esac + done EXIT_CODE=0 PERMISSION_LIST=$(permissionList) || exit $? set -e while read -r USR GRP DIR do - if [ -z "$DIR" ]; then # skip empty lines + IS_EXIST_CHECK_PASSED=false + IS_OWNER_CHECK_PASSED=false + + # skip empty lines + if [ -z "$DIR" ]; then continue fi - MESSAGE="Checking user ${USR} group ${GRP} dir ${DIR}" - if [[ -d "$DIR" ]] && - [[ $(ls -ldn "$DIR" | awk '{print $3}') -eq "$USR" ]] && - [[ $(ls -ldn "$DIR" | awk '{print $4}') -eq "$GRP" ]] - then - MESSAGE="$MESSAGE OK" + + # checks section + echo "Checking if dir ${DIR} exists..." + if [[ -d "$DIR" ]]; then + echo "> OK" + IS_EXIST_CHECK_PASSED=true + if [ "$SKIP_CHOWN" = false ]; then + echo "Checking user ${USR} group ${GRP} ownership for dir ${DIR}..." + if [[ $(ls -ldn "$DIR" | awk '{print $3}') -eq "$USR" ]] && [[ $(ls -ldn "$DIR" | awk '{print $4}') -eq "$GRP" ]]; then + echo "> OK" + IS_OWNER_CHECK_PASSED=true + else + echo "...ownership check failed" + if [ "$CREATE" = false ]; then + EXIT_CODE=1 + fi + fi + fi else - if [ "$1" = "--create" ]; then - echo "Create and chown: user ${USR} group ${GRP} dir ${DIR}" - mkdir -p "$DIR" && sudo chown -R "$USR":"$GRP" "$DIR" - else - echo "$MESSAGE FAILED" + echo "...does not exist" + if [ "$CREATE" = false ]; then EXIT_CODE=1 fi fi + + # create/chown section + if [ "$CREATE" = true ]; then + if [ "$IS_EXIST_CHECK_PASSED" = false ]; then + echo "...will create dir ${DIR}" + if [ "$SKIP_CHOWN" = false ]; then + echo "...will change ownership to user ${USR} group ${GRP} for dir ${DIR}" + mkdir -p "$DIR" && sudo chown -R "$USR":"$GRP" "$DIR" && echo "> OK" + else + mkdir -p "$DIR" && echo "> OK" + fi + elif [ "$IS_OWNER_CHECK_PASSED" = false ] && [ "$SKIP_CHOWN" = false ]; then + echo "...will change ownership to user ${USR} group ${GRP} for dir ${DIR}" + sudo chown -R "$USR":"$GRP" "$DIR" && echo "> OK" + fi + fi + done < <(echo "$PERMISSION_LIST") return $EXIT_CODE } diff --git a/docker/docker-check-log-folders.sh b/docker/docker-check-log-folders.sh index e293968a69..6122f3d2c1 100755 --- a/docker/docker-check-log-folders.sh +++ b/docker/docker-check-log-folders.sh @@ -17,5 +17,12 @@ set -e source compose-utils.sh -checkFolders || exit $? -echo "OK" +if checkFolders "$@" ; then + echo "------" + echo "All checks have passed" +else + CHECK_EXIT_CODE=$? + echo "------" + echo "Some checks did not pass - check the output" + exit $CHECK_EXIT_CODE +fi \ No newline at end of file diff --git a/docker/docker-compose.edqs.volumes.yml b/docker/docker-compose.edqs.volumes.yml index 9d2ce946c8..89b4b3a59c 100644 --- a/docker/docker-compose.edqs.volumes.yml +++ b/docker/docker-compose.edqs.volumes.yml @@ -19,10 +19,10 @@ version: '3.0' services: tb-edqs-1: volumes: - - tb-edqs-log-volume:/var/log/edqs + - tb-edqs-log-volume:/var/log/tb-edqs tb-edqs-2: volumes: - - tb-edqs-log-volume:/var/log/edqs + - tb-edqs-log-volume:/var/log/tb-edqs volumes: tb-edqs-log-volume: diff --git a/docker/docker-compose.edqs.yml b/docker/docker-compose.edqs.yml index 67e9c987e3..6dd9606ee6 100644 --- a/docker/docker-compose.edqs.yml +++ b/docker/docker-compose.edqs.yml @@ -33,10 +33,10 @@ services: restart: always image: "${DOCKER_REPO}/${EDQS_DOCKER_NAME}:${TB_VERSION}" env_file: - - edqs.env + - tb-edqs.env volumes: - - ./edqs/conf:/usr/share/edqs/conf - - ./edqs/log:/var/log/edqs + - ./tb-edqs/conf:/usr/share/tb-edqs/conf + - ./tb-edqs/log:/var/log/tb-edqs ports: - "8080" depends_on: @@ -46,10 +46,10 @@ services: restart: always image: "${DOCKER_REPO}/${EDQS_DOCKER_NAME}:${TB_VERSION}" env_file: - - edqs.env + - tb-edqs.env volumes: - - ./edqs/conf:/usr/share/edqs/conf - - ./edqs/log:/var/log/edqs + - ./tb-edqs/conf:/usr/share/tb-edqs/conf + - ./tb-edqs/log:/var/log/tb-edqs ports: - "8080" depends_on: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 69b7722459..1cee5ad5ad 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -20,7 +20,7 @@ version: '3.0' services: zookeeper: restart: always - image: "zookeeper:3.8.0" + image: "zookeeper:3.8.1" ports: - "2181" environment: diff --git a/docker/docker-create-log-folders.sh b/docker/docker-create-log-folders.sh index 098ffabb31..ed66d4e156 100755 --- a/docker/docker-create-log-folders.sh +++ b/docker/docker-create-log-folders.sh @@ -17,4 +17,4 @@ set -e source compose-utils.sh -checkFolders --create +checkFolders --create "$@" diff --git a/docker/docker-install-tb.sh b/docker/docker-install-tb.sh index da09684d4f..aa68a2252f 100755 --- a/docker/docker-install-tb.sh +++ b/docker/docker-install-tb.sh @@ -53,8 +53,6 @@ ADDITIONAL_COMPOSE_EDQS_ARGS=$(additionalComposeEdqsArgs) || exit $? ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? -checkFolders --create || exit $? - if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then COMPOSE_ARGS="\ diff --git a/docker/docker-start-services.sh b/docker/docker-start-services.sh index 8b380f199b..0d256abcf6 100755 --- a/docker/docker-start-services.sh +++ b/docker/docker-start-services.sh @@ -31,8 +31,6 @@ ADDITIONAL_COMPOSE_MONITORING_ARGS=$(additionalComposeMonitoringArgs) || exit $? ADDITIONAL_COMPOSE_EDQS_ARGS=$(additionalComposeEdqsArgs) || exit $? -checkFolders --create || exit $? - COMPOSE_ARGS="\ -f docker-compose.yml ${ADDITIONAL_CACHE_ARGS} ${ADDITIONAL_COMPOSE_ARGS} ${ADDITIONAL_COMPOSE_QUEUE_ARGS} ${ADDITIONAL_COMPOSE_MONITORING_ARGS} ${ADDITIONAL_COMPOSE_EDQS_ARGS} \ up -d" diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index eca5d34957..05293e475e 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -46,8 +46,6 @@ ADDITIONAL_COMPOSE_EDQS_ARGS=$(additionalComposeEdqsArgs) || exit $? ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? -checkFolders --create || exit $? - COMPOSE_ARGS_PULL="\ -f docker-compose.yml ${ADDITIONAL_CACHE_ARGS} ${ADDITIONAL_COMPOSE_ARGS} ${ADDITIONAL_COMPOSE_QUEUE_ARGS} ${ADDITIONAL_COMPOSE_EDQS_ARGS} \ diff --git a/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json b/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json index e04a5c2d84..15ff06ab8c 100644 --- a/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json +++ b/docker/monitoring/grafana/provisioning/dashboards/core_and_js_metrics.json @@ -223,8 +223,8 @@ "fill": 1, "fillGradient": 0, "gridPos": { - "h": 12, - "w": 24, + "h": 10, + "w": 12, "x": 0, "y": 10 }, @@ -303,6 +303,100 @@ "align": false, "alignLevel": null } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 10 + }, + "hiddenSeries": false, + "id": 19, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum by(statsName) (increase(tbelInvoke_total[1m]))", + "interval": "", + "legendFormat": "{{statsName}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "TbelInvoke Stats", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } } ], "schemaVersion": 27, diff --git a/docker/edqs.env b/docker/tb-edqs.env similarity index 100% rename from docker/edqs.env rename to docker/tb-edqs.env diff --git a/docker/edqs/conf/logback.xml b/docker/tb-edqs/conf/logback.xml similarity index 91% rename from docker/edqs/conf/logback.xml rename to docker/tb-edqs/conf/logback.xml index 40481a8c35..14b5b0f04b 100644 --- a/docker/edqs/conf/logback.xml +++ b/docker/tb-edqs/conf/logback.xml @@ -21,10 +21,10 @@ - /var/log/edqs/${TB_SERVICE_ID}/tb-edqs.log + /var/log/tb-edqs/${TB_SERVICE_ID}/tb-edqs.log - /var/log/edqs/tb-edqs.%d{yyyy-MM-dd}.%i.log + /var/log/tb-edqs/tb-edqs.%d{yyyy-MM-dd}.%i.log 100MB 30 3GB diff --git a/docker/edqs/conf/edqs.conf b/docker/tb-edqs/conf/tb-edqs.conf similarity index 88% rename from docker/edqs/conf/edqs.conf rename to docker/tb-edqs/conf/tb-edqs.conf index 8c6b5d1826..a5a4e6a10c 100644 --- a/docker/edqs/conf/edqs.conf +++ b/docker/tb-edqs/conf/tb-edqs.conf @@ -14,9 +14,9 @@ # limitations under the License. # -export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=/var/log/edqs/${TB_SERVICE_ID}-gc.log:time,uptime,level,tags:filecount=10,filesize=10M" +export JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,heap*,age*,safepoint=debug:file=/var/log/tb-edqs/${TB_SERVICE_ID}-gc.log:time,uptime,level,tags:filecount=10,filesize=10M" export JAVA_OPTS="$JAVA_OPTS -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError" export JAVA_OPTS="$JAVA_OPTS -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:MaxTenuringThreshold=10" export LOG_FILENAME=tb-edqs.out -export LOADER_PATH=/usr/share/edqs/conf +export LOADER_PATH=/usr/share/tb-edqs/conf diff --git a/edqs/src/main/resources/edqs.yml b/edqs/src/main/resources/edqs.yml index 1d7f111e9a..f7d0eda841 100644 --- a/edqs/src/main/resources/edqs.yml +++ b/edqs/src/main/resources/edqs.yml @@ -70,8 +70,6 @@ queue: stats: # Enable/disable statistics for EDQS enabled: "${TB_EDQS_STATS_ENABLED:true}" - # Statistics printing interval for EDQS - print-interval-ms: "${TB_EDQS_STATS_PRINT_INTERVAL_MS:300000}" kafka: # Kafka Bootstrap nodes in "host:port" format diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 9c49ffe2f9..20a0d8cf7a 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -29,7 +29,6 @@ ENV CASSANDRA_DATA=/data/cassandra ENV SPRING_DRIVER_CLASS_NAME=org.postgresql.Driver ENV SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/thingsboard ENV SPRING_DATASOURCE_USERNAME=${pkg.user} -ENV SPRING_DATASOURCE_PASSWORD=postgres ENV CASSANDRA_HOST=localhost ENV CASSANDRA_PORT=9042 diff --git a/msa/tb/docker-postgres/Dockerfile b/msa/tb/docker-postgres/Dockerfile index f9e36ed61e..7c66d626c7 100644 --- a/msa/tb/docker-postgres/Dockerfile +++ b/msa/tb/docker-postgres/Dockerfile @@ -29,7 +29,6 @@ ENV PATH=$PATH:/usr/lib/postgresql/$PG_MAJOR/bin ENV SPRING_DRIVER_CLASS_NAME=org.postgresql.Driver ENV SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/thingsboard ENV SPRING_DATASOURCE_USERNAME=${pkg.user} -ENV SPRING_DATASOURCE_PASSWORD=postgres ENV PGLOG=/var/log/postgres diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index e473df4c54..b66c9e13d5 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -375,12 +375,6 @@ public interface TbContext { ScriptEngine createScriptEngine(ScriptLanguage scriptLang, String script, String... argNames); - void logJsEvalRequest(); - - void logJsEvalResponse(); - - void logJsEvalFailure(); - String getServiceId(); EventLoopGroup getSharedEventLoop(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java index c78bc77d72..6a806e7bc1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java @@ -69,10 +69,8 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) { - ctx.logJsEvalRequest(); ListenableFuture asyncDetails = buildAlarmDetails(msg, alarm.getDetails()); return Futures.transform(asyncDetails, details -> { - ctx.logJsEvalResponse(); AlarmApiCallResult result = ctx.getAlarmService().clearAlarm(ctx.getTenantId(), alarm.getId(), System.currentTimeMillis(), details); if (result.isSuccessful()) { return new TbAlarmResult(false, false, result.isCleared(), result.getAlarm()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java index 837c5ba692..78411d7cec 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java @@ -118,15 +118,11 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode asyncDetails; boolean buildDetails = !config.isUseMessageAlarmData() || config.isOverwriteAlarmDetails(); if (buildDetails) { - ctx.logJsEvalRequest(); asyncDetails = buildAlarmDetails(msg, null); } else { asyncDetails = Futures.immediateFuture(null); } ListenableFuture asyncAlarm = Futures.transform(asyncDetails, details -> { - if (buildDetails) { - ctx.logJsEvalResponse(); - } Alarm newAlarm; if (msgAlarm != null) { newAlarm = msgAlarm; @@ -147,15 +143,11 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode asyncDetails; boolean buildDetails = !config.isUseMessageAlarmData() || config.isOverwriteAlarmDetails(); if (buildDetails) { - ctx.logJsEvalRequest(); asyncDetails = buildAlarmDetails(msg, existingAlarm.getDetails()); } else { asyncDetails = Futures.immediateFuture(null); } ListenableFuture asyncUpdated = Futures.transform(asyncDetails, details -> { - if (buildDetails) { - ctx.logJsEvalResponse(); - } if (msgAlarm != null) { existingAlarm.setSeverity(msgAlarm.getSeverity()); existingAlarm.setPropagate(msgAlarm.isPropagate()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java index 7e241832cf..47ecac154f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java @@ -75,18 +75,15 @@ public class TbLogNode implements TbNode { return; } - ctx.logJsEvalRequest(); Futures.addCallback(scriptEngine.executeToStringAsync(msg), new FutureCallback() { @Override public void onSuccess(@Nullable String result) { - ctx.logJsEvalResponse(); log.info(result); ctx.tellSuccess(msg); } @Override public void onFailure(Throwable t) { - ctx.logJsEvalResponse(); ctx.tellFailure(msg, t); } }, MoreExecutors.directExecutor()); //usually js responses runs on js callback executor diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index 2848e7c45a..90ee0c9048 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -160,10 +160,8 @@ public class TbMsgGeneratorNode implements TbNode { prevMsg = ctx.newMsg(queueName, TbMsg.EMPTY_STRING, originatorId, msg.getCustomerId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); } if (initialized.get()) { - ctx.logJsEvalRequest(); return Futures.transformAsync(scriptEngine.executeGenerateAsync(prevMsg), generated -> { log.trace("generate process response, generated {}, config {}", generated, config); - ctx.logJsEvalResponse(); prevMsg = ctx.newMsg(queueName, generated.getType(), originatorId, msg.getCustomerId(), generated.getMetaData(), generated.getData()); return Futures.immediateFuture(prevMsg); }, MoreExecutors.directExecutor()); //usually it runs on js-executor-remote-callback thread pool diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java index 50f168a5af..e36494a2b6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java @@ -60,15 +60,12 @@ public class TbJsFilterNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ctx.logJsEvalRequest(); withCallback(scriptEngine.executeFilterAsync(msg), filterResult -> { - ctx.logJsEvalResponse(); ctx.tellNext(msg, filterResult ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE); }, t -> { ctx.tellFailure(msg, t); - ctx.logJsEvalFailure(); }, ctx.getDbCallbackExecutor()); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index 3c27740c84..e039eaeb14 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -61,17 +61,14 @@ public class TbJsSwitchNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ctx.logJsEvalRequest(); Futures.addCallback(scriptEngine.executeSwitchAsync(msg), new FutureCallback<>() { @Override public void onSuccess(@Nullable Set result) { - ctx.logJsEvalResponse(); processSwitch(ctx, msg, result); } @Override public void onFailure(Throwable t) { - ctx.logJsEvalFailure(); ctx.tellFailure(msg, t); } }, MoreExecutors.directExecutor()); //usually runs in a callbackExecutor diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java index 6008305570..d544d0647d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java @@ -15,6 +15,8 @@ */ package org.thingsboard.rule.engine.kafka; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; import org.apache.kafka.clients.producer.KafkaProducer; @@ -26,6 +28,7 @@ import org.apache.kafka.common.config.SslConfigs; import org.apache.kafka.common.header.Headers; import org.apache.kafka.common.header.internals.RecordHeader; import org.apache.kafka.common.header.internals.RecordHeaders; +import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.util.ReflectionUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; @@ -35,6 +38,7 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.exception.ThingsboardKafkaClientError; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -48,6 +52,7 @@ import java.util.Properties; type = ComponentType.EXTERNAL, name = "kafka", configClazz = TbKafkaNodeConfiguration.class, + version = 1, nodeDescription = "Publish messages to Kafka server", nodeDetails = "Will send record via Kafka producer to Kafka server. " + "Outbound message will contain response fields (offset, partition, topic)" + @@ -83,8 +88,8 @@ public class TbKafkaNode extends TbAbstractExternalNode { Properties properties = new Properties(); properties.put(ProducerConfig.CLIENT_ID_CONFIG, "producer-tb-kafka-node-" + ctx.getSelfId().getId().toString() + "-" + ctx.getServiceId()); properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); - properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, config.getValueSerializer()); - properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, config.getKeySerializer()); + properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); properties.put(ProducerConfig.ACKS_CONFIG, config.getAcks()); properties.put(ProducerConfig.RETRIES_CONFIG, config.getRetries()); properties.put(ProducerConfig.BATCH_SIZE_CONFIG, config.getBatchSize()); @@ -200,4 +205,22 @@ public class TbKafkaNode extends TbAbstractExternalNode { .build(); } + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + boolean hasChanges = false; + switch (fromVersion) { + case 0 -> { + if (oldConfiguration.has("keySerializer") || oldConfiguration.has("valueSerializer")) { + ObjectNode objectConfiguration = (ObjectNode) oldConfiguration; + objectConfiguration.remove("keySerializer"); + objectConfiguration.remove("valueSerializer"); + hasChanges = true; + } + } + default -> { + } + } + return new TbPair<>(hasChanges, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeConfiguration.java index 867dd2e54e..9a31e58ce7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeConfiguration.java @@ -16,7 +16,6 @@ package org.thingsboard.rule.engine.kafka; import lombok.Data; -import org.apache.kafka.common.serialization.StringSerializer; import org.thingsboard.rule.engine.api.NodeConfiguration; import java.util.Collections; @@ -33,8 +32,6 @@ public class TbKafkaNodeConfiguration implements NodeConfiguration otherProperties; private boolean addMetadataKeyValuesAsKafkaHeaders; @@ -50,8 +47,6 @@ public class TbKafkaNodeConfiguration implements NodeConfiguration> transform(TbContext ctx, TbMsg msg) { - ctx.logJsEvalRequest(); return scriptEngine.executeUpdateAsync(msg); } @Override protected void transformFailure(TbContext ctx, TbMsg msg, Throwable t) { - ctx.logJsEvalFailure(); super.transformFailure(ctx, msg, t); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java index 1d4c55a780..6cb8299b67 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeTest.java @@ -119,8 +119,8 @@ class TbCreateAlarmNodeTest { delete metadata.prevAlarmDetails; //now metadata is the same as it comes IN this rule node } - - + + return details;"""); assertThat(config.getAlarmDetailsBuildTbel()).isEqualTo(""" \ @@ -131,8 +131,8 @@ class TbCreateAlarmNodeTest { metadata.remove('prevAlarmDetails'); //now metadata is the same as it comes IN this rule node } - - + + return details;"""); assertThat(config.getSeverity()).isEqualTo(AlarmSeverity.CRITICAL.name()); assertThat(config.isPropagate()).isFalse(); @@ -247,9 +247,7 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); then(alarmDetailsScriptMock).should().executeJsonAsync(incomingMsg); - then(ctxMock).should().logJsEvalResponse(); // verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest); @@ -421,9 +419,7 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); then(alarmDetailsScriptMock).should().executeJsonAsync(incomingMsg); - then(ctxMock).should().logJsEvalResponse(); // verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest); @@ -616,14 +612,12 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture()); TbMsg actualDummyMsg = dummyMsgCaptor.getValue(); assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType()); assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData()); assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(oldAlarmDetails)); - then(ctxMock).should().logJsEvalResponse(); // verify we called updateAlarm() with correct AlarmUpdateRequest then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest); @@ -793,9 +787,7 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script was not evaluated - then(ctxMock).should(never()).logJsEvalRequest(); then(alarmDetailsScriptMock).should(never()).executeJsonAsync(any()); - then(ctxMock).should(never()).logJsEvalResponse(); // verify we called createAlarm() with correct AlarmCreateOrUpdateActiveRequest then(alarmServiceMock).should().createAlarm(expectedCreateAlarmRequest); @@ -985,14 +977,12 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture()); TbMsg actualDummyMsg = dummyMsgCaptor.getValue(); assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType()); assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData()); assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(oldAlarmDetails)); - then(ctxMock).should().logJsEvalResponse(); // verify we called updateAlarm() with correct AlarmUpdateRequest then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest); @@ -1171,14 +1161,12 @@ class TbCreateAlarmNodeTest { // THEN // verify alarm details script evaluation - then(ctxMock).should().logJsEvalRequest(); var dummyMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); then(alarmDetailsScriptMock).should().executeJsonAsync(dummyMsgCaptor.capture()); TbMsg actualDummyMsg = dummyMsgCaptor.getValue(); assertThat(actualDummyMsg.getType()).isEqualTo(incomingMsg.getType()); assertThat(actualDummyMsg.getData()).isEqualTo(incomingMsg.getData()); assertThat(actualDummyMsg.getMetaData().getData()).containsEntry("prevAlarmDetails", JacksonUtil.toString(alarmDetails)); - then(ctxMock).should().logJsEvalResponse(); // verify we called updateAlarm() with correct AlarmUpdateRequest then(alarmServiceMock).should().updateAlarm(expectedUpdateAlarmRequest); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java index de81d55fdb..486e707571 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/kafka/TbKafkaNodeTest.java @@ -39,8 +39,10 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; import org.thingsboard.rule.engine.TestDbCallbackExecutor; import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; @@ -73,7 +75,7 @@ import static org.mockito.BDDMockito.willReturn; import static org.mockito.BDDMockito.willThrow; @ExtendWith(MockitoExtension.class) -public class TbKafkaNodeTest { +public class TbKafkaNodeTest extends AbstractRuleNodeUpgradeTest { private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("5f2eac08-bd1f-4635-a6c2-437369f996cf")); private final RuleNodeId RULE_NODE_ID = new RuleNodeId(UUID.fromString("d46bb666-ecab-4d89-a28f-5abdca23ac29")); @@ -117,8 +119,6 @@ public class TbKafkaNodeTest { assertThat(config.getLinger()).isEqualTo(0); assertThat(config.getBufferMemory()).isEqualTo(33554432); assertThat(config.getAcks()).isEqualTo("-1"); - assertThat(config.getKeySerializer()).isEqualTo(StringSerializer.class.getName()); - assertThat(config.getValueSerializer()).isEqualTo(StringSerializer.class.getName()); assertThat(config.getOtherProperties()).isEmpty(); assertThat(config.isAddMetadataKeyValuesAsKafkaHeaders()).isFalse(); assertThat(config.getKafkaHeadersCharset()).isEqualTo("UTF-8"); @@ -163,8 +163,8 @@ public class TbKafkaNodeTest { Properties expectedProperties = new Properties(); expectedProperties.put(ProducerConfig.CLIENT_ID_CONFIG, "producer-tb-kafka-node-" + RULE_NODE_ID.getId() + "-" + SERVICE_ID_STR); expectedProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); - expectedProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, config.getValueSerializer()); - expectedProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, config.getKeySerializer()); + expectedProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + expectedProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); expectedProperties.put(ProducerConfig.ACKS_CONFIG, config.getAcks()); expectedProperties.put(ProducerConfig.RETRIES_CONFIG, config.getRetries()); expectedProperties.put(ProducerConfig.BATCH_SIZE_CONFIG, config.getBatchSize()); @@ -454,4 +454,75 @@ public class TbKafkaNodeTest { assertThat(actualMsg).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { + return Stream.of( + //config for version 0 + Arguments.of(0, + "{\n" + + " \"topicPattern\": \"test-topic\",\n" + + " \"keyPattern\": \"test-key\",\n" + + " \"bootstrapServers\": \"localhost:9092\",\n" + + " \"retries\": 0,\n" + + " \"batchSize\": 16384,\n" + + " \"linger\": 0,\n" + + " \"bufferMemory\": 33554432,\n" + + " \"acks\": \"-1\",\n" + + " \"otherProperties\": {},\n" + + " \"addMetadataKeyValuesAsKafkaHeaders\": false,\n" + + " \"kafkaHeadersCharset\": \"UTF-8\",\n" + + " \"keySerializer\": \"org.apache.kafka.common.serialization.StringSerializer\",\n" + + " \"valueSerializer\": \"org.apache.kafka.common.serialization.StringSerializer\"\n" + + "}", + true, + "{\n" + + " \"topicPattern\": \"test-topic\",\n" + + " \"keyPattern\": \"test-key\",\n" + + " \"bootstrapServers\": \"localhost:9092\",\n" + + " \"retries\": 0,\n" + + " \"batchSize\": 16384,\n" + + " \"linger\": 0,\n" + + " \"bufferMemory\": 33554432,\n" + + " \"acks\": \"-1\",\n" + + " \"otherProperties\": {},\n" + + " \"addMetadataKeyValuesAsKafkaHeaders\": false,\n" + + " \"kafkaHeadersCharset\": \"UTF-8\"\n" + + "}" + ), + //config for version 1 with upgrade from version 0 + Arguments.of(1, + "{\n" + + " \"topicPattern\": \"test-topic\",\n" + + " \"keyPattern\": \"test-key\",\n" + + " \"bootstrapServers\": \"localhost:9092\",\n" + + " \"retries\": 0,\n" + + " \"batchSize\": 16384,\n" + + " \"linger\": 0,\n" + + " \"bufferMemory\": 33554432,\n" + + " \"acks\": \"-1\",\n" + + " \"otherProperties\": {},\n" + + " \"addMetadataKeyValuesAsKafkaHeaders\": false,\n" + + " \"kafkaHeadersCharset\": \"UTF-8\"\n" + + "}", + false, + "{\n" + + " \"topicPattern\": \"test-topic\",\n" + + " \"keyPattern\": \"test-key\",\n" + + " \"bootstrapServers\": \"localhost:9092\",\n" + + " \"retries\": 0,\n" + + " \"batchSize\": 16384,\n" + + " \"linger\": 0,\n" + + " \"bufferMemory\": 33554432,\n" + + " \"acks\": \"-1\",\n" + + " \"otherProperties\": {},\n" + + " \"addMetadataKeyValuesAsKafkaHeaders\": false,\n" + + " \"kafkaHeadersCharset\": \"UTF-8\"\n" + + "}" + ) + ); + } + + @Override + protected TbNode getTestNode() { + return node; + } } diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index 8f98a6cf07..dbac347096 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -101,7 +101,8 @@ "node_modules/tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css", "node_modules/jstree-bootstrap-theme/dist/themes/proton/style.min.css", "node_modules/leaflet/dist/leaflet.css", - "src/app/modules/home/components/widget/lib/maps/markers.scss", + "src/app/modules/home/components/widget/lib/maps/map.scss", + "src/app/modules/home/components/widget/lib/maps-legacy/markers.scss", "src/app/modules/home/components/widget/lib/home-page/home-page.scss", "node_modules/leaflet.markercluster/dist/MarkerCluster.css", "node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css", diff --git a/ui-ngx/package.json b/ui-ngx/package.json index f62c991187..1536858de8 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -18,7 +18,6 @@ "@angular/common": "18.2.13", "@angular/compiler": "18.2.13", "@angular/core": "18.2.13", - "@angular/flex-layout": "^15.0.0-beta.42", "@angular/forms": "18.2.13", "@angular/material": "18.2.14", "@angular/platform-browser": "18.2.13", @@ -29,6 +28,7 @@ "@flowjs/ngx-flow": "18.0.1", "@geoman-io/leaflet-geoman-free": "2.17.0", "@iplab/ngx-color-picker": "^18.0.1", + "@maplibre/maplibre-gl-leaflet": "^0.0.22", "@mat-datetimepicker/core": "~14.0.0", "@mdi/svg": "^7.4.47", "@messageformat/core": "^3.4.0", @@ -65,6 +65,7 @@ "leaflet.gridlayer.googlemutant": "0.14.1", "leaflet.markercluster": "1.5.3", "libphonenumber-js": "^1.11.15", + "maplibre-gl": "^4.7.1", "marked": "~12.0.2", "moment": "^2.30.1", "moment-timezone": "^0.5.45", diff --git a/ui-ngx/patches/@angular+flex-layout+15.0.0-beta.42.patch b/ui-ngx/patches/@angular+flex-layout+15.0.0-beta.42.patch deleted file mode 100644 index d4bf9d42cd..0000000000 --- a/ui-ngx/patches/@angular+flex-layout+15.0.0-beta.42.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff --git a/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs b/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs -index 735dff5..52b6392 100644 ---- a/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs -+++ b/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs -@@ -124,7 +124,7 @@ class ClassDirective extends BaseDirective2 { - if (!this.ngClassInstance) { - // Create an instance NgClass Directive instance only if `ngClass=""` has NOT been defined on - // the same host element; since the responsive variations may be defined... -- this.ngClassInstance = new NgClass(iterableDiffers, keyValueDiffers, elementRef, renderer2); -+ this.ngClassInstance = new NgClass(elementRef, renderer2); - } - this.init(); - this.setValue('', ''); -diff --git a/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs.map b/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs.map -index a184784..2d0682e 100644 ---- a/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs.map -+++ b/node_modules/@angular/flex-layout/fesm2020/angular-flex-layout-extended.mjs.map -@@ -1 +1 @@ --{"version":3,"file":"angular-flex-layout-extended.mjs","sources":["../../../../projects/libs/flex-layout/extended/img-src/img-src.ts","../../../../projects/libs/flex-layout/extended/class/class.ts","../../../../projects/libs/flex-layout/extended/show-hide/show-hide.ts","../../../../projects/libs/flex-layout/extended/style/style-transforms.ts","../../../../projects/libs/flex-layout/extended/style/style.ts","../../../../projects/libs/flex-layout/extended/module.ts","../../../../projects/libs/flex-layout/extended/public-api.ts","../../../../projects/libs/flex-layout/extended/angular-flex-layout-extended.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {Directive, ElementRef, Inject, PLATFORM_ID, Injectable, Input} from '@angular/core';\nimport {isPlatformServer} from '@angular/common';\nimport {\n MediaMarshaller,\n BaseDirective2,\n SERVER_TOKEN,\n StyleBuilder,\n StyleDefinition,\n StyleUtils,\n} from '@angular/flex-layout/core';\n\n@Injectable({providedIn: 'root'})\nexport class ImgSrcStyleBuilder extends StyleBuilder {\n buildStyles(url: string) {\n return {'content': url ? `url(${url})` : ''};\n }\n}\n\n@Directive()\nexport class ImgSrcDirective extends BaseDirective2 {\n protected override DIRECTIVE_KEY = 'img-src';\n protected defaultSrc = '';\n\n @Input('src')\n set src(val: string) {\n this.defaultSrc = val;\n this.setValue(this.defaultSrc, '');\n }\n\n constructor(elementRef: ElementRef,\n styleBuilder: ImgSrcStyleBuilder,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n @Inject(PLATFORM_ID) protected platformId: Object,\n @Inject(SERVER_TOKEN) protected serverModuleLoaded: boolean) {\n super(elementRef, styleBuilder, styler, marshal);\n this.init();\n this.setValue(this.nativeElement.getAttribute('src') || '', '');\n if (isPlatformServer(this.platformId) && this.serverModuleLoaded) {\n this.nativeElement.setAttribute('src', '');\n }\n }\n\n /**\n * Use the [responsively] activated input value to update\n * the host img src attribute or assign a default `img.src=''`\n * if the src has not been defined.\n *\n * Do nothing to standard `` usages, only when responsive\n * keys are present do we actually call `setAttribute()`\n */\n protected override updateWithValue(value?: string) {\n const url = value || this.defaultSrc;\n if (isPlatformServer(this.platformId) && this.serverModuleLoaded) {\n this.addStyles(url);\n } else {\n this.nativeElement.setAttribute('src', url);\n }\n }\n\n protected override styleCache = imgSrcCache;\n}\n\nconst imgSrcCache: Map = new Map();\n\nconst inputs = [\n 'src.xs', 'src.sm', 'src.md', 'src.lg', 'src.xl',\n 'src.lt-sm', 'src.lt-md', 'src.lt-lg', 'src.lt-xl',\n 'src.gt-xs', 'src.gt-sm', 'src.gt-md', 'src.gt-lg'\n];\n\nconst selector = `\n img[src.xs], img[src.sm], img[src.md], img[src.lg], img[src.xl],\n img[src.lt-sm], img[src.lt-md], img[src.lt-lg], img[src.lt-xl],\n img[src.gt-xs], img[src.gt-sm], img[src.gt-md], img[src.gt-lg]\n`;\n\n/**\n * This directive provides a responsive API for the HTML 'src' attribute\n * and will update the img.src property upon each responsive activation.\n *\n * e.g.\n * \n *\n * @see https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-src/\n */\n@Directive({selector, inputs})\nexport class DefaultImgSrcDirective extends ImgSrcDirective {\n protected override inputs = inputs;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {\n Directive,\n DoCheck,\n ElementRef,\n Input,\n IterableDiffers,\n KeyValueDiffers,\n Optional,\n Renderer2,\n Self,\n} from '@angular/core';\nimport {NgClass} from '@angular/common';\nimport {BaseDirective2, StyleUtils, MediaMarshaller} from '@angular/flex-layout/core';\n\n@Directive()\nexport class ClassDirective extends BaseDirective2 implements DoCheck {\n\n protected override DIRECTIVE_KEY = 'ngClass';\n\n /**\n * Capture class assignments so we cache the default classes\n * which are merged with activated styles and used as fallbacks.\n */\n @Input('class')\n set klass(val: string) {\n this.ngClassInstance.klass = val;\n this.setValue(val, '');\n }\n\n constructor(elementRef: ElementRef,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n iterableDiffers: IterableDiffers,\n keyValueDiffers: KeyValueDiffers,\n renderer2: Renderer2,\n @Optional() @Self() protected readonly ngClassInstance: NgClass) {\n super(elementRef, null!, styler, marshal);\n if (!this.ngClassInstance) {\n // Create an instance NgClass Directive instance only if `ngClass=\"\"` has NOT been defined on\n // the same host element; since the responsive variations may be defined...\n this.ngClassInstance = new NgClass(iterableDiffers, keyValueDiffers, elementRef, renderer2);\n }\n this.init();\n this.setValue('', '');\n }\n\n protected override updateWithValue(value: any) {\n this.ngClassInstance.ngClass = value;\n this.ngClassInstance.ngDoCheck();\n }\n\n // ******************************************************************\n // Lifecycle Hooks\n // ******************************************************************\n\n /**\n * For ChangeDetectionStrategy.onPush and ngOnChanges() updates\n */\n ngDoCheck() {\n this.ngClassInstance.ngDoCheck();\n }\n}\n\nconst inputs = [\n 'ngClass', 'ngClass.xs', 'ngClass.sm', 'ngClass.md', 'ngClass.lg', 'ngClass.xl',\n 'ngClass.lt-sm', 'ngClass.lt-md', 'ngClass.lt-lg', 'ngClass.lt-xl',\n 'ngClass.gt-xs', 'ngClass.gt-sm', 'ngClass.gt-md', 'ngClass.gt-lg'\n];\n\nconst selector = `\n [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl],\n [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl],\n [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]\n`;\n\n/**\n * Directive to add responsive support for ngClass.\n * This maintains the core functionality of 'ngClass' and adds responsive API\n * Note: this class is a no-op when rendered on the server\n */\n@Directive({selector, inputs})\nexport class DefaultClassDirective extends ClassDirective {\n protected override inputs = inputs;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {\n Directive,\n ElementRef,\n OnChanges,\n SimpleChanges,\n Inject,\n PLATFORM_ID,\n Injectable,\n AfterViewInit,\n} from '@angular/core';\nimport {isPlatformServer} from '@angular/common';\nimport {\n BaseDirective2,\n LAYOUT_CONFIG,\n LayoutConfigOptions,\n MediaMarshaller,\n SERVER_TOKEN,\n StyleUtils,\n StyleBuilder,\n} from '@angular/flex-layout/core';\nimport {coerceBooleanProperty} from '@angular/cdk/coercion';\nimport {takeUntil} from 'rxjs/operators';\n\nexport interface ShowHideParent {\n display: string;\n isServer: boolean;\n}\n\n@Injectable({providedIn: 'root'})\nexport class ShowHideStyleBuilder extends StyleBuilder {\n buildStyles(show: string, parent: ShowHideParent) {\n const shouldShow = show === 'true';\n return {'display': shouldShow ? parent.display || (parent.isServer ? 'initial' : '') : 'none'};\n }\n}\n\n@Directive()\nexport class ShowHideDirective extends BaseDirective2 implements AfterViewInit, OnChanges {\n protected override DIRECTIVE_KEY = 'show-hide';\n\n /** Original DOM Element CSS display style */\n protected display: string = '';\n protected hasLayout = false;\n protected hasFlexChild = false;\n\n constructor(elementRef: ElementRef,\n styleBuilder: ShowHideStyleBuilder,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n @Inject(LAYOUT_CONFIG) protected layoutConfig: LayoutConfigOptions,\n @Inject(PLATFORM_ID) protected platformId: Object,\n @Inject(SERVER_TOKEN) protected serverModuleLoaded: boolean) {\n super(elementRef, styleBuilder, styler, marshal);\n }\n\n // *********************************************\n // Lifecycle Methods\n // *********************************************\n\n ngAfterViewInit() {\n this.trackExtraTriggers();\n\n const children = Array.from(this.nativeElement.children);\n for (let i = 0; i < children.length; i++) {\n if (this.marshal.hasValue(children[i] as HTMLElement, 'flex')) {\n this.hasFlexChild = true;\n break;\n }\n }\n\n if (DISPLAY_MAP.has(this.nativeElement)) {\n this.display = DISPLAY_MAP.get(this.nativeElement)!;\n } else {\n this.display = this.getDisplayStyle();\n DISPLAY_MAP.set(this.nativeElement, this.display);\n }\n\n this.init();\n // set the default to show unless explicitly overridden\n const defaultValue = this.marshal.getValue(this.nativeElement, this.DIRECTIVE_KEY, '');\n if (defaultValue === undefined || defaultValue === '') {\n this.setValue(true, '');\n } else {\n this.triggerUpdate();\n }\n }\n\n /**\n * On changes to any @Input properties...\n * Default to use the non-responsive Input value ('fxShow')\n * Then conditionally override with the mq-activated Input's current value\n */\n override ngOnChanges(changes: SimpleChanges) {\n Object.keys(changes).forEach(key => {\n if (this.inputs.indexOf(key) !== -1) {\n const inputKey = key.split('.');\n const bp = inputKey.slice(1).join('.');\n const inputValue = changes[key].currentValue;\n let shouldShow = inputValue !== '' ?\n inputValue !== 0 ? coerceBooleanProperty(inputValue) : false\n : true;\n if (inputKey[0] === 'fxHide') {\n shouldShow = !shouldShow;\n }\n this.setValue(shouldShow, bp);\n }\n });\n }\n\n // *********************************************\n // Protected methods\n // *********************************************\n\n /**\n * Watch for these extra triggers to update fxShow, fxHide stylings\n */\n protected trackExtraTriggers() {\n this.hasLayout = this.marshal.hasValue(this.nativeElement, 'layout');\n\n ['layout', 'layout-align'].forEach(key => {\n this.marshal\n .trackValue(this.nativeElement, key)\n .pipe(takeUntil(this.destroySubject))\n .subscribe(this.triggerUpdate.bind(this));\n });\n }\n\n /**\n * Override accessor to the current HTMLElement's `display` style\n * Note: Show/Hide will not change the display to 'flex' but will set it to 'block'\n * unless it was already explicitly specified inline or in a CSS stylesheet.\n */\n protected getDisplayStyle(): string {\n return (this.hasLayout || (this.hasFlexChild && this.layoutConfig.addFlexToParent)) ?\n 'flex' : this.styler.lookupStyle(this.nativeElement, 'display', true);\n }\n\n /** Validate the visibility value and then update the host's inline display style */\n protected override updateWithValue(value: boolean | string = true) {\n if (value === '') {\n return;\n }\n const isServer = isPlatformServer(this.platformId);\n this.addStyles(value ? 'true' : 'false', {display: this.display, isServer});\n if (isServer && this.serverModuleLoaded) {\n this.nativeElement.style.setProperty('display', '');\n }\n this.marshal.triggerUpdate(this.parentElement!, 'layout-gap');\n }\n}\n\nconst DISPLAY_MAP: WeakMap = new WeakMap();\n\nconst inputs = [\n 'fxShow', 'fxShow.print',\n 'fxShow.xs', 'fxShow.sm', 'fxShow.md', 'fxShow.lg', 'fxShow.xl',\n 'fxShow.lt-sm', 'fxShow.lt-md', 'fxShow.lt-lg', 'fxShow.lt-xl',\n 'fxShow.gt-xs', 'fxShow.gt-sm', 'fxShow.gt-md', 'fxShow.gt-lg',\n 'fxHide', 'fxHide.print',\n 'fxHide.xs', 'fxHide.sm', 'fxHide.md', 'fxHide.lg', 'fxHide.xl',\n 'fxHide.lt-sm', 'fxHide.lt-md', 'fxHide.lt-lg', 'fxHide.lt-xl',\n 'fxHide.gt-xs', 'fxHide.gt-sm', 'fxHide.gt-md', 'fxHide.gt-lg'\n];\n\nconst selector = `\n [fxShow], [fxShow.print],\n [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl],\n [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl],\n [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg],\n [fxHide], [fxHide.print],\n [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl],\n [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl],\n [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]\n`;\n\n/**\n * 'show' Layout API directive\n */\n@Directive({selector, inputs})\nexport class DefaultShowHideDirective extends ShowHideDirective {\n protected override inputs = inputs;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nexport type NgStyleRawList = string[];\nexport type NgStyleMap = {[klass: string]: string};\n// NgStyle selectors accept NgStyleType values\nexport type NgStyleType = string | Set | NgStyleRawList | NgStyleMap;\n\n/**\n * Callback function for SecurityContext.STYLE sanitization\n */\nexport type NgStyleSanitizer = (val: any) => string;\n\n/** NgStyle allowed inputs */\nexport class NgStyleKeyValue {\n constructor(public key: string, public value: string, noQuotes = true) {\n this.key = noQuotes ? key.replace(/['\"]/g, '').trim() : key.trim();\n\n this.value = noQuotes ? value.replace(/['\"]/g, '').trim() : value.trim();\n this.value = this.value.replace(/;/, '');\n }\n}\n\nexport function getType(target: any): string {\n let what = typeof target;\n if (what === 'object') {\n return (target.constructor === Array) ? 'array' :\n (target.constructor === Set) ? 'set' : 'object';\n }\n return what;\n}\n\n/**\n * Split string of key:value pairs into Array of k-v pairs\n * e.g. 'key:value; key:value; key:value;' -> ['key:value',...]\n */\nexport function buildRawList(source: any, delimiter = ';'): NgStyleRawList {\n return String(source)\n .trim()\n .split(delimiter)\n .map((val: string) => val.trim())\n .filter(val => val !== '');\n}\n\n/** Convert array of key:value strings to a iterable map object */\nexport function buildMapFromList(styles: NgStyleRawList, sanitize?: NgStyleSanitizer): NgStyleMap {\n const sanitizeValue = (it: NgStyleKeyValue) => {\n if (sanitize) {\n it.value = sanitize(it.value);\n }\n return it;\n };\n\n return styles\n .map(stringToKeyValue)\n .filter(entry => !!entry)\n .map(sanitizeValue)\n .reduce(keyValuesToMap, {} as NgStyleMap);\n}\n\n/** Convert Set or raw Object to an iterable NgStyleMap */\nexport function buildMapFromSet(source: NgStyleType, sanitize?: NgStyleSanitizer): NgStyleMap {\n let list: string[] = [];\n if (getType(source) === 'set') {\n (source as Set).forEach(entry => list.push(entry));\n } else {\n Object.keys(source).forEach((key: string) => {\n list.push(`${key}:${(source as NgStyleMap)[key]}`);\n });\n }\n return buildMapFromList(list, sanitize);\n}\n\n\n/** Convert 'key:value' -> [key, value] */\nexport function stringToKeyValue(it: string): NgStyleKeyValue {\n const [key, ...vals] = it.split(':');\n return new NgStyleKeyValue(key, vals.join(':'));\n}\n\n/** Convert [ [key,value] ] -> { key : value } */\nexport function keyValuesToMap(map: NgStyleMap, entry: NgStyleKeyValue): NgStyleMap {\n if (!!entry.key) {\n map[entry.key] = entry.value;\n }\n return map;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {\n Directive,\n DoCheck,\n ElementRef,\n Inject,\n KeyValueDiffers,\n Optional,\n PLATFORM_ID,\n Renderer2,\n SecurityContext,\n Self,\n} from '@angular/core';\nimport {isPlatformServer, NgStyle} from '@angular/common';\nimport {DomSanitizer} from '@angular/platform-browser';\nimport {\n BaseDirective2,\n StyleUtils,\n MediaMarshaller,\n SERVER_TOKEN,\n} from '@angular/flex-layout/core';\n\nimport {\n NgStyleRawList,\n NgStyleType,\n NgStyleSanitizer,\n buildRawList,\n getType,\n buildMapFromSet,\n NgStyleMap,\n NgStyleKeyValue,\n stringToKeyValue,\n keyValuesToMap,\n} from './style-transforms';\n\n@Directive()\nexport class StyleDirective extends BaseDirective2 implements DoCheck {\n\n protected override DIRECTIVE_KEY = 'ngStyle';\n protected fallbackStyles: NgStyleMap;\n protected isServer: boolean;\n\n constructor(elementRef: ElementRef,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n protected sanitizer: DomSanitizer,\n differs: KeyValueDiffers,\n renderer2: Renderer2,\n @Optional() @Self() private readonly ngStyleInstance: NgStyle,\n @Inject(SERVER_TOKEN) serverLoaded: boolean,\n @Inject(PLATFORM_ID) platformId: Object) {\n super(elementRef, null!, styler, marshal);\n if (!this.ngStyleInstance) {\n // Create an instance NgStyle Directive instance only if `ngStyle=\"\"` has NOT been\n // defined on the same host element; since the responsive variations may be defined...\n this.ngStyleInstance = new NgStyle(elementRef, differs, renderer2);\n }\n this.init();\n const styles = this.nativeElement.getAttribute('style') ?? '';\n this.fallbackStyles = this.buildStyleMap(styles);\n this.isServer = serverLoaded && isPlatformServer(platformId);\n }\n\n /** Add generated styles */\n protected override updateWithValue(value: any) {\n const styles = this.buildStyleMap(value);\n this.ngStyleInstance.ngStyle = {...this.fallbackStyles, ...styles};\n if (this.isServer) {\n this.applyStyleToElement(styles);\n }\n this.ngStyleInstance.ngDoCheck();\n }\n\n /** Remove generated styles */\n protected override clearStyles() {\n this.ngStyleInstance.ngStyle = this.fallbackStyles;\n this.ngStyleInstance.ngDoCheck();\n }\n\n /**\n * Convert raw strings to ngStyleMap; which is required by ngStyle\n * NOTE: Raw string key-value pairs MUST be delimited by `;`\n * Comma-delimiters are not supported due to complexities of\n * possible style values such as `rgba(x,x,x,x)` and others\n */\n protected buildStyleMap(styles: NgStyleType): NgStyleMap {\n // Always safe-guard (aka sanitize) style property values\n const sanitizer: NgStyleSanitizer = (val: any) =>\n this.sanitizer.sanitize(SecurityContext.STYLE, val) ?? '';\n if (styles) {\n switch (getType(styles)) {\n case 'string': return buildMapFromList(buildRawList(styles),\n sanitizer);\n case 'array' : return buildMapFromList(styles as NgStyleRawList, sanitizer);\n case 'set' : return buildMapFromSet(styles, sanitizer);\n default : return buildMapFromSet(styles, sanitizer);\n }\n }\n\n return {};\n }\n\n // ******************************************************************\n // Lifecycle Hooks\n // ******************************************************************\n\n /** For ChangeDetectionStrategy.onPush and ngOnChanges() updates */\n ngDoCheck() {\n this.ngStyleInstance.ngDoCheck();\n }\n}\n\nconst inputs = [\n 'ngStyle',\n 'ngStyle.xs', 'ngStyle.sm', 'ngStyle.md', 'ngStyle.lg', 'ngStyle.xl',\n 'ngStyle.lt-sm', 'ngStyle.lt-md', 'ngStyle.lt-lg', 'ngStyle.lt-xl',\n 'ngStyle.gt-xs', 'ngStyle.gt-sm', 'ngStyle.gt-md', 'ngStyle.gt-lg'\n];\n\nconst selector = `\n [ngStyle],\n [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl],\n [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl],\n [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]\n`;\n\n/**\n * Directive to add responsive support for ngStyle.\n *\n */\n@Directive({selector, inputs})\nexport class DefaultStyleDirective extends StyleDirective implements DoCheck {\n protected override inputs = inputs;\n}\n\n/** Build a styles map from a list of styles, while sanitizing bad values first */\nfunction buildMapFromList(styles: NgStyleRawList, sanitize?: NgStyleSanitizer): NgStyleMap {\n const sanitizeValue = (it: NgStyleKeyValue) => {\n if (sanitize) {\n it.value = sanitize(it.value);\n }\n return it;\n };\n\n return styles\n .map(stringToKeyValue)\n .filter(entry => !!entry)\n .map(sanitizeValue)\n .reduce(keyValuesToMap, {} as NgStyleMap);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {NgModule} from '@angular/core';\nimport {CoreModule} from '@angular/flex-layout/core';\n\nimport {DefaultImgSrcDirective} from './img-src/img-src';\nimport {DefaultClassDirective} from './class/class';\nimport {DefaultShowHideDirective} from './show-hide/show-hide';\nimport {DefaultStyleDirective} from './style/style';\n\n\nconst ALL_DIRECTIVES = [\n DefaultShowHideDirective,\n DefaultClassDirective,\n DefaultStyleDirective,\n DefaultImgSrcDirective,\n];\n\n/**\n * *****************************************************************\n * Define module for the Extended API\n * *****************************************************************\n */\n\n@NgModule({\n imports: [CoreModule],\n declarations: [...ALL_DIRECTIVES],\n exports: [...ALL_DIRECTIVES]\n})\nexport class ExtendedModule {\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nexport * from './module';\n\nexport * from './class/class';\nexport * from './img-src/img-src';\nexport * from './show-hide/show-hide';\nexport * from './style/style';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["inputs","selector","buildMapFromList","i2","i3"],"mappings":"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"} -\ No newline at end of file -+{"version":3,"file":"angular-flex-layout-extended.mjs","sources":["../../../../projects/libs/flex-layout/extended/img-src/img-src.ts","../../../../projects/libs/flex-layout/extended/class/class.ts","../../../../projects/libs/flex-layout/extended/show-hide/show-hide.ts","../../../../projects/libs/flex-layout/extended/style/style-transforms.ts","../../../../projects/libs/flex-layout/extended/style/style.ts","../../../../projects/libs/flex-layout/extended/module.ts","../../../../projects/libs/flex-layout/extended/public-api.ts","../../../../projects/libs/flex-layout/extended/angular-flex-layout-extended.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {Directive, ElementRef, Inject, PLATFORM_ID, Injectable, Input} from '@angular/core';\nimport {isPlatformServer} from '@angular/common';\nimport {\n MediaMarshaller,\n BaseDirective2,\n SERVER_TOKEN,\n StyleBuilder,\n StyleDefinition,\n StyleUtils,\n} from '@angular/flex-layout/core';\n\n@Injectable({providedIn: 'root'})\nexport class ImgSrcStyleBuilder extends StyleBuilder {\n buildStyles(url: string) {\n return {'content': url ? `url(${url})` : ''};\n }\n}\n\n@Directive()\nexport class ImgSrcDirective extends BaseDirective2 {\n protected override DIRECTIVE_KEY = 'img-src';\n protected defaultSrc = '';\n\n @Input('src')\n set src(val: string) {\n this.defaultSrc = val;\n this.setValue(this.defaultSrc, '');\n }\n\n constructor(elementRef: ElementRef,\n styleBuilder: ImgSrcStyleBuilder,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n @Inject(PLATFORM_ID) protected platformId: Object,\n @Inject(SERVER_TOKEN) protected serverModuleLoaded: boolean) {\n super(elementRef, styleBuilder, styler, marshal);\n this.init();\n this.setValue(this.nativeElement.getAttribute('src') || '', '');\n if (isPlatformServer(this.platformId) && this.serverModuleLoaded) {\n this.nativeElement.setAttribute('src', '');\n }\n }\n\n /**\n * Use the [responsively] activated input value to update\n * the host img src attribute or assign a default `img.src=''`\n * if the src has not been defined.\n *\n * Do nothing to standard `` usages, only when responsive\n * keys are present do we actually call `setAttribute()`\n */\n protected override updateWithValue(value?: string) {\n const url = value || this.defaultSrc;\n if (isPlatformServer(this.platformId) && this.serverModuleLoaded) {\n this.addStyles(url);\n } else {\n this.nativeElement.setAttribute('src', url);\n }\n }\n\n protected override styleCache = imgSrcCache;\n}\n\nconst imgSrcCache: Map = new Map();\n\nconst inputs = [\n 'src.xs', 'src.sm', 'src.md', 'src.lg', 'src.xl',\n 'src.lt-sm', 'src.lt-md', 'src.lt-lg', 'src.lt-xl',\n 'src.gt-xs', 'src.gt-sm', 'src.gt-md', 'src.gt-lg'\n];\n\nconst selector = `\n img[src.xs], img[src.sm], img[src.md], img[src.lg], img[src.xl],\n img[src.lt-sm], img[src.lt-md], img[src.lt-lg], img[src.lt-xl],\n img[src.gt-xs], img[src.gt-sm], img[src.gt-md], img[src.gt-lg]\n`;\n\n/**\n * This directive provides a responsive API for the HTML 'src' attribute\n * and will update the img.src property upon each responsive activation.\n *\n * e.g.\n * \n *\n * @see https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-src/\n */\n@Directive({selector, inputs})\nexport class DefaultImgSrcDirective extends ImgSrcDirective {\n protected override inputs = inputs;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {\n Directive,\n DoCheck,\n ElementRef,\n Input,\n IterableDiffers,\n KeyValueDiffers,\n Optional,\n Renderer2,\n Self,\n} from '@angular/core';\nimport {NgClass} from '@angular/common';\nimport {BaseDirective2, StyleUtils, MediaMarshaller} from '@angular/flex-layout/core';\n\n@Directive()\nexport class ClassDirective extends BaseDirective2 implements DoCheck {\n\n protected override DIRECTIVE_KEY = 'ngClass';\n\n /**\n * Capture class assignments so we cache the default classes\n * which are merged with activated styles and used as fallbacks.\n */\n @Input('class')\n set klass(val: string) {\n this.ngClassInstance.klass = val;\n this.setValue(val, '');\n }\n\n constructor(elementRef: ElementRef,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n iterableDiffers: IterableDiffers,\n keyValueDiffers: KeyValueDiffers,\n renderer2: Renderer2,\n @Optional() @Self() protected readonly ngClassInstance: NgClass) {\n super(elementRef, null!, styler, marshal);\n if (!this.ngClassInstance) {\n // Create an instance NgClass Directive instance only if `ngClass=\"\"` has NOT been defined on\n // the same host element; since the responsive variations may be defined...\n this.ngClassInstance = new NgClass(elementRef, renderer2);\n }\n this.init();\n this.setValue('', '');\n }\n\n protected override updateWithValue(value: any) {\n this.ngClassInstance.ngClass = value;\n this.ngClassInstance.ngDoCheck();\n }\n\n // ******************************************************************\n // Lifecycle Hooks\n // ******************************************************************\n\n /**\n * For ChangeDetectionStrategy.onPush and ngOnChanges() updates\n */\n ngDoCheck() {\n this.ngClassInstance.ngDoCheck();\n }\n}\n\nconst inputs = [\n 'ngClass', 'ngClass.xs', 'ngClass.sm', 'ngClass.md', 'ngClass.lg', 'ngClass.xl',\n 'ngClass.lt-sm', 'ngClass.lt-md', 'ngClass.lt-lg', 'ngClass.lt-xl',\n 'ngClass.gt-xs', 'ngClass.gt-sm', 'ngClass.gt-md', 'ngClass.gt-lg'\n];\n\nconst selector = `\n [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl],\n [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl],\n [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]\n`;\n\n/**\n * Directive to add responsive support for ngClass.\n * This maintains the core functionality of 'ngClass' and adds responsive API\n * Note: this class is a no-op when rendered on the server\n */\n@Directive({selector, inputs})\nexport class DefaultClassDirective extends ClassDirective {\n protected override inputs = inputs;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {\n Directive,\n ElementRef,\n OnChanges,\n SimpleChanges,\n Inject,\n PLATFORM_ID,\n Injectable,\n AfterViewInit,\n} from '@angular/core';\nimport {isPlatformServer} from '@angular/common';\nimport {\n BaseDirective2,\n LAYOUT_CONFIG,\n LayoutConfigOptions,\n MediaMarshaller,\n SERVER_TOKEN,\n StyleUtils,\n StyleBuilder,\n} from '@angular/flex-layout/core';\nimport {coerceBooleanProperty} from '@angular/cdk/coercion';\nimport {takeUntil} from 'rxjs/operators';\n\nexport interface ShowHideParent {\n display: string;\n isServer: boolean;\n}\n\n@Injectable({providedIn: 'root'})\nexport class ShowHideStyleBuilder extends StyleBuilder {\n buildStyles(show: string, parent: ShowHideParent) {\n const shouldShow = show === 'true';\n return {'display': shouldShow ? parent.display || (parent.isServer ? 'initial' : '') : 'none'};\n }\n}\n\n@Directive()\nexport class ShowHideDirective extends BaseDirective2 implements AfterViewInit, OnChanges {\n protected override DIRECTIVE_KEY = 'show-hide';\n\n /** Original DOM Element CSS display style */\n protected display: string = '';\n protected hasLayout = false;\n protected hasFlexChild = false;\n\n constructor(elementRef: ElementRef,\n styleBuilder: ShowHideStyleBuilder,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n @Inject(LAYOUT_CONFIG) protected layoutConfig: LayoutConfigOptions,\n @Inject(PLATFORM_ID) protected platformId: Object,\n @Inject(SERVER_TOKEN) protected serverModuleLoaded: boolean) {\n super(elementRef, styleBuilder, styler, marshal);\n }\n\n // *********************************************\n // Lifecycle Methods\n // *********************************************\n\n ngAfterViewInit() {\n this.trackExtraTriggers();\n\n const children = Array.from(this.nativeElement.children);\n for (let i = 0; i < children.length; i++) {\n if (this.marshal.hasValue(children[i] as HTMLElement, 'flex')) {\n this.hasFlexChild = true;\n break;\n }\n }\n\n if (DISPLAY_MAP.has(this.nativeElement)) {\n this.display = DISPLAY_MAP.get(this.nativeElement)!;\n } else {\n this.display = this.getDisplayStyle();\n DISPLAY_MAP.set(this.nativeElement, this.display);\n }\n\n this.init();\n // set the default to show unless explicitly overridden\n const defaultValue = this.marshal.getValue(this.nativeElement, this.DIRECTIVE_KEY, '');\n if (defaultValue === undefined || defaultValue === '') {\n this.setValue(true, '');\n } else {\n this.triggerUpdate();\n }\n }\n\n /**\n * On changes to any @Input properties...\n * Default to use the non-responsive Input value ('fxShow')\n * Then conditionally override with the mq-activated Input's current value\n */\n override ngOnChanges(changes: SimpleChanges) {\n Object.keys(changes).forEach(key => {\n if (this.inputs.indexOf(key) !== -1) {\n const inputKey = key.split('.');\n const bp = inputKey.slice(1).join('.');\n const inputValue = changes[key].currentValue;\n let shouldShow = inputValue !== '' ?\n inputValue !== 0 ? coerceBooleanProperty(inputValue) : false\n : true;\n if (inputKey[0] === 'fxHide') {\n shouldShow = !shouldShow;\n }\n this.setValue(shouldShow, bp);\n }\n });\n }\n\n // *********************************************\n // Protected methods\n // *********************************************\n\n /**\n * Watch for these extra triggers to update fxShow, fxHide stylings\n */\n protected trackExtraTriggers() {\n this.hasLayout = this.marshal.hasValue(this.nativeElement, 'layout');\n\n ['layout', 'layout-align'].forEach(key => {\n this.marshal\n .trackValue(this.nativeElement, key)\n .pipe(takeUntil(this.destroySubject))\n .subscribe(this.triggerUpdate.bind(this));\n });\n }\n\n /**\n * Override accessor to the current HTMLElement's `display` style\n * Note: Show/Hide will not change the display to 'flex' but will set it to 'block'\n * unless it was already explicitly specified inline or in a CSS stylesheet.\n */\n protected getDisplayStyle(): string {\n return (this.hasLayout || (this.hasFlexChild && this.layoutConfig.addFlexToParent)) ?\n 'flex' : this.styler.lookupStyle(this.nativeElement, 'display', true);\n }\n\n /** Validate the visibility value and then update the host's inline display style */\n protected override updateWithValue(value: boolean | string = true) {\n if (value === '') {\n return;\n }\n const isServer = isPlatformServer(this.platformId);\n this.addStyles(value ? 'true' : 'false', {display: this.display, isServer});\n if (isServer && this.serverModuleLoaded) {\n this.nativeElement.style.setProperty('display', '');\n }\n this.marshal.triggerUpdate(this.parentElement!, 'layout-gap');\n }\n}\n\nconst DISPLAY_MAP: WeakMap = new WeakMap();\n\nconst inputs = [\n 'fxShow', 'fxShow.print',\n 'fxShow.xs', 'fxShow.sm', 'fxShow.md', 'fxShow.lg', 'fxShow.xl',\n 'fxShow.lt-sm', 'fxShow.lt-md', 'fxShow.lt-lg', 'fxShow.lt-xl',\n 'fxShow.gt-xs', 'fxShow.gt-sm', 'fxShow.gt-md', 'fxShow.gt-lg',\n 'fxHide', 'fxHide.print',\n 'fxHide.xs', 'fxHide.sm', 'fxHide.md', 'fxHide.lg', 'fxHide.xl',\n 'fxHide.lt-sm', 'fxHide.lt-md', 'fxHide.lt-lg', 'fxHide.lt-xl',\n 'fxHide.gt-xs', 'fxHide.gt-sm', 'fxHide.gt-md', 'fxHide.gt-lg'\n];\n\nconst selector = `\n [fxShow], [fxShow.print],\n [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl],\n [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl],\n [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg],\n [fxHide], [fxHide.print],\n [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl],\n [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl],\n [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]\n`;\n\n/**\n * 'show' Layout API directive\n */\n@Directive({selector, inputs})\nexport class DefaultShowHideDirective extends ShowHideDirective {\n protected override inputs = inputs;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nexport type NgStyleRawList = string[];\nexport type NgStyleMap = {[klass: string]: string};\n// NgStyle selectors accept NgStyleType values\nexport type NgStyleType = string | Set | NgStyleRawList | NgStyleMap;\n\n/**\n * Callback function for SecurityContext.STYLE sanitization\n */\nexport type NgStyleSanitizer = (val: any) => string;\n\n/** NgStyle allowed inputs */\nexport class NgStyleKeyValue {\n constructor(public key: string, public value: string, noQuotes = true) {\n this.key = noQuotes ? key.replace(/['\"]/g, '').trim() : key.trim();\n\n this.value = noQuotes ? value.replace(/['\"]/g, '').trim() : value.trim();\n this.value = this.value.replace(/;/, '');\n }\n}\n\nexport function getType(target: any): string {\n let what = typeof target;\n if (what === 'object') {\n return (target.constructor === Array) ? 'array' :\n (target.constructor === Set) ? 'set' : 'object';\n }\n return what;\n}\n\n/**\n * Split string of key:value pairs into Array of k-v pairs\n * e.g. 'key:value; key:value; key:value;' -> ['key:value',...]\n */\nexport function buildRawList(source: any, delimiter = ';'): NgStyleRawList {\n return String(source)\n .trim()\n .split(delimiter)\n .map((val: string) => val.trim())\n .filter(val => val !== '');\n}\n\n/** Convert array of key:value strings to a iterable map object */\nexport function buildMapFromList(styles: NgStyleRawList, sanitize?: NgStyleSanitizer): NgStyleMap {\n const sanitizeValue = (it: NgStyleKeyValue) => {\n if (sanitize) {\n it.value = sanitize(it.value);\n }\n return it;\n };\n\n return styles\n .map(stringToKeyValue)\n .filter(entry => !!entry)\n .map(sanitizeValue)\n .reduce(keyValuesToMap, {} as NgStyleMap);\n}\n\n/** Convert Set or raw Object to an iterable NgStyleMap */\nexport function buildMapFromSet(source: NgStyleType, sanitize?: NgStyleSanitizer): NgStyleMap {\n let list: string[] = [];\n if (getType(source) === 'set') {\n (source as Set).forEach(entry => list.push(entry));\n } else {\n Object.keys(source).forEach((key: string) => {\n list.push(`${key}:${(source as NgStyleMap)[key]}`);\n });\n }\n return buildMapFromList(list, sanitize);\n}\n\n\n/** Convert 'key:value' -> [key, value] */\nexport function stringToKeyValue(it: string): NgStyleKeyValue {\n const [key, ...vals] = it.split(':');\n return new NgStyleKeyValue(key, vals.join(':'));\n}\n\n/** Convert [ [key,value] ] -> { key : value } */\nexport function keyValuesToMap(map: NgStyleMap, entry: NgStyleKeyValue): NgStyleMap {\n if (!!entry.key) {\n map[entry.key] = entry.value;\n }\n return map;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {\n Directive,\n DoCheck,\n ElementRef,\n Inject,\n KeyValueDiffers,\n Optional,\n PLATFORM_ID,\n Renderer2,\n SecurityContext,\n Self,\n} from '@angular/core';\nimport {isPlatformServer, NgStyle} from '@angular/common';\nimport {DomSanitizer} from '@angular/platform-browser';\nimport {\n BaseDirective2,\n StyleUtils,\n MediaMarshaller,\n SERVER_TOKEN,\n} from '@angular/flex-layout/core';\n\nimport {\n NgStyleRawList,\n NgStyleType,\n NgStyleSanitizer,\n buildRawList,\n getType,\n buildMapFromSet,\n NgStyleMap,\n NgStyleKeyValue,\n stringToKeyValue,\n keyValuesToMap,\n} from './style-transforms';\n\n@Directive()\nexport class StyleDirective extends BaseDirective2 implements DoCheck {\n\n protected override DIRECTIVE_KEY = 'ngStyle';\n protected fallbackStyles: NgStyleMap;\n protected isServer: boolean;\n\n constructor(elementRef: ElementRef,\n styler: StyleUtils,\n marshal: MediaMarshaller,\n protected sanitizer: DomSanitizer,\n differs: KeyValueDiffers,\n renderer2: Renderer2,\n @Optional() @Self() private readonly ngStyleInstance: NgStyle,\n @Inject(SERVER_TOKEN) serverLoaded: boolean,\n @Inject(PLATFORM_ID) platformId: Object) {\n super(elementRef, null!, styler, marshal);\n if (!this.ngStyleInstance) {\n // Create an instance NgStyle Directive instance only if `ngStyle=\"\"` has NOT been\n // defined on the same host element; since the responsive variations may be defined...\n this.ngStyleInstance = new NgStyle(elementRef, differs, renderer2);\n }\n this.init();\n const styles = this.nativeElement.getAttribute('style') ?? '';\n this.fallbackStyles = this.buildStyleMap(styles);\n this.isServer = serverLoaded && isPlatformServer(platformId);\n }\n\n /** Add generated styles */\n protected override updateWithValue(value: any) {\n const styles = this.buildStyleMap(value);\n this.ngStyleInstance.ngStyle = {...this.fallbackStyles, ...styles};\n if (this.isServer) {\n this.applyStyleToElement(styles);\n }\n this.ngStyleInstance.ngDoCheck();\n }\n\n /** Remove generated styles */\n protected override clearStyles() {\n this.ngStyleInstance.ngStyle = this.fallbackStyles;\n this.ngStyleInstance.ngDoCheck();\n }\n\n /**\n * Convert raw strings to ngStyleMap; which is required by ngStyle\n * NOTE: Raw string key-value pairs MUST be delimited by `;`\n * Comma-delimiters are not supported due to complexities of\n * possible style values such as `rgba(x,x,x,x)` and others\n */\n protected buildStyleMap(styles: NgStyleType): NgStyleMap {\n // Always safe-guard (aka sanitize) style property values\n const sanitizer: NgStyleSanitizer = (val: any) =>\n this.sanitizer.sanitize(SecurityContext.STYLE, val) ?? '';\n if (styles) {\n switch (getType(styles)) {\n case 'string': return buildMapFromList(buildRawList(styles),\n sanitizer);\n case 'array' : return buildMapFromList(styles as NgStyleRawList, sanitizer);\n case 'set' : return buildMapFromSet(styles, sanitizer);\n default : return buildMapFromSet(styles, sanitizer);\n }\n }\n\n return {};\n }\n\n // ******************************************************************\n // Lifecycle Hooks\n // ******************************************************************\n\n /** For ChangeDetectionStrategy.onPush and ngOnChanges() updates */\n ngDoCheck() {\n this.ngStyleInstance.ngDoCheck();\n }\n}\n\nconst inputs = [\n 'ngStyle',\n 'ngStyle.xs', 'ngStyle.sm', 'ngStyle.md', 'ngStyle.lg', 'ngStyle.xl',\n 'ngStyle.lt-sm', 'ngStyle.lt-md', 'ngStyle.lt-lg', 'ngStyle.lt-xl',\n 'ngStyle.gt-xs', 'ngStyle.gt-sm', 'ngStyle.gt-md', 'ngStyle.gt-lg'\n];\n\nconst selector = `\n [ngStyle],\n [ngStyle.xs], [ngStyle.sm], [ngStyle.md], [ngStyle.lg], [ngStyle.xl],\n [ngStyle.lt-sm], [ngStyle.lt-md], [ngStyle.lt-lg], [ngStyle.lt-xl],\n [ngStyle.gt-xs], [ngStyle.gt-sm], [ngStyle.gt-md], [ngStyle.gt-lg]\n`;\n\n/**\n * Directive to add responsive support for ngStyle.\n *\n */\n@Directive({selector, inputs})\nexport class DefaultStyleDirective extends StyleDirective implements DoCheck {\n protected override inputs = inputs;\n}\n\n/** Build a styles map from a list of styles, while sanitizing bad values first */\nfunction buildMapFromList(styles: NgStyleRawList, sanitize?: NgStyleSanitizer): NgStyleMap {\n const sanitizeValue = (it: NgStyleKeyValue) => {\n if (sanitize) {\n it.value = sanitize(it.value);\n }\n return it;\n };\n\n return styles\n .map(stringToKeyValue)\n .filter(entry => !!entry)\n .map(sanitizeValue)\n .reduce(keyValuesToMap, {} as NgStyleMap);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nimport {NgModule} from '@angular/core';\nimport {CoreModule} from '@angular/flex-layout/core';\n\nimport {DefaultImgSrcDirective} from './img-src/img-src';\nimport {DefaultClassDirective} from './class/class';\nimport {DefaultShowHideDirective} from './show-hide/show-hide';\nimport {DefaultStyleDirective} from './style/style';\n\n\nconst ALL_DIRECTIVES = [\n DefaultShowHideDirective,\n DefaultClassDirective,\n DefaultStyleDirective,\n DefaultImgSrcDirective,\n];\n\n/**\n * *****************************************************************\n * Define module for the Extended API\n * *****************************************************************\n */\n\n@NgModule({\n imports: [CoreModule],\n declarations: [...ALL_DIRECTIVES],\n exports: [...ALL_DIRECTIVES]\n})\nexport class ExtendedModule {\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nexport * from './module';\n\nexport * from './class/class';\nexport * from './img-src/img-src';\nexport * from './show-hide/show-hide';\nexport * from './style/style';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["inputs","selector","buildMapFromList","i2","i3"],"mappings":"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"} diff --git a/ui-ngx/patches/tooltipster+4.2.8.patch b/ui-ngx/patches/tooltipster+4.2.8.patch new file mode 100644 index 0000000000..d328300720 --- /dev/null +++ b/ui-ngx/patches/tooltipster+4.2.8.patch @@ -0,0 +1,223 @@ +diff --git a/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.js b/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.js +index 8d94dc7..8123bac 100644 +--- a/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.js ++++ b/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.js +@@ -1,32 +1,34 @@ + (function (root, factory) { ++ if (root === undefined && window !== undefined) root = window; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["tooltipster"], function (a0) { + return (factory(a0)); + }); +- } else if (typeof exports === 'object') { ++ } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("tooltipster")); + } else { +- factory(jQuery); ++ factory(root["jQuery"]); + } + }(this, function ($) { + + (function (root, factory) { ++ if (root === undefined && window !== undefined) root = window; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["jquery"], function (a0) { + return (factory(a0)); + }); +- } else if (typeof exports === 'object') { ++ } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { +- factory(jQuery); ++ factory(root["jQuery"]); + } + }(this, function ($) { + +diff --git a/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js b/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js +index df77068..84bdfdd 100644 +--- a/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js ++++ b/node_modules/tooltipster/dist/js/plugins/tooltipster/SVG/tooltipster-SVG.min.js +@@ -1 +1 @@ +-!function(a,b){"function"==typeof define&&define.amd?define(["tooltipster"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("tooltipster")):b(jQuery)}(this,function(a){!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){var b="tooltipster.SVG";return a.tooltipster._plugin({name:b,core:{__init:function(){a.tooltipster._on("init",function(c){var d=a.tooltipster._env.window;d.SVGElement&&c.origin instanceof d.SVGElement&&c.instance._plug(b)})}},instance:{__init:function(b){var c=this;if(c.__hadTitleTag=!1,c.__instance=b,!c.__instance._$origin.hasClass("tooltipstered")){var d=c.__instance._$origin.attr("class")||"";-1==d.indexOf("tooltipstered")&&c.__instance._$origin.attr("class",d+" tooltipstered")}if(null===c.__instance.content()){var e=c.__instance._$origin.find(">title");if(e[0]){var f=e.text();c.__hadTitleTag=!0,c.__instance._$origin.data("tooltipster-initialTitle",f),c.__instance.content(f),e.remove()}}c.__instance._on("geometry."+c.namespace,function(b){var c=a.tooltipster._env.window;if(c.SVG.svgjs){c.SVG.parser||c.SVG.prepare();var d=c.SVG.adopt(b.origin);if(d&&d.screenBBox){var e=d.screenBBox();b.edit({height:e.height,left:e.x,top:e.y,width:e.width})}}})._on("destroy."+c.namespace,function(){c.__destroy()})},__destroy:function(){var b=this;if(!b.__instance._$origin.hasClass("tooltipstered")){var c=b.__instance._$origin.attr("class").replace("tooltipstered","");b.__instance._$origin.attr("class",c)}b.__instance._off("."+b.namespace),b.__hadTitleTag&&b.__instance.one("destroyed",function(){var c=b.__instance._$origin.attr("title");c&&(a(document.createElementNS("http://www.w3.org/2000/svg","title")).text(c).appendTo(b.__instance._$origin),b.__instance._$origin.removeAttr("title"))})}}}),a})}); +\ No newline at end of file ++!function(a,b){void 0===a&&void 0!==window&&(a=window),"function"==typeof define&&define.amd?define(["tooltipster"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("tooltipster")):b(a.jQuery)}(this,function(a){!function(a,b){void 0===a&&void 0!==window&&(a=window),"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){var b="tooltipster.SVG";return a.tooltipster._plugin({name:b,core:{__init:function(){a.tooltipster._on("init",function(c){var d=a.tooltipster._env.window;d.SVGElement&&c.origin instanceof d.SVGElement&&c.instance._plug(b)})}},instance:{__init:function(b){var c=this;if(c.__hadTitleTag=!1,c.__instance=b,!c.__instance._$origin.hasClass("tooltipstered")){var d=c.__instance._$origin.attr("class")||"";d.indexOf("tooltipstered")==-1&&c.__instance._$origin.attr("class",d+" tooltipstered")}if(null===c.__instance.content()){var e=c.__instance._$origin.find(">title");if(e[0]){var f=e.text();c.__hadTitleTag=!0,c.__instance._$origin.data("tooltipster-initialTitle",f),c.__instance.content(f),e.remove()}}c.__instance._on("geometry."+c.namespace,function(b){var c=a.tooltipster._env.window;if(c.SVG.svgjs){c.SVG.parser||c.SVG.prepare();var d=c.SVG.adopt(b.origin);if(d&&d.screenBBox){var e=d.screenBBox();b.edit({height:e.height,left:e.x,top:e.y,width:e.width})}}})._on("destroy."+c.namespace,function(){c.__destroy()})},__destroy:function(){var b=this;if(!b.__instance._$origin.hasClass("tooltipstered")){var c=b.__instance._$origin.attr("class").replace("tooltipstered","");b.__instance._$origin.attr("class",c)}b.__instance._off("."+b.namespace),b.__hadTitleTag&&b.__instance.one("destroyed",function(){var c=b.__instance._$origin.attr("title");c&&(a(document.createElementNS("http://www.w3.org/2000/svg","title")).text(c).appendTo(b.__instance._$origin),b.__instance._$origin.removeAttr("title"))})}}}),a})}); +\ No newline at end of file +diff --git a/node_modules/tooltipster/dist/js/tooltipster.bundle.js b/node_modules/tooltipster/dist/js/tooltipster.bundle.js +index ed97bdc..2b3381f 100644 +--- a/node_modules/tooltipster/dist/js/tooltipster.bundle.js ++++ b/node_modules/tooltipster/dist/js/tooltipster.bundle.js +@@ -5,18 +5,19 @@ + * MIT license + */ + (function (root, factory) { ++ if (root === undefined && window !== undefined) root = window; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["jquery"], function (a0) { + return (factory(a0)); + }); +- } else if (typeof exports === 'object') { ++ } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { +- factory(jQuery); ++ factory(root["jQuery"]); + } + }(this, function ($) { + +@@ -1260,8 +1261,14 @@ $.Tooltipster.prototype = { + if ( geo.origin.windowOffset.top < bcr.top + || geo.origin.windowOffset.bottom > bcr.bottom + ) { +- overflows = true; +- return false; ++ if (self.__options.checkOverflowY) { ++ overflows = self.__options.checkOverflowY(geo, bcr); ++ } else { ++ overflows = true; ++ } ++ if (overflows) { ++ return false; ++ } + } + } + } +@@ -1412,7 +1419,7 @@ $.Tooltipster.prototype = { + + // close the tooltip when using the mouseleave close trigger + // (see https://github.com/iamceege/tooltipster/pull/253) +- if (self.__options.triggerClose.mouseleave) { ++ if (self.__options.triggerClose.mouseleave && !self.__options.ignoreCloseOnScroll) { + self._close(); + } + else { +@@ -3341,6 +3348,7 @@ function transitionSupport() { + // we'll return jQuery for plugins not to have to declare it as a dependency, + // but it's done by a build task since it should be included only once at the + // end when we concatenate the main file with a plugin ++ + // sideTip is Tooltipster's default plugin. + // This file will be UMDified by a build task. + +diff --git a/node_modules/tooltipster/dist/js/tooltipster.bundle.min.js b/node_modules/tooltipster/dist/js/tooltipster.bundle.min.js +index bbbd6ad..af0ffc0 100644 +--- a/node_modules/tooltipster/dist/js/tooltipster.bundle.min.js ++++ b/node_modules/tooltipster/dist/js/tooltipster.bundle.min.js +@@ -1,2 +1,2 @@ +-/*! tooltipster v4.2.8 */!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){function b(a){this.$container,this.constraints=null,this.__$tooltip,this.__init(a)}function c(b,c){var d=!0;return a.each(b,function(a,e){return void 0===c[a]||b[a]!==c[a]?(d=!1,!1):void 0}),d}function d(b){var c=b.attr("id"),d=c?h.window.document.getElementById(c):null;return d?d===b[0]:a.contains(h.window.document.body,b[0])}function e(){if(!g)return!1;var a=g.document.body||g.document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e0?e=c.__plugins[d]:a.each(c.__plugins,function(a,b){return b.name.substring(b.name.length-d.length-1)=="."+d?(e=b,!1):void 0}),e}if(b.name.indexOf(".")<0)throw new Error("Plugins must be namespaced");return c.__plugins[b.name]=b,b.core&&c.__bridge(b.core,c,b.name),this},_trigger:function(){var a=Array.prototype.slice.apply(arguments);return"string"==typeof a[0]&&(a[0]={type:a[0]}),this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,a),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,a),this},instances:function(b){var c=[],d=b||".tooltipstered";return a(d).each(function(){var b=a(this),d=b.data("tooltipster-ns");d&&a.each(d,function(a,d){c.push(b.data(d))})}),c},instancesLatest:function(){return this.__instancesLatestArr},off:function(){return this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},origins:function(b){var c=b?b+" ":"";return a(c+".tooltipstered").toArray()},setDefaults:function(b){return a.extend(f,b),this},triggerHandler:function(){return this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.tooltipster=new i,a.Tooltipster=function(b,c){this.__callbacks={close:[],open:[]},this.__closingTime,this.__Content,this.__contentBcr,this.__destroyed=!1,this.__$emitterPrivate=a({}),this.__$emitterPublic=a({}),this.__enabled=!0,this.__garbageCollector,this.__Geometry,this.__lastPosition,this.__namespace="tooltipster-"+Math.round(1e6*Math.random()),this.__options,this.__$originParents,this.__pointerIsOverOrigin=!1,this.__previousThemes=[],this.__state="closed",this.__timeouts={close:[],open:null},this.__touchEvents=[],this.__tracker=null,this._$origin,this._$tooltip,this.__init(b,c)},a.Tooltipster.prototype={__init:function(b,c){var d=this;if(d._$origin=a(b),d.__options=a.extend(!0,{},f,c),d.__optionsFormat(),!h.IE||h.IE>=d.__options.IEmin){var e=null;if(void 0===d._$origin.data("tooltipster-initialTitle")&&(e=d._$origin.attr("title"),void 0===e&&(e=null),d._$origin.data("tooltipster-initialTitle",e)),null!==d.__options.content)d.__contentSet(d.__options.content);else{var g,i=d._$origin.attr("data-tooltip-content");i&&(g=a(i)),g&&g[0]?d.__contentSet(g.first()):d.__contentSet(e)}d._$origin.removeAttr("title").addClass("tooltipstered"),d.__prepareOrigin(),d.__prepareGC(),a.each(d.__options.plugins,function(a,b){d._plug(b)}),h.hasTouchCapability&&a(h.window.document.body).on("touchmove."+d.__namespace+"-triggerOpen",function(a){d._touchRecordEvent(a)}),d._on("created",function(){d.__prepareTooltip()})._on("repositioned",function(a){d.__lastPosition=a.position})}else d.__options.disabled=!0},__contentInsert:function(){var a=this,b=a._$tooltip.find(".tooltipster-content"),c=a.__Content,d=function(a){c=a};return a._trigger({type:"format",content:a.__Content,format:d}),a.__options.functionFormat&&(c=a.__options.functionFormat.call(a,a,{origin:a._$origin[0]},a.__Content)),"string"!=typeof c||a.__options.contentAsHTML?b.empty().append(c):b.text(c),a},__contentSet:function(b){return b instanceof a&&this.__options.contentCloning&&(b=b.clone(!0)),this.__Content=b,this._trigger({type:"updated",content:b}),this},__destroyError:function(){throw new Error("This tooltip has been destroyed and cannot execute your method call.")},__geometry:function(){var b=this,c=b._$origin,d=b._$origin.is("area");if(d){var e=b._$origin.parent().attr("name");c=a('img[usemap="#'+e+'"]')}var f=c[0].getBoundingClientRect(),g=a(h.window.document),i=a(h.window),j=c,k={available:{document:null,window:null},document:{size:{height:g.height(),width:g.width()}},window:{scroll:{left:h.window.scrollX||h.window.document.documentElement.scrollLeft,top:h.window.scrollY||h.window.document.documentElement.scrollTop},size:{height:i.height(),width:i.width()}},origin:{fixedLineage:!1,offset:{},size:{height:f.bottom-f.top,width:f.right-f.left},usemapImage:d?c[0]:null,windowOffset:{bottom:f.bottom,left:f.left,right:f.right,top:f.top}}};if(d){var l=b._$origin.attr("shape"),m=b._$origin.attr("coords");if(m&&(m=m.split(","),a.map(m,function(a,b){m[b]=parseInt(a)})),"default"!=l)switch(l){case"circle":var n=m[0],o=m[1],p=m[2],q=o-p,r=n-p;k.origin.size.height=2*p,k.origin.size.width=k.origin.size.height,k.origin.windowOffset.left+=r,k.origin.windowOffset.top+=q;break;case"rect":var s=m[0],t=m[1],u=m[2],v=m[3];k.origin.size.height=v-t,k.origin.size.width=u-s,k.origin.windowOffset.left+=s,k.origin.windowOffset.top+=t;break;case"poly":for(var w=0,x=0,y=0,z=0,A="even",B=0;By&&(y=C,0===B&&(w=y)),w>C&&(w=C),A="odd"):(C>z&&(z=C,1==B&&(x=z)),x>C&&(x=C),A="even")}k.origin.size.height=z-x,k.origin.size.width=y-w,k.origin.windowOffset.left+=w,k.origin.windowOffset.top+=x}}var D=function(a){k.origin.size.height=a.height,k.origin.windowOffset.left=a.left,k.origin.windowOffset.top=a.top,k.origin.size.width=a.width};for(b._trigger({type:"geometry",edit:D,geometry:{height:k.origin.size.height,left:k.origin.windowOffset.left,top:k.origin.windowOffset.top,width:k.origin.size.width}}),k.origin.windowOffset.right=k.origin.windowOffset.left+k.origin.size.width,k.origin.windowOffset.bottom=k.origin.windowOffset.top+k.origin.size.height,k.origin.offset.left=k.origin.windowOffset.left+k.window.scroll.left,k.origin.offset.top=k.origin.windowOffset.top+k.window.scroll.top,k.origin.offset.bottom=k.origin.offset.top+k.origin.size.height,k.origin.offset.right=k.origin.offset.left+k.origin.size.width,k.available.document={bottom:{height:k.document.size.height-k.origin.offset.bottom,width:k.document.size.width},left:{height:k.document.size.height,width:k.origin.offset.left},right:{height:k.document.size.height,width:k.document.size.width-k.origin.offset.right},top:{height:k.origin.offset.top,width:k.document.size.width}},k.available.window={bottom:{height:Math.max(k.window.size.height-Math.max(k.origin.windowOffset.bottom,0),0),width:k.window.size.width},left:{height:k.window.size.height,width:Math.max(k.origin.windowOffset.left,0)},right:{height:k.window.size.height,width:Math.max(k.window.size.width-Math.max(k.origin.windowOffset.right,0),0)},top:{height:Math.max(k.origin.windowOffset.top,0),width:k.window.size.width}};"html"!=j[0].tagName.toLowerCase();){if("fixed"==j.css("position")){k.origin.fixedLineage=!0;break}j=j.parent()}return k},__optionsFormat:function(){return"number"==typeof this.__options.animationDuration&&(this.__options.animationDuration=[this.__options.animationDuration,this.__options.animationDuration]),"number"==typeof this.__options.delay&&(this.__options.delay=[this.__options.delay,this.__options.delay]),"number"==typeof this.__options.delayTouch&&(this.__options.delayTouch=[this.__options.delayTouch,this.__options.delayTouch]),"string"==typeof this.__options.theme&&(this.__options.theme=[this.__options.theme]),null===this.__options.parent?this.__options.parent=a(h.window.document.body):"string"==typeof this.__options.parent&&(this.__options.parent=a(this.__options.parent)),"hover"==this.__options.trigger?(this.__options.triggerOpen={mouseenter:!0,touchstart:!0},this.__options.triggerClose={mouseleave:!0,originClick:!0,touchleave:!0}):"click"==this.__options.trigger&&(this.__options.triggerOpen={click:!0,tap:!0},this.__options.triggerClose={click:!0,tap:!0}),this._trigger("options"),this},__prepareGC:function(){var b=this;return b.__options.selfDestruction?b.__garbageCollector=setInterval(function(){var c=(new Date).getTime();b.__touchEvents=a.grep(b.__touchEvents,function(a,b){return c-a.time>6e4}),d(b._$origin)||b.close(function(){b.destroy()})},2e4):clearInterval(b.__garbageCollector),b},__prepareOrigin:function(){var a=this;if(a._$origin.off("."+a.__namespace+"-triggerOpen"),h.hasTouchCapability&&a._$origin.on("touchstart."+a.__namespace+"-triggerOpen touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen",function(b){a._touchRecordEvent(b)}),a.__options.triggerOpen.click||a.__options.triggerOpen.tap&&h.hasTouchCapability){var b="";a.__options.triggerOpen.click&&(b+="click."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.tap&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&a._open(b)})}if(a.__options.triggerOpen.mouseenter||a.__options.triggerOpen.touchstart&&h.hasTouchCapability){var b="";a.__options.triggerOpen.mouseenter&&(b+="mouseenter."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.touchstart&&h.hasTouchCapability&&(b+="touchstart."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){!a._touchIsTouchEvent(b)&&a._touchIsEmulatedEvent(b)||(a.__pointerIsOverOrigin=!0,a._openShortly(b))})}if(a.__options.triggerClose.mouseleave||a.__options.triggerClose.touchleave&&h.hasTouchCapability){var b="";a.__options.triggerClose.mouseleave&&(b+="mouseleave."+a.__namespace+"-triggerOpen "),a.__options.triggerClose.touchleave&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&(a.__pointerIsOverOrigin=!1)})}return a},__prepareTooltip:function(){var b=this,c=b.__options.interactive?"auto":"";return b._$tooltip.attr("id",b.__namespace).css({"pointer-events":c,zIndex:b.__options.zIndex}),a.each(b.__previousThemes,function(a,c){b._$tooltip.removeClass(c)}),a.each(b.__options.theme,function(a,c){b._$tooltip.addClass(c)}),b.__previousThemes=a.merge([],b.__options.theme),b},__scrollHandler:function(b){var c=this;if(c.__options.triggerClose.scroll)c._close(b);else if(d(c._$origin)&&d(c._$tooltip)){var e=null;if(b.target===h.window.document)c.__Geometry.origin.fixedLineage||c.__options.repositionOnScroll&&c.reposition(b);else{e=c.__geometry();var f=!1;if("fixed"!=c._$origin.css("position")&&c.__$originParents.each(function(b,c){var d=a(c),g=d.css("overflow-x"),h=d.css("overflow-y");if("visible"!=g||"visible"!=h){var i=c.getBoundingClientRect();if("visible"!=g&&(e.origin.windowOffset.lefti.right))return f=!0,!1;if("visible"!=h&&(e.origin.windowOffset.topi.bottom))return f=!0,!1}return"fixed"==d.css("position")?!1:void 0}),f)c._$tooltip.css("visibility","hidden");else if(c._$tooltip.css("visibility","visible"),c.__options.repositionOnScroll)c.reposition(b);else{var g=e.origin.offset.left-c.__Geometry.origin.offset.left,i=e.origin.offset.top-c.__Geometry.origin.offset.top;c._$tooltip.css({left:c.__lastPosition.coord.left+g,top:c.__lastPosition.coord.top+i})}}c._trigger({type:"scroll",event:b,geo:e})}return c},__stateSet:function(a){return this.__state=a,this._trigger({type:"state",state:a}),this},__timeoutsClear:function(){return clearTimeout(this.__timeouts.open),this.__timeouts.open=null,a.each(this.__timeouts.close,function(a,b){clearTimeout(b)}),this.__timeouts.close=[],this},__trackerStart:function(){var a=this,b=a._$tooltip.find(".tooltipster-content");return a.__options.trackTooltip&&(a.__contentBcr=b[0].getBoundingClientRect()),a.__tracker=setInterval(function(){if(d(a._$origin)&&d(a._$tooltip)){if(a.__options.trackOrigin){var e=a.__geometry(),f=!1;c(e.origin.size,a.__Geometry.origin.size)&&(a.__Geometry.origin.fixedLineage?c(e.origin.windowOffset,a.__Geometry.origin.windowOffset)&&(f=!0):c(e.origin.offset,a.__Geometry.origin.offset)&&(f=!0)),f||(a.__options.triggerClose.mouseleave?a._close():a.reposition())}if(a.__options.trackTooltip){var g=b[0].getBoundingClientRect();g.height===a.__contentBcr.height&&g.width===a.__contentBcr.width||(a.reposition(),a.__contentBcr=g)}}else a._close()},a.__options.trackerInterval),a},_close:function(b,c,d){var e=this,f=!0;if(e._trigger({type:"close",event:b,stop:function(){f=!1}}),f||d){c&&e.__callbacks.close.push(c),e.__callbacks.open=[],e.__timeoutsClear();var g=function(){a.each(e.__callbacks.close,function(a,c){c.call(e,e,{event:b,origin:e._$origin[0]})}),e.__callbacks.close=[]};if("closed"!=e.__state){var i=!0,j=new Date,k=j.getTime(),l=k+e.__options.animationDuration[1];if("disappearing"==e.__state&&l>e.__closingTime&&e.__options.animationDuration[1]>0&&(i=!1),i){e.__closingTime=l,"disappearing"!=e.__state&&e.__stateSet("disappearing");var m=function(){clearInterval(e.__tracker),e._trigger({type:"closing",event:b}),e._$tooltip.off("."+e.__namespace+"-triggerClose").removeClass("tooltipster-dying"),a(h.window).off("."+e.__namespace+"-triggerClose"),e.__$originParents.each(function(b,c){a(c).off("scroll."+e.__namespace+"-triggerClose")}),e.__$originParents=null,a(h.window.document.body).off("."+e.__namespace+"-triggerClose"),e._$origin.off("."+e.__namespace+"-triggerClose"),e._off("dismissable"),e.__stateSet("closed"),e._trigger({type:"after",event:b}),e.__options.functionAfter&&e.__options.functionAfter.call(e,e,{event:b,origin:e._$origin[0]}),g()};h.hasTransitions?(e._$tooltip.css({"-moz-animation-duration":e.__options.animationDuration[1]+"ms","-ms-animation-duration":e.__options.animationDuration[1]+"ms","-o-animation-duration":e.__options.animationDuration[1]+"ms","-webkit-animation-duration":e.__options.animationDuration[1]+"ms","animation-duration":e.__options.animationDuration[1]+"ms","transition-duration":e.__options.animationDuration[1]+"ms"}),e._$tooltip.clearQueue().removeClass("tooltipster-show").addClass("tooltipster-dying"),e.__options.animationDuration[1]>0&&e._$tooltip.delay(e.__options.animationDuration[1]),e._$tooltip.queue(m)):e._$tooltip.stop().fadeOut(e.__options.animationDuration[1],m)}}else g()}return e},_off:function(){return this.__$emitterPrivate.off.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_on:function(){return this.__$emitterPrivate.on.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_one:function(){return this.__$emitterPrivate.one.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_open:function(b,c){var e=this;if(!e.__destroying&&d(e._$origin)&&e.__enabled){var f=!0;if("closed"==e.__state&&(e._trigger({type:"before",event:b,stop:function(){f=!1}}),f&&e.__options.functionBefore&&(f=e.__options.functionBefore.call(e,e,{event:b,origin:e._$origin[0]}))),f!==!1&&null!==e.__Content){c&&e.__callbacks.open.push(c),e.__callbacks.close=[],e.__timeoutsClear();var g,i=function(){"stable"!=e.__state&&e.__stateSet("stable"),a.each(e.__callbacks.open,function(a,b){b.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}),e.__callbacks.open=[]};if("closed"!==e.__state)g=0,"disappearing"===e.__state?(e.__stateSet("appearing"),h.hasTransitions?(e._$tooltip.clearQueue().removeClass("tooltipster-dying").addClass("tooltipster-show"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i)):e._$tooltip.stop().fadeIn(i)):"stable"==e.__state&&i();else{if(e.__stateSet("appearing"),g=e.__options.animationDuration[0],e.__contentInsert(),e.reposition(b,!0),h.hasTransitions?(e._$tooltip.addClass("tooltipster-"+e.__options.animation).addClass("tooltipster-initial").css({"-moz-animation-duration":e.__options.animationDuration[0]+"ms","-ms-animation-duration":e.__options.animationDuration[0]+"ms","-o-animation-duration":e.__options.animationDuration[0]+"ms","-webkit-animation-duration":e.__options.animationDuration[0]+"ms","animation-duration":e.__options.animationDuration[0]+"ms","transition-duration":e.__options.animationDuration[0]+"ms"}),setTimeout(function(){"closed"!=e.__state&&(e._$tooltip.addClass("tooltipster-show").removeClass("tooltipster-initial"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i))},0)):e._$tooltip.css("display","none").fadeIn(e.__options.animationDuration[0],i),e.__trackerStart(),a(h.window).on("resize."+e.__namespace+"-triggerClose",function(b){var c=a(document.activeElement);(c.is("input")||c.is("textarea"))&&a.contains(e._$tooltip[0],c[0])||e.reposition(b)}).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)}),e.__$originParents=e._$origin.parents(),e.__$originParents.each(function(b,c){a(c).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)})}),e.__options.triggerClose.mouseleave||e.__options.triggerClose.touchleave&&h.hasTouchCapability){e._on("dismissable",function(a){a.dismissable?a.delay?(m=setTimeout(function(){e._close(a.event)},a.delay),e.__timeouts.close.push(m)):e._close(a):clearTimeout(m)});var j=e._$origin,k="",l="",m=null;e.__options.interactive&&(j=j.add(e._$tooltip)),e.__options.triggerClose.mouseleave&&(k+="mouseenter."+e.__namespace+"-triggerClose ",l+="mouseleave."+e.__namespace+"-triggerClose "),e.__options.triggerClose.touchleave&&h.hasTouchCapability&&(k+="touchstart."+e.__namespace+"-triggerClose",l+="touchend."+e.__namespace+"-triggerClose touchcancel."+e.__namespace+"-triggerClose"),j.on(l,function(a){if(e._touchIsTouchEvent(a)||!e._touchIsEmulatedEvent(a)){var b="mouseleave"==a.type?e.__options.delay:e.__options.delayTouch;e._trigger({delay:b[1],dismissable:!0,event:a,type:"dismissable"})}}).on(k,function(a){!e._touchIsTouchEvent(a)&&e._touchIsEmulatedEvent(a)||e._trigger({dismissable:!1,event:a,type:"dismissable"})})}e.__options.triggerClose.originClick&&e._$origin.on("click."+e.__namespace+"-triggerClose",function(a){e._touchIsTouchEvent(a)||e._touchIsEmulatedEvent(a)||e._close(a)}),(e.__options.triggerClose.click||e.__options.triggerClose.tap&&h.hasTouchCapability)&&setTimeout(function(){if("closed"!=e.__state){var b="",c=a(h.window.document.body);e.__options.triggerClose.click&&(b+="click."+e.__namespace+"-triggerClose "),e.__options.triggerClose.tap&&h.hasTouchCapability&&(b+="touchend."+e.__namespace+"-triggerClose"),c.on(b,function(b){e._touchIsMeaningfulEvent(b)&&(e._touchRecordEvent(b),e.__options.interactive&&a.contains(e._$tooltip[0],b.target)||e._close(b))}),e.__options.triggerClose.tap&&h.hasTouchCapability&&c.on("touchstart."+e.__namespace+"-triggerClose",function(a){e._touchRecordEvent(a)})}},0),e._trigger("ready"),e.__options.functionReady&&e.__options.functionReady.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}if(e.__options.timer>0){var m=setTimeout(function(){e._close()},e.__options.timer+g);e.__timeouts.close.push(m)}}}return e},_openShortly:function(a){var b=this,c=!0;if("stable"!=b.__state&&"appearing"!=b.__state&&!b.__timeouts.open&&(b._trigger({type:"start",event:a,stop:function(){c=!1}}),c)){var d=0==a.type.indexOf("touch")?b.__options.delayTouch:b.__options.delay;d[0]?b.__timeouts.open=setTimeout(function(){b.__timeouts.open=null,b.__pointerIsOverOrigin&&b._touchIsMeaningfulEvent(a)?(b._trigger("startend"),b._open(a)):b._trigger("startcancel")},d[0]):(b._trigger("startend"),b._open(a))}return b},_optionsExtract:function(b,c){var d=this,e=a.extend(!0,{},c),f=d.__options[b];return f||(f={},a.each(c,function(a,b){var c=d.__options[a];void 0!==c&&(f[a]=c)})),a.each(e,function(b,c){void 0!==f[b]&&("object"!=typeof c||c instanceof Array||null==c||"object"!=typeof f[b]||f[b]instanceof Array||null==f[b]?e[b]=f[b]:a.extend(e[b],f[b]))}),e},_plug:function(b){var c=a.tooltipster._plugin(b);if(!c)throw new Error('The "'+b+'" plugin is not defined');return c.instance&&a.tooltipster.__bridge(c.instance,this,c.name),this},_touchIsEmulatedEvent:function(a){for(var b=!1,c=(new Date).getTime(),d=this.__touchEvents.length-1;d>=0;d--){var e=this.__touchEvents[d];if(!(c-e.time<500))break;e.target===a.target&&(b=!0)}return b},_touchIsMeaningfulEvent:function(a){return this._touchIsTouchEvent(a)&&!this._touchSwiped(a.target)||!this._touchIsTouchEvent(a)&&!this._touchIsEmulatedEvent(a)},_touchIsTouchEvent:function(a){return 0==a.type.indexOf("touch")},_touchRecordEvent:function(a){return this._touchIsTouchEvent(a)&&(a.time=(new Date).getTime(),this.__touchEvents.push(a)),this},_touchSwiped:function(a){for(var b=!1,c=this.__touchEvents.length-1;c>=0;c--){var d=this.__touchEvents[c];if("touchmove"==d.type){b=!0;break}if("touchstart"==d.type&&a===d.target)break}return b},_trigger:function(){var b=Array.prototype.slice.apply(arguments);return"string"==typeof b[0]&&(b[0]={type:b[0]}),b[0].instance=this,b[0].origin=this._$origin?this._$origin[0]:null,b[0].tooltip=this._$tooltip?this._$tooltip[0]:null,this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,b),a.tooltipster._trigger.apply(a.tooltipster,b),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,b),this},_unplug:function(b){var c=this;if(c[b]){var d=a.tooltipster._plugin(b);d.instance&&a.each(d.instance,function(a,d){c[a]&&c[a].bridged===c[b]&&delete c[a]}),c[b].__destroy&&c[b].__destroy(),delete c[b]}return c},close:function(a){return this.__destroyed?this.__destroyError():this._close(null,a),this},content:function(a){var b=this;if(void 0===a)return b.__Content;if(b.__destroyed)b.__destroyError();else if(b.__contentSet(a),null!==b.__Content){if("closed"!==b.__state&&(b.__contentInsert(),b.reposition(),b.__options.updateAnimation))if(h.hasTransitions){var c=b.__options.updateAnimation;b._$tooltip.addClass("tooltipster-update-"+c),setTimeout(function(){"closed"!=b.__state&&b._$tooltip.removeClass("tooltipster-update-"+c)},1e3)}else b._$tooltip.fadeTo(200,.5,function(){"closed"!=b.__state&&b._$tooltip.fadeTo(200,1)})}else b._close();return b},destroy:function(){var b=this;if(b.__destroyed)b.__destroyError();else{"closed"!=b.__state?b.option("animationDuration",0)._close(null,null,!0):b.__timeoutsClear(),b._trigger("destroy"),b.__destroyed=!0,b._$origin.removeData(b.__namespace).off("."+b.__namespace+"-triggerOpen"),a(h.window.document.body).off("."+b.__namespace+"-triggerOpen");var c=b._$origin.data("tooltipster-ns");if(c)if(1===c.length){var d=null;"previous"==b.__options.restoration?d=b._$origin.data("tooltipster-initialTitle"):"current"==b.__options.restoration&&(d="string"==typeof b.__Content?b.__Content:a("
").append(b.__Content).html()),d&&b._$origin.attr("title",d),b._$origin.removeClass("tooltipstered"),b._$origin.removeData("tooltipster-ns").removeData("tooltipster-initialTitle")}else c=a.grep(c,function(a,c){return a!==b.__namespace}),b._$origin.data("tooltipster-ns",c);b._trigger("destroyed"),b._off(),b.off(),b.__Content=null,b.__$emitterPrivate=null,b.__$emitterPublic=null,b.__options.parent=null,b._$origin=null,b._$tooltip=null,a.tooltipster.__instancesLatestArr=a.grep(a.tooltipster.__instancesLatestArr,function(a,c){return b!==a}),clearInterval(b.__garbageCollector)}return b},disable:function(){return this.__destroyed?(this.__destroyError(),this):(this._close(),this.__enabled=!1,this)},elementOrigin:function(){return this.__destroyed?void this.__destroyError():this._$origin[0]},elementTooltip:function(){return this._$tooltip?this._$tooltip[0]:null},enable:function(){return this.__enabled=!0,this},hide:function(a){return this.close(a)},instance:function(){return this},off:function(){return this.__destroyed||this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},open:function(a){return this.__destroyed?this.__destroyError():this._open(null,a),this},option:function(b,c){return void 0===c?this.__options[b]:(this.__destroyed?this.__destroyError():(this.__options[b]=c,this.__optionsFormat(),a.inArray(b,["trigger","triggerClose","triggerOpen"])>=0&&this.__prepareOrigin(),"selfDestruction"===b&&this.__prepareGC()),this)},reposition:function(a,b){var c=this;return c.__destroyed?c.__destroyError():"closed"!=c.__state&&d(c._$origin)&&(b||d(c._$tooltip))&&(b||c._$tooltip.detach(),c.__Geometry=c.__geometry(),c._trigger({type:"reposition",event:a,helper:{geo:c.__Geometry}})),c},show:function(a){return this.open(a)},status:function(){return{destroyed:this.__destroyed,enabled:this.__enabled,open:"closed"!==this.__state,state:this.__state}},triggerHandler:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.fn.tooltipster=function(){var b=Array.prototype.slice.apply(arguments),c="You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.";if(0===this.length)return this;if("string"==typeof b[0]){var d="#*$~&";return this.each(function(){var e=a(this).data("tooltipster-ns"),f=e?a(this).data(e[0]):null;if(!f)throw new Error("You called Tooltipster's \""+b[0]+'" method on an uninitialized element');if("function"!=typeof f[b[0]])throw new Error('Unknown method "'+b[0]+'"');this.length>1&&"content"==b[0]&&(b[1]instanceof a||"object"==typeof b[1]&&null!=b[1]&&b[1].tagName)&&!f.__options.contentCloning&&f.__options.debug&&console.log(c);var g=f[b[0]](b[1],b[2]);return g!==f||"instance"===b[0]?(d=g,!1):void 0}),"#*$~&"!==d?d:this}a.tooltipster.__instancesLatestArr=[];var e=b[0]&&void 0!==b[0].multiple,g=e&&b[0].multiple||!e&&f.multiple,h=b[0]&&void 0!==b[0].content,i=h&&b[0].content||!h&&f.content,j=b[0]&&void 0!==b[0].contentCloning,k=j&&b[0].contentCloning||!j&&f.contentCloning,l=b[0]&&void 0!==b[0].debug,m=l&&b[0].debug||!l&&f.debug;return this.length>1&&(i instanceof a||"object"==typeof i&&null!=i&&i.tagName)&&!k&&m&&console.log(c),this.each(function(){var c=!1,d=a(this),e=d.data("tooltipster-ns"),f=null;e?g?c=!0:m&&(console.log("Tooltipster: one or more tooltips are already attached to the element below. Ignoring."),console.log(this)):c=!0,c&&(f=new a.Tooltipster(this,b[0]),e||(e=[]),e.push(f.__namespace),d.data("tooltipster-ns",e),d.data(f.__namespace,f),f.__options.functionInit&&f.__options.functionInit.call(f,f,{origin:this}),f._trigger("init")),a.tooltipster.__instancesLatestArr.push(f)}),this},b.prototype={__init:function(b){this.__$tooltip=b,this.__$tooltip.css({left:0,overflow:"hidden",position:"absolute",top:0}).find(".tooltipster-content").css("overflow","auto"),this.$container=a('
').append(this.__$tooltip).appendTo(h.window.document.body)},__forceRedraw:function(){var a=this.__$tooltip.parent();this.__$tooltip.detach(),this.__$tooltip.appendTo(a)},constrain:function(a,b){return this.constraints={width:a,height:b},this.__$tooltip.css({display:"block",height:"",overflow:"auto",width:a}),this},destroy:function(){this.__$tooltip.detach().find(".tooltipster-content").css({display:"",overflow:""}),this.$container.remove()},free:function(){return this.constraints=null,this.__$tooltip.css({display:"",height:"",overflow:"visible",width:""}),this},measure:function(){this.__forceRedraw();var a=this.__$tooltip[0].getBoundingClientRect(),b={size:{height:a.height||a.bottom-a.top,width:a.width||a.right-a.left}};if(this.constraints){var c=this.__$tooltip.find(".tooltipster-content"),d=this.__$tooltip.outerHeight(),e=c[0].getBoundingClientRect(),f={height:d<=this.constraints.height,width:a.width<=this.constraints.width&&e.width>=c[0].scrollWidth-1};b.fits=f.height&&f.width}return h.IE&&h.IE<=11&&b.size.width!==h.window.document.documentElement.clientWidth&&(b.size.width=Math.ceil(b.size.width)+1),b}};var j=navigator.userAgent.toLowerCase();-1!=j.indexOf("msie")?h.IE=parseInt(j.split("msie")[1]):-1!==j.toLowerCase().indexOf("trident")&&-1!==j.indexOf(" rv:11")?h.IE=11:-1!=j.toLowerCase().indexOf("edge/")&&(h.IE=parseInt(j.toLowerCase().split("edge/")[1]));var k="tooltipster.sideTip";return a.tooltipster._plugin({name:k,instance:{__defaults:function(){return{arrow:!0,distance:6,functionPosition:null,maxWidth:null,minIntersection:16,minWidth:0,position:null,side:"top",viewportAware:!0}},__init:function(a){var b=this;b.__instance=a,b.__namespace="tooltipster-sideTip-"+Math.round(1e6*Math.random()),b.__previousState="closed",b.__options,b.__optionsFormat(),b.__instance._on("state."+b.__namespace,function(a){"closed"==a.state?b.__close():"appearing"==a.state&&"closed"==b.__previousState&&b.__create(),b.__previousState=a.state}),b.__instance._on("options."+b.__namespace,function(){b.__optionsFormat()}),b.__instance._on("reposition."+b.__namespace,function(a){b.__reposition(a.event,a.helper)})},__close:function(){this.__instance.content()instanceof a&&this.__instance.content().detach(),this.__instance._$tooltip.remove(),this.__instance._$tooltip=null},__create:function(){var b=a('
');this.__options.arrow||b.find(".tooltipster-box").css("margin",0).end().find(".tooltipster-arrow").hide(),this.__options.minWidth&&b.css("min-width",this.__options.minWidth+"px"),this.__options.maxWidth&&b.css("max-width",this.__options.maxWidth+"px"), +-this.__instance._$tooltip=b,this.__instance._trigger("created")},__destroy:function(){this.__instance._off("."+self.__namespace)},__optionsFormat:function(){var b=this;if(b.__options=b.__instance._optionsExtract(k,b.__defaults()),b.__options.position&&(b.__options.side=b.__options.position),"object"!=typeof b.__options.distance&&(b.__options.distance=[b.__options.distance]),b.__options.distance.length<4&&(void 0===b.__options.distance[1]&&(b.__options.distance[1]=b.__options.distance[0]),void 0===b.__options.distance[2]&&(b.__options.distance[2]=b.__options.distance[0]),void 0===b.__options.distance[3]&&(b.__options.distance[3]=b.__options.distance[1])),b.__options.distance={top:b.__options.distance[0],right:b.__options.distance[1],bottom:b.__options.distance[2],left:b.__options.distance[3]},"string"==typeof b.__options.side){var c={top:"bottom",right:"left",bottom:"top",left:"right"};b.__options.side=[b.__options.side,c[b.__options.side]],"left"==b.__options.side[0]||"right"==b.__options.side[0]?b.__options.side.push("top","bottom"):b.__options.side.push("right","left")}6===a.tooltipster._env.IE&&b.__options.arrow!==!0&&(b.__options.arrow=!1)},__reposition:function(b,c){var d,e=this,f=e.__targetFind(c),g=[];e.__instance._$tooltip.detach();var h=e.__instance._$tooltip.clone(),i=a.tooltipster._getRuler(h),j=!1,k=e.__instance.option("animation");switch(k&&h.removeClass("tooltipster-"+k),a.each(["window","document"],function(d,k){var l=null;if(e.__instance._trigger({container:k,helper:c,satisfied:j,takeTest:function(a){l=a},results:g,type:"positionTest"}),1==l||0!=l&&0==j&&("window"!=k||e.__options.viewportAware))for(var d=0;d=h.outerSize.width&&c.geo.available[k][n].height>=h.outerSize.height?h.fits=!0:h.fits=!1:h.fits=p.fits,"window"==k&&(h.fits?"top"==n||"bottom"==n?h.whole=c.geo.origin.windowOffset.right>=e.__options.minIntersection&&c.geo.window.size.width-c.geo.origin.windowOffset.left>=e.__options.minIntersection:h.whole=c.geo.origin.windowOffset.bottom>=e.__options.minIntersection&&c.geo.window.size.height-c.geo.origin.windowOffset.top>=e.__options.minIntersection:h.whole=!1),g.push(h),h.whole)j=!0;else if("natural"==h.mode&&(h.fits||h.size.width<=c.geo.available[k][n].width))return!1}})}}),e.__instance._trigger({edit:function(a){g=a},event:b,helper:c,results:g,type:"positionTested"}),g.sort(function(a,b){if(a.whole&&!b.whole)return-1;if(!a.whole&&b.whole)return 1;if(a.whole&&b.whole){var c=e.__options.side.indexOf(a.side),d=e.__options.side.indexOf(b.side);return d>c?-1:c>d?1:"natural"==a.mode?-1:1}if(a.fits&&!b.fits)return-1;if(!a.fits&&b.fits)return 1;if(a.fits&&b.fits){var c=e.__options.side.indexOf(a.side),d=e.__options.side.indexOf(b.side);return d>c?-1:c>d?1:"natural"==a.mode?-1:1}return"document"==a.container&&"bottom"==a.side&&"natural"==a.mode?-1:1}),d=g[0],d.coord={},d.side){case"left":case"right":d.coord.top=Math.floor(d.target-d.size.height/2);break;case"bottom":case"top":d.coord.left=Math.floor(d.target-d.size.width/2)}switch(d.side){case"left":d.coord.left=c.geo.origin.windowOffset.left-d.outerSize.width;break;case"right":d.coord.left=c.geo.origin.windowOffset.right+d.distance.horizontal;break;case"top":d.coord.top=c.geo.origin.windowOffset.top-d.outerSize.height;break;case"bottom":d.coord.top=c.geo.origin.windowOffset.bottom+d.distance.vertical}"window"==d.container?"top"==d.side||"bottom"==d.side?d.coord.left<0?c.geo.origin.windowOffset.right-this.__options.minIntersection>=0?d.coord.left=0:d.coord.left=c.geo.origin.windowOffset.right-this.__options.minIntersection-1:d.coord.left>c.geo.window.size.width-d.size.width&&(c.geo.origin.windowOffset.left+this.__options.minIntersection<=c.geo.window.size.width?d.coord.left=c.geo.window.size.width-d.size.width:d.coord.left=c.geo.origin.windowOffset.left+this.__options.minIntersection+1-d.size.width):d.coord.top<0?c.geo.origin.windowOffset.bottom-this.__options.minIntersection>=0?d.coord.top=0:d.coord.top=c.geo.origin.windowOffset.bottom-this.__options.minIntersection-1:d.coord.top>c.geo.window.size.height-d.size.height&&(c.geo.origin.windowOffset.top+this.__options.minIntersection<=c.geo.window.size.height?d.coord.top=c.geo.window.size.height-d.size.height:d.coord.top=c.geo.origin.windowOffset.top+this.__options.minIntersection+1-d.size.height):(d.coord.left>c.geo.window.size.width-d.size.width&&(d.coord.left=c.geo.window.size.width-d.size.width),d.coord.left<0&&(d.coord.left=0)),e.__sideChange(h,d.side),c.tooltipClone=h[0],c.tooltipParent=e.__instance.option("parent").parent[0],c.mode=d.mode,c.whole=d.whole,c.origin=e.__instance._$origin[0],c.tooltip=e.__instance._$tooltip[0],delete d.container,delete d.fits,delete d.mode,delete d.outerSize,delete d.whole,d.distance=d.distance.horizontal||d.distance.vertical;var l=a.extend(!0,{},d);if(e.__instance._trigger({edit:function(a){d=a},event:b,helper:c,position:l,type:"position"}),e.__options.functionPosition){var m=e.__options.functionPosition.call(e,e.__instance,c,l);m&&(d=m)}i.destroy();var n,o;"top"==d.side||"bottom"==d.side?(n={prop:"left",val:d.target-d.coord.left},o=d.size.width-this.__options.minIntersection):(n={prop:"top",val:d.target-d.coord.top},o=d.size.height-this.__options.minIntersection),n.valo&&(n.val=o);var p;p=c.geo.origin.fixedLineage?c.geo.origin.windowOffset:{left:c.geo.origin.windowOffset.left+c.geo.window.scroll.left,top:c.geo.origin.windowOffset.top+c.geo.window.scroll.top},d.coord={left:p.left+(d.coord.left-c.geo.origin.windowOffset.left),top:p.top+(d.coord.top-c.geo.origin.windowOffset.top)},e.__sideChange(e.__instance._$tooltip,d.side),c.geo.origin.fixedLineage?e.__instance._$tooltip.css("position","fixed"):e.__instance._$tooltip.css("position",""),e.__instance._$tooltip.css({left:d.coord.left,top:d.coord.top,height:d.size.height,width:d.size.width}).find(".tooltipster-arrow").css({left:"",top:""}).css(n.prop,n.val),e.__instance._$tooltip.appendTo(e.__instance.option("parent")),e.__instance._trigger({type:"repositioned",event:b,position:d})},__sideChange:function(a,b){a.removeClass("tooltipster-bottom").removeClass("tooltipster-left").removeClass("tooltipster-right").removeClass("tooltipster-top").addClass("tooltipster-"+b)},__targetFind:function(a){var b={},c=this.__instance._$origin[0].getClientRects();if(c.length>1){var d=this.__instance._$origin.css("opacity");1==d&&(this.__instance._$origin.css("opacity",.99),c=this.__instance._$origin[0].getClientRects(),this.__instance._$origin.css("opacity",1))}if(c.length<2)b.top=Math.floor(a.geo.origin.windowOffset.left+a.geo.origin.size.width/2),b.bottom=b.top,b.left=Math.floor(a.geo.origin.windowOffset.top+a.geo.origin.size.height/2),b.right=b.left;else{var e=c[0];b.top=Math.floor(e.left+(e.right-e.left)/2),e=c.length>2?c[Math.ceil(c.length/2)-1]:c[0],b.right=Math.floor(e.top+(e.bottom-e.top)/2),e=c[c.length-1],b.bottom=Math.floor(e.left+(e.right-e.left)/2),e=c.length>2?c[Math.ceil((c.length+1)/2)-1]:c[c.length-1],b.left=Math.floor(e.top+(e.bottom-e.top)/2)}return b}}}),a}); +\ No newline at end of file ++/*! tooltipster v4.2.8 */!function(a,b){void 0===a&&void 0!==window&&(a=window),"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){function b(a){this.$container,this.constraints=null,this.__$tooltip,this.__init(a)}function c(b,c){var d=!0;return a.each(b,function(a,e){if(void 0===c[a]||b[a]!==c[a])return d=!1,!1}),d}function d(b){var c=b.attr("id"),d=c?h.window.document.getElementById(c):null;return d?d===b[0]:a.contains(h.window.document.body,b[0])}function e(){if(!g)return!1;var a=g.document.body||g.document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e0?e=c.__plugins[d]:a.each(c.__plugins,function(a,b){if(b.name.substring(b.name.length-d.length-1)=="."+d)return e=b,!1}),e}if(b.name.indexOf(".")<0)throw new Error("Plugins must be namespaced");return c.__plugins[b.name]=b,b.core&&c.__bridge(b.core,c,b.name),this},_trigger:function(){var a=Array.prototype.slice.apply(arguments);return"string"==typeof a[0]&&(a[0]={type:a[0]}),this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,a),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,a),this},instances:function(b){var c=[],d=b||".tooltipstered";return a(d).each(function(){var b=a(this),d=b.data("tooltipster-ns");d&&a.each(d,function(a,d){c.push(b.data(d))})}),c},instancesLatest:function(){return this.__instancesLatestArr},off:function(){return this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},origins:function(b){var c=b?b+" ":"";return a(c+".tooltipstered").toArray()},setDefaults:function(b){return a.extend(f,b),this},triggerHandler:function(){return this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.tooltipster=new i,a.Tooltipster=function(b,c){this.__callbacks={close:[],open:[]},this.__closingTime,this.__Content,this.__contentBcr,this.__destroyed=!1,this.__$emitterPrivate=a({}),this.__$emitterPublic=a({}),this.__enabled=!0,this.__garbageCollector,this.__Geometry,this.__lastPosition,this.__namespace="tooltipster-"+Math.round(1e6*Math.random()),this.__options,this.__$originParents,this.__pointerIsOverOrigin=!1,this.__previousThemes=[],this.__state="closed",this.__timeouts={close:[],open:null},this.__touchEvents=[],this.__tracker=null,this._$origin,this._$tooltip,this.__init(b,c)},a.Tooltipster.prototype={__init:function(b,c){var d=this;if(d._$origin=a(b),d.__options=a.extend(!0,{},f,c),d.__optionsFormat(),!h.IE||h.IE>=d.__options.IEmin){var e=null;if(void 0===d._$origin.data("tooltipster-initialTitle")&&(e=d._$origin.attr("title"),void 0===e&&(e=null),d._$origin.data("tooltipster-initialTitle",e)),null!==d.__options.content)d.__contentSet(d.__options.content);else{var g,i=d._$origin.attr("data-tooltip-content");i&&(g=a(i)),g&&g[0]?d.__contentSet(g.first()):d.__contentSet(e)}d._$origin.removeAttr("title").addClass("tooltipstered"),d.__prepareOrigin(),d.__prepareGC(),a.each(d.__options.plugins,function(a,b){d._plug(b)}),h.hasTouchCapability&&a(h.window.document.body).on("touchmove."+d.__namespace+"-triggerOpen",function(a){d._touchRecordEvent(a)}),d._on("created",function(){d.__prepareTooltip()})._on("repositioned",function(a){d.__lastPosition=a.position})}else d.__options.disabled=!0},__contentInsert:function(){var a=this,b=a._$tooltip.find(".tooltipster-content"),c=a.__Content,d=function(a){c=a};return a._trigger({type:"format",content:a.__Content,format:d}),a.__options.functionFormat&&(c=a.__options.functionFormat.call(a,a,{origin:a._$origin[0]},a.__Content)),"string"!=typeof c||a.__options.contentAsHTML?b.empty().append(c):b.text(c),a},__contentSet:function(b){return b instanceof a&&this.__options.contentCloning&&(b=b.clone(!0)),this.__Content=b,this._trigger({type:"updated",content:b}),this},__destroyError:function(){throw new Error("This tooltip has been destroyed and cannot execute your method call.")},__geometry:function(){var b=this,c=b._$origin,d=b._$origin.is("area");if(d){var e=b._$origin.parent().attr("name");c=a('img[usemap="#'+e+'"]')}var f=c[0].getBoundingClientRect(),g=a(h.window.document),i=a(h.window),j=c,k={available:{document:null,window:null},document:{size:{height:g.height(),width:g.width()}},window:{scroll:{left:h.window.scrollX||h.window.document.documentElement.scrollLeft,top:h.window.scrollY||h.window.document.documentElement.scrollTop},size:{height:i.height(),width:i.width()}},origin:{fixedLineage:!1,offset:{},size:{height:f.bottom-f.top,width:f.right-f.left},usemapImage:d?c[0]:null,windowOffset:{bottom:f.bottom,left:f.left,right:f.right,top:f.top}}};if(d){var l=b._$origin.attr("shape"),m=b._$origin.attr("coords");if(m&&(m=m.split(","),a.map(m,function(a,b){m[b]=parseInt(a)})),"default"!=l)switch(l){case"circle":var n=m[0],o=m[1],p=m[2],q=o-p,r=n-p;k.origin.size.height=2*p,k.origin.size.width=k.origin.size.height,k.origin.windowOffset.left+=r,k.origin.windowOffset.top+=q;break;case"rect":var s=m[0],t=m[1],u=m[2],v=m[3];k.origin.size.height=v-t,k.origin.size.width=u-s,k.origin.windowOffset.left+=s,k.origin.windowOffset.top+=t;break;case"poly":for(var w=0,x=0,y=0,z=0,A="even",B=0;By&&(y=C,0===B&&(w=y)),Cz&&(z=C,1==B&&(x=z)),C6e4}),d(b._$origin)||b.close(function(){b.destroy()})},2e4):clearInterval(b.__garbageCollector),b},__prepareOrigin:function(){var a=this;if(a._$origin.off("."+a.__namespace+"-triggerOpen"),h.hasTouchCapability&&a._$origin.on("touchstart."+a.__namespace+"-triggerOpen touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen",function(b){a._touchRecordEvent(b)}),a.__options.triggerOpen.click||a.__options.triggerOpen.tap&&h.hasTouchCapability){var b="";a.__options.triggerOpen.click&&(b+="click."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.tap&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&a._open(b)})}if(a.__options.triggerOpen.mouseenter||a.__options.triggerOpen.touchstart&&h.hasTouchCapability){var b="";a.__options.triggerOpen.mouseenter&&(b+="mouseenter."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.touchstart&&h.hasTouchCapability&&(b+="touchstart."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){!a._touchIsTouchEvent(b)&&a._touchIsEmulatedEvent(b)||(a.__pointerIsOverOrigin=!0,a._openShortly(b))})}if(a.__options.triggerClose.mouseleave||a.__options.triggerClose.touchleave&&h.hasTouchCapability){var b="";a.__options.triggerClose.mouseleave&&(b+="mouseleave."+a.__namespace+"-triggerOpen "),a.__options.triggerClose.touchleave&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&(a.__pointerIsOverOrigin=!1)})}return a},__prepareTooltip:function(){var b=this,c=b.__options.interactive?"auto":"";return b._$tooltip.attr("id",b.__namespace).css({"pointer-events":c,zIndex:b.__options.zIndex}),a.each(b.__previousThemes,function(a,c){b._$tooltip.removeClass(c)}),a.each(b.__options.theme,function(a,c){b._$tooltip.addClass(c)}),b.__previousThemes=a.merge([],b.__options.theme),b},__scrollHandler:function(b){var c=this;if(c.__options.triggerClose.scroll)c._close(b);else if(d(c._$origin)&&d(c._$tooltip)){var e=null;if(b.target===h.window.document)c.__Geometry.origin.fixedLineage||c.__options.repositionOnScroll&&c.reposition(b);else{e=c.__geometry();var f=!1;if("fixed"!=c._$origin.css("position")&&c.__$originParents.each(function(b,d){var g=a(d),h=g.css("overflow-x"),i=g.css("overflow-y");if("visible"!=h||"visible"!=i){var j=d.getBoundingClientRect();if("visible"!=h&&(e.origin.windowOffset.leftj.right))return f=!0,!1;if("visible"!=i&&(e.origin.windowOffset.topj.bottom)&&(f=!c.__options.checkOverflowY||c.__options.checkOverflowY(e,j)))return!1}if("fixed"==g.css("position"))return!1}),f)c._$tooltip.css("visibility","hidden");else if(c._$tooltip.css("visibility","visible"),c.__options.repositionOnScroll)c.reposition(b);else{var g=e.origin.offset.left-c.__Geometry.origin.offset.left,i=e.origin.offset.top-c.__Geometry.origin.offset.top;c._$tooltip.css({left:c.__lastPosition.coord.left+g,top:c.__lastPosition.coord.top+i})}}c._trigger({type:"scroll",event:b,geo:e})}return c},__stateSet:function(a){return this.__state=a,this._trigger({type:"state",state:a}),this},__timeoutsClear:function(){return clearTimeout(this.__timeouts.open),this.__timeouts.open=null,a.each(this.__timeouts.close,function(a,b){clearTimeout(b)}),this.__timeouts.close=[],this},__trackerStart:function(){var a=this,b=a._$tooltip.find(".tooltipster-content");return a.__options.trackTooltip&&(a.__contentBcr=b[0].getBoundingClientRect()),a.__tracker=setInterval(function(){if(d(a._$origin)&&d(a._$tooltip)){if(a.__options.trackOrigin){var e=a.__geometry(),f=!1;c(e.origin.size,a.__Geometry.origin.size)&&(a.__Geometry.origin.fixedLineage?c(e.origin.windowOffset,a.__Geometry.origin.windowOffset)&&(f=!0):c(e.origin.offset,a.__Geometry.origin.offset)&&(f=!0)),f||(a.__options.triggerClose.mouseleave&&!a.__options.ignoreCloseOnScroll?a._close():a.reposition())}if(a.__options.trackTooltip){var g=b[0].getBoundingClientRect();g.height===a.__contentBcr.height&&g.width===a.__contentBcr.width||(a.reposition(),a.__contentBcr=g)}}else a._close()},a.__options.trackerInterval),a},_close:function(b,c,d){var e=this,f=!0;if(e._trigger({type:"close",event:b,stop:function(){f=!1}}),f||d){c&&e.__callbacks.close.push(c),e.__callbacks.open=[],e.__timeoutsClear();var g=function(){a.each(e.__callbacks.close,function(a,c){c.call(e,e,{event:b,origin:e._$origin[0]})}),e.__callbacks.close=[]};if("closed"!=e.__state){var i=!0,j=new Date,k=j.getTime(),l=k+e.__options.animationDuration[1];if("disappearing"==e.__state&&l>e.__closingTime&&e.__options.animationDuration[1]>0&&(i=!1),i){e.__closingTime=l,"disappearing"!=e.__state&&e.__stateSet("disappearing");var m=function(){clearInterval(e.__tracker),e._trigger({type:"closing",event:b}),e._$tooltip.off("."+e.__namespace+"-triggerClose").removeClass("tooltipster-dying"),a(h.window).off("."+e.__namespace+"-triggerClose"),e.__$originParents.each(function(b,c){a(c).off("scroll."+e.__namespace+"-triggerClose")}),e.__$originParents=null,a(h.window.document.body).off("."+e.__namespace+"-triggerClose"),e._$origin.off("."+e.__namespace+"-triggerClose"),e._off("dismissable"),e.__stateSet("closed"),e._trigger({type:"after",event:b}),e.__options.functionAfter&&e.__options.functionAfter.call(e,e,{event:b,origin:e._$origin[0]}),g()};h.hasTransitions?(e._$tooltip.css({"-moz-animation-duration":e.__options.animationDuration[1]+"ms","-ms-animation-duration":e.__options.animationDuration[1]+"ms","-o-animation-duration":e.__options.animationDuration[1]+"ms","-webkit-animation-duration":e.__options.animationDuration[1]+"ms","animation-duration":e.__options.animationDuration[1]+"ms","transition-duration":e.__options.animationDuration[1]+"ms"}),e._$tooltip.clearQueue().removeClass("tooltipster-show").addClass("tooltipster-dying"),e.__options.animationDuration[1]>0&&e._$tooltip.delay(e.__options.animationDuration[1]),e._$tooltip.queue(m)):e._$tooltip.stop().fadeOut(e.__options.animationDuration[1],m)}}else g()}return e},_off:function(){return this.__$emitterPrivate.off.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_on:function(){return this.__$emitterPrivate.on.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_one:function(){return this.__$emitterPrivate.one.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_open:function(b,c){var e=this;if(!e.__destroying&&d(e._$origin)&&e.__enabled){var f=!0;if("closed"==e.__state&&(e._trigger({type:"before",event:b,stop:function(){f=!1}}),f&&e.__options.functionBefore&&(f=e.__options.functionBefore.call(e,e,{event:b,origin:e._$origin[0]}))),f!==!1&&null!==e.__Content){c&&e.__callbacks.open.push(c),e.__callbacks.close=[],e.__timeoutsClear();var g,i=function(){"stable"!=e.__state&&e.__stateSet("stable"),a.each(e.__callbacks.open,function(a,b){b.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}),e.__callbacks.open=[]};if("closed"!==e.__state)g=0,"disappearing"===e.__state?(e.__stateSet("appearing"),h.hasTransitions?(e._$tooltip.clearQueue().removeClass("tooltipster-dying").addClass("tooltipster-show"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i)):e._$tooltip.stop().fadeIn(i)):"stable"==e.__state&&i();else{if(e.__stateSet("appearing"),g=e.__options.animationDuration[0],e.__contentInsert(),e.reposition(b,!0),h.hasTransitions?(e._$tooltip.addClass("tooltipster-"+e.__options.animation).addClass("tooltipster-initial").css({"-moz-animation-duration":e.__options.animationDuration[0]+"ms","-ms-animation-duration":e.__options.animationDuration[0]+"ms","-o-animation-duration":e.__options.animationDuration[0]+"ms","-webkit-animation-duration":e.__options.animationDuration[0]+"ms","animation-duration":e.__options.animationDuration[0]+"ms","transition-duration":e.__options.animationDuration[0]+"ms"}),setTimeout(function(){"closed"!=e.__state&&(e._$tooltip.addClass("tooltipster-show").removeClass("tooltipster-initial"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i))},0)):e._$tooltip.css("display","none").fadeIn(e.__options.animationDuration[0],i),e.__trackerStart(),a(h.window).on("resize."+e.__namespace+"-triggerClose",function(b){var c=a(document.activeElement);(c.is("input")||c.is("textarea"))&&a.contains(e._$tooltip[0],c[0])||e.reposition(b)}).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)}),e.__$originParents=e._$origin.parents(),e.__$originParents.each(function(b,c){a(c).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)})}),e.__options.triggerClose.mouseleave||e.__options.triggerClose.touchleave&&h.hasTouchCapability){e._on("dismissable",function(a){a.dismissable?a.delay?(m=setTimeout(function(){e._close(a.event)},a.delay),e.__timeouts.close.push(m)):e._close(a):clearTimeout(m)});var j=e._$origin,k="",l="",m=null;e.__options.interactive&&(j=j.add(e._$tooltip)),e.__options.triggerClose.mouseleave&&(k+="mouseenter."+e.__namespace+"-triggerClose ",l+="mouseleave."+e.__namespace+"-triggerClose "),e.__options.triggerClose.touchleave&&h.hasTouchCapability&&(k+="touchstart."+e.__namespace+"-triggerClose",l+="touchend."+e.__namespace+"-triggerClose touchcancel."+e.__namespace+"-triggerClose"),j.on(l,function(a){if(e._touchIsTouchEvent(a)||!e._touchIsEmulatedEvent(a)){var b="mouseleave"==a.type?e.__options.delay:e.__options.delayTouch;e._trigger({delay:b[1],dismissable:!0,event:a,type:"dismissable"})}}).on(k,function(a){!e._touchIsTouchEvent(a)&&e._touchIsEmulatedEvent(a)||e._trigger({dismissable:!1,event:a,type:"dismissable"})})}e.__options.triggerClose.originClick&&e._$origin.on("click."+e.__namespace+"-triggerClose",function(a){e._touchIsTouchEvent(a)||e._touchIsEmulatedEvent(a)||e._close(a)}),(e.__options.triggerClose.click||e.__options.triggerClose.tap&&h.hasTouchCapability)&&setTimeout(function(){if("closed"!=e.__state){var b="",c=a(h.window.document.body);e.__options.triggerClose.click&&(b+="click."+e.__namespace+"-triggerClose "),e.__options.triggerClose.tap&&h.hasTouchCapability&&(b+="touchend."+e.__namespace+"-triggerClose"),c.on(b,function(b){e._touchIsMeaningfulEvent(b)&&(e._touchRecordEvent(b),e.__options.interactive&&a.contains(e._$tooltip[0],b.target)||e._close(b))}),e.__options.triggerClose.tap&&h.hasTouchCapability&&c.on("touchstart."+e.__namespace+"-triggerClose",function(a){e._touchRecordEvent(a)})}},0),e._trigger("ready"),e.__options.functionReady&&e.__options.functionReady.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}if(e.__options.timer>0){var m=setTimeout(function(){e._close()},e.__options.timer+g);e.__timeouts.close.push(m)}}}return e},_openShortly:function(a){var b=this,c=!0;if("stable"!=b.__state&&"appearing"!=b.__state&&!b.__timeouts.open&&(b._trigger({type:"start",event:a,stop:function(){c=!1}}),c)){var d=0==a.type.indexOf("touch")?b.__options.delayTouch:b.__options.delay;d[0]?b.__timeouts.open=setTimeout(function(){b.__timeouts.open=null,b.__pointerIsOverOrigin&&b._touchIsMeaningfulEvent(a)?(b._trigger("startend"),b._open(a)):b._trigger("startcancel")},d[0]):(b._trigger("startend"),b._open(a))}return b},_optionsExtract:function(b,c){var d=this,e=a.extend(!0,{},c),f=d.__options[b];return f||(f={},a.each(c,function(a,b){var c=d.__options[a];void 0!==c&&(f[a]=c)})),a.each(e,function(b,c){void 0!==f[b]&&("object"!=typeof c||c instanceof Array||null==c||"object"!=typeof f[b]||f[b]instanceof Array||null==f[b]?e[b]=f[b]:a.extend(e[b],f[b]))}),e},_plug:function(b){var c=a.tooltipster._plugin(b);if(!c)throw new Error('The "'+b+'" plugin is not defined');return c.instance&&a.tooltipster.__bridge(c.instance,this,c.name),this},_touchIsEmulatedEvent:function(a){for(var b=!1,c=(new Date).getTime(),d=this.__touchEvents.length-1;d>=0;d--){var e=this.__touchEvents[d];if(!(c-e.time<500))break;e.target===a.target&&(b=!0)}return b},_touchIsMeaningfulEvent:function(a){return this._touchIsTouchEvent(a)&&!this._touchSwiped(a.target)||!this._touchIsTouchEvent(a)&&!this._touchIsEmulatedEvent(a)},_touchIsTouchEvent:function(a){return 0==a.type.indexOf("touch")},_touchRecordEvent:function(a){return this._touchIsTouchEvent(a)&&(a.time=(new Date).getTime(),this.__touchEvents.push(a)),this},_touchSwiped:function(a){for(var b=!1,c=this.__touchEvents.length-1;c>=0;c--){var d=this.__touchEvents[c];if("touchmove"==d.type){b=!0;break}if("touchstart"==d.type&&a===d.target)break}return b},_trigger:function(){var b=Array.prototype.slice.apply(arguments);return"string"==typeof b[0]&&(b[0]={type:b[0]}),b[0].instance=this,b[0].origin=this._$origin?this._$origin[0]:null,b[0].tooltip=this._$tooltip?this._$tooltip[0]:null,this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,b),a.tooltipster._trigger.apply(a.tooltipster,b),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,b),this},_unplug:function(b){var c=this;if(c[b]){var d=a.tooltipster._plugin(b);d.instance&&a.each(d.instance,function(a,d){c[a]&&c[a].bridged===c[b]&&delete c[a]}),c[b].__destroy&&c[b].__destroy(),delete c[b]}return c},close:function(a){return this.__destroyed?this.__destroyError():this._close(null,a),this},content:function(a){var b=this;if(void 0===a)return b.__Content;if(b.__destroyed)b.__destroyError();else if(b.__contentSet(a),null!==b.__Content){if("closed"!==b.__state&&(b.__contentInsert(),b.reposition(),b.__options.updateAnimation))if(h.hasTransitions){var c=b.__options.updateAnimation;b._$tooltip.addClass("tooltipster-update-"+c),setTimeout(function(){"closed"!=b.__state&&b._$tooltip.removeClass("tooltipster-update-"+c)},1e3)}else b._$tooltip.fadeTo(200,.5,function(){"closed"!=b.__state&&b._$tooltip.fadeTo(200,1)})}else b._close();return b},destroy:function(){var b=this;if(b.__destroyed)b.__destroyError();else{"closed"!=b.__state?b.option("animationDuration",0)._close(null,null,!0):b.__timeoutsClear(),b._trigger("destroy"),b.__destroyed=!0,b._$origin.removeData(b.__namespace).off("."+b.__namespace+"-triggerOpen"),a(h.window.document.body).off("."+b.__namespace+"-triggerOpen");var c=b._$origin.data("tooltipster-ns");if(c)if(1===c.length){var d=null;"previous"==b.__options.restoration?d=b._$origin.data("tooltipster-initialTitle"):"current"==b.__options.restoration&&(d="string"==typeof b.__Content?b.__Content:a("
").append(b.__Content).html()),d&&b._$origin.attr("title",d),b._$origin.removeClass("tooltipstered"),b._$origin.removeData("tooltipster-ns").removeData("tooltipster-initialTitle")}else c=a.grep(c,function(a,c){return a!==b.__namespace}),b._$origin.data("tooltipster-ns",c);b._trigger("destroyed"),b._off(),b.off(),b.__Content=null,b.__$emitterPrivate=null,b.__$emitterPublic=null,b.__options.parent=null,b._$origin=null,b._$tooltip=null,a.tooltipster.__instancesLatestArr=a.grep(a.tooltipster.__instancesLatestArr,function(a,c){return b!==a}),clearInterval(b.__garbageCollector)}return b},disable:function(){return this.__destroyed?(this.__destroyError(),this):(this._close(),this.__enabled=!1,this)},elementOrigin:function(){return this.__destroyed?void this.__destroyError():this._$origin[0]},elementTooltip:function(){return this._$tooltip?this._$tooltip[0]:null},enable:function(){return this.__enabled=!0,this},hide:function(a){return this.close(a)},instance:function(){return this},off:function(){return this.__destroyed||this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},open:function(a){return this.__destroyed?this.__destroyError():this._open(null,a),this},option:function(b,c){return void 0===c?this.__options[b]:(this.__destroyed?this.__destroyError():(this.__options[b]=c,this.__optionsFormat(),a.inArray(b,["trigger","triggerClose","triggerOpen"])>=0&&this.__prepareOrigin(),"selfDestruction"===b&&this.__prepareGC()),this)},reposition:function(a,b){var c=this;return c.__destroyed?c.__destroyError():"closed"!=c.__state&&d(c._$origin)&&(b||d(c._$tooltip))&&(b||c._$tooltip.detach(),c.__Geometry=c.__geometry(),c._trigger({type:"reposition",event:a,helper:{geo:c.__Geometry}})),c},show:function(a){return this.open(a)},status:function(){return{destroyed:this.__destroyed,enabled:this.__enabled,open:"closed"!==this.__state,state:this.__state}},triggerHandler:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.fn.tooltipster=function(){var b=Array.prototype.slice.apply(arguments),c="You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.";if(0===this.length)return this;if("string"==typeof b[0]){var d="#*$~&";return this.each(function(){var e=a(this).data("tooltipster-ns"),f=e?a(this).data(e[0]):null;if(!f)throw new Error("You called Tooltipster's \""+b[0]+'" method on an uninitialized element');if("function"!=typeof f[b[0]])throw new Error('Unknown method "'+b[0]+'"');this.length>1&&"content"==b[0]&&(b[1]instanceof a||"object"==typeof b[1]&&null!=b[1]&&b[1].tagName)&&!f.__options.contentCloning&&f.__options.debug&&console.log(c);var g=f[b[0]](b[1],b[2]);if(g!==f||"instance"===b[0])return d=g,!1}),"#*$~&"!==d?d:this}a.tooltipster.__instancesLatestArr=[];var e=b[0]&&void 0!==b[0].multiple,g=e&&b[0].multiple||!e&&f.multiple,h=b[0]&&void 0!==b[0].content,i=h&&b[0].content||!h&&f.content,j=b[0]&&void 0!==b[0].contentCloning,k=j&&b[0].contentCloning||!j&&f.contentCloning,l=b[0]&&void 0!==b[0].debug,m=l&&b[0].debug||!l&&f.debug;return this.length>1&&(i instanceof a||"object"==typeof i&&null!=i&&i.tagName)&&!k&&m&&console.log(c),this.each(function(){var c=!1,d=a(this),e=d.data("tooltipster-ns"),f=null;e?g?c=!0:m&&(console.log("Tooltipster: one or more tooltips are already attached to the element below. Ignoring."),console.log(this)):c=!0,c&&(f=new a.Tooltipster(this,b[0]),e||(e=[]),e.push(f.__namespace),d.data("tooltipster-ns",e),d.data(f.__namespace,f),f.__options.functionInit&&f.__options.functionInit.call(f,f,{origin:this}),f._trigger("init")),a.tooltipster.__instancesLatestArr.push(f)}),this},b.prototype={__init:function(b){this.__$tooltip=b,this.__$tooltip.css({left:0,overflow:"hidden",position:"absolute",top:0}).find(".tooltipster-content").css("overflow","auto"),this.$container=a('
').append(this.__$tooltip).appendTo(h.window.document.body)},__forceRedraw:function(){var a=this.__$tooltip.parent();this.__$tooltip.detach(),this.__$tooltip.appendTo(a)},constrain:function(a,b){return this.constraints={width:a,height:b},this.__$tooltip.css({display:"block",height:"",overflow:"auto",width:a}),this},destroy:function(){this.__$tooltip.detach().find(".tooltipster-content").css({display:"",overflow:""}),this.$container.remove()},free:function(){return this.constraints=null,this.__$tooltip.css({display:"",height:"",overflow:"visible",width:""}),this},measure:function(){this.__forceRedraw();var a=this.__$tooltip[0].getBoundingClientRect(),b={size:{height:a.height||a.bottom-a.top,width:a.width||a.right-a.left}};if(this.constraints){var c=this.__$tooltip.find(".tooltipster-content"),d=this.__$tooltip.outerHeight(),e=c[0].getBoundingClientRect(),f={height:d<=this.constraints.height,width:a.width<=this.constraints.width&&e.width>=c[0].scrollWidth-1};b.fits=f.height&&f.width}return h.IE&&h.IE<=11&&b.size.width!==h.window.document.documentElement.clientWidth&&(b.size.width=Math.ceil(b.size.width)+1),b}};var j=navigator.userAgent.toLowerCase();j.indexOf("msie")!=-1?h.IE=parseInt(j.split("msie")[1]):j.toLowerCase().indexOf("trident")!==-1&&j.indexOf(" rv:11")!==-1?h.IE=11:j.toLowerCase().indexOf("edge/")!=-1&&(h.IE=parseInt(j.toLowerCase().split("edge/")[1]));var k="tooltipster.sideTip";return a.tooltipster._plugin({name:k,instance:{__defaults:function(){return{arrow:!0,distance:6,functionPosition:null,maxWidth:null,minIntersection:16,minWidth:0,position:null,side:"top",viewportAware:!0}},__init:function(a){var b=this;b.__instance=a,b.__namespace="tooltipster-sideTip-"+Math.round(1e6*Math.random()),b.__previousState="closed",b.__options,b.__optionsFormat(),b.__instance._on("state."+b.__namespace,function(a){"closed"==a.state?b.__close():"appearing"==a.state&&"closed"==b.__previousState&&b.__create(),b.__previousState=a.state}),b.__instance._on("options."+b.__namespace,function(){b.__optionsFormat()}),b.__instance._on("reposition."+b.__namespace,function(a){b.__reposition(a.event,a.helper)})},__close:function(){this.__instance.content()instanceof a&&this.__instance.content().detach(),this.__instance._$tooltip.remove(),this.__instance._$tooltip=null},__create:function(){var b=a('
');this.__options.arrow||b.find(".tooltipster-box").css("margin",0).end().find(".tooltipster-arrow").hide(), ++this.__options.minWidth&&b.css("min-width",this.__options.minWidth+"px"),this.__options.maxWidth&&b.css("max-width",this.__options.maxWidth+"px"),this.__instance._$tooltip=b,this.__instance._trigger("created")},__destroy:function(){this.__instance._off("."+self.__namespace)},__optionsFormat:function(){var b=this;if(b.__options=b.__instance._optionsExtract(k,b.__defaults()),b.__options.position&&(b.__options.side=b.__options.position),"object"!=typeof b.__options.distance&&(b.__options.distance=[b.__options.distance]),b.__options.distance.length<4&&(void 0===b.__options.distance[1]&&(b.__options.distance[1]=b.__options.distance[0]),void 0===b.__options.distance[2]&&(b.__options.distance[2]=b.__options.distance[0]),void 0===b.__options.distance[3]&&(b.__options.distance[3]=b.__options.distance[1])),b.__options.distance={top:b.__options.distance[0],right:b.__options.distance[1],bottom:b.__options.distance[2],left:b.__options.distance[3]},"string"==typeof b.__options.side){var c={top:"bottom",right:"left",bottom:"top",left:"right"};b.__options.side=[b.__options.side,c[b.__options.side]],"left"==b.__options.side[0]||"right"==b.__options.side[0]?b.__options.side.push("top","bottom"):b.__options.side.push("right","left")}6===a.tooltipster._env.IE&&b.__options.arrow!==!0&&(b.__options.arrow=!1)},__reposition:function(b,c){var d,e=this,f=e.__targetFind(c),g=[];e.__instance._$tooltip.detach();var h=e.__instance._$tooltip.clone(),i=a.tooltipster._getRuler(h),j=!1,k=e.__instance.option("animation");switch(k&&h.removeClass("tooltipster-"+k),a.each(["window","document"],function(d,k){var l=null;if(e.__instance._trigger({container:k,helper:c,satisfied:j,takeTest:function(a){l=a},results:g,type:"positionTest"}),1==l||0!=l&&0==j&&("window"!=k||e.__options.viewportAware))for(var d=0;d=h.outerSize.width&&c.geo.available[k][n].height>=h.outerSize.height?h.fits=!0:h.fits=!1:h.fits=p.fits,"window"==k&&(h.fits?"top"==n||"bottom"==n?h.whole=c.geo.origin.windowOffset.right>=e.__options.minIntersection&&c.geo.window.size.width-c.geo.origin.windowOffset.left>=e.__options.minIntersection:h.whole=c.geo.origin.windowOffset.bottom>=e.__options.minIntersection&&c.geo.window.size.height-c.geo.origin.windowOffset.top>=e.__options.minIntersection:h.whole=!1),g.push(h),h.whole)j=!0;else if("natural"==h.mode&&(h.fits||h.size.width<=c.geo.available[k][n].width))return!1}})}}),e.__instance._trigger({edit:function(a){g=a},event:b,helper:c,results:g,type:"positionTested"}),g.sort(function(a,b){if(a.whole&&!b.whole)return-1;if(!a.whole&&b.whole)return 1;if(a.whole&&b.whole){var c=e.__options.side.indexOf(a.side),d=e.__options.side.indexOf(b.side);return cd?1:"natural"==a.mode?-1:1}if(a.fits&&!b.fits)return-1;if(!a.fits&&b.fits)return 1;if(a.fits&&b.fits){var c=e.__options.side.indexOf(a.side),d=e.__options.side.indexOf(b.side);return cd?1:"natural"==a.mode?-1:1}return"document"==a.container&&"bottom"==a.side&&"natural"==a.mode?-1:1}),d=g[0],d.coord={},d.side){case"left":case"right":d.coord.top=Math.floor(d.target-d.size.height/2);break;case"bottom":case"top":d.coord.left=Math.floor(d.target-d.size.width/2)}switch(d.side){case"left":d.coord.left=c.geo.origin.windowOffset.left-d.outerSize.width;break;case"right":d.coord.left=c.geo.origin.windowOffset.right+d.distance.horizontal;break;case"top":d.coord.top=c.geo.origin.windowOffset.top-d.outerSize.height;break;case"bottom":d.coord.top=c.geo.origin.windowOffset.bottom+d.distance.vertical}"window"==d.container?"top"==d.side||"bottom"==d.side?d.coord.left<0?c.geo.origin.windowOffset.right-this.__options.minIntersection>=0?d.coord.left=0:d.coord.left=c.geo.origin.windowOffset.right-this.__options.minIntersection-1:d.coord.left>c.geo.window.size.width-d.size.width&&(c.geo.origin.windowOffset.left+this.__options.minIntersection<=c.geo.window.size.width?d.coord.left=c.geo.window.size.width-d.size.width:d.coord.left=c.geo.origin.windowOffset.left+this.__options.minIntersection+1-d.size.width):d.coord.top<0?c.geo.origin.windowOffset.bottom-this.__options.minIntersection>=0?d.coord.top=0:d.coord.top=c.geo.origin.windowOffset.bottom-this.__options.minIntersection-1:d.coord.top>c.geo.window.size.height-d.size.height&&(c.geo.origin.windowOffset.top+this.__options.minIntersection<=c.geo.window.size.height?d.coord.top=c.geo.window.size.height-d.size.height:d.coord.top=c.geo.origin.windowOffset.top+this.__options.minIntersection+1-d.size.height):(d.coord.left>c.geo.window.size.width-d.size.width&&(d.coord.left=c.geo.window.size.width-d.size.width),d.coord.left<0&&(d.coord.left=0)),e.__sideChange(h,d.side),c.tooltipClone=h[0],c.tooltipParent=e.__instance.option("parent").parent[0],c.mode=d.mode,c.whole=d.whole,c.origin=e.__instance._$origin[0],c.tooltip=e.__instance._$tooltip[0],delete d.container,delete d.fits,delete d.mode,delete d.outerSize,delete d.whole,d.distance=d.distance.horizontal||d.distance.vertical;var l=a.extend(!0,{},d);if(e.__instance._trigger({edit:function(a){d=a},event:b,helper:c,position:l,type:"position"}),e.__options.functionPosition){var m=e.__options.functionPosition.call(e,e.__instance,c,l);m&&(d=m)}i.destroy();var n,o;"top"==d.side||"bottom"==d.side?(n={prop:"left",val:d.target-d.coord.left},o=d.size.width-this.__options.minIntersection):(n={prop:"top",val:d.target-d.coord.top},o=d.size.height-this.__options.minIntersection),n.valo&&(n.val=o);var p;p=c.geo.origin.fixedLineage?c.geo.origin.windowOffset:{left:c.geo.origin.windowOffset.left+c.geo.window.scroll.left,top:c.geo.origin.windowOffset.top+c.geo.window.scroll.top},d.coord={left:p.left+(d.coord.left-c.geo.origin.windowOffset.left),top:p.top+(d.coord.top-c.geo.origin.windowOffset.top)},e.__sideChange(e.__instance._$tooltip,d.side),c.geo.origin.fixedLineage?e.__instance._$tooltip.css("position","fixed"):e.__instance._$tooltip.css("position",""),e.__instance._$tooltip.css({left:d.coord.left,top:d.coord.top,height:d.size.height,width:d.size.width}).find(".tooltipster-arrow").css({left:"",top:""}).css(n.prop,n.val),e.__instance._$tooltip.appendTo(e.__instance.option("parent")),e.__instance._trigger({type:"repositioned",event:b,position:d})},__sideChange:function(a,b){a.removeClass("tooltipster-bottom").removeClass("tooltipster-left").removeClass("tooltipster-right").removeClass("tooltipster-top").addClass("tooltipster-"+b)},__targetFind:function(a){var b={},c=this.__instance._$origin[0].getClientRects();if(c.length>1){var d=this.__instance._$origin.css("opacity");1==d&&(this.__instance._$origin.css("opacity",.99),c=this.__instance._$origin[0].getClientRects(),this.__instance._$origin.css("opacity",1))}if(c.length<2)b.top=Math.floor(a.geo.origin.windowOffset.left+a.geo.origin.size.width/2),b.bottom=b.top,b.left=Math.floor(a.geo.origin.windowOffset.top+a.geo.origin.size.height/2),b.right=b.left;else{var e=c[0];b.top=Math.floor(e.left+(e.right-e.left)/2),e=c.length>2?c[Math.ceil(c.length/2)-1]:c[0],b.right=Math.floor(e.top+(e.bottom-e.top)/2),e=c[c.length-1],b.bottom=Math.floor(e.left+(e.right-e.left)/2),e=c.length>2?c[Math.ceil((c.length+1)/2)-1]:c[c.length-1],b.left=Math.floor(e.top+(e.bottom-e.top)/2)}return b}}}),a}); +\ No newline at end of file +diff --git a/node_modules/tooltipster/dist/js/tooltipster.main.js b/node_modules/tooltipster/dist/js/tooltipster.main.js +index 34c11d8..44efcd5 100644 +--- a/node_modules/tooltipster/dist/js/tooltipster.main.js ++++ b/node_modules/tooltipster/dist/js/tooltipster.main.js +@@ -5,18 +5,19 @@ + * MIT license + */ + (function (root, factory) { ++ if (root === undefined && window !== undefined) root = window; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["jquery"], function (a0) { + return (factory(a0)); + }); +- } else if (typeof exports === 'object') { ++ } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { +- factory(jQuery); ++ factory(root["jQuery"]); + } + }(this, function ($) { + +@@ -1260,8 +1261,14 @@ $.Tooltipster.prototype = { + if ( geo.origin.windowOffset.top < bcr.top + || geo.origin.windowOffset.bottom > bcr.bottom + ) { +- overflows = true; +- return false; ++ if (self.__options.checkOverflowY) { ++ overflows = self.__options.checkOverflowY(geo, bcr); ++ } else { ++ overflows = true; ++ } ++ if (overflows) { ++ return false; ++ } + } + } + } +@@ -1412,7 +1419,7 @@ $.Tooltipster.prototype = { + + // close the tooltip when using the mouseleave close trigger + // (see https://github.com/iamceege/tooltipster/pull/253) +- if (self.__options.triggerClose.mouseleave) { ++ if (self.__options.triggerClose.mouseleave && !self.__options.ignoreCloseOnScroll) { + self._close(); + } + else { +@@ -3340,6 +3347,7 @@ function transitionSupport() { + + // we'll return jQuery for plugins not to have to declare it as a dependency, + // but it's done by a build task since it should be included only once at the +-// end when we concatenate the main file with a pluginreturn $; ++// end when we concatenate the main file with a plugin ++return $; + + })); +diff --git a/node_modules/tooltipster/dist/js/tooltipster.main.min.js b/node_modules/tooltipster/dist/js/tooltipster.main.min.js +index 1221ae9..b2b3ac9 100644 +--- a/node_modules/tooltipster/dist/js/tooltipster.main.min.js ++++ b/node_modules/tooltipster/dist/js/tooltipster.main.min.js +@@ -1 +1 @@ +-/*! tooltipster v4.2.8 */!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){function b(a){this.$container,this.constraints=null,this.__$tooltip,this.__init(a)}function c(b,c){var d=!0;return a.each(b,function(a,e){return void 0===c[a]||b[a]!==c[a]?(d=!1,!1):void 0}),d}function d(b){var c=b.attr("id"),d=c?h.window.document.getElementById(c):null;return d?d===b[0]:a.contains(h.window.document.body,b[0])}function e(){if(!g)return!1;var a=g.document.body||g.document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e0?e=c.__plugins[d]:a.each(c.__plugins,function(a,b){return b.name.substring(b.name.length-d.length-1)=="."+d?(e=b,!1):void 0}),e}if(b.name.indexOf(".")<0)throw new Error("Plugins must be namespaced");return c.__plugins[b.name]=b,b.core&&c.__bridge(b.core,c,b.name),this},_trigger:function(){var a=Array.prototype.slice.apply(arguments);return"string"==typeof a[0]&&(a[0]={type:a[0]}),this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,a),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,a),this},instances:function(b){var c=[],d=b||".tooltipstered";return a(d).each(function(){var b=a(this),d=b.data("tooltipster-ns");d&&a.each(d,function(a,d){c.push(b.data(d))})}),c},instancesLatest:function(){return this.__instancesLatestArr},off:function(){return this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},origins:function(b){var c=b?b+" ":"";return a(c+".tooltipstered").toArray()},setDefaults:function(b){return a.extend(f,b),this},triggerHandler:function(){return this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.tooltipster=new i,a.Tooltipster=function(b,c){this.__callbacks={close:[],open:[]},this.__closingTime,this.__Content,this.__contentBcr,this.__destroyed=!1,this.__$emitterPrivate=a({}),this.__$emitterPublic=a({}),this.__enabled=!0,this.__garbageCollector,this.__Geometry,this.__lastPosition,this.__namespace="tooltipster-"+Math.round(1e6*Math.random()),this.__options,this.__$originParents,this.__pointerIsOverOrigin=!1,this.__previousThemes=[],this.__state="closed",this.__timeouts={close:[],open:null},this.__touchEvents=[],this.__tracker=null,this._$origin,this._$tooltip,this.__init(b,c)},a.Tooltipster.prototype={__init:function(b,c){var d=this;if(d._$origin=a(b),d.__options=a.extend(!0,{},f,c),d.__optionsFormat(),!h.IE||h.IE>=d.__options.IEmin){var e=null;if(void 0===d._$origin.data("tooltipster-initialTitle")&&(e=d._$origin.attr("title"),void 0===e&&(e=null),d._$origin.data("tooltipster-initialTitle",e)),null!==d.__options.content)d.__contentSet(d.__options.content);else{var g,i=d._$origin.attr("data-tooltip-content");i&&(g=a(i)),g&&g[0]?d.__contentSet(g.first()):d.__contentSet(e)}d._$origin.removeAttr("title").addClass("tooltipstered"),d.__prepareOrigin(),d.__prepareGC(),a.each(d.__options.plugins,function(a,b){d._plug(b)}),h.hasTouchCapability&&a(h.window.document.body).on("touchmove."+d.__namespace+"-triggerOpen",function(a){d._touchRecordEvent(a)}),d._on("created",function(){d.__prepareTooltip()})._on("repositioned",function(a){d.__lastPosition=a.position})}else d.__options.disabled=!0},__contentInsert:function(){var a=this,b=a._$tooltip.find(".tooltipster-content"),c=a.__Content,d=function(a){c=a};return a._trigger({type:"format",content:a.__Content,format:d}),a.__options.functionFormat&&(c=a.__options.functionFormat.call(a,a,{origin:a._$origin[0]},a.__Content)),"string"!=typeof c||a.__options.contentAsHTML?b.empty().append(c):b.text(c),a},__contentSet:function(b){return b instanceof a&&this.__options.contentCloning&&(b=b.clone(!0)),this.__Content=b,this._trigger({type:"updated",content:b}),this},__destroyError:function(){throw new Error("This tooltip has been destroyed and cannot execute your method call.")},__geometry:function(){var b=this,c=b._$origin,d=b._$origin.is("area");if(d){var e=b._$origin.parent().attr("name");c=a('img[usemap="#'+e+'"]')}var f=c[0].getBoundingClientRect(),g=a(h.window.document),i=a(h.window),j=c,k={available:{document:null,window:null},document:{size:{height:g.height(),width:g.width()}},window:{scroll:{left:h.window.scrollX||h.window.document.documentElement.scrollLeft,top:h.window.scrollY||h.window.document.documentElement.scrollTop},size:{height:i.height(),width:i.width()}},origin:{fixedLineage:!1,offset:{},size:{height:f.bottom-f.top,width:f.right-f.left},usemapImage:d?c[0]:null,windowOffset:{bottom:f.bottom,left:f.left,right:f.right,top:f.top}}};if(d){var l=b._$origin.attr("shape"),m=b._$origin.attr("coords");if(m&&(m=m.split(","),a.map(m,function(a,b){m[b]=parseInt(a)})),"default"!=l)switch(l){case"circle":var n=m[0],o=m[1],p=m[2],q=o-p,r=n-p;k.origin.size.height=2*p,k.origin.size.width=k.origin.size.height,k.origin.windowOffset.left+=r,k.origin.windowOffset.top+=q;break;case"rect":var s=m[0],t=m[1],u=m[2],v=m[3];k.origin.size.height=v-t,k.origin.size.width=u-s,k.origin.windowOffset.left+=s,k.origin.windowOffset.top+=t;break;case"poly":for(var w=0,x=0,y=0,z=0,A="even",B=0;By&&(y=C,0===B&&(w=y)),w>C&&(w=C),A="odd"):(C>z&&(z=C,1==B&&(x=z)),x>C&&(x=C),A="even")}k.origin.size.height=z-x,k.origin.size.width=y-w,k.origin.windowOffset.left+=w,k.origin.windowOffset.top+=x}}var D=function(a){k.origin.size.height=a.height,k.origin.windowOffset.left=a.left,k.origin.windowOffset.top=a.top,k.origin.size.width=a.width};for(b._trigger({type:"geometry",edit:D,geometry:{height:k.origin.size.height,left:k.origin.windowOffset.left,top:k.origin.windowOffset.top,width:k.origin.size.width}}),k.origin.windowOffset.right=k.origin.windowOffset.left+k.origin.size.width,k.origin.windowOffset.bottom=k.origin.windowOffset.top+k.origin.size.height,k.origin.offset.left=k.origin.windowOffset.left+k.window.scroll.left,k.origin.offset.top=k.origin.windowOffset.top+k.window.scroll.top,k.origin.offset.bottom=k.origin.offset.top+k.origin.size.height,k.origin.offset.right=k.origin.offset.left+k.origin.size.width,k.available.document={bottom:{height:k.document.size.height-k.origin.offset.bottom,width:k.document.size.width},left:{height:k.document.size.height,width:k.origin.offset.left},right:{height:k.document.size.height,width:k.document.size.width-k.origin.offset.right},top:{height:k.origin.offset.top,width:k.document.size.width}},k.available.window={bottom:{height:Math.max(k.window.size.height-Math.max(k.origin.windowOffset.bottom,0),0),width:k.window.size.width},left:{height:k.window.size.height,width:Math.max(k.origin.windowOffset.left,0)},right:{height:k.window.size.height,width:Math.max(k.window.size.width-Math.max(k.origin.windowOffset.right,0),0)},top:{height:Math.max(k.origin.windowOffset.top,0),width:k.window.size.width}};"html"!=j[0].tagName.toLowerCase();){if("fixed"==j.css("position")){k.origin.fixedLineage=!0;break}j=j.parent()}return k},__optionsFormat:function(){return"number"==typeof this.__options.animationDuration&&(this.__options.animationDuration=[this.__options.animationDuration,this.__options.animationDuration]),"number"==typeof this.__options.delay&&(this.__options.delay=[this.__options.delay,this.__options.delay]),"number"==typeof this.__options.delayTouch&&(this.__options.delayTouch=[this.__options.delayTouch,this.__options.delayTouch]),"string"==typeof this.__options.theme&&(this.__options.theme=[this.__options.theme]),null===this.__options.parent?this.__options.parent=a(h.window.document.body):"string"==typeof this.__options.parent&&(this.__options.parent=a(this.__options.parent)),"hover"==this.__options.trigger?(this.__options.triggerOpen={mouseenter:!0,touchstart:!0},this.__options.triggerClose={mouseleave:!0,originClick:!0,touchleave:!0}):"click"==this.__options.trigger&&(this.__options.triggerOpen={click:!0,tap:!0},this.__options.triggerClose={click:!0,tap:!0}),this._trigger("options"),this},__prepareGC:function(){var b=this;return b.__options.selfDestruction?b.__garbageCollector=setInterval(function(){var c=(new Date).getTime();b.__touchEvents=a.grep(b.__touchEvents,function(a,b){return c-a.time>6e4}),d(b._$origin)||b.close(function(){b.destroy()})},2e4):clearInterval(b.__garbageCollector),b},__prepareOrigin:function(){var a=this;if(a._$origin.off("."+a.__namespace+"-triggerOpen"),h.hasTouchCapability&&a._$origin.on("touchstart."+a.__namespace+"-triggerOpen touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen",function(b){a._touchRecordEvent(b)}),a.__options.triggerOpen.click||a.__options.triggerOpen.tap&&h.hasTouchCapability){var b="";a.__options.triggerOpen.click&&(b+="click."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.tap&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&a._open(b)})}if(a.__options.triggerOpen.mouseenter||a.__options.triggerOpen.touchstart&&h.hasTouchCapability){var b="";a.__options.triggerOpen.mouseenter&&(b+="mouseenter."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.touchstart&&h.hasTouchCapability&&(b+="touchstart."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){!a._touchIsTouchEvent(b)&&a._touchIsEmulatedEvent(b)||(a.__pointerIsOverOrigin=!0,a._openShortly(b))})}if(a.__options.triggerClose.mouseleave||a.__options.triggerClose.touchleave&&h.hasTouchCapability){var b="";a.__options.triggerClose.mouseleave&&(b+="mouseleave."+a.__namespace+"-triggerOpen "),a.__options.triggerClose.touchleave&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&(a.__pointerIsOverOrigin=!1)})}return a},__prepareTooltip:function(){var b=this,c=b.__options.interactive?"auto":"";return b._$tooltip.attr("id",b.__namespace).css({"pointer-events":c,zIndex:b.__options.zIndex}),a.each(b.__previousThemes,function(a,c){b._$tooltip.removeClass(c)}),a.each(b.__options.theme,function(a,c){b._$tooltip.addClass(c)}),b.__previousThemes=a.merge([],b.__options.theme),b},__scrollHandler:function(b){var c=this;if(c.__options.triggerClose.scroll)c._close(b);else if(d(c._$origin)&&d(c._$tooltip)){var e=null;if(b.target===h.window.document)c.__Geometry.origin.fixedLineage||c.__options.repositionOnScroll&&c.reposition(b);else{e=c.__geometry();var f=!1;if("fixed"!=c._$origin.css("position")&&c.__$originParents.each(function(b,c){var d=a(c),g=d.css("overflow-x"),h=d.css("overflow-y");if("visible"!=g||"visible"!=h){var i=c.getBoundingClientRect();if("visible"!=g&&(e.origin.windowOffset.lefti.right))return f=!0,!1;if("visible"!=h&&(e.origin.windowOffset.topi.bottom))return f=!0,!1}return"fixed"==d.css("position")?!1:void 0}),f)c._$tooltip.css("visibility","hidden");else if(c._$tooltip.css("visibility","visible"),c.__options.repositionOnScroll)c.reposition(b);else{var g=e.origin.offset.left-c.__Geometry.origin.offset.left,i=e.origin.offset.top-c.__Geometry.origin.offset.top;c._$tooltip.css({left:c.__lastPosition.coord.left+g,top:c.__lastPosition.coord.top+i})}}c._trigger({type:"scroll",event:b,geo:e})}return c},__stateSet:function(a){return this.__state=a,this._trigger({type:"state",state:a}),this},__timeoutsClear:function(){return clearTimeout(this.__timeouts.open),this.__timeouts.open=null,a.each(this.__timeouts.close,function(a,b){clearTimeout(b)}),this.__timeouts.close=[],this},__trackerStart:function(){var a=this,b=a._$tooltip.find(".tooltipster-content");return a.__options.trackTooltip&&(a.__contentBcr=b[0].getBoundingClientRect()),a.__tracker=setInterval(function(){if(d(a._$origin)&&d(a._$tooltip)){if(a.__options.trackOrigin){var e=a.__geometry(),f=!1;c(e.origin.size,a.__Geometry.origin.size)&&(a.__Geometry.origin.fixedLineage?c(e.origin.windowOffset,a.__Geometry.origin.windowOffset)&&(f=!0):c(e.origin.offset,a.__Geometry.origin.offset)&&(f=!0)),f||(a.__options.triggerClose.mouseleave?a._close():a.reposition())}if(a.__options.trackTooltip){var g=b[0].getBoundingClientRect();g.height===a.__contentBcr.height&&g.width===a.__contentBcr.width||(a.reposition(),a.__contentBcr=g)}}else a._close()},a.__options.trackerInterval),a},_close:function(b,c,d){var e=this,f=!0;if(e._trigger({type:"close",event:b,stop:function(){f=!1}}),f||d){c&&e.__callbacks.close.push(c),e.__callbacks.open=[],e.__timeoutsClear();var g=function(){a.each(e.__callbacks.close,function(a,c){c.call(e,e,{event:b,origin:e._$origin[0]})}),e.__callbacks.close=[]};if("closed"!=e.__state){var i=!0,j=new Date,k=j.getTime(),l=k+e.__options.animationDuration[1];if("disappearing"==e.__state&&l>e.__closingTime&&e.__options.animationDuration[1]>0&&(i=!1),i){e.__closingTime=l,"disappearing"!=e.__state&&e.__stateSet("disappearing");var m=function(){clearInterval(e.__tracker),e._trigger({type:"closing",event:b}),e._$tooltip.off("."+e.__namespace+"-triggerClose").removeClass("tooltipster-dying"),a(h.window).off("."+e.__namespace+"-triggerClose"),e.__$originParents.each(function(b,c){a(c).off("scroll."+e.__namespace+"-triggerClose")}),e.__$originParents=null,a(h.window.document.body).off("."+e.__namespace+"-triggerClose"),e._$origin.off("."+e.__namespace+"-triggerClose"),e._off("dismissable"),e.__stateSet("closed"),e._trigger({type:"after",event:b}),e.__options.functionAfter&&e.__options.functionAfter.call(e,e,{event:b,origin:e._$origin[0]}),g()};h.hasTransitions?(e._$tooltip.css({"-moz-animation-duration":e.__options.animationDuration[1]+"ms","-ms-animation-duration":e.__options.animationDuration[1]+"ms","-o-animation-duration":e.__options.animationDuration[1]+"ms","-webkit-animation-duration":e.__options.animationDuration[1]+"ms","animation-duration":e.__options.animationDuration[1]+"ms","transition-duration":e.__options.animationDuration[1]+"ms"}),e._$tooltip.clearQueue().removeClass("tooltipster-show").addClass("tooltipster-dying"),e.__options.animationDuration[1]>0&&e._$tooltip.delay(e.__options.animationDuration[1]),e._$tooltip.queue(m)):e._$tooltip.stop().fadeOut(e.__options.animationDuration[1],m)}}else g()}return e},_off:function(){return this.__$emitterPrivate.off.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_on:function(){return this.__$emitterPrivate.on.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_one:function(){return this.__$emitterPrivate.one.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_open:function(b,c){var e=this;if(!e.__destroying&&d(e._$origin)&&e.__enabled){var f=!0;if("closed"==e.__state&&(e._trigger({type:"before",event:b,stop:function(){f=!1}}),f&&e.__options.functionBefore&&(f=e.__options.functionBefore.call(e,e,{event:b,origin:e._$origin[0]}))),f!==!1&&null!==e.__Content){c&&e.__callbacks.open.push(c),e.__callbacks.close=[],e.__timeoutsClear();var g,i=function(){"stable"!=e.__state&&e.__stateSet("stable"),a.each(e.__callbacks.open,function(a,b){b.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}),e.__callbacks.open=[]};if("closed"!==e.__state)g=0,"disappearing"===e.__state?(e.__stateSet("appearing"),h.hasTransitions?(e._$tooltip.clearQueue().removeClass("tooltipster-dying").addClass("tooltipster-show"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i)):e._$tooltip.stop().fadeIn(i)):"stable"==e.__state&&i();else{if(e.__stateSet("appearing"),g=e.__options.animationDuration[0],e.__contentInsert(),e.reposition(b,!0),h.hasTransitions?(e._$tooltip.addClass("tooltipster-"+e.__options.animation).addClass("tooltipster-initial").css({"-moz-animation-duration":e.__options.animationDuration[0]+"ms","-ms-animation-duration":e.__options.animationDuration[0]+"ms","-o-animation-duration":e.__options.animationDuration[0]+"ms","-webkit-animation-duration":e.__options.animationDuration[0]+"ms","animation-duration":e.__options.animationDuration[0]+"ms","transition-duration":e.__options.animationDuration[0]+"ms"}),setTimeout(function(){"closed"!=e.__state&&(e._$tooltip.addClass("tooltipster-show").removeClass("tooltipster-initial"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i))},0)):e._$tooltip.css("display","none").fadeIn(e.__options.animationDuration[0],i),e.__trackerStart(),a(h.window).on("resize."+e.__namespace+"-triggerClose",function(b){var c=a(document.activeElement);(c.is("input")||c.is("textarea"))&&a.contains(e._$tooltip[0],c[0])||e.reposition(b)}).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)}),e.__$originParents=e._$origin.parents(),e.__$originParents.each(function(b,c){a(c).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)})}),e.__options.triggerClose.mouseleave||e.__options.triggerClose.touchleave&&h.hasTouchCapability){e._on("dismissable",function(a){a.dismissable?a.delay?(m=setTimeout(function(){e._close(a.event)},a.delay),e.__timeouts.close.push(m)):e._close(a):clearTimeout(m)});var j=e._$origin,k="",l="",m=null;e.__options.interactive&&(j=j.add(e._$tooltip)),e.__options.triggerClose.mouseleave&&(k+="mouseenter."+e.__namespace+"-triggerClose ",l+="mouseleave."+e.__namespace+"-triggerClose "),e.__options.triggerClose.touchleave&&h.hasTouchCapability&&(k+="touchstart."+e.__namespace+"-triggerClose",l+="touchend."+e.__namespace+"-triggerClose touchcancel."+e.__namespace+"-triggerClose"),j.on(l,function(a){if(e._touchIsTouchEvent(a)||!e._touchIsEmulatedEvent(a)){var b="mouseleave"==a.type?e.__options.delay:e.__options.delayTouch;e._trigger({delay:b[1],dismissable:!0,event:a,type:"dismissable"})}}).on(k,function(a){!e._touchIsTouchEvent(a)&&e._touchIsEmulatedEvent(a)||e._trigger({dismissable:!1,event:a,type:"dismissable"})})}e.__options.triggerClose.originClick&&e._$origin.on("click."+e.__namespace+"-triggerClose",function(a){e._touchIsTouchEvent(a)||e._touchIsEmulatedEvent(a)||e._close(a)}),(e.__options.triggerClose.click||e.__options.triggerClose.tap&&h.hasTouchCapability)&&setTimeout(function(){if("closed"!=e.__state){var b="",c=a(h.window.document.body);e.__options.triggerClose.click&&(b+="click."+e.__namespace+"-triggerClose "),e.__options.triggerClose.tap&&h.hasTouchCapability&&(b+="touchend."+e.__namespace+"-triggerClose"),c.on(b,function(b){e._touchIsMeaningfulEvent(b)&&(e._touchRecordEvent(b),e.__options.interactive&&a.contains(e._$tooltip[0],b.target)||e._close(b))}),e.__options.triggerClose.tap&&h.hasTouchCapability&&c.on("touchstart."+e.__namespace+"-triggerClose",function(a){e._touchRecordEvent(a)})}},0),e._trigger("ready"),e.__options.functionReady&&e.__options.functionReady.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}if(e.__options.timer>0){var m=setTimeout(function(){e._close()},e.__options.timer+g);e.__timeouts.close.push(m)}}}return e},_openShortly:function(a){var b=this,c=!0;if("stable"!=b.__state&&"appearing"!=b.__state&&!b.__timeouts.open&&(b._trigger({type:"start",event:a,stop:function(){c=!1}}),c)){var d=0==a.type.indexOf("touch")?b.__options.delayTouch:b.__options.delay;d[0]?b.__timeouts.open=setTimeout(function(){b.__timeouts.open=null,b.__pointerIsOverOrigin&&b._touchIsMeaningfulEvent(a)?(b._trigger("startend"),b._open(a)):b._trigger("startcancel")},d[0]):(b._trigger("startend"),b._open(a))}return b},_optionsExtract:function(b,c){var d=this,e=a.extend(!0,{},c),f=d.__options[b];return f||(f={},a.each(c,function(a,b){var c=d.__options[a];void 0!==c&&(f[a]=c)})),a.each(e,function(b,c){void 0!==f[b]&&("object"!=typeof c||c instanceof Array||null==c||"object"!=typeof f[b]||f[b]instanceof Array||null==f[b]?e[b]=f[b]:a.extend(e[b],f[b]))}),e},_plug:function(b){var c=a.tooltipster._plugin(b);if(!c)throw new Error('The "'+b+'" plugin is not defined');return c.instance&&a.tooltipster.__bridge(c.instance,this,c.name),this},_touchIsEmulatedEvent:function(a){for(var b=!1,c=(new Date).getTime(),d=this.__touchEvents.length-1;d>=0;d--){var e=this.__touchEvents[d];if(!(c-e.time<500))break;e.target===a.target&&(b=!0)}return b},_touchIsMeaningfulEvent:function(a){return this._touchIsTouchEvent(a)&&!this._touchSwiped(a.target)||!this._touchIsTouchEvent(a)&&!this._touchIsEmulatedEvent(a)},_touchIsTouchEvent:function(a){return 0==a.type.indexOf("touch")},_touchRecordEvent:function(a){return this._touchIsTouchEvent(a)&&(a.time=(new Date).getTime(),this.__touchEvents.push(a)),this},_touchSwiped:function(a){for(var b=!1,c=this.__touchEvents.length-1;c>=0;c--){var d=this.__touchEvents[c];if("touchmove"==d.type){b=!0;break}if("touchstart"==d.type&&a===d.target)break}return b},_trigger:function(){var b=Array.prototype.slice.apply(arguments);return"string"==typeof b[0]&&(b[0]={type:b[0]}),b[0].instance=this,b[0].origin=this._$origin?this._$origin[0]:null,b[0].tooltip=this._$tooltip?this._$tooltip[0]:null,this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,b),a.tooltipster._trigger.apply(a.tooltipster,b),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,b),this},_unplug:function(b){var c=this;if(c[b]){var d=a.tooltipster._plugin(b);d.instance&&a.each(d.instance,function(a,d){c[a]&&c[a].bridged===c[b]&&delete c[a]}),c[b].__destroy&&c[b].__destroy(),delete c[b]}return c},close:function(a){return this.__destroyed?this.__destroyError():this._close(null,a),this},content:function(a){var b=this;if(void 0===a)return b.__Content;if(b.__destroyed)b.__destroyError();else if(b.__contentSet(a),null!==b.__Content){if("closed"!==b.__state&&(b.__contentInsert(),b.reposition(),b.__options.updateAnimation))if(h.hasTransitions){var c=b.__options.updateAnimation;b._$tooltip.addClass("tooltipster-update-"+c),setTimeout(function(){"closed"!=b.__state&&b._$tooltip.removeClass("tooltipster-update-"+c)},1e3)}else b._$tooltip.fadeTo(200,.5,function(){"closed"!=b.__state&&b._$tooltip.fadeTo(200,1)})}else b._close();return b},destroy:function(){var b=this;if(b.__destroyed)b.__destroyError();else{"closed"!=b.__state?b.option("animationDuration",0)._close(null,null,!0):b.__timeoutsClear(),b._trigger("destroy"),b.__destroyed=!0,b._$origin.removeData(b.__namespace).off("."+b.__namespace+"-triggerOpen"),a(h.window.document.body).off("."+b.__namespace+"-triggerOpen");var c=b._$origin.data("tooltipster-ns");if(c)if(1===c.length){var d=null;"previous"==b.__options.restoration?d=b._$origin.data("tooltipster-initialTitle"):"current"==b.__options.restoration&&(d="string"==typeof b.__Content?b.__Content:a("
").append(b.__Content).html()),d&&b._$origin.attr("title",d),b._$origin.removeClass("tooltipstered"),b._$origin.removeData("tooltipster-ns").removeData("tooltipster-initialTitle")}else c=a.grep(c,function(a,c){return a!==b.__namespace}),b._$origin.data("tooltipster-ns",c);b._trigger("destroyed"),b._off(),b.off(),b.__Content=null,b.__$emitterPrivate=null,b.__$emitterPublic=null,b.__options.parent=null,b._$origin=null,b._$tooltip=null,a.tooltipster.__instancesLatestArr=a.grep(a.tooltipster.__instancesLatestArr,function(a,c){return b!==a}),clearInterval(b.__garbageCollector)}return b},disable:function(){return this.__destroyed?(this.__destroyError(),this):(this._close(),this.__enabled=!1,this)},elementOrigin:function(){return this.__destroyed?void this.__destroyError():this._$origin[0]},elementTooltip:function(){return this._$tooltip?this._$tooltip[0]:null},enable:function(){return this.__enabled=!0,this},hide:function(a){return this.close(a)},instance:function(){return this},off:function(){return this.__destroyed||this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},open:function(a){return this.__destroyed?this.__destroyError():this._open(null,a),this},option:function(b,c){return void 0===c?this.__options[b]:(this.__destroyed?this.__destroyError():(this.__options[b]=c,this.__optionsFormat(),a.inArray(b,["trigger","triggerClose","triggerOpen"])>=0&&this.__prepareOrigin(),"selfDestruction"===b&&this.__prepareGC()),this)},reposition:function(a,b){var c=this;return c.__destroyed?c.__destroyError():"closed"!=c.__state&&d(c._$origin)&&(b||d(c._$tooltip))&&(b||c._$tooltip.detach(),c.__Geometry=c.__geometry(),c._trigger({type:"reposition",event:a,helper:{geo:c.__Geometry}})),c},show:function(a){return this.open(a)},status:function(){return{destroyed:this.__destroyed,enabled:this.__enabled,open:"closed"!==this.__state,state:this.__state}},triggerHandler:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.fn.tooltipster=function(){var b=Array.prototype.slice.apply(arguments),c="You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.";if(0===this.length)return this;if("string"==typeof b[0]){var d="#*$~&";return this.each(function(){var e=a(this).data("tooltipster-ns"),f=e?a(this).data(e[0]):null;if(!f)throw new Error("You called Tooltipster's \""+b[0]+'" method on an uninitialized element');if("function"!=typeof f[b[0]])throw new Error('Unknown method "'+b[0]+'"');this.length>1&&"content"==b[0]&&(b[1]instanceof a||"object"==typeof b[1]&&null!=b[1]&&b[1].tagName)&&!f.__options.contentCloning&&f.__options.debug&&console.log(c);var g=f[b[0]](b[1],b[2]);return g!==f||"instance"===b[0]?(d=g,!1):void 0}),"#*$~&"!==d?d:this}a.tooltipster.__instancesLatestArr=[];var e=b[0]&&void 0!==b[0].multiple,g=e&&b[0].multiple||!e&&f.multiple,h=b[0]&&void 0!==b[0].content,i=h&&b[0].content||!h&&f.content,j=b[0]&&void 0!==b[0].contentCloning,k=j&&b[0].contentCloning||!j&&f.contentCloning,l=b[0]&&void 0!==b[0].debug,m=l&&b[0].debug||!l&&f.debug;return this.length>1&&(i instanceof a||"object"==typeof i&&null!=i&&i.tagName)&&!k&&m&&console.log(c),this.each(function(){var c=!1,d=a(this),e=d.data("tooltipster-ns"),f=null;e?g?c=!0:m&&(console.log("Tooltipster: one or more tooltips are already attached to the element below. Ignoring."),console.log(this)):c=!0,c&&(f=new a.Tooltipster(this,b[0]),e||(e=[]),e.push(f.__namespace),d.data("tooltipster-ns",e),d.data(f.__namespace,f),f.__options.functionInit&&f.__options.functionInit.call(f,f,{origin:this}),f._trigger("init")),a.tooltipster.__instancesLatestArr.push(f)}),this},b.prototype={__init:function(b){this.__$tooltip=b,this.__$tooltip.css({left:0,overflow:"hidden",position:"absolute",top:0}).find(".tooltipster-content").css("overflow","auto"),this.$container=a('
').append(this.__$tooltip).appendTo(h.window.document.body)},__forceRedraw:function(){var a=this.__$tooltip.parent();this.__$tooltip.detach(),this.__$tooltip.appendTo(a)},constrain:function(a,b){return this.constraints={width:a,height:b},this.__$tooltip.css({display:"block",height:"",overflow:"auto",width:a}),this},destroy:function(){this.__$tooltip.detach().find(".tooltipster-content").css({display:"",overflow:""}),this.$container.remove()},free:function(){return this.constraints=null,this.__$tooltip.css({display:"",height:"",overflow:"visible",width:""}),this},measure:function(){this.__forceRedraw();var a=this.__$tooltip[0].getBoundingClientRect(),b={size:{height:a.height||a.bottom-a.top,width:a.width||a.right-a.left}};if(this.constraints){var c=this.__$tooltip.find(".tooltipster-content"),d=this.__$tooltip.outerHeight(),e=c[0].getBoundingClientRect(),f={height:d<=this.constraints.height,width:a.width<=this.constraints.width&&e.width>=c[0].scrollWidth-1};b.fits=f.height&&f.width}return h.IE&&h.IE<=11&&b.size.width!==h.window.document.documentElement.clientWidth&&(b.size.width=Math.ceil(b.size.width)+1),b}};var j=navigator.userAgent.toLowerCase();-1!=j.indexOf("msie")?h.IE=parseInt(j.split("msie")[1]):-1!==j.toLowerCase().indexOf("trident")&&-1!==j.indexOf(" rv:11")?h.IE=11:-1!=j.toLowerCase().indexOf("edge/")&&(h.IE=parseInt(j.toLowerCase().split("edge/")[1]))}); +\ No newline at end of file ++/*! tooltipster v4.2.8 */!function(a,b){void 0===a&&void 0!==window&&(a=window),"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){function b(a){this.$container,this.constraints=null,this.__$tooltip,this.__init(a)}function c(b,c){var d=!0;return a.each(b,function(a,e){if(void 0===c[a]||b[a]!==c[a])return d=!1,!1}),d}function d(b){var c=b.attr("id"),d=c?h.window.document.getElementById(c):null;return d?d===b[0]:a.contains(h.window.document.body,b[0])}function e(){if(!g)return!1;var a=g.document.body||g.document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e0?e=c.__plugins[d]:a.each(c.__plugins,function(a,b){if(b.name.substring(b.name.length-d.length-1)=="."+d)return e=b,!1}),e}if(b.name.indexOf(".")<0)throw new Error("Plugins must be namespaced");return c.__plugins[b.name]=b,b.core&&c.__bridge(b.core,c,b.name),this},_trigger:function(){var a=Array.prototype.slice.apply(arguments);return"string"==typeof a[0]&&(a[0]={type:a[0]}),this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,a),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,a),this},instances:function(b){var c=[],d=b||".tooltipstered";return a(d).each(function(){var b=a(this),d=b.data("tooltipster-ns");d&&a.each(d,function(a,d){c.push(b.data(d))})}),c},instancesLatest:function(){return this.__instancesLatestArr},off:function(){return this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},origins:function(b){var c=b?b+" ":"";return a(c+".tooltipstered").toArray()},setDefaults:function(b){return a.extend(f,b),this},triggerHandler:function(){return this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.tooltipster=new i,a.Tooltipster=function(b,c){this.__callbacks={close:[],open:[]},this.__closingTime,this.__Content,this.__contentBcr,this.__destroyed=!1,this.__$emitterPrivate=a({}),this.__$emitterPublic=a({}),this.__enabled=!0,this.__garbageCollector,this.__Geometry,this.__lastPosition,this.__namespace="tooltipster-"+Math.round(1e6*Math.random()),this.__options,this.__$originParents,this.__pointerIsOverOrigin=!1,this.__previousThemes=[],this.__state="closed",this.__timeouts={close:[],open:null},this.__touchEvents=[],this.__tracker=null,this._$origin,this._$tooltip,this.__init(b,c)},a.Tooltipster.prototype={__init:function(b,c){var d=this;if(d._$origin=a(b),d.__options=a.extend(!0,{},f,c),d.__optionsFormat(),!h.IE||h.IE>=d.__options.IEmin){var e=null;if(void 0===d._$origin.data("tooltipster-initialTitle")&&(e=d._$origin.attr("title"),void 0===e&&(e=null),d._$origin.data("tooltipster-initialTitle",e)),null!==d.__options.content)d.__contentSet(d.__options.content);else{var g,i=d._$origin.attr("data-tooltip-content");i&&(g=a(i)),g&&g[0]?d.__contentSet(g.first()):d.__contentSet(e)}d._$origin.removeAttr("title").addClass("tooltipstered"),d.__prepareOrigin(),d.__prepareGC(),a.each(d.__options.plugins,function(a,b){d._plug(b)}),h.hasTouchCapability&&a(h.window.document.body).on("touchmove."+d.__namespace+"-triggerOpen",function(a){d._touchRecordEvent(a)}),d._on("created",function(){d.__prepareTooltip()})._on("repositioned",function(a){d.__lastPosition=a.position})}else d.__options.disabled=!0},__contentInsert:function(){var a=this,b=a._$tooltip.find(".tooltipster-content"),c=a.__Content,d=function(a){c=a};return a._trigger({type:"format",content:a.__Content,format:d}),a.__options.functionFormat&&(c=a.__options.functionFormat.call(a,a,{origin:a._$origin[0]},a.__Content)),"string"!=typeof c||a.__options.contentAsHTML?b.empty().append(c):b.text(c),a},__contentSet:function(b){return b instanceof a&&this.__options.contentCloning&&(b=b.clone(!0)),this.__Content=b,this._trigger({type:"updated",content:b}),this},__destroyError:function(){throw new Error("This tooltip has been destroyed and cannot execute your method call.")},__geometry:function(){var b=this,c=b._$origin,d=b._$origin.is("area");if(d){var e=b._$origin.parent().attr("name");c=a('img[usemap="#'+e+'"]')}var f=c[0].getBoundingClientRect(),g=a(h.window.document),i=a(h.window),j=c,k={available:{document:null,window:null},document:{size:{height:g.height(),width:g.width()}},window:{scroll:{left:h.window.scrollX||h.window.document.documentElement.scrollLeft,top:h.window.scrollY||h.window.document.documentElement.scrollTop},size:{height:i.height(),width:i.width()}},origin:{fixedLineage:!1,offset:{},size:{height:f.bottom-f.top,width:f.right-f.left},usemapImage:d?c[0]:null,windowOffset:{bottom:f.bottom,left:f.left,right:f.right,top:f.top}}};if(d){var l=b._$origin.attr("shape"),m=b._$origin.attr("coords");if(m&&(m=m.split(","),a.map(m,function(a,b){m[b]=parseInt(a)})),"default"!=l)switch(l){case"circle":var n=m[0],o=m[1],p=m[2],q=o-p,r=n-p;k.origin.size.height=2*p,k.origin.size.width=k.origin.size.height,k.origin.windowOffset.left+=r,k.origin.windowOffset.top+=q;break;case"rect":var s=m[0],t=m[1],u=m[2],v=m[3];k.origin.size.height=v-t,k.origin.size.width=u-s,k.origin.windowOffset.left+=s,k.origin.windowOffset.top+=t;break;case"poly":for(var w=0,x=0,y=0,z=0,A="even",B=0;By&&(y=C,0===B&&(w=y)),Cz&&(z=C,1==B&&(x=z)),C6e4}),d(b._$origin)||b.close(function(){b.destroy()})},2e4):clearInterval(b.__garbageCollector),b},__prepareOrigin:function(){var a=this;if(a._$origin.off("."+a.__namespace+"-triggerOpen"),h.hasTouchCapability&&a._$origin.on("touchstart."+a.__namespace+"-triggerOpen touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen",function(b){a._touchRecordEvent(b)}),a.__options.triggerOpen.click||a.__options.triggerOpen.tap&&h.hasTouchCapability){var b="";a.__options.triggerOpen.click&&(b+="click."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.tap&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&a._open(b)})}if(a.__options.triggerOpen.mouseenter||a.__options.triggerOpen.touchstart&&h.hasTouchCapability){var b="";a.__options.triggerOpen.mouseenter&&(b+="mouseenter."+a.__namespace+"-triggerOpen "),a.__options.triggerOpen.touchstart&&h.hasTouchCapability&&(b+="touchstart."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){!a._touchIsTouchEvent(b)&&a._touchIsEmulatedEvent(b)||(a.__pointerIsOverOrigin=!0,a._openShortly(b))})}if(a.__options.triggerClose.mouseleave||a.__options.triggerClose.touchleave&&h.hasTouchCapability){var b="";a.__options.triggerClose.mouseleave&&(b+="mouseleave."+a.__namespace+"-triggerOpen "),a.__options.triggerClose.touchleave&&h.hasTouchCapability&&(b+="touchend."+a.__namespace+"-triggerOpen touchcancel."+a.__namespace+"-triggerOpen"),a._$origin.on(b,function(b){a._touchIsMeaningfulEvent(b)&&(a.__pointerIsOverOrigin=!1)})}return a},__prepareTooltip:function(){var b=this,c=b.__options.interactive?"auto":"";return b._$tooltip.attr("id",b.__namespace).css({"pointer-events":c,zIndex:b.__options.zIndex}),a.each(b.__previousThemes,function(a,c){b._$tooltip.removeClass(c)}),a.each(b.__options.theme,function(a,c){b._$tooltip.addClass(c)}),b.__previousThemes=a.merge([],b.__options.theme),b},__scrollHandler:function(b){var c=this;if(c.__options.triggerClose.scroll)c._close(b);else if(d(c._$origin)&&d(c._$tooltip)){var e=null;if(b.target===h.window.document)c.__Geometry.origin.fixedLineage||c.__options.repositionOnScroll&&c.reposition(b);else{e=c.__geometry();var f=!1;if("fixed"!=c._$origin.css("position")&&c.__$originParents.each(function(b,d){var g=a(d),h=g.css("overflow-x"),i=g.css("overflow-y");if("visible"!=h||"visible"!=i){var j=d.getBoundingClientRect();if("visible"!=h&&(e.origin.windowOffset.leftj.right))return f=!0,!1;if("visible"!=i&&(e.origin.windowOffset.topj.bottom)&&(f=!c.__options.checkOverflowY||c.__options.checkOverflowY(e,j)))return!1}if("fixed"==g.css("position"))return!1}),f)c._$tooltip.css("visibility","hidden");else if(c._$tooltip.css("visibility","visible"),c.__options.repositionOnScroll)c.reposition(b);else{var g=e.origin.offset.left-c.__Geometry.origin.offset.left,i=e.origin.offset.top-c.__Geometry.origin.offset.top;c._$tooltip.css({left:c.__lastPosition.coord.left+g,top:c.__lastPosition.coord.top+i})}}c._trigger({type:"scroll",event:b,geo:e})}return c},__stateSet:function(a){return this.__state=a,this._trigger({type:"state",state:a}),this},__timeoutsClear:function(){return clearTimeout(this.__timeouts.open),this.__timeouts.open=null,a.each(this.__timeouts.close,function(a,b){clearTimeout(b)}),this.__timeouts.close=[],this},__trackerStart:function(){var a=this,b=a._$tooltip.find(".tooltipster-content");return a.__options.trackTooltip&&(a.__contentBcr=b[0].getBoundingClientRect()),a.__tracker=setInterval(function(){if(d(a._$origin)&&d(a._$tooltip)){if(a.__options.trackOrigin){var e=a.__geometry(),f=!1;c(e.origin.size,a.__Geometry.origin.size)&&(a.__Geometry.origin.fixedLineage?c(e.origin.windowOffset,a.__Geometry.origin.windowOffset)&&(f=!0):c(e.origin.offset,a.__Geometry.origin.offset)&&(f=!0)),f||(a.__options.triggerClose.mouseleave&&!a.__options.ignoreCloseOnScroll?a._close():a.reposition())}if(a.__options.trackTooltip){var g=b[0].getBoundingClientRect();g.height===a.__contentBcr.height&&g.width===a.__contentBcr.width||(a.reposition(),a.__contentBcr=g)}}else a._close()},a.__options.trackerInterval),a},_close:function(b,c,d){var e=this,f=!0;if(e._trigger({type:"close",event:b,stop:function(){f=!1}}),f||d){c&&e.__callbacks.close.push(c),e.__callbacks.open=[],e.__timeoutsClear();var g=function(){a.each(e.__callbacks.close,function(a,c){c.call(e,e,{event:b,origin:e._$origin[0]})}),e.__callbacks.close=[]};if("closed"!=e.__state){var i=!0,j=new Date,k=j.getTime(),l=k+e.__options.animationDuration[1];if("disappearing"==e.__state&&l>e.__closingTime&&e.__options.animationDuration[1]>0&&(i=!1),i){e.__closingTime=l,"disappearing"!=e.__state&&e.__stateSet("disappearing");var m=function(){clearInterval(e.__tracker),e._trigger({type:"closing",event:b}),e._$tooltip.off("."+e.__namespace+"-triggerClose").removeClass("tooltipster-dying"),a(h.window).off("."+e.__namespace+"-triggerClose"),e.__$originParents.each(function(b,c){a(c).off("scroll."+e.__namespace+"-triggerClose")}),e.__$originParents=null,a(h.window.document.body).off("."+e.__namespace+"-triggerClose"),e._$origin.off("."+e.__namespace+"-triggerClose"),e._off("dismissable"),e.__stateSet("closed"),e._trigger({type:"after",event:b}),e.__options.functionAfter&&e.__options.functionAfter.call(e,e,{event:b,origin:e._$origin[0]}),g()};h.hasTransitions?(e._$tooltip.css({"-moz-animation-duration":e.__options.animationDuration[1]+"ms","-ms-animation-duration":e.__options.animationDuration[1]+"ms","-o-animation-duration":e.__options.animationDuration[1]+"ms","-webkit-animation-duration":e.__options.animationDuration[1]+"ms","animation-duration":e.__options.animationDuration[1]+"ms","transition-duration":e.__options.animationDuration[1]+"ms"}),e._$tooltip.clearQueue().removeClass("tooltipster-show").addClass("tooltipster-dying"),e.__options.animationDuration[1]>0&&e._$tooltip.delay(e.__options.animationDuration[1]),e._$tooltip.queue(m)):e._$tooltip.stop().fadeOut(e.__options.animationDuration[1],m)}}else g()}return e},_off:function(){return this.__$emitterPrivate.off.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_on:function(){return this.__$emitterPrivate.on.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_one:function(){return this.__$emitterPrivate.one.apply(this.__$emitterPrivate,Array.prototype.slice.apply(arguments)),this},_open:function(b,c){var e=this;if(!e.__destroying&&d(e._$origin)&&e.__enabled){var f=!0;if("closed"==e.__state&&(e._trigger({type:"before",event:b,stop:function(){f=!1}}),f&&e.__options.functionBefore&&(f=e.__options.functionBefore.call(e,e,{event:b,origin:e._$origin[0]}))),f!==!1&&null!==e.__Content){c&&e.__callbacks.open.push(c),e.__callbacks.close=[],e.__timeoutsClear();var g,i=function(){"stable"!=e.__state&&e.__stateSet("stable"),a.each(e.__callbacks.open,function(a,b){b.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}),e.__callbacks.open=[]};if("closed"!==e.__state)g=0,"disappearing"===e.__state?(e.__stateSet("appearing"),h.hasTransitions?(e._$tooltip.clearQueue().removeClass("tooltipster-dying").addClass("tooltipster-show"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i)):e._$tooltip.stop().fadeIn(i)):"stable"==e.__state&&i();else{if(e.__stateSet("appearing"),g=e.__options.animationDuration[0],e.__contentInsert(),e.reposition(b,!0),h.hasTransitions?(e._$tooltip.addClass("tooltipster-"+e.__options.animation).addClass("tooltipster-initial").css({"-moz-animation-duration":e.__options.animationDuration[0]+"ms","-ms-animation-duration":e.__options.animationDuration[0]+"ms","-o-animation-duration":e.__options.animationDuration[0]+"ms","-webkit-animation-duration":e.__options.animationDuration[0]+"ms","animation-duration":e.__options.animationDuration[0]+"ms","transition-duration":e.__options.animationDuration[0]+"ms"}),setTimeout(function(){"closed"!=e.__state&&(e._$tooltip.addClass("tooltipster-show").removeClass("tooltipster-initial"),e.__options.animationDuration[0]>0&&e._$tooltip.delay(e.__options.animationDuration[0]),e._$tooltip.queue(i))},0)):e._$tooltip.css("display","none").fadeIn(e.__options.animationDuration[0],i),e.__trackerStart(),a(h.window).on("resize."+e.__namespace+"-triggerClose",function(b){var c=a(document.activeElement);(c.is("input")||c.is("textarea"))&&a.contains(e._$tooltip[0],c[0])||e.reposition(b)}).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)}),e.__$originParents=e._$origin.parents(),e.__$originParents.each(function(b,c){a(c).on("scroll."+e.__namespace+"-triggerClose",function(a){e.__scrollHandler(a)})}),e.__options.triggerClose.mouseleave||e.__options.triggerClose.touchleave&&h.hasTouchCapability){e._on("dismissable",function(a){a.dismissable?a.delay?(m=setTimeout(function(){e._close(a.event)},a.delay),e.__timeouts.close.push(m)):e._close(a):clearTimeout(m)});var j=e._$origin,k="",l="",m=null;e.__options.interactive&&(j=j.add(e._$tooltip)),e.__options.triggerClose.mouseleave&&(k+="mouseenter."+e.__namespace+"-triggerClose ",l+="mouseleave."+e.__namespace+"-triggerClose "),e.__options.triggerClose.touchleave&&h.hasTouchCapability&&(k+="touchstart."+e.__namespace+"-triggerClose",l+="touchend."+e.__namespace+"-triggerClose touchcancel."+e.__namespace+"-triggerClose"),j.on(l,function(a){if(e._touchIsTouchEvent(a)||!e._touchIsEmulatedEvent(a)){var b="mouseleave"==a.type?e.__options.delay:e.__options.delayTouch;e._trigger({delay:b[1],dismissable:!0,event:a,type:"dismissable"})}}).on(k,function(a){!e._touchIsTouchEvent(a)&&e._touchIsEmulatedEvent(a)||e._trigger({dismissable:!1,event:a,type:"dismissable"})})}e.__options.triggerClose.originClick&&e._$origin.on("click."+e.__namespace+"-triggerClose",function(a){e._touchIsTouchEvent(a)||e._touchIsEmulatedEvent(a)||e._close(a)}),(e.__options.triggerClose.click||e.__options.triggerClose.tap&&h.hasTouchCapability)&&setTimeout(function(){if("closed"!=e.__state){var b="",c=a(h.window.document.body);e.__options.triggerClose.click&&(b+="click."+e.__namespace+"-triggerClose "),e.__options.triggerClose.tap&&h.hasTouchCapability&&(b+="touchend."+e.__namespace+"-triggerClose"),c.on(b,function(b){e._touchIsMeaningfulEvent(b)&&(e._touchRecordEvent(b),e.__options.interactive&&a.contains(e._$tooltip[0],b.target)||e._close(b))}),e.__options.triggerClose.tap&&h.hasTouchCapability&&c.on("touchstart."+e.__namespace+"-triggerClose",function(a){e._touchRecordEvent(a)})}},0),e._trigger("ready"),e.__options.functionReady&&e.__options.functionReady.call(e,e,{origin:e._$origin[0],tooltip:e._$tooltip[0]})}if(e.__options.timer>0){var m=setTimeout(function(){e._close()},e.__options.timer+g);e.__timeouts.close.push(m)}}}return e},_openShortly:function(a){var b=this,c=!0;if("stable"!=b.__state&&"appearing"!=b.__state&&!b.__timeouts.open&&(b._trigger({type:"start",event:a,stop:function(){c=!1}}),c)){var d=0==a.type.indexOf("touch")?b.__options.delayTouch:b.__options.delay;d[0]?b.__timeouts.open=setTimeout(function(){b.__timeouts.open=null,b.__pointerIsOverOrigin&&b._touchIsMeaningfulEvent(a)?(b._trigger("startend"),b._open(a)):b._trigger("startcancel")},d[0]):(b._trigger("startend"),b._open(a))}return b},_optionsExtract:function(b,c){var d=this,e=a.extend(!0,{},c),f=d.__options[b];return f||(f={},a.each(c,function(a,b){var c=d.__options[a];void 0!==c&&(f[a]=c)})),a.each(e,function(b,c){void 0!==f[b]&&("object"!=typeof c||c instanceof Array||null==c||"object"!=typeof f[b]||f[b]instanceof Array||null==f[b]?e[b]=f[b]:a.extend(e[b],f[b]))}),e},_plug:function(b){var c=a.tooltipster._plugin(b);if(!c)throw new Error('The "'+b+'" plugin is not defined');return c.instance&&a.tooltipster.__bridge(c.instance,this,c.name),this},_touchIsEmulatedEvent:function(a){for(var b=!1,c=(new Date).getTime(),d=this.__touchEvents.length-1;d>=0;d--){var e=this.__touchEvents[d];if(!(c-e.time<500))break;e.target===a.target&&(b=!0)}return b},_touchIsMeaningfulEvent:function(a){return this._touchIsTouchEvent(a)&&!this._touchSwiped(a.target)||!this._touchIsTouchEvent(a)&&!this._touchIsEmulatedEvent(a)},_touchIsTouchEvent:function(a){return 0==a.type.indexOf("touch")},_touchRecordEvent:function(a){return this._touchIsTouchEvent(a)&&(a.time=(new Date).getTime(),this.__touchEvents.push(a)),this},_touchSwiped:function(a){for(var b=!1,c=this.__touchEvents.length-1;c>=0;c--){var d=this.__touchEvents[c];if("touchmove"==d.type){b=!0;break}if("touchstart"==d.type&&a===d.target)break}return b},_trigger:function(){var b=Array.prototype.slice.apply(arguments);return"string"==typeof b[0]&&(b[0]={type:b[0]}),b[0].instance=this,b[0].origin=this._$origin?this._$origin[0]:null,b[0].tooltip=this._$tooltip?this._$tooltip[0]:null,this.__$emitterPrivate.trigger.apply(this.__$emitterPrivate,b),a.tooltipster._trigger.apply(a.tooltipster,b),this.__$emitterPublic.trigger.apply(this.__$emitterPublic,b),this},_unplug:function(b){var c=this;if(c[b]){var d=a.tooltipster._plugin(b);d.instance&&a.each(d.instance,function(a,d){c[a]&&c[a].bridged===c[b]&&delete c[a]}),c[b].__destroy&&c[b].__destroy(),delete c[b]}return c},close:function(a){return this.__destroyed?this.__destroyError():this._close(null,a),this},content:function(a){var b=this;if(void 0===a)return b.__Content;if(b.__destroyed)b.__destroyError();else if(b.__contentSet(a),null!==b.__Content){if("closed"!==b.__state&&(b.__contentInsert(),b.reposition(),b.__options.updateAnimation))if(h.hasTransitions){var c=b.__options.updateAnimation;b._$tooltip.addClass("tooltipster-update-"+c),setTimeout(function(){"closed"!=b.__state&&b._$tooltip.removeClass("tooltipster-update-"+c)},1e3)}else b._$tooltip.fadeTo(200,.5,function(){"closed"!=b.__state&&b._$tooltip.fadeTo(200,1)})}else b._close();return b},destroy:function(){var b=this;if(b.__destroyed)b.__destroyError();else{"closed"!=b.__state?b.option("animationDuration",0)._close(null,null,!0):b.__timeoutsClear(),b._trigger("destroy"),b.__destroyed=!0,b._$origin.removeData(b.__namespace).off("."+b.__namespace+"-triggerOpen"),a(h.window.document.body).off("."+b.__namespace+"-triggerOpen");var c=b._$origin.data("tooltipster-ns");if(c)if(1===c.length){var d=null;"previous"==b.__options.restoration?d=b._$origin.data("tooltipster-initialTitle"):"current"==b.__options.restoration&&(d="string"==typeof b.__Content?b.__Content:a("
").append(b.__Content).html()),d&&b._$origin.attr("title",d),b._$origin.removeClass("tooltipstered"),b._$origin.removeData("tooltipster-ns").removeData("tooltipster-initialTitle")}else c=a.grep(c,function(a,c){return a!==b.__namespace}),b._$origin.data("tooltipster-ns",c);b._trigger("destroyed"),b._off(),b.off(),b.__Content=null,b.__$emitterPrivate=null,b.__$emitterPublic=null,b.__options.parent=null,b._$origin=null,b._$tooltip=null,a.tooltipster.__instancesLatestArr=a.grep(a.tooltipster.__instancesLatestArr,function(a,c){return b!==a}),clearInterval(b.__garbageCollector)}return b},disable:function(){return this.__destroyed?(this.__destroyError(),this):(this._close(),this.__enabled=!1,this)},elementOrigin:function(){return this.__destroyed?void this.__destroyError():this._$origin[0]},elementTooltip:function(){return this._$tooltip?this._$tooltip[0]:null},enable:function(){return this.__enabled=!0,this},hide:function(a){return this.close(a)},instance:function(){return this},off:function(){return this.__destroyed||this.__$emitterPublic.off.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},on:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.on.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},one:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.one.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this},open:function(a){return this.__destroyed?this.__destroyError():this._open(null,a),this},option:function(b,c){return void 0===c?this.__options[b]:(this.__destroyed?this.__destroyError():(this.__options[b]=c,this.__optionsFormat(),a.inArray(b,["trigger","triggerClose","triggerOpen"])>=0&&this.__prepareOrigin(),"selfDestruction"===b&&this.__prepareGC()),this)},reposition:function(a,b){var c=this;return c.__destroyed?c.__destroyError():"closed"!=c.__state&&d(c._$origin)&&(b||d(c._$tooltip))&&(b||c._$tooltip.detach(),c.__Geometry=c.__geometry(),c._trigger({type:"reposition",event:a,helper:{geo:c.__Geometry}})),c},show:function(a){return this.open(a)},status:function(){return{destroyed:this.__destroyed,enabled:this.__enabled,open:"closed"!==this.__state,state:this.__state}},triggerHandler:function(){return this.__destroyed?this.__destroyError():this.__$emitterPublic.triggerHandler.apply(this.__$emitterPublic,Array.prototype.slice.apply(arguments)),this}},a.fn.tooltipster=function(){var b=Array.prototype.slice.apply(arguments),c="You are using a single HTML element as content for several tooltips. You probably want to set the contentCloning option to TRUE.";if(0===this.length)return this;if("string"==typeof b[0]){var d="#*$~&";return this.each(function(){var e=a(this).data("tooltipster-ns"),f=e?a(this).data(e[0]):null;if(!f)throw new Error("You called Tooltipster's \""+b[0]+'" method on an uninitialized element');if("function"!=typeof f[b[0]])throw new Error('Unknown method "'+b[0]+'"');this.length>1&&"content"==b[0]&&(b[1]instanceof a||"object"==typeof b[1]&&null!=b[1]&&b[1].tagName)&&!f.__options.contentCloning&&f.__options.debug&&console.log(c);var g=f[b[0]](b[1],b[2]);if(g!==f||"instance"===b[0])return d=g,!1}),"#*$~&"!==d?d:this}a.tooltipster.__instancesLatestArr=[];var e=b[0]&&void 0!==b[0].multiple,g=e&&b[0].multiple||!e&&f.multiple,h=b[0]&&void 0!==b[0].content,i=h&&b[0].content||!h&&f.content,j=b[0]&&void 0!==b[0].contentCloning,k=j&&b[0].contentCloning||!j&&f.contentCloning,l=b[0]&&void 0!==b[0].debug,m=l&&b[0].debug||!l&&f.debug;return this.length>1&&(i instanceof a||"object"==typeof i&&null!=i&&i.tagName)&&!k&&m&&console.log(c),this.each(function(){var c=!1,d=a(this),e=d.data("tooltipster-ns"),f=null;e?g?c=!0:m&&(console.log("Tooltipster: one or more tooltips are already attached to the element below. Ignoring."),console.log(this)):c=!0,c&&(f=new a.Tooltipster(this,b[0]),e||(e=[]),e.push(f.__namespace),d.data("tooltipster-ns",e),d.data(f.__namespace,f),f.__options.functionInit&&f.__options.functionInit.call(f,f,{origin:this}),f._trigger("init")),a.tooltipster.__instancesLatestArr.push(f)}),this},b.prototype={__init:function(b){this.__$tooltip=b,this.__$tooltip.css({left:0,overflow:"hidden",position:"absolute",top:0}).find(".tooltipster-content").css("overflow","auto"),this.$container=a('
').append(this.__$tooltip).appendTo(h.window.document.body)},__forceRedraw:function(){var a=this.__$tooltip.parent();this.__$tooltip.detach(),this.__$tooltip.appendTo(a)},constrain:function(a,b){return this.constraints={width:a,height:b},this.__$tooltip.css({display:"block",height:"",overflow:"auto",width:a}),this},destroy:function(){this.__$tooltip.detach().find(".tooltipster-content").css({display:"",overflow:""}),this.$container.remove()},free:function(){return this.constraints=null,this.__$tooltip.css({display:"",height:"",overflow:"visible",width:""}),this},measure:function(){this.__forceRedraw();var a=this.__$tooltip[0].getBoundingClientRect(),b={size:{height:a.height||a.bottom-a.top,width:a.width||a.right-a.left}};if(this.constraints){var c=this.__$tooltip.find(".tooltipster-content"),d=this.__$tooltip.outerHeight(),e=c[0].getBoundingClientRect(),f={height:d<=this.constraints.height,width:a.width<=this.constraints.width&&e.width>=c[0].scrollWidth-1};b.fits=f.height&&f.width}return h.IE&&h.IE<=11&&b.size.width!==h.window.document.documentElement.clientWidth&&(b.size.width=Math.ceil(b.size.width)+1),b}};var j=navigator.userAgent.toLowerCase();return j.indexOf("msie")!=-1?h.IE=parseInt(j.split("msie")[1]):j.toLowerCase().indexOf("trident")!==-1&&j.indexOf(" rv:11")!==-1?h.IE=11:j.toLowerCase().indexOf("edge/")!=-1&&(h.IE=parseInt(j.toLowerCase().split("edge/")[1])),a}); +\ No newline at end of file +diff --git a/node_modules/tooltipster/src/js/tooltipster.js b/node_modules/tooltipster/src/js/tooltipster.js +index bcf5abd..d22ed9e 100644 +--- a/node_modules/tooltipster/src/js/tooltipster.js ++++ b/node_modules/tooltipster/src/js/tooltipster.js +@@ -1238,8 +1238,14 @@ $.Tooltipster.prototype = { + if ( geo.origin.windowOffset.top < bcr.top + || geo.origin.windowOffset.bottom > bcr.bottom + ) { +- overflows = true; +- return false; ++ if (self.__options.checkOverflowY) { ++ overflows = self.__options.checkOverflowY(geo, bcr); ++ } else { ++ overflows = true; ++ } ++ if (overflows) { ++ return false; ++ } + } + } + } +@@ -1390,7 +1396,7 @@ $.Tooltipster.prototype = { + + // close the tooltip when using the mouseleave close trigger + // (see https://github.com/iamceege/tooltipster/pull/253) +- if (self.__options.triggerClose.mouseleave) { ++ if (self.__options.triggerClose.mouseleave && !self.__options.ignoreCloseOnScroll) { + self._close(); + } + else { diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index 3547391ed0..7d0f091739 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -90,10 +90,17 @@ export interface IWidgetUtils { getEntityDetailsPageURL: (id: string, entityType: EntityType) => string; } +export interface PlaceMapItemActionData { + action: WidgetAction; + additionalParams?: any; + afterPlaceItemCallback: ($event: Event, descriptor: WidgetAction, entityId?: EntityId, entityName?: string, + additionalParams?: any, entityLabel?: string) => void; +} + export interface WidgetActionsApi { actionDescriptorsBySourceId: {[sourceId: string]: Array}; getActionDescriptors: (actionSourceId: string) => Array; - handleWidgetAction: ($event: Event, descriptor: WidgetActionDescriptor, + handleWidgetAction: ($event: Event, descriptor: WidgetAction, entityId?: EntityId, entityName?: string, additionalParams?: any, entityLabel?: string) => void; onWidgetAction: ($event: Event, action: WidgetAction) => void; elementClick: ($event: Event) => void; @@ -106,6 +113,7 @@ export interface WidgetActionsApi { hideDashboardToolbar?: boolean, preferredPlacement?: PopoverPlacement, hideOnClickOutside?: boolean, popoverWidth?: string, popoverHeight?: string, popoverStyle?: { [klass: string]: any }) => void; + placeMapItem: (action: PlaceMapItemActionData) => void; } export interface AliasInfo { diff --git a/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts b/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts index 3dd7f2a055..75bfa6b1e1 100644 --- a/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts +++ b/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts @@ -15,11 +15,10 @@ /// import { Component, Injectable, Type, ɵComponentDef, ɵNG_COMP_DEF } from '@angular/core'; -import { forkJoin, from, Observable, of } from 'rxjs'; +import { from, Observable, of } from 'rxjs'; import { CommonModule } from '@angular/common'; import { mergeMap } from 'rxjs/operators'; import { guid } from '@core/utils'; -import { getFlexLayoutModule } from '@shared/legacy/flex-layout.models'; @Injectable({ providedIn: 'root' @@ -35,9 +34,9 @@ export class DynamicComponentFactoryService { imports?: Type[], preserveWhitespaces?: boolean, styles?: string[]): Observable> { - return forkJoin({flexLayoutModule: getFlexLayoutModule(), compiler: from(import('@angular/compiler'))}).pipe( - mergeMap((data) => { - let componentImports: Type[] = [CommonModule, data.flexLayoutModule]; + return from(import('@angular/compiler')).pipe( + mergeMap(() => { + let componentImports: Type[] = [CommonModule]; if (imports) { componentImports = [...componentImports, ...imports]; } diff --git a/ui-ngx/src/app/core/services/item-buffer.service.ts b/ui-ngx/src/app/core/services/item-buffer.service.ts index 8806b15867..cef9bbe7a3 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { BreakpointId, Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models'; -import { AliasesInfo, EntityAlias, EntityAliases, EntityAliasInfo } from '@shared/models/alias.models'; +import { AliasesInfo, EntityAlias, EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models'; import { Datasource, DatasourceType, @@ -34,7 +34,8 @@ import { map } from 'rxjs/operators'; import { FcRuleNode, ruleNodeTypeDescriptors } from '@shared/models/rule-node.models'; import { RuleChainService } from '@core/http/rule-chain.service'; import { RuleChainImport } from '@shared/models/rule-chain.models'; -import { Filter, FilterInfo, Filters, FiltersInfo } from '@shared/models/query/query.models'; +import { Filter, FilterInfo, Filters, FiltersInfo, getFilterId } from '@shared/models/query/query.models'; +import { getWidgetExportDefinition } from '@shared/models/widget/widget-export.models'; const WIDGET_ITEM = 'widget_item'; const WIDGET_REFERENCE = 'widget_reference'; @@ -47,6 +48,7 @@ export interface WidgetItem { filtersInfo: FiltersInfo; originalSize: WidgetSize; originalColumns: number; + widgetExportInfo?: any; } export interface WidgetReference { @@ -139,12 +141,18 @@ export class ItemBufferService { } } } + let widgetExportInfo: any; + const exportDefinition = getWidgetExportDefinition(widget); + if (exportDefinition) { + widgetExportInfo = exportDefinition.prepareExportInfo(dashboard, widget); + } return { widget, aliasesInfo, filtersInfo, originalSize, - originalColumns + originalColumns, + widgetExportInfo }; } @@ -189,6 +197,7 @@ export class ItemBufferService { const filtersInfo = widgetItem.filtersInfo; const originalColumns = widgetItem.originalColumns; const originalSize = widgetItem.originalSize; + const widgetExportInfo = widgetItem.widgetExportInfo; let targetRow = -1; let targetColumn = -1; if (position) { @@ -199,7 +208,7 @@ export class ItemBufferService { return this.addWidgetToDashboard(targetDashboard, targetState, targetLayout, widget, aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, - originalColumns, originalSize, targetRow, targetColumn, breakpoint).pipe( + originalColumns, originalSize, targetRow, targetColumn, breakpoint, widgetExportInfo).pipe( map(() => widget) ); } else { @@ -248,7 +257,8 @@ export class ItemBufferService { originalSize: WidgetSize, row: number, column: number, - breakpoint = 'default'): Observable { + breakpoint = 'default', + widgetExportInfo?: any): Observable { let theDashboard: Dashboard; if (dashboard) { theDashboard = dashboard; @@ -258,26 +268,39 @@ export class ItemBufferService { theDashboard = this.dashboardUtils.validateAndUpdateDashboard(theDashboard); let callAliasUpdateFunction = false; let callFilterUpdateFunction = false; + let newEntityAliases: EntityAliases; + let newFilters: Filters; + const exportDefinition = getWidgetExportDefinition(widget); + if (exportDefinition && widgetExportInfo || aliasesInfo) { + newEntityAliases = deepClone(dashboard.configuration.entityAliases); + } + if (exportDefinition && widgetExportInfo || filtersInfo) { + newFilters = deepClone(dashboard.configuration.filters); + } if (aliasesInfo) { - const newEntityAliases = this.updateAliases(theDashboard, widget, aliasesInfo); - const aliasesUpdated = !isEqual(newEntityAliases, theDashboard.configuration.entityAliases); - if (aliasesUpdated) { - theDashboard.configuration.entityAliases = newEntityAliases; - if (onAliasesUpdateFunction) { - callAliasUpdateFunction = true; - } - } + this.updateAliases(widget, newEntityAliases, aliasesInfo); } if (filtersInfo) { - const newFilters = this.updateFilters(theDashboard, widget, filtersInfo); - const filtersUpdated = !isEqual(newFilters, theDashboard.configuration.filters); - if (filtersUpdated) { - theDashboard.configuration.filters = newFilters; - if (onFiltersUpdateFunction) { - callFilterUpdateFunction = true; - } + this.updateFilters(widget, newFilters, filtersInfo); + } + if (exportDefinition && widgetExportInfo) { + exportDefinition.updateFromExportInfo(widget, newEntityAliases, newFilters, widgetExportInfo); + } + const aliasesUpdated = newEntityAliases && !isEqual(newEntityAliases, theDashboard.configuration.entityAliases); + if (aliasesUpdated) { + theDashboard.configuration.entityAliases = newEntityAliases; + if (onAliasesUpdateFunction) { + callAliasUpdateFunction = true; } } + const filtersUpdated = newFilters && !isEqual(newFilters, theDashboard.configuration.filters); + if (filtersUpdated) { + theDashboard.configuration.filters = newFilters; + if (onFiltersUpdateFunction) { + callFilterUpdateFunction = true; + } + } + this.dashboardUtils.addWidgetToLayout(theDashboard, targetState, targetLayout, widget, originalColumns, originalSize, row, column, breakpoint); if (callAliasUpdateFunction) { @@ -430,14 +453,13 @@ export class ItemBufferService { }; } - private updateAliases(dashboard: Dashboard, widget: Widget, aliasesInfo: AliasesInfo): EntityAliases { - const entityAliases = deepClone(dashboard.configuration.entityAliases); + private updateAliases(widget: Widget, entityAliases: EntityAliases, aliasesInfo: AliasesInfo): void { let aliasInfo: EntityAliasInfo; let newAliasId: string; for (const datasourceIndexStr of Object.keys(aliasesInfo.datasourceAliases)) { const datasourceIndex = Number(datasourceIndexStr); aliasInfo = aliasesInfo.datasourceAliases[datasourceIndex]; - newAliasId = this.getEntityAliasId(entityAliases, aliasInfo); + newAliasId = getEntityAliasId(entityAliases, aliasInfo); if (widget.type === widgetType.alarm) { widget.config.alarmSource.entityAliasId = newAliasId; } else { @@ -446,7 +468,7 @@ export class ItemBufferService { } if (aliasesInfo.targetDeviceAlias) { aliasInfo = aliasesInfo.targetDeviceAlias; - newAliasId = this.getEntityAliasId(entityAliases, aliasInfo); + newAliasId = getEntityAliasId(entityAliases, aliasInfo); if (widget.config.targetDevice?.type !== TargetDeviceType.entity) { widget.config.targetDevice = { type: TargetDeviceType.entity @@ -454,101 +476,21 @@ export class ItemBufferService { } widget.config.targetDevice.entityAliasId = newAliasId; } - return entityAliases; } - private updateFilters(dashboard: Dashboard, widget: Widget, filtersInfo: FiltersInfo): Filters { - const filters = deepClone(dashboard.configuration.filters); + private updateFilters(widget: Widget, filters: Filters, filtersInfo: FiltersInfo): void { let filterInfo: FilterInfo; let newFilterId: string; for (const datasourceIndexStr of Object.keys(filtersInfo.datasourceFilters)) { const datasourceIndex = Number(datasourceIndexStr); filterInfo = filtersInfo.datasourceFilters[datasourceIndex]; - newFilterId = this.getFilterId(filters, filterInfo); + newFilterId = getFilterId(filters, filterInfo); if (widget.type === widgetType.alarm) { widget.config.alarmSource.filterId = newFilterId; } else { widget.config.datasources[datasourceIndex].filterId = newFilterId; } } - return filters; - } - - private isEntityAliasEqual(alias1: EntityAliasInfo, alias2: EntityAliasInfo): boolean { - return isEqual(alias1.filter, alias2.filter); - } - - private getEntityAliasId(entityAliases: EntityAliases, aliasInfo: EntityAliasInfo): string { - let newAliasId: string; - for (const aliasId of Object.keys(entityAliases)) { - if (this.isEntityAliasEqual(entityAliases[aliasId], aliasInfo)) { - newAliasId = aliasId; - break; - } - } - if (!newAliasId) { - const newAliasName = this.createEntityAliasName(entityAliases, aliasInfo.alias); - newAliasId = this.utils.guid(); - entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter}; - } - return newAliasId; - } - - private createEntityAliasName(entityAliases: EntityAliases, alias: string): string { - let c = 0; - let newAlias = alias; - let unique = false; - while (!unique) { - unique = true; - for (const entAliasId of Object.keys(entityAliases)) { - const entAlias = entityAliases[entAliasId]; - if (newAlias === entAlias.alias) { - c++; - newAlias = alias + c; - unique = false; - } - } - } - return newAlias; - } - - private isFilterEqual(filter1: FilterInfo, filter2: FilterInfo): boolean { - return isEqual(filter1.keyFilters, filter2.keyFilters); - } - - private getFilterId(filters: Filters, filterInfo: FilterInfo): string { - let newFilterId: string; - for (const filterId of Object.keys(filters)) { - if (this.isFilterEqual(filters[filterId], filterInfo)) { - newFilterId = filterId; - break; - } - } - if (!newFilterId) { - const newFilterName = this.createFilterName(filters, filterInfo.filter); - newFilterId = this.utils.guid(); - filters[newFilterId] = {id: newFilterId, filter: newFilterName, - keyFilters: filterInfo.keyFilters, editable: filterInfo.editable}; - } - return newFilterId; - } - - private createFilterName(filters: Filters, filter: string): string { - let c = 0; - let newFilter = filter; - let unique = false; - while (!unique) { - unique = true; - for (const entFilterId of Object.keys(filters)) { - const entFilter = filters[entFilterId]; - if (newFilter === entFilter.filter) { - c++; - newFilter = filter + c; - unique = false; - } - } - } - return newFilter; } private storeSet(key: string, elem: any) { diff --git a/ui-ngx/src/app/core/services/resources.service.ts b/ui-ngx/src/app/core/services/resources.service.ts index e884fb59ab..a50f445266 100644 --- a/ui-ngx/src/app/core/services/resources.service.ts +++ b/ui-ngx/src/app/core/services/resources.service.ts @@ -38,7 +38,6 @@ import { selectIsAuthenticated } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; import { map, tap } from 'rxjs/operators'; import { RequestConfig } from '@core/http/http-utils'; -import { getFlexLayoutModule } from '@app/shared/legacy/flex-layout.models'; import { isJSResource, removeTbResourcePrefix } from '@shared/models/resource.models'; export interface ModuleInfo { @@ -206,42 +205,25 @@ export class ResourcesService { const subject = new ReplaySubject(); this.loadedModulesWithComponents[url] = subject; - forkJoin( - [ - modulesMap.init(), - from(import('@angular/compiler')) - ] - ).subscribe( + forkJoin([ + modulesMap.init(), + from(import('@angular/compiler')) + ]).subscribe( () => { // @ts-ignore System.import(url, undefined, meta).then( (module: any) => { try { const modulesWithComponents = this.extractModulesWithComponents(module); - this.patchModulesWithFlexLayout(modulesWithComponents).subscribe( - { - next: modules => { - if (modules.modules.length || modules.standaloneComponents.length) { - try { - for (const module of modules.modules) { - createNgModule(module.module.type, this.injector); - } - this.loadedModulesWithComponents[url].next(modulesWithComponents); - this.loadedModulesWithComponents[url].complete(); - } catch (e) { - console.log(`Unable to parse module from url: ${url}`, e); - this.loadedModulesWithComponents[url].error(new Error(`Unable to parse module from url: ${url}`)); - } - } else { - this.loadedModulesWithComponents[url].error(new Error(`Module '${url}' doesn't have exported modules or components!`)); - } - }, - error: err => { - console.log(`Unable to patch module with flexLayout, module url: ${url}`, err); - this.loadedModulesWithComponents[url].error(new Error(`Unable to patch module with flexLayout, module url: ${url}`)); - } + if (modulesWithComponents.modules.length || modulesWithComponents.standaloneComponents.length) { + for (const module of modulesWithComponents.modules) { + createNgModule(module.module.type, this.injector); } - ); + this.loadedModulesWithComponents[url].next(modulesWithComponents); + this.loadedModulesWithComponents[url].complete(); + } else { + this.loadedModulesWithComponents[url].error(new Error(`Module '${url}' doesn't have exported modules or components!`)); + } } catch (e) { console.log(`Unable to parse module from url: ${url}`, e); this.loadedModulesWithComponents[url].error(new Error(`Unable to parse module from url: ${url}`)); @@ -343,40 +325,6 @@ export class ResourcesService { return modulesWithComponents; } - private patchModulesWithFlexLayout(modulesWithComponents: ModulesWithComponents): Observable { - return getFlexLayoutModule().pipe( - map((flexLayoutModule) => { - modulesWithComponents.modules.forEach(m => { - if (Array.isArray(m.module.imports)) { - if (!m.module.imports.includes(flexLayoutModule)) { - m.module.imports.push(flexLayoutModule); - } - } else { - const imports = m.module.imports(); - if (!imports.includes(flexLayoutModule)) { - imports.push(flexLayoutModule); - m.module.imports = imports; - } - } - }); - modulesWithComponents.standaloneComponents.forEach(c => { - if (Array.isArray(c.dependencies)) { - if (!c.dependencies.includes(flexLayoutModule)) { - c.dependencies.push(flexLayoutModule); - } - } else { - const dependencies = c.dependencies(); - if (!dependencies.includes(flexLayoutModule)) { - dependencies.push(flexLayoutModule); - c.dependencies = dependencies; - } - } - }); - return modulesWithComponents; - }) - ); - } - private loadResourceByType(type: 'css' | 'js', url: string): Observable { const subject = new ReplaySubject(); this.loadedResources[url] = subject; diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 3096054a67..c37cae599e 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -15,9 +15,9 @@ /// import _ from 'lodash'; -import { from, Observable, of, ReplaySubject, Subject } from 'rxjs'; +import { from, isObservable, Observable, of, ReplaySubject, Subject } from 'rxjs'; import { catchError, finalize, share } from 'rxjs/operators'; -import { Datasource, DatasourceData, FormattedData, ReplaceInfo } from '@app/shared/models/widget.models'; +import { DataKey, Datasource, DatasourceData, FormattedData, ReplaceInfo } from '@app/shared/models/widget.models'; import { EntityId } from '@shared/models/id/entity-id'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { baseDetailsPageByEntityType, EntityType } from '@shared/models/entity-type.models'; @@ -331,6 +331,10 @@ export function deepClone(target: T, ignoreFields?: string[]): T { if (target === null) { return target; } + // Observables can't be cloned using the spread operator, because they have non-enumerable methods (like .pipe). + if (isObservable(target)) { + return target; + } if (target instanceof Date) { return new Date(target.getTime()) as any; } @@ -491,11 +495,12 @@ export const createLabelFromSubscriptionEntityInfo = (entityInfo: SubscriptionEn export const hasDatasourceLabelsVariables = (pattern: string): boolean => varsRegex.test(pattern) !== null; -export function formattedDataFormDatasourceData(input: DatasourceData[], dataIndex?: number, ts?: number): FormattedData[] { - return _(input).groupBy(el => el.datasource.entityName + el.datasource.entityType) +export function formattedDataFormDatasourceData(input: DatasourceData[], dataIndex?: number, ts?: number, + groupFunction: (el: DatasourceData) => any = (el) => el.datasource.entityName + el.datasource.entityType): FormattedData[] { + return _(input).groupBy(groupFunction) .values().value().map((entityArray, i) => { - const datasource = entityArray[0].datasource; - const obj = formattedDataFromDatasource(datasource, i); + const datasource = entityArray[0].datasource as D; + const obj = formattedDataFromDatasource(datasource, i); entityArray.filter(el => el.data.length).forEach(el => { const index = isDefined(dataIndex) ? dataIndex : el.data.length - 1; const dataSet = isDefined(ts) ? el.data.find(data => data[0] === ts) : el.data[index]; @@ -511,18 +516,20 @@ export function formattedDataFormDatasourceData(input: DatasourceData[], dataInd }); } -export function formattedDataArrayFromDatasourceData(input: DatasourceData[]): FormattedData[][] { - return _(input).groupBy(el => el.datasource.entityName) +export function formattedDataArrayFromDatasourceData(input: DatasourceData[], + groupFunction: (el: DatasourceData) => any = + (el) => el.datasource.entityName + el.datasource.entityType): FormattedData[][] { + return _(input).groupBy(groupFunction) .values().value().map((entityArray, dsIndex) => { - const timeDataMap: {[time: number]: FormattedData} = {}; + const timeDataMap: {[time: number]: FormattedData} = {}; entityArray.filter(e => e.data.length).forEach(entity => { entity.data.forEach(tsData => { const time = tsData[0]; const value = tsData[1]; let data = timeDataMap[time]; if (!data) { - const datasource = entity.datasource; - data = formattedDataFromDatasource(datasource, dsIndex); + const datasource = entity.datasource as D; + data = formattedDataFromDatasource(datasource, dsIndex); data.time = time; timeDataMap[time] = data; } @@ -537,7 +544,7 @@ export function formattedDataArrayFromDatasourceData(input: DatasourceData[]): F }); } -export function formattedDataFromDatasource(datasource: Datasource, dsIndex: number): FormattedData { +export function formattedDataFromDatasource(datasource: D, dsIndex: number): FormattedData { return { entityName: datasource.entityName, deviceName: datasource.entityName, @@ -845,7 +852,7 @@ function prepareMessageFromData(data): string { } } -export function genNextLabel(name: string, datasources: Datasource[]): string { +export const genNextLabel = (name: string, datasources: Datasource[]): string => { let label = name; let i = 1; let matches = false; @@ -879,6 +886,25 @@ export function genNextLabel(name: string, datasources: Datasource[]): string { return label; } +export const genNextLabelForDataKeys = (name: string, dataKeys: DataKey[]): string => { + let label = name; + let i = 1; + let matches = false; + if (dataKeys) { + do { + matches = false; + dataKeys.forEach((dataKey) => { + if (dataKey?.label === label) { + i++; + label = name + ' ' + i; + matches = true; + } + }); + } while (matches) + } + return label; +} + export const getOS = (): string => { const userAgent = window.navigator.userAgent.toLowerCase(); const macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos|mac_powerpc)/i; diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 6ba51ee872..70432e8cdf 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -231,10 +231,10 @@ import * as EntityFilterViewComponent from '@home/components/entity/entity-filte import * as EntityAliasDialogComponent from '@home/components/alias/entity-alias-dialog.component'; import * as EntityFilterComponent from '@home/components/entity/entity-filter.component'; import * as RelationFiltersComponent from '@home/components/relation/relation-filters.component'; -import * as EntityAliasSelectComponent from '@home/components/alias/entity-alias-select.component'; -import * as DataKeysComponent from '@home/components/widget/config/data-keys.component'; -import * as DataKeyConfigDialogComponent from '@home/components/widget/config/data-key-config-dialog.component'; -import * as DataKeyConfigComponent from '@home/components/widget/config/data-key-config.component'; +import * as EntityAliasSelectComponent from '@home/components/widget/lib/settings/common/alias/entity-alias-select.component'; +import * as DataKeysComponent from '@home/components/widget/lib/settings/common/key/data-keys.component'; +import * as DataKeyConfigDialogComponent from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component'; +import * as DataKeyConfigComponent from '@home/components/widget/lib/settings/common/key/data-key-config.component'; import * as LegendConfigComponent from '@home/components/widget/lib/settings/common/legend-config.component'; import * as ManageWidgetActionsComponent from '@home/components/widget/action/manage-widget-actions.component'; import * as WidgetActionDialogComponent from '@home/components/widget/action/widget-action-dialog.component'; @@ -268,7 +268,7 @@ import * as ComplexFilterPredicateDialogComponent from '@home/components/filter/ import * as KeyFilterDialogComponent from '@home/components/filter/key-filter-dialog.component'; import * as FiltersDialogComponent from '@home/components/filter/filters-dialog.component'; import * as FilterDialogComponent from '@home/components/filter/filter-dialog.component'; -import * as FilterSelectComponent from '@home/components/filter/filter-select.component'; +import * as FilterSelectComponent from '@home/components/widget/lib/settings/common/filter/filter-select.component'; import * as FiltersEditComponent from '@home/components/filter/filters-edit.component'; import * as FiltersEditPanelComponent from '@home/components/filter/filters-edit-panel.component'; import * as UserFilterDialogComponent from '@home/components/filter/user-filter-dialog.component'; @@ -338,8 +338,7 @@ import * as AggregationOptionsConfigComponent from '@shared/components/time/aggr import * as IntervalOptionsConfigPanelComponent from '@shared/components/time/interval-options-config-panel.component'; import { IModulesMap } from '@modules/common/modules-map.models'; -import { Observable, map, of } from 'rxjs'; -import { getFlexLayout } from '@shared/legacy/flex-layout.models'; +import { Observable, of } from 'rxjs'; import { isJSResourceUrl } from '@shared/public-api'; class ModulesMap implements IModulesMap { @@ -352,10 +351,6 @@ class ModulesMap implements IModulesMap { '@angular/common': AngularCommon, '@angular/common/http': HttpClientModule, '@angular/forms': AngularForms, - '@angular/flex-layout': {}, - '@angular/flex-layout/flex': {}, - '@angular/flex-layout/grid': {}, - '@angular/flex-layout/extended': {}, '@angular/platform-browser': AngularPlatformBrowser, '@angular/platform-browser/animations': AngularPlatformBrowserAnimations, '@angular/router': AngularRouter, @@ -577,10 +572,10 @@ class ModulesMap implements IModulesMap { '@home/components/alias/entity-alias-dialog.component': EntityAliasDialogComponent, '@home/components/entity/entity-filter.component': EntityFilterComponent, '@home/components/relation/relation-filters.component': RelationFiltersComponent, - '@home/components/alias/entity-alias-select.component': EntityAliasSelectComponent, - '@home/components/widget/config/data-keys.component': DataKeysComponent, - '@home/components/widget/config/data-key-config-dialog.component': DataKeyConfigDialogComponent, - '@home/components/widget/config/data-key-config.component': DataKeyConfigComponent, + '@home/components/widget/lib/settings/common/alias/entity-alias-select.component': EntityAliasSelectComponent, + '@home/components/widget/lib/settings/common/key/data-keys.component': DataKeysComponent, + '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component': DataKeyConfigDialogComponent, + '@home/components/widget/lib/settings/common/key/data-key-config.component': DataKeyConfigComponent, '@home/components/widget/lib/settings/common/legend-config.component': LegendConfigComponent, '@home/components/widget/action/manage-widget-actions.component': ManageWidgetActionsComponent, '@home/components/widget/action/widget-action-dialog.component': WidgetActionDialogComponent, @@ -610,7 +605,7 @@ class ModulesMap implements IModulesMap { '@home/components/filter/key-filter-dialog.component': KeyFilterDialogComponent, '@home/components/filter/filters-dialog.component': FiltersDialogComponent, '@home/components/filter/filter-dialog.component': FilterDialogComponent, - '@home/components/filter/filter-select.component': FilterSelectComponent, + '@home/components/widget/lib/settings/common/filter/filter-select.component': FilterSelectComponent, '@home/components/filter/filters-edit.component': FiltersEditComponent, '@home/components/filter/filters-edit-panel.component': FiltersEditPanelComponent, '@home/components/filter/user-filter-dialog.component': UserFilterDialogComponent, @@ -678,39 +673,30 @@ class ModulesMap implements IModulesMap { init(): Observable { if (!this.initialized) { - return getFlexLayout().pipe( - map((flexLayout) => { - this.modulesMap['@angular/flex-layout'] = flexLayout; - this.modulesMap['@angular/flex-layout/flex'] = flexLayout; - this.modulesMap['@angular/flex-layout/grid'] = flexLayout; - this.modulesMap['@angular/flex-layout/extended'] = flexLayout; - System.constructor.prototype.resolve = (id: string) => { - try { - if (this.modulesMap[id]) { - return 'app:' + id; - } else { - return id; - } - } catch (err) { - return id; - } - }; - for (const moduleId of Object.keys(this.modulesMap)) { - System.set('app:' + moduleId, this.modulesMap[moduleId]); + System.constructor.prototype.resolve = (id: string) => { + try { + if (this.modulesMap[id]) { + return 'app:' + id; + } else { + return id; } - System.constructor.prototype.shouldFetch = (url: string) => url.endsWith('/download') || isJSResourceUrl(url); - System.constructor.prototype.fetch = (url: string, options: RequestInit & {meta?: any}) => { - if (options?.meta?.additionalHeaders) { - options.headers = { ...options.headers, ...options.meta.additionalHeaders }; - } - return fetch(url, options); - }; - this.initialized = true; - }) - ); - } else { - return of(null); + } catch (err) { + return id; + } + }; + for (const moduleId of Object.keys(this.modulesMap)) { + System.set('app:' + moduleId, this.modulesMap[moduleId]); + } + System.constructor.prototype.shouldFetch = (url: string) => url.endsWith('/download') || isJSResourceUrl(url); + System.constructor.prototype.fetch = (url: string, options: RequestInit & {meta?: any}) => { + if (options?.meta?.additionalHeaders) { + options.headers = { ...options.headers, ...options.meta.additionalHeaders }; + } + return fetch(url, options); + }; + this.initialized = true; } + return of(null); } } diff --git a/ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.ts b/ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.ts index b8ff2b60e1..c3a9490792 100644 --- a/ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/alias/aliases-entity-select.component.ts @@ -93,6 +93,7 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy { setTimeout(() => { this.updateDisplayValue(); this.updateEntityAliasesInfo(); + this.cd.detectChanges(); }, 0); } )); @@ -101,6 +102,7 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy { setTimeout(() => { this.updateDisplayValue(); this.updateEntityAliasesInfo(); + this.cd.detectChanges(); }, 0); } )); @@ -184,7 +186,6 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy { displayValue = this.translate.instant('entity.entities'); } this.displayValue = displayValue; - this.cd.detectChanges(); } private updateEntityAliasesInfo() { diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html b/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html index 4d7f20d6d5..79f2b6ebbc 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/panel/calculated-field-argument-panel.component.html @@ -83,8 +83,7 @@ - {{ 'tenant-profile.max-transport-messages-range' | translate}} + {{ 'tenant-profile.max-transport-messages-required' | translate}} - {{ 'tenant-profile.max-transport-messages-required' | translate}} + {{ 'tenant-profile.max-transport-messages-range' | translate}} diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html index 3aa743247f..1132c9982c 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/kafka-config.component.html @@ -73,20 +73,6 @@ - - rule-node-config.key-serializer - - - {{ 'rule-node-config.key-serializer-required' | translate }} - - - - rule-node-config.value-serializer - - - {{ 'rule-node-config.value-serializer-required' | translate }} - - diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions-dialog.component.ts index cdc77f51a7..bc3de16c69 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { widgetType } from '@shared/models/widget.models'; +import { WidgetActionType, widgetType } from '@shared/models/widget.models'; import { WidgetActionCallbacks, WidgetActionsData @@ -32,6 +32,7 @@ export interface ManageWidgetActionsDialogData { actionsData: WidgetActionsData; callbacks: WidgetActionCallbacks; widgetType: widgetType; + additionalWidgetActionTypes?: WidgetActionType[]; } @Component({ diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts index b3d8c5e654..730be78656 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts @@ -46,7 +46,7 @@ import { WidgetActionsDatasource } from '@home/components/widget/action/manage-widget-actions.component.models'; import { UtilsService } from '@core/services/utils.service'; -import { WidgetActionDescriptor, WidgetActionSource, widgetType } from '@shared/models/widget.models'; +import { WidgetActionDescriptor, WidgetActionSource, WidgetActionType, widgetType } from '@shared/models/widget.models'; import { WidgetActionDialogComponent, WidgetActionDialogData @@ -77,6 +77,8 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni @Input() actionSources: {[actionSourceId: string]: WidgetActionSource}; + @Input() additionalWidgetActionTypes: WidgetActionType[]; + innerValue: WidgetActionsData; displayedColumns: string[]; @@ -236,7 +238,8 @@ export class ManageWidgetActionsComponent extends PageComponent implements OnIni callbacks: this.callbacks, actionsData, action: deepClone(action), - widgetType: this.widgetType + widgetType: this.widgetType, + additionalWidgetActionTypes: this.additionalWidgetActionTypes } }).afterClosed().subscribe( (res) => { diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html index ec1b165401..db7e68516c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html @@ -121,7 +121,8 @@ + [widgetType]="data.widgetType" + [additionalWidgetActionTypes]="data.additionalWidgetActionTypes"> diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts index df8df4a882..ab2b8870d0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts @@ -43,6 +43,7 @@ import { CellClickColumnInfo, defaultWidgetAction, WidgetActionSource, + WidgetActionType, widgetType } from '@shared/models/widget.models'; import { takeUntil } from 'rxjs/operators'; @@ -58,6 +59,7 @@ export interface WidgetActionDialogData { actionsData: WidgetActionsData; action?: WidgetActionDescriptorInfo; widgetType: widgetType; + additionalWidgetActionTypes?: WidgetActionType[]; } @Component({ diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 20722a4ea4..fb6cc40f29 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -149,6 +149,7 @@ import { import { ValueStepperBasicConfigComponent } from '@home/components/widget/config/basic/rpc/value-stepper-basic-config.component'; +import { MapBasicConfigComponent } from '@home/components/widget/config/basic/map/map-basic-config.component'; @NgModule({ declarations: [ @@ -199,7 +200,8 @@ import { LabelCardBasicConfigComponent, LabelValueCardBasicConfigComponent, UnreadNotificationBasicConfigComponent, - ScadaSymbolBasicConfigComponent + ScadaSymbolBasicConfigComponent, + MapBasicConfigComponent ], imports: [ CommonModule, @@ -252,7 +254,8 @@ import { MobileAppQrCodeBasicConfigComponent, LabelCardBasicConfigComponent, LabelValueCardBasicConfigComponent, - UnreadNotificationBasicConfigComponent + UnreadNotificationBasicConfigComponent, + MapBasicConfigComponent ] }) export class BasicWidgetConfigModule { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts index 9c75a2fcd1..d790b1d477 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -45,7 +45,7 @@ import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { DataKeyConfigDialogComponent, DataKeyConfigDialogData -} from '@home/components/widget/config/data-key-config-dialog.component'; +} from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component'; import { deepClone, formatValue } from '@core/utils'; import { AggregatedValueCardKeyPosition, @@ -59,7 +59,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-aggregated-data-key-row', templateUrl: './aggregated-data-key-row.component.html', - styleUrls: ['./aggregated-data-key-row.component.scss', '../../data-keys.component.scss'], + styleUrls: ['./aggregated-data-key-row.component.scss', '../../../lib/settings/common/key/data-keys.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts index df2ba6ef9f..f8e22d54dc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts @@ -38,7 +38,7 @@ import { WidgetConfigComponent } from '@home/components/widget/widget-config.com import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { aggregatedValueCardDefaultKeySettings } from '@home/components/widget/lib/cards/aggregated-value-card.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts index 7a06ef38f9..686f868423 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/comparison-key-row.component.ts @@ -34,7 +34,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-comparison-key-row', templateUrl: './comparison-key-row.component.html', - styleUrls: ['./comparison-key-row.component.scss', '../../data-keys.component.scss'], + styleUrls: ['./comparison-key-row.component.scss', '../../../lib/settings/common/key/data-keys.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts index 2fd004f0fd..7997e332d5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts @@ -41,12 +41,12 @@ import { MatDialog } from '@angular/material/dialog'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKey, DataKeyConfigMode, DatasourceType, Widget, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeySettingsFunction } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { merge } from 'rxjs'; import { DataKeyConfigDialogComponent, DataKeyConfigDialogData -} from '@home/components/widget/config/data-key-config-dialog.component'; +} from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component'; import { deepClone } from '@core/utils'; import { Dashboard } from '@shared/models/dashboard.models'; import { IAliasController } from '@core/api/widget-api.models'; @@ -78,7 +78,7 @@ export const dataKeyRowValidator = (control: AbstractControl): ValidationErrors @Component({ selector: 'tb-data-key-row', templateUrl: './data-key-row.component.html', - styleUrls: ['./data-key-row.component.scss', '../../data-keys.component.scss'], + styleUrls: ['./data-key-row.component.scss', '../../../lib/settings/common/key/data-keys.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts index 89a2173ba0..fbd149571e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts @@ -43,7 +43,7 @@ import { dataKeyRowValidator, dataKeyValid } from '@home/components/widget/confi import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; -import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { coerceBoolean } from '@shared/decorators/coercion'; import { TimeSeriesChartYAxisId } from '@home/components/widget/lib/chart/time-series-chart.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts index 438808fb37..353ca04cb4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/widget-actions-panel.component.ts @@ -122,7 +122,8 @@ export class WidgetActionsPanelComponent implements ControlValueAccessor, OnInit widgetTitle: this.widgetConfigComponent.modelValue.widgetName, callbacks: this.widgetConfigComponent.widgetConfigCallbacks, actionsData, - widgetType: this.widgetConfigComponent.widgetType + widgetType: this.widgetConfigComponent.widgetType, + additionalWidgetActionTypes: this.widgetConfigComponent.modelValue.typeParameters.additionalWidgetActionTypes } }).afterClosed().subscribe( (res) => { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.html new file mode 100644 index 0000000000..dcefd50801 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.html @@ -0,0 +1,100 @@ + + + + + + +
+
widget-config.appearance
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + + + + + +
+
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.ts new file mode 100644 index 0000000000..9ce53ded4f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/map/map-basic-config.component.ts @@ -0,0 +1,187 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { isDefinedAndNotNull, isUndefined, mergeDeep, mergeDeepIgnoreArray } from '@core/utils'; +import { mapWidgetDefaultSettings, MapWidgetSettings } from '@home/components/widget/lib/maps/map-widget.models'; +import { cssSizeToStrSize, resolveCssSize } from '@shared/models/widget-settings.models'; +import { WidgetConfig } from '@shared/models/widget.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; + +@Component({ + selector: 'tb-map-basic-config', + templateUrl: './map-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class MapBasicConfigComponent extends BasicWidgetConfigComponent { + + mapWidgetConfigForm: UntypedFormGroup; + + trip = false; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.mapWidgetConfigForm; + } + + protected setupConfig(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + if (isDefinedAndNotNull(params.trip)) { + this.trip = params.trip === true; + } + super.setupConfig(widgetConfig); + } + + protected setupDefaults(configData: WidgetConfigComponentData) { + const settings = configData.config.settings as MapWidgetSettings; + if (settings?.markers?.length) { + settings.markers = []; + } + if (settings?.polygons?.length) { + settings.polygons = []; + } + if (settings?.circles?.length) { + settings.circles = []; + } + if (this.trip) { + if (settings?.trips?.length) { + settings.trips = []; + } + } + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: MapWidgetSettings = mergeDeepIgnoreArray({} as MapWidgetSettings, + mapWidgetDefaultSettings, configData.config.settings as MapWidgetSettings); + const iconSize = resolveCssSize(configData.config.iconSize); + this.mapWidgetConfigForm = this.fb.group({ + mapSettings: [settings, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + padding: [settings.padding, []], + + actions: [configData.config.actions || {}, []] + }); + if (this.trip) { + this.mapWidgetConfigForm.addControl('timewindowConfig', this.fb.control(getTimewindowConfig(configData.config))) + } + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + if (this.trip) { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + } + this.widgetConfig.config.settings = config.mapSettings || {}; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + this.widgetConfig.config.settings.padding = config.padding; + + this.widgetConfig.config.actions = config.actions; + + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.mapWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.mapWidgetConfigForm.get('showIcon').value; + + if (showTitle) { + this.mapWidgetConfigForm.get('title').enable(); + this.mapWidgetConfigForm.get('titleFont').enable(); + this.mapWidgetConfigForm.get('titleColor').enable(); + this.mapWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.mapWidgetConfigForm.get('iconSize').enable(); + this.mapWidgetConfigForm.get('iconSizeUnit').enable(); + this.mapWidgetConfigForm.get('icon').enable(); + this.mapWidgetConfigForm.get('iconColor').enable(); + } else { + this.mapWidgetConfigForm.get('iconSize').disable(); + this.mapWidgetConfigForm.get('iconSizeUnit').disable(); + this.mapWidgetConfigForm.get('icon').disable(); + this.mapWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.mapWidgetConfigForm.get('title').disable(); + this.mapWidgetConfigForm.get('titleFont').disable(); + this.mapWidgetConfigForm.get('titleColor').disable(); + this.mapWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.mapWidgetConfigForm.get('iconSize').disable(); + this.mapWidgetConfigForm.get('iconSizeUnit').disable(); + this.mapWidgetConfigForm.get('icon').disable(); + this.mapWidgetConfigForm.get('iconColor').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html index 0219872626..f7bb0662e9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html @@ -66,6 +66,10 @@ (); widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.html index 19a0a95abf..acd4fc43be 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.html @@ -78,7 +78,7 @@ - + {{ column.title }} 0) { this.defaultPageSize = pageSize; } - this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + + if (!this.defaultPageSize) { + this.defaultPageSize = pageStepIncrement ?? 10; + } + + if (!isDefinedAndNotNull(pageStepIncrement) || !isDefinedAndNotNull(pageStepCount)) { + pageStepIncrement = this.defaultPageSize; + pageStepCount = 3; + } + + for (let i = 1; i <= pageStepCount; i++) { + this.pageSizeOptions.push(pageStepIncrement * i); + } this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : 1024; const alarmFilter = this.entityService.resolveAlarmFilter(this.widgetConfig.alarmFilterConfig, false); @@ -429,6 +444,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); dataKey.title = getHeaderTitle(dataKey, keySettings, this.utils); dataKey.def = 'def' + this.columns.length; + dataKey.sortable = !keySettings.disableSorting && !(dataKey.type === DataKeyType.alarm && dataKey.name.startsWith('details.')); if (dataKey.type === DataKeyType.alarm && !isDefined(keySettings.columnWidth)) { const alarmField = alarmFields[dataKey.name]; if (alarmField && alarmField.time) { @@ -1146,10 +1162,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, } } - isSorting(column: EntityColumn): boolean { - return column.type === DataKeyType.alarm && column.name.startsWith('details.'); - } - private clearCache() { this.cellContentCache.length = 0; this.cellStyleCache.length = 0; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index 646569c405..9086bb1896 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -71,7 +71,7 @@ import { BehaviorSubject } from 'rxjs'; import { AggregationType } from '@shared/models/time/time.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeySettingsFunction } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { DeepPartial } from '@shared/models/common'; import { BarRenderSharedContext } from '@home/components/widget/lib/chart/time-series-chart-bar.models'; import { TimeSeriesChartStateValueConverter } from '@home/components/widget/lib/chart/time-series-chart-state.models'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts index 85b4775b97..c395192a60 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts @@ -42,7 +42,7 @@ import { import { IWidgetSubscription } from '@core/api/widget-api.models'; import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; -import { deepClone, hashCode, isDefined, isNumber, isObject, isUndefined } from '@core/utils'; +import { deepClone, hashCode, isDefined, isDefinedAndNotNull, isNumber, isObject, isUndefined } from '@core/utils'; import cssjs from '@core/css/css'; import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -139,7 +139,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni public enableStickyHeader = true; public enableStickyAction = true; public showCellActionsMenu = true; - public pageSizeOptions; + public pageSizeOptions = []; public pageLink: EntityDataPageLink; public sortOrderProperty: string; public textSearchMode = false; @@ -161,7 +161,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni private widgetResize$: ResizeObserver; private destroy$ = new Subject(); - private defaultPageSize = 10; + private defaultPageSize; private defaultSortOrder = 'entityName'; private contentsInfo: {[key: string]: CellContentInfo} = {}; @@ -311,10 +311,25 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni this.rowStylesInfo = getRowStyleInfo(this.ctx, this.settings, 'entity, ctx'); const pageSize = this.settings.defaultPageSize; + let pageStepIncrement = this.settings.pageStepIncrement; + let pageStepCount = this.settings.pageStepCount; + if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { this.defaultPageSize = pageSize; } - this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + + if (!this.defaultPageSize) { + this.defaultPageSize = pageStepIncrement ?? 10; + } + + if (!isDefinedAndNotNull(pageStepIncrement) || !isDefinedAndNotNull(pageStepCount)) { + pageStepIncrement = this.defaultPageSize; + pageStepCount = 3; + } + + for (let i = 1; i <= pageStepCount; i++) { + this.pageSizeOptions.push(pageStepIncrement * i); + } this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : 1024; this.noDataDisplayMessageText = @@ -448,7 +463,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); dataKey.title = getHeaderTitle(dataKey, keySettings, this.utils); dataKey.def = 'def' + this.columns.length; - dataKey.sortable = !dataKey.usePostProcessing && (!dataKey.aggregationType || dataKey.aggregationType === AggregationType.NONE); + dataKey.sortable = !keySettings.disableSorting && !dataKey.usePostProcessing + && (!dataKey.aggregationType ||dataKey.aggregationType === AggregationType.NONE); if (dataKey.type === DataKeyType.entityField && !isDefined(keySettings.columnWidth) || keySettings.columnWidth === '0px') { const entityField = entityFields[dataKey.name]; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/circle.ts similarity index 96% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/circle.ts index 9b55040daa..dbba0ada83 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/circle.ts @@ -15,10 +15,10 @@ /// import L, { LeafletMouseEvent } from 'leaflet'; -import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps/map-models'; -import { functionValueCalculator, parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils'; -import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; -import { createTooltip } from '@home/components/widget/lib/maps/maps-utils'; +import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps-legacy/map-models'; +import { functionValueCalculator, parseWithTranslation } from '@home/components/widget/lib/maps-legacy/common-maps-utils'; +import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map'; +import { createTooltip } from '@home/components/widget/lib/maps-legacy/maps-utils'; import { FormattedData } from '@shared/models/widget.models'; import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/common-maps-utils.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/common-maps-utils.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/dialogs/select-entity-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/dialogs/select-entity-dialog.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/dialogs/select-entity-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/dialogs/select-entity-dialog.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/dialogs/select-entity-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/dialogs/select-entity-dialog.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/leaflet-map.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/leaflet-map.ts index ef704abc7e..efd473e857 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/leaflet-map.ts @@ -16,9 +16,7 @@ import L, { FeatureGroup, LatLngBounds, LatLngTuple, PointExpression, Projection } from 'leaflet'; import tinycolor from 'tinycolor2'; -import 'leaflet-providers'; -import 'leaflet.markercluster'; -import '@geoman-io/leaflet-geoman-free'; +import '@home/components/widget/lib/maps/leaflet/leaflet-tb'; import { CircleData, @@ -43,8 +41,8 @@ import { isJSON, isValidLatLng, LabelSettings -} from '@home/components/widget/lib/maps/maps-utils'; -import { checkLngLat, createLoadingDiv } from '@home/components/widget/lib/maps/common-maps-utils'; +} from '@home/components/widget/lib/maps-legacy/maps-utils'; +import { checkLngLat, createLoadingDiv } from '@home/components/widget/lib/maps-legacy/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { deepClone, @@ -60,7 +58,7 @@ import { TranslateService } from '@ngx-translate/core'; import { SelectEntityDialogComponent, SelectEntityDialogData -} from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component'; +} from '@home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component'; import { MatDialog } from '@angular/material/dialog'; import { FormattedData, ReplaceInfo } from '@shared/models/widget.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; @@ -125,10 +123,6 @@ export default abstract class LeafletMap { private initMarkerClusterSettings() { const markerClusteringSettings: WidgetMarkerClusteringSettings = this.options; if (markerClusteringSettings.useClusterMarkers) { - // disabled marker cluster icon - (L as any).MarkerCluster = (L as any).MarkerCluster.extend({ - options: { pmIgnore: true, ...L.Icon.prototype.options } - }); this.clusteringSettings = { spiderfyOnMaxZoom: markerClusteringSettings.spiderfyOnMaxZoom, zoomToBoundsOnClick: markerClusteringSettings.zoomOnClick, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-models.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-models.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-widget.interface.ts similarity index 91% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-widget.interface.ts index 8c8a192570..fd0629444a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-widget.interface.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; +import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map'; export interface MapWidgetInterface { map?: LeafletMap; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-widget2.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-widget2.ts index 887956f212..c0a081fe2b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/map-widget2.ts @@ -23,7 +23,7 @@ import { Datasource, DatasourceData, FormattedData, WidgetActionDescriptor } fro import { TranslateService } from '@ngx-translate/core'; import { UtilsService } from '@core/services/utils.service'; import { EntityDataPageLink } from '@shared/models/query/query.models'; -import { providerClass } from '@home/components/widget/lib/maps/providers/public-api'; +import { providerClass } from '@home/components/widget/lib/maps-legacy/providers/public-api'; import { isDefined, isDefinedAndNotNull, parseTbFunction } from '@core/utils'; import L from 'leaflet'; import { firstValueFrom, forkJoin, from, Observable, of } from 'rxjs'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/maps-utils.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/maps-utils.ts index 49abdceaf3..1722442fc7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/maps-utils.ts @@ -18,7 +18,7 @@ import L from 'leaflet'; import { GenericFunction, ShowTooltipAction, WidgetToolipSettings } from './map-models'; import { Datasource, FormattedData } from '@app/shared/models/widget.models'; import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecuteTbFunction } from '@core/utils'; -import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils'; +import { parseWithTranslation } from '@home/components/widget/lib/maps-legacy/common-maps-utils'; import { CompiledTbFunction } from '@shared/models/js-function.models'; export function createTooltip(target: L.Layer, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/markers.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/markers.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/markers.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/markers.ts index 15cf6a97b0..e300939b37 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/markers.ts @@ -52,7 +52,7 @@ export class Marker { this.leafletMarker = L.marker(this.location, { pmIgnore: !settings.draggableMarker, snapIgnore: !snappable, - tbMarkerData: this.data + tbMarkerData: this.data as any }); this.markerOffset = [ @@ -101,7 +101,7 @@ export class Marker { setDataSources(data: FormattedData, dataSources: FormattedData[]) { this.data = data; this.dataSources = dataSources; - this.leafletMarker.options.tbMarkerData = data; + this.leafletMarker.options.tbMarkerData = data as any; } updateMarkerTooltip(data: FormattedData) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/polygon.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/polygon.ts index 9304a815d1..c3170cd008 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/polygon.ts @@ -20,7 +20,7 @@ import { functionValueCalculator, parseWithTranslation } from './common-maps-uti import { WidgetPolygonSettings } from './map-models'; import { FormattedData } from '@shared/models/widget.models'; import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils'; -import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; +import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map'; export class Polygon { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/polyline.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/polyline.ts index ccec4568d5..2421247242 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polyline.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/polyline.ts @@ -19,7 +19,7 @@ import 'leaflet-polylinedecorator'; import L, { PolylineDecorator, PolylineDecoratorOptions, Symbol } from 'leaflet'; import { WidgetPolylineSettings } from './map-models'; -import { functionValueCalculator } from '@home/components/widget/lib/maps/common-maps-utils'; +import { functionValueCalculator } from '@home/components/widget/lib/maps-legacy/common-maps-utils'; import { FormattedData } from '@shared/models/widget.models'; export class Polyline { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/google-map.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/google-map.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/google-map.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/here-map.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/here-map.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/here-map.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/image-map.ts similarity index 97% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/image-map.ts index b2d2522beb..51bfdd059d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/image-map.ts @@ -25,7 +25,7 @@ import { } from '../map-models'; import { combineLatest, Observable, of, ReplaySubject, switchMap } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils'; +import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps-legacy/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -127,32 +127,33 @@ export class ImageMap extends LeafletMap { return this.imageFromAlias(result); } - private imageFromUrl(url: string): Observable { + private imageFromUrl(url: string, update = false): Observable { return loadImageWithAspect(this.ctx.$injector.get(ImagePipe), url).pipe( switchMap( aspectImage => { if (aspectImage) { return of({ imageUrl: aspectImage.url, aspect: aspectImage.aspect, - update: false + update }); } else { - return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl); + return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl, update); } } ), - catchError(() => this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl)) + catchError(() => this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl, update)) ); } private imageFromAlias(alias: Observable<[DataSet, boolean]>): Observable { return alias.pipe( switchMap(res => { + const update = res[1]; const url = res[0][0][1]; const mapImage: MapImage = { imageUrl: null, aspect: null, - update: res[1] + update }; return loadImageWithAspect(this.ctx.$injector.get(ImagePipe), url).pipe( switchMap((aspectImage) => { @@ -161,10 +162,10 @@ export class ImageMap extends LeafletMap { mapImage.imageUrl = aspectImage.url; return of(mapImage); } else { - return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl); + return this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl, update); } }), - catchError(() => this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl)) + catchError(() => this.imageFromUrl(defaultImageMapProviderSettings.mapImageUrl, update)) ); }) ); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/openstreet-map.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/openstreet-map.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/openstreet-map.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/public-api.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/public-api.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/public-api.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/public-api.ts index 8010597f59..c45730733d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/public-api.ts @@ -20,7 +20,7 @@ import { GoogleMap } from './google-map'; import { HEREMap } from './here-map'; import { ImageMap } from './image-map'; import { Type } from '@angular/core'; -import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; +import LeafletMap from '@home/components/widget/lib/maps-legacy/leaflet-map'; export const providerClass: { [key: string]: Type } = { 'openstreet-map': OpenStreetMap, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/tencent-map.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/tencent-map.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/maps-legacy/providers/tencent-map.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/circles-data-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/circles-data-layer.ts new file mode 100644 index 0000000000..9e25ddb486 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/circles-data-layer.ts @@ -0,0 +1,237 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + CirclesDataLayerSettings, + defaultBaseCirclesDataLayerSettings, + isJSON, MapDataLayerType, + TbCircleData, + TbMapDatasource +} from '@shared/models/widget/maps/map.models'; +import L from 'leaflet'; +import { FormattedData } from '@shared/models/widget.models'; +import { TbShapesDataLayer } from '@home/components/widget/lib/maps/data-layer/shapes-data-layer'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { Observable } from 'rxjs'; +import { isNotEmptyStr } from '@core/utils'; +import { + TbLatestDataLayerItem, + UnplacedMapDataItem +} from '@home/components/widget/lib/maps/data-layer/latest-map-data-layer'; +import { map } from 'rxjs/operators'; + +class TbCircleDataLayerItem extends TbLatestDataLayerItem { + + private circle: L.Circle; + private circleStyle: L.PathOptions; + private editing = false; + + constructor(data: FormattedData, + dsData: FormattedData[], + protected settings: CirclesDataLayerSettings, + protected dataLayer: TbCirclesDataLayer) { + super(data, dsData, settings, dataLayer); + } + + public isEditing() { + return this.editing; + } + + public updateBubblingMouseEvents() { + this.circle.options.bubblingMouseEvents = !this.dataLayer.isEditMode(); + } + + protected create(data: FormattedData, dsData: FormattedData[]): L.Layer { + const circleData = this.dataLayer.extractCircleCoordinates(data); + const center = new L.LatLng(circleData.latitude, circleData.longitude); + this.circleStyle = this.dataLayer.getShapeStyle(data, dsData); + this.circle = L.circle(center, { + bubblingMouseEvents: !this.dataLayer.isEditMode(), + radius: circleData.radius, + ...this.circleStyle, + snapIgnore: !this.dataLayer.isSnappable() + }); + this.updateLabel(data, dsData); + return this.circle; + } + + protected unbindLabel() { + this.circle.unbindTooltip(); + } + + protected bindLabel(content: L.Content): void { + this.circle.bindTooltip(content, { className: 'tb-circle-label', permanent: true, direction: 'center'}) + .openTooltip(this.circle.getLatLng()); + } + + protected doUpdate(data: FormattedData, dsData: FormattedData[]): void { + this.circleStyle = this.dataLayer.getShapeStyle(data, dsData); + this.updateCircleShape(data); + this.updateTooltip(data, dsData); + this.updateLabel(data, dsData); + this.circle.setStyle(this.circleStyle); + } + + protected doInvalidateCoordinates(data: FormattedData, _dsData: FormattedData[]): void { + this.updateCircleShape(data); + } + + protected addItemClass(clazz: string): void { + if ((this.circle as any)._path) { + L.DomUtil.addClass((this.circle as any)._path, clazz); + } + } + + protected removeItemClass(clazz: string): void { + if ((this.circle as any)._path) { + L.DomUtil.removeClass((this.circle as any)._path, clazz); + } + } + + protected enableDrag(): void { + this.circle.pm.setOptions({ + snappable: this.dataLayer.isSnappable() + }); + this.circle.pm.enableLayerDrag(); + this.circle.on('pm:dragstart', () => { + this.editing = true; + }); + this.circle.on('pm:dragend', () => { + this.saveCircleCoordinates(); + this.editing = false; + }); + } + + protected disableDrag(): void { + this.circle.pm.disableLayerDrag(); + this.circle.off('pm:dragstart'); + this.circle.off('pm:dragend'); + } + + protected onSelected(): L.TB.ToolbarButtonOptions[] { + if (this.dataLayer.isEditEnabled()) { + this.circle.on('pm:markerdragstart', () => this.editing = true); + this.circle.on('pm:markerdragend', () => this.editing = false); + this.circle.on('pm:edit', () => this.saveCircleCoordinates()); + this.circle.pm.enable(); + } + return []; + } + + protected onDeselected(): void { + if (this.dataLayer.isEditEnabled()) { + this.circle.pm.disable(); + this.circle.off('pm:markerdragstart'); + this.circle.off('pm:markerdragend'); + this.circle.off('pm:edit'); + } + } + + protected removeDataItemTitle(): string { + return this.dataLayer.getCtx().translate.instant('widgets.maps.data-layer.circle.remove-circle-for', {entityName: this.data.entityName}); + } + + protected removeDataItem(): Observable { + return this.dataLayer.saveCircleCoordinates(this.data, null, null); + } + + private saveCircleCoordinates() { + const center = this.circle.getLatLng(); + const radius = this.circle.getRadius(); + this.dataLayer.saveCircleCoordinates(this.data, center, radius).subscribe(); + } + + private updateCircleShape(data: FormattedData) { + if (this.editing) { + return; + } + const circleData = this.dataLayer.extractCircleCoordinates(data); + const center = new L.LatLng(circleData.latitude, circleData.longitude); + if (!this.circle.getLatLng().equals(center)) { + this.circle.setLatLng(center); + } + if (this.circle.getRadius() !== circleData.radius) { + this.circle.setRadius(circleData.radius); + } + } +} + +export class TbCirclesDataLayer extends TbShapesDataLayer { + + constructor(protected map: TbMap, + inputSettings: CirclesDataLayerSettings) { + super(map, inputSettings); + } + + public dataLayerType(): MapDataLayerType { + return 'circles'; + } + + public placeItem(item: UnplacedMapDataItem, layer: L.Layer): void { + if (layer instanceof L.Circle) { + const center = layer.getLatLng(); + const radius = layer.getRadius(); + this.saveCircleCoordinates(item.entity, center, radius).subscribe( + (converted) => { + item.entity[this.settings.circleKey.label] = JSON.stringify(converted); + this.createItemFromUnplaced(item); + } + ); + } else { + console.warn('Unable to place item, layer is not a circle.'); + } + } + + public extractCircleCoordinates(data: FormattedData) { + const circleData: TbCircleData = JSON.parse(data[this.settings.circleKey.label]); + return this.map.circleDataToCoordinates(circleData); + } + + public saveCircleCoordinates(data: FormattedData, center: L.LatLng, radius: number): Observable { + const converted = center ? this.map.coordinatesToCircleData(center, radius) : null; + const circleData = [ + { + dataKey: this.settings.circleKey, + value: converted + } + ]; + return this.map.saveItemData(data.$datasource, circleData, this.settings.edit?.attributeScope).pipe( + map(() => converted) + ); + } + + protected setupDatasource(datasource: TbMapDatasource): TbMapDatasource { + datasource.dataKeys.push(this.settings.circleKey); + return datasource; + } + + protected defaultBaseSettings(map: TbMap): Partial { + return defaultBaseCirclesDataLayerSettings(map.type()); + } + + protected doSetup(): Observable { + return super.doSetup(); + } + + protected isValidLayerData(layerData: FormattedData): boolean { + return layerData && isNotEmptyStr(layerData[this.settings.circleKey.label]) && isJSON(layerData[this.settings.circleKey.label]); + } + + protected createLayerItem(data: FormattedData, dsData: FormattedData[]): TbLatestDataLayerItem { + return new TbCircleDataLayerItem(data, dsData, this.settings, this); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/data-layer-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/data-layer-utils.ts new file mode 100644 index 0000000000..d5329586be --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/data-layer-utils.ts @@ -0,0 +1,96 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + DataLayerTooltipSettings, + DataLayerTooltipTrigger, processTooltipTemplate, + TbMapDatasource +} from '@shared/models/widget/maps/map.models'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { FormattedData } from '@shared/models/widget.models'; +import L from 'leaflet'; +import { DataLayerPatternProcessor } from '@home/components/widget/lib/maps/data-layer/map-data-layer'; + +export const createTooltip = (map: TbMap, + layer: L.Layer, + settings: DataLayerTooltipSettings, + data: FormattedData, + canOpen: () => boolean): L.Popup => { + const tooltip = L.popup(); + layer.bindPopup(tooltip, {autoClose: settings.autoclose, closeOnClick: false}); + layer.off('click'); + if (settings.trigger === DataLayerTooltipTrigger.click) { + layer.on('click', () => { + if (tooltip.isOpen()) { + layer.closePopup(); + } else if (canOpen()) { + layer.openPopup(); + } + }); + } else if (settings.trigger === DataLayerTooltipTrigger.hover) { + layer.on('mouseover', () => { + if (canOpen()) { + layer.openPopup(); + } + }); + layer.on('mousemove', (e) => { + tooltip.setLatLng(e.latlng); + }); + layer.on('mouseout', () => { + layer.closePopup(); + }); + } + layer.on('popupopen', () => { + bindTooltipActions(map, tooltip, settings, data); + (layer as any)._popup._closeButton.addEventListener('click', (event: Event) => { + event.preventDefault(); + }); + }); + return tooltip; +} + +export const updateTooltip = (map: TbMap, + tooltip: L.Popup, + settings: DataLayerTooltipSettings, + processor: DataLayerPatternProcessor, + data: FormattedData, + dsData: FormattedData[]): void => { + let tooltipTemplate = processor.processPattern(data, dsData); + tooltipTemplate = processTooltipTemplate(tooltipTemplate); + tooltip.setContent(tooltipTemplate); + if (tooltip.isOpen() && tooltip.getElement()) { + bindTooltipActions(map, tooltip, settings, data); + } +} + +const bindTooltipActions = (map: TbMap, tooltip: L.Popup, settings: DataLayerTooltipSettings, data: FormattedData): void => { + const actions = tooltip.getElement().getElementsByClassName('tb-custom-action'); + Array.from(actions).forEach( + (element: HTMLElement) => { + const actionName = element.getAttribute('data-action-name'); + if (settings?.tagActions) { + const action = settings.tagActions.find(action => action.name === actionName); + if (action) { + element.onclick = ($event) => + { + map.dataItemClick($event, action, data); + return false; + }; + } + } + } + ); +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/latest-map-data-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/latest-map-data-layer.ts new file mode 100644 index 0000000000..3a537ba8e7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/latest-map-data-layer.ts @@ -0,0 +1,439 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + DataLayerEditAction, + MapDataLayerSettings, + TbMapDatasource +} from '@shared/models/widget/maps/map.models'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { FormattedData, WidgetActionType } from '@shared/models/widget.models'; +import { Observable } from 'rxjs'; +import L from 'leaflet'; +import { createTooltip, updateTooltip } from './data-layer-utils'; +import { TbDataLayerItem, TbMapDataLayer } from '@home/components/widget/lib/maps/data-layer/map-data-layer'; + +export abstract class TbLatestDataLayerItem = TbLatestMapDataLayer, L extends L.Layer = L.Layer> extends TbDataLayerItem { + + protected tooltip: L.Popup; + protected data: FormattedData; + protected selected = false; + + protected constructor(data: FormattedData, + dsData: FormattedData[], + settings: S, + dataLayer: D) { + super(settings, dataLayer); + this.data = data; + this.layer = this.create(data, dsData); + if (this.settings.tooltip?.show) { + this.tooltip = createTooltip(this.dataLayer.getMap(), + this.layer, this.settings.tooltip, this.data, () => { + return !this.isEditing(); + }); + updateTooltip(this.dataLayer.getMap(), this.tooltip, + this.settings.tooltip, this.dataLayer.dataLayerTooltipProcessor, data, dsData); + } + this.bindEvents(); + try { + this.dataLayer.getDataLayerContainer().addLayer(this.layer); + this.editModeUpdated(); + } catch (e) { + console.warn(e); + } + } + + public invalidateCoordinates(): void { + this.doInvalidateCoordinates(this.data, this.dataLayer.getMap().getData()); + } + + public select(): L.TB.ToolbarButtonOptions[] { + if (!this.selected) { + this.selected = true; + this.disableEdit(); + this.updateSelectedState(); + const buttons = this.onSelected(); + if (this.dataLayer.isRemoveEnabled()) { + buttons.push({ + id: 'remove', + title: this.removeDataItemTitle(), + click: () => { + this.removeDataItem().subscribe( + () => this.dataLayer.removeItem(this.data.entityId) + ); + }, + iconClass: 'tb-remove' + }); + } + return buttons; + } else { + return []; + } + } + + public deselect(cancel = false, force = false): boolean { + if (this.selected) { + if (this.canDeselect(cancel) || force) { + this.selected = false; + this.layer.closePopup(); + this.updateSelectedState(); + this.onDeselected(); + this.editModeUpdated(); + } else { + return false; + } + } + return true; + } + + public isSelected() { + return this.selected; + } + + public editModeUpdated() { + if (this.dataLayer.isEditMode() && !this.selected) { + this.enableEdit(); + } else { + this.disableEdit(); + } + this.updateSelectedState(); + this.updateBubblingMouseEvents(); + } + + public dragModeUpdated() { + if (this.dataLayer.isEditMode() && !this.selected) { + if (this.dataLayer.allowDrag()) { + this.enableDrag(); + this.addItemClass('tb-draggable'); + } else { + this.disableDrag(); + this.removeItemClass('tb-draggable'); + } + } + } + + public update(data: FormattedData, dsData: FormattedData[]): void { + this.data = data; + this.doUpdate(data, dsData); + } + + public remove() { + if (this.selected) { + this.dataLayer.getMap().deselectItem(false, true); + } + this.dataLayer.getDataLayerContainer().removeLayer(this.layer); + this.layer.off(); + } + + public isEditing() { + return false; + } + + protected bindEvents(): void { + if (this.dataLayer.isSelectable()) { + this.layer.on('click', () => { + if (!this.isEditing()) { + this.dataLayer.getMap().selectItem(this); + } + }); + } + const clickAction = this.settings.click; + if (clickAction && clickAction.type !== WidgetActionType.doNothing) { + this.layer.on('click', (event) => { + this.dataLayer.getMap().dataItemClick(event.originalEvent, clickAction, this.data); + }); + } + } + + protected enableEdit(): void { + if (this.dataLayer.isHoverable()) { + this.addItemClass('tb-hoverable'); + } + if (this.dataLayer.allowDrag()) { + this.enableDrag(); + this.addItemClass('tb-draggable'); + } + } + + protected disableEdit(): void { + if (this.dataLayer.isHoverable()) { + this.removeItemClass('tb-hoverable'); + } + if (this.dataLayer.isDragEnabled()) { + this.disableDrag(); + this.removeItemClass('tb-draggable'); + } + } + + protected updateSelectedState() { + if (this.selected) { + this.addItemClass('tb-selected'); + } else { + this.removeItemClass('tb-selected'); + } + } + + protected updateTooltip(data: FormattedData, dsData: FormattedData[]) { + if (this.settings.tooltip.show) { + updateTooltip(this.dataLayer.getMap(), this.tooltip, + this.settings.tooltip, this.dataLayer.dataLayerTooltipProcessor, data, dsData); + } + } + + protected updateLabel(data: FormattedData, dsData: FormattedData[]) { + if (this.settings.label.show) { + this.unbindLabel(); + const label = this.dataLayer.dataLayerLabelProcessor.processPattern(data, dsData); + const labelColor = this.dataLayer.getCtx().widgetConfig.color; + const content: L.Content = `
${label}
`; + this.bindLabel(content); + } + } + + protected canDeselect(cancel = false): boolean { + return true; + } + + protected onSelected(): L.TB.ToolbarButtonOptions[] { + return []; + } + + protected onDeselected(): void {} + + protected abstract create(data: FormattedData, dsData: FormattedData[]): L; + + protected abstract doUpdate(data: FormattedData, dsData: FormattedData[]): void; + + protected abstract doInvalidateCoordinates(data: FormattedData, dsData: FormattedData[]): void; + + protected abstract unbindLabel(): void; + + protected abstract bindLabel(content: L.Content): void; + + protected abstract addItemClass(clazz: string): void; + + protected abstract removeItemClass(clazz: string): void; + + protected abstract enableDrag(): void; + + protected abstract disableDrag(): void; + + protected abstract updateBubblingMouseEvents(): void; + + protected abstract removeDataItemTitle(): string; + + protected abstract removeDataItem(): Observable; + +} + +export interface UnplacedMapDataItem { + entity: FormattedData; + dataLayer: TbLatestMapDataLayer; +} + +export abstract class TbLatestMapDataLayer = any, L extends L.Layer = L.Layer> extends TbMapDataLayer> implements L.TB.DataLayer { + + protected addEnabled = false; + protected dragEnabled = false; + protected editEnabled = false; + protected removeEnabled = false; + + protected editable = false; + protected selectable = false; + protected hoverable = false; + + private editMode = false; + + private unplacedItems: UnplacedMapDataItem[] = []; + + protected constructor(map: TbMap, + inputSettings: S) { + super(map, inputSettings); + if (this.settings.edit?.enabledActions) { + this.addEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.add); + this.dragEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.move); + this.editEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.edit); + this.removeEnabled = this.settings.edit.enabledActions.includes(DataLayerEditAction.remove); + + this.editable = this.addEnabled || this.dragEnabled || this.editEnabled || this.removeEnabled; + this.selectable = this.removeEnabled || this.editEnabled; + this.hoverable = this.selectable || this.dragEnabled; + } + this.snappable = this.settings.edit?.snappable; + this.enableEditMode(); + } + + public isEditMode(): boolean { + return this.editMode; + } + + public isAddEnabled(): boolean { + return this.addEnabled; + } + + public isDragEnabled(): boolean { + return this.dragEnabled; + } + + public allowDrag(): boolean { + return this.dragEnabled && (!this.map.useDragModeButton() || this.map.dragModeEnabled()); + } + + public isEditEnabled(): boolean { + return this.editEnabled; + } + + public isRemoveEnabled(): boolean { + return this.removeEnabled; + } + + public isEditable(): boolean { + return this.editable; + } + + public isHoverable(): boolean { + return this.hoverable; + } + + public isSelectable(): boolean { + return this.selectable; + } + + public isSnappable(): boolean { + return this.snappable; + } + + public updateData(dsData: FormattedData[]) { + this.unplacedItems.length = 0; + const layerData = dsData.filter(d => d.$datasource.mapDataIds.includes(this.mapDataId)); + const toDelete = new Set(Array.from(this.layerItems.keys())); + const updatedItems: TbLatestDataLayerItem[] = []; + layerData.forEach((data) => { + if (this.isValidLayerData(data)) { + let layerItem = this.layerItems.get(data.entityId); + if (layerItem) { + layerItem.update(data, dsData); + updatedItems.push(layerItem); + } else { + layerItem = this.createLayerItem(data, dsData); + this.layerItems.set(data.entityId, layerItem); + } + toDelete.delete(data.entityId); + } else { + this.unplacedItems.push({ + entity: data, + dataLayer: this + }); + } + }); + toDelete.forEach((key) => { + this.removeItem(key); + }); + if (updatedItems.length) { + this.layerItemsUpdated(updatedItems); + } + } + + public hasUnplacedItems(): boolean { + return !!this.unplacedItems.length; + } + + public getUnplacedItems(): UnplacedMapDataItem[] { + return this.prepareUnplacedItems(); + } + + public enableEditMode() { + if (this.editable) { + if (!this.editMode) { + this.editMode = true; + this.updateItemsEditMode(); + } + } + } + + public disableEditMode() { + if (this.editMode) { + this.editMode = false; + this.updateItemsEditMode(); + } + } + + public dragModeUpdated() { + this.updateItemsDragMode(); + } + + protected createDataLayerContainer(): L.FeatureGroup { + return L.featureGroup([], {snapIgnore: !this.settings.edit?.snappable}); + } + + protected onDataLayerEnabled(): void { + this.updateItemsEditMode(); + } + + protected onDataLayerDisabled(): void { + for (const item of this.layerItems) { + if (item[1].isSelected()) { + this.getMap().deselectItem(false, true); + break; + } + } + } + + protected createItemFromUnplaced(unplacedItem: UnplacedMapDataItem): void { + const index = this.unplacedItems.indexOf(unplacedItem); + if (index > -1) { + this.unplacedItems.splice(index, 1); + const layerItem = this.createLayerItem(unplacedItem.entity, this.map.getData()); + this.layerItems.set(unplacedItem.entity.entityId, layerItem); + this.map.enabledDataLayersUpdated(); + } + } + + protected layerItemsUpdated(_updatedItems: TbLatestDataLayerItem[]): void { + } + + private prepareUnplacedItems(): UnplacedMapDataItem[] { + const div = document.createElement('div'); + for (const item of this.unplacedItems) { + if (!item.entity.entityDisplayName) { + if (this.settings.label.show) { + div.innerHTML = this.dataLayerLabelProcessor.processPattern(item.entity, this.getMap().getData()); + item.entity.entityDisplayName = div.textContent || div.innerText || ''; + } else { + item.entity.entityDisplayName = item.entity.entityName; + } + } + } + return this.unplacedItems; + } + + private updateItemsEditMode() { + this.layerItems.forEach(item => item.editModeUpdated()); + } + + private updateItemsDragMode() { + this.layerItems.forEach(item => item.dragModeUpdated()); + } + + public abstract placeItem(item: UnplacedMapDataItem, layer: L.Layer): void; + + protected abstract isValidLayerData(layerData: FormattedData): boolean; + + protected abstract createLayerItem(data: FormattedData, dsData: FormattedData[]): TbLatestDataLayerItem; + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/map-data-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/map-data-layer.ts new file mode 100644 index 0000000000..3ae61642ce --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/map-data-layer.ts @@ -0,0 +1,265 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + DataLayerColorSettings, DataLayerColorType, + DataLayerPatternSettings, DataLayerPatternType, + MapDataLayerSettings, MapDataLayerType, mapDataSourceSettingsToDatasource, + MapStringFunction, MapType, + TbMapDatasource +} from '@shared/models/widget/maps/map.models'; +import { + createLabelFromPattern, + guid, isDefined, + mergeDeepIgnoreArray, + parseTbFunction, + safeExecuteTbFunction +} from '@core/utils'; +import L from 'leaflet'; +import { CompiledTbFunction } from '@shared/models/js-function.models'; +import { forkJoin, Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { FormattedData } from '@shared/models/widget.models'; +import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { WidgetContext } from '@home/models/widget-component.models'; + +export class DataLayerPatternProcessor { + + private patternFunction: CompiledTbFunction; + private pattern: string; + + constructor(private dataLayer: TbMapDataLayer, + private settings: DataLayerPatternSettings) {} + + public setup(): Observable { + if (this.settings.type === DataLayerPatternType.function) { + return parseTbFunction(this.dataLayer.getCtx().http, this.settings.patternFunction, ['data', 'dsData']).pipe( + map((parsed) => { + this.patternFunction = parsed; + return null; + }) + ); + } else { + this.pattern = this.settings.pattern; + return of(null) + } + } + + public processPattern(data: FormattedData, dsData: FormattedData[]): string { + let pattern: string; + if (this.settings.type === DataLayerPatternType.function) { + pattern = safeExecuteTbFunction(this.patternFunction, [data, dsData]); + } else { + pattern = this.pattern; + } + const text = createLabelFromPattern(pattern, data); + const customTranslate = this.dataLayer.getCtx().$injector.get(CustomTranslatePipe); + return customTranslate.transform(text); + } + +} + +export class DataLayerColorProcessor { + + private colorFunction: CompiledTbFunction; + private color: string; + + constructor(private dataLayer: TbMapDataLayer, + private settings: DataLayerColorSettings) {} + + public setup(): Observable { + this.color = this.settings.color; + if (this.settings.type === DataLayerColorType.function) { + return parseTbFunction(this.dataLayer.getCtx().http, this.settings.colorFunction, ['data', 'dsData']).pipe( + map((parsed) => { + this.colorFunction = parsed; + return null; + }) + ); + } else { + return of(null) + } + } + + public processColor(data: FormattedData, dsData: FormattedData[]): string { + let color: string; + if (this.settings.type === DataLayerColorType.function) { + color = safeExecuteTbFunction(this.colorFunction, [data, dsData]); + if (!color) { + color = this.color; + } + } else { + color = this.color; + } + return color; + } + +} + +export abstract class TbDataLayerItem { + + protected layer: L; + + protected constructor(protected settings: S, + protected dataLayer: D) {} + + public getLayer(): L { + return this.layer; + } + + public getDataLayer(): D { + return this.dataLayer; + } + + public abstract remove(): void; + + public abstract invalidateCoordinates(): void; + +} + +export abstract class TbMapDataLayer { + + protected settings: S; + + protected datasource: TbMapDatasource; + + protected mapDataId = guid(); + + protected dataLayerContainer: L.FeatureGroup; + + protected layerItems = new Map(); + + protected groupsState: {[group: string]: boolean} = {}; + + protected enabled = true; + + protected snappable = false; + + public dataLayerLabelProcessor: DataLayerPatternProcessor; + public dataLayerTooltipProcessor: DataLayerPatternProcessor; + + protected constructor(protected map: TbMap, + inputSettings: S) { + this.settings = mergeDeepIgnoreArray({} as S, this.defaultBaseSettings(map) as S, inputSettings); + if (this.settings.groups?.length) { + this.settings.groups.forEach((group) => { + this.groupsState[group] = true; + }); + } + this.dataLayerContainer = this.createDataLayerContainer(); + this.dataLayerLabelProcessor = this.settings.label.show ? new DataLayerPatternProcessor(this, this.settings.label) : null; + this.dataLayerTooltipProcessor = this.settings.tooltip.show ? new DataLayerPatternProcessor(this, this.settings.tooltip): null; + this.map.getMap().addLayer(this.dataLayerContainer); + } + + public setup(): Observable { + this.datasource = mapDataSourceSettingsToDatasource(this.settings); + this.datasource.dataKeys = this.settings.additionalDataKeys ? [...this.settings.additionalDataKeys] : []; + this.mapDataId = this.datasource.mapDataIds[0]; + this.datasource = this.setupDatasource(this.datasource); + return forkJoin( + [ + this.dataLayerLabelProcessor ? this.dataLayerLabelProcessor.setup() : of(null), + this.dataLayerTooltipProcessor ? this.dataLayerTooltipProcessor.setup() : of(null), + this.doSetup() + ]); + } + + public removeItem(key: string): void { + const item = this.layerItems.get(key); + if (item) { + item.remove(); + this.layerItems.delete(key); + } + } + + public invalidateCoordinates(): void { + this.layerItems.forEach(item => item.invalidateCoordinates()); + } + + public getCtx(): WidgetContext { + return this.map.getCtx(); + } + + public getMap(): TbMap { + return this.map; + } + + public mapType(): MapType { + return this.map.type(); + } + + public getDatasource(): TbMapDatasource { + return this.datasource; + } + + public getDataLayerContainer(): L.FeatureGroup { + return this.dataLayerContainer; + } + + public getBounds(): L.LatLngBounds { + return this.dataLayerContainer.getBounds(); + } + + public isEnabled(): boolean { + return this.enabled; + } + + public getGroups(): string[] { + return this.settings.groups || []; + } + + public toggleGroup(group: string): boolean { + if (isDefined(this.groupsState[group])) { + this.groupsState[group] = !this.groupsState[group]; + const enabled = Object.values(this.groupsState).some(v => v); + if (this.enabled !== enabled) { + this.enabled = enabled; + if (this.enabled) { + this.map.getMap().addLayer(this.dataLayerContainer); + this.onDataLayerEnabled(); + } else { + this.onDataLayerDisabled(); + this.map.getMap().removeLayer(this.dataLayerContainer); + } + this.map.enabledDataLayersUpdated(); + return true; + } + } + return false; + } + + protected createDataLayerContainer(): L.FeatureGroup { + return L.featureGroup([], {snapIgnore: true}); + } + + protected setupDatasource(datasource: TbMapDatasource): TbMapDatasource { + return datasource; + } + + protected onDataLayerEnabled(): void {} + + protected onDataLayerDisabled(): void {} + + public abstract dataLayerType(): MapDataLayerType; + + protected abstract defaultBaseSettings(map: TbMap): Partial; + + protected abstract doSetup(): Observable; + +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/markers-data-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/markers-data-layer.ts new file mode 100644 index 0000000000..2b11a3c669 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/markers-data-layer.ts @@ -0,0 +1,738 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BaseMarkerShapeSettings, + ClusterMarkerColorFunction, + DataLayerColorType, + defaultBaseMarkersDataLayerSettings, + isValidLatLng, + loadImageWithAspect, MapDataLayerType, + MapStringFunction, + MapType, + MarkerIconInfo, + MarkerIconSettings, + MarkerImageFunction, + MarkerImageInfo, + MarkerImageSettings, + MarkerImageType, + MarkerPositionFunction, + MarkersDataLayerSettings, + MarkerShapeSettings, + MarkerType, + TbMapDatasource +} from '@shared/models/widget/maps/map.models'; +import L, { FeatureGroup } from 'leaflet'; +import { FormattedData } from '@shared/models/widget.models'; +import { forkJoin, Observable, of } from 'rxjs'; +import { CompiledTbFunction } from '@shared/models/js-function.models'; +import { + deepClone, + isDefined, + isDefinedAndNotNull, + isEmptyStr, + parseTbFunction, + safeExecuteTbFunction +} from '@core/utils'; +import { catchError, map, switchMap } from 'rxjs/operators'; +import tinycolor from 'tinycolor2'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { + createColorMarkerIconElement, + createColorMarkerShapeURI, MarkerIconContainer, + MarkerShape +} from '@shared/models/widget/maps/marker-shape.models'; +import { MatIconRegistry } from '@angular/material/icon'; +import { DomSanitizer } from '@angular/platform-browser'; +import { + TbLatestDataLayerItem, + TbLatestMapDataLayer, UnplacedMapDataItem +} from '@home/components/widget/lib/maps/data-layer/latest-map-data-layer'; +import { TbImageMap } from '@home/components/widget/lib/maps/image-map'; +import { TbMapDataLayer } from '@home/components/widget/lib/maps/data-layer/map-data-layer'; + +export class MarkerDataProcessor { + + private positionFunction: CompiledTbFunction; + private markerIconProcessor: MarkerIconProcessor; + + constructor(public dataLayer: TbMapDataLayer, + private settings: S, + public markerOffset: L.LatLngTuple, + public tooltipOffset: L.LatLngTuple) { + } + + public setup(): Observable { + this.markerIconProcessor = MarkerIconProcessor.fromSettings(this, this.settings); + const setup$: Observable[] = [this.markerIconProcessor.setup()]; + if (this.dataLayer.mapType() === MapType.image) { + setup$.push( + parseTbFunction(this.dataLayer.getCtx().http, this.settings.positionFunction, ['origXPos', 'origYPos', 'data', 'dsData', 'aspect']).pipe( + map((parsed) => { + this.positionFunction = parsed; + return null; + }) + ) + ); + } + return forkJoin(setup$).pipe(map(() => null)); + } + + public extractLocation(data: FormattedData, dsData: FormattedData[]): L.LatLng { + let locationData = this.extractLocationData(data); + if (locationData) { + if (this.dataLayer.mapType() === MapType.image && this.positionFunction) { + const imageMap = this.dataLayer.getMap() as TbImageMap; + locationData = this.positionFunction.execute(locationData.x, locationData.y, data, dsData, imageMap.getAspect()) || {x: 0, y: 0}; + } + return this.dataLayer.getMap().locationDataToLatLng(locationData); + } else { + return null; + } + } + + public extractLocationData(data: FormattedData): {x: number; y: number} { + if (data) { + const xKeyVal = data[this.settings.xKey.label]; + const yKeyVal = data[this.settings.yKey.label]; + switch (this.dataLayer.mapType()) { + case MapType.geoMap: + if (!isValidLatLng(xKeyVal, yKeyVal)) { + return null; + } + break; + case MapType.image: + if (!isDefinedAndNotNull(xKeyVal) || isEmptyStr(xKeyVal) || isNaN(xKeyVal) || !isDefinedAndNotNull(yKeyVal) || isEmptyStr(yKeyVal) || isNaN(yKeyVal)) { + return null; + } + break; + } + return {x: xKeyVal, y: yKeyVal}; + } else { + return null; + } + } + + public createMarkerIcon(data: FormattedData, + dsData: FormattedData[], + rotationAngle?: number): Observable { + return this.markerIconProcessor.createMarkerIcon(data, dsData, rotationAngle); + } + + public createDefaultMarkerIcon(rotationAngle = 0): Observable { + const color = this.settings.markerShape?.color?.color || '#307FE5'; + return this.createColoredMarkerShape(MarkerShape.markerShape1, tinycolor(color), rotationAngle); + } + + public createColoredMarkerShape(shape: MarkerShape, color: tinycolor.Instance, rotationAngle = 0, size = 34): Observable { + return createColorMarkerShapeURI(this.dataLayer.getCtx().$injector.get(MatIconRegistry), this.dataLayer.getCtx().$injector.get(DomSanitizer), shape, color).pipe( + map((iconUrl) => { + let icon: L.Icon; + if (rotationAngle === 0) { + icon = L.icon({ + iconUrl, + iconSize: [size, size], + iconAnchor: [size * this.markerOffset[0], size * this.markerOffset[1]], + popupAnchor: [size * this.tooltipOffset[0], size * this.tooltipOffset[1]] + }); + } else { + const style = `background-image: url(${iconUrl}); transform: rotate(${rotationAngle}deg); height: ${size}px; width: ${size}px;`; + icon = L.divIcon({ + html: `
`, + className: 'tb-marker-div-icon', + iconSize: [size, size], + iconAnchor: [size * this.markerOffset[0], size * this.markerOffset[1]], + popupAnchor: [size * this.tooltipOffset[0], size * this.tooltipOffset[1]] + }); + } + return { + size: [size, size], + icon + } + }) + ); + } + + public createColoredMarkerIcon(iconContainer: MarkerIconContainer, + icon: string, color: tinycolor.Instance, rotationAngle = 0, size = 34): Observable { + return createColorMarkerIconElement(this.dataLayer.getCtx().$injector.get(MatIconRegistry), this.dataLayer.getCtx().$injector.get(DomSanitizer), + iconContainer, icon, color).pipe( + map((element) => { + if (rotationAngle !== 0) { + element.style.transform = `rotate(${rotationAngle}deg)`; + } + return { + size: [size, size], + icon: L.divIcon({ + html: element.outerHTML, + className: 'tb-marker-div-icon', + iconSize: [size, size], + iconAnchor: [size * this.markerOffset[0], size * this.markerOffset[1]], + popupAnchor: [size * this.tooltipOffset[0], size * this.tooltipOffset[1]] + }) + }; + }) + ); + } +} + + +abstract class MarkerIconProcessor { + + static fromSettings(dataProcessor: MarkerDataProcessor, + settings: MarkersDataLayerSettings): MarkerIconProcessor { + switch (settings.markerType) { + case MarkerType.shape: + return new ShapeMarkerIconProcessor(dataProcessor, settings.markerShape); + case MarkerType.icon: + return new IconMarkerIconProcessor(dataProcessor, settings.markerIcon); + case MarkerType.image: + return new ImageMarkerIconProcessor(dataProcessor, settings.markerImage); + } + } + + protected constructor(protected dataProcessor: MarkerDataProcessor, + protected settings: S) {} + + public abstract setup(): Observable; + + public abstract createMarkerIcon(data: FormattedData, + dsData: FormattedData[], + rotationAngle?: number): Observable; + +} + +abstract class BaseColorMarkerShapeProcessor extends MarkerIconProcessor { + + private markerColorFunction: CompiledTbFunction; + + private defaultMarkerIconInfo: MarkerIconInfo; + + protected constructor(protected dataProcessor: MarkerDataProcessor, + protected settings: S) { + super(dataProcessor, settings); + } + + public setup(): Observable { + const colorSettings = this.settings.color; + if (colorSettings.type === DataLayerColorType.function) { + return parseTbFunction(this.dataProcessor.dataLayer.getCtx().http, colorSettings.colorFunction, ['data', 'dsData']).pipe( + map((parsed) => { + this.markerColorFunction = parsed; + return null; + }) + ); + } else { + const color = tinycolor(colorSettings.color); + return this.createMarkerShape(color, 0, this.settings.size).pipe( + map((info) => { + this.defaultMarkerIconInfo = info; + return null; + } + )); + } + } + + public createMarkerIcon(data: FormattedData, dsData: FormattedData[], rotationAngle = 0): Observable { + const colorSettings = this.settings.color; + let color: tinycolor.Instance; + if (colorSettings.type === DataLayerColorType.function) { + const functionColor = safeExecuteTbFunction(this.markerColorFunction, [data, dsData]); + if (isDefinedAndNotNull(functionColor)) { + color = tinycolor(functionColor); + } else { + color = tinycolor(colorSettings.color); + } + return this.createMarkerShape(color, rotationAngle, this.settings.size); + } else if (rotationAngle === 0) { + return of(this.defaultMarkerIconInfo); + } else { + color = tinycolor(colorSettings.color); + return this.createMarkerShape(color, rotationAngle, this.settings.size); + } + } + + protected abstract createMarkerShape(color: tinycolor.Instance, rotationAngle: number, size: number): Observable; +} + +class ShapeMarkerIconProcessor extends BaseColorMarkerShapeProcessor { + + constructor(protected dataProcessor: MarkerDataProcessor, + protected settings: MarkerShapeSettings) { + super(dataProcessor, settings); + } + + protected createMarkerShape(color: tinycolor.Instance, rotationAngle: number, size: number): Observable { + return this.dataProcessor.createColoredMarkerShape(this.settings.shape, color, rotationAngle, size); + } + +} + +class IconMarkerIconProcessor extends BaseColorMarkerShapeProcessor { + + constructor(protected dataProcessor: MarkerDataProcessor, + protected settings: MarkerIconSettings) { + super(dataProcessor, settings); + } + + protected createMarkerShape(color: tinycolor.Instance, rotationAngle: number, size: number): Observable { + return this.dataProcessor.createColoredMarkerIcon(this.settings.iconContainer, this.settings.icon, color, rotationAngle, size); + } + +} + +class ImageMarkerIconProcessor extends MarkerIconProcessor { + + private markerImageFunction: CompiledTbFunction; + + private defaultMarkerIconInfo: MarkerIconInfo; + + constructor(protected dataProcessor: MarkerDataProcessor, + protected settings: MarkerImageSettings) { + super(dataProcessor, settings); + } + + public setup(): Observable { + if (this.settings.type === MarkerImageType.function) { + return parseTbFunction(this.dataProcessor.dataLayer.getCtx().http, this.settings.imageFunction, ['data', 'images', 'dsData']).pipe( + map((parsed) => { + this.markerImageFunction = parsed; + return null; + }) + ); + } else { + const currentImage: MarkerImageInfo = { + url: this.settings.image, + size: this.settings.imageSize || 34 + }; + return this.loadMarkerIconInfo(currentImage, 0).pipe( + map((iconInfo) => { + this.defaultMarkerIconInfo = iconInfo; + return null; + } + )); + } + } + + public createMarkerIcon(data: FormattedData, dsData: FormattedData[], rotationAngle = 0): Observable { + if (this.settings.type === MarkerImageType.function) { + const currentImage: MarkerImageInfo = safeExecuteTbFunction(this.markerImageFunction, [data, this.settings.images, dsData]); + return this.loadMarkerIconInfo(currentImage, rotationAngle); + } else if (rotationAngle === 0) { + return of(this.defaultMarkerIconInfo); + } else { + const currentImage: MarkerImageInfo = { + url: this.settings.image, + size: this.settings.imageSize || 34 + }; + return this.loadMarkerIconInfo(currentImage, rotationAngle); + } + } + + private loadMarkerIconInfo(image: MarkerImageInfo, rotationAngle = 0): Observable { + if (image && image.url) { + return loadImageWithAspect(this.dataProcessor.dataLayer.getCtx().$injector.get(ImagePipe), image.url).pipe( + switchMap((aspectImage) => { + if (aspectImage?.aspect) { + let width: number; + let height: number; + if (aspectImage.aspect > 1) { + width = image.size; + height = image.size / aspectImage.aspect; + } else { + width = image.size * aspectImage.aspect; + height = image.size; + } + let iconAnchor = image.markerOffset; + let popupAnchor = image.tooltipOffset; + if (!iconAnchor) { + iconAnchor = [width * this.dataProcessor.markerOffset[0], height * this.dataProcessor.markerOffset[1]]; + } + if (!popupAnchor) { + popupAnchor = [width * this.dataProcessor.tooltipOffset[0], height * this.dataProcessor.tooltipOffset[1]]; + } + let icon: L.Icon; + if (rotationAngle === 0) { + icon = L.icon({ + iconUrl: aspectImage.url, + iconSize: [width, height], + iconAnchor, + popupAnchor + }); + } else { + const style = `background-image: url(${aspectImage.url}); background-size: contain; transform: rotate(${rotationAngle}deg); height: ${height}px; width: ${width}px;`; + icon = L.divIcon({ + html: `
`, + className: 'tb-marker-div-icon', + iconSize: [width, height], + iconAnchor, + popupAnchor + }); + } + const iconInfo: MarkerIconInfo = { + size: [width, height], + icon + }; + return of(iconInfo); + } else { + return this.dataProcessor.createDefaultMarkerIcon(rotationAngle); + } + }), + catchError(() => this.dataProcessor.createDefaultMarkerIcon(rotationAngle)) + ); + } else { + return this.dataProcessor.createDefaultMarkerIcon(rotationAngle); + } + } +} + +class TbMarkerDataLayerItem extends TbLatestDataLayerItem { + + private marker: L.Marker; + private labelOffset: L.PointTuple; + private iconClassList: string[]; + private moving = false; + + constructor(data: FormattedData, + dsData: FormattedData[], + protected settings: MarkersDataLayerSettings, + protected dataLayer: TbMarkersDataLayer) { + super(data, dsData, settings, dataLayer); + } + + public isEditing() { + return this.moving; + } + + public updateBubblingMouseEvents() { + this.marker.options.bubblingMouseEvents = !this.dataLayer.isEditMode(); + } + + protected create(data: FormattedData, dsData: FormattedData[]): L.Marker { + this.iconClassList = []; + const location = this.dataLayer.dataProcessor.extractLocation(data, dsData); + this.marker = L.marker(location, { + tbMarkerData: data, + snapIgnore: !this.dataLayer.isSnappable(), + bubblingMouseEvents: !this.dataLayer.isEditMode() + }); + this.updateMarkerIcon(data, dsData); + return this.marker; + } + + protected unbindLabel() { + this.marker.unbindTooltip(); + } + + protected bindLabel(content: L.Content): void { + this.marker.bindTooltip(content, { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.labelOffset }); + } + + protected doUpdate(data: FormattedData, dsData: FormattedData[]): void { + this.marker.options.tbMarkerData = data; + this.updateMarkerLocation(data, dsData); + this.updateTooltip(data, dsData); + this.updateMarkerIcon(data, dsData); + } + + protected doInvalidateCoordinates(data: FormattedData, dsData: FormattedData[]): void { + this.updateMarkerLocation(data, dsData); + } + + protected addItemClass(clazz: string): void { + if (!this.iconClassList.includes(clazz)) { + this.iconClassList.push(clazz); + this.marker.options.icon.options.className = this.updateIconClasses(this.marker.options.icon.options.className); + if ((this.marker as any)._icon) { + L.DomUtil.addClass((this.marker as any)._icon, clazz); + } + } + } + + protected removeItemClass(clazz: string): void { + const index = this.iconClassList.indexOf(clazz); + if (index !== -1) { + this.iconClassList.splice(index, 1); + this.marker.options.icon.options.className = this.updateIconClasses(this.marker.options.icon.options.className, clazz); + if ((this.marker as any)._icon) { + L.DomUtil.removeClass((this.marker as any)._icon, clazz); + } + } + } + + protected enableDrag(): void { + if (this.settings.markerClustering?.enable) { + this.marker.options.draggable = true; + this.marker.on('dragstart', () => { + this.moving = true; + }); + this.marker.on('dragend', () => { + this.saveMarkerLocation(); + this.moving = false; + }); + } else { + this.marker.pm.setOptions({ + snappable: this.dataLayer.isSnappable() + }); + this.marker.pm.enableLayerDrag(); + this.marker.on('pm:dragstart', () => { + this.moving = true; + }); + this.marker.on('pm:dragend', () => { + this.saveMarkerLocation(); + this.moving = false; + }); + } + } + + protected disableDrag(): void { + if (this.settings.markerClustering?.enable) { + this.marker.options.draggable = false; + this.marker.off('dragstart'); + this.marker.off('dragend'); + } else { + this.marker.pm.disableLayerDrag(); + this.marker.off('pm:dragstart'); + this.marker.off('pm:dragend'); + } + } + + protected removeDataItemTitle(): string { + return this.dataLayer.getCtx().translate.instant('widgets.maps.data-layer.marker.remove-marker-for', {entityName: this.data.entityName}); + } + + protected removeDataItem(): Observable { + return this.dataLayer.saveMarkerLocation(this.data, null); + } + + private saveMarkerLocation() { + const location = this.marker.getLatLng(); + this.dataLayer.saveMarkerLocation(this.data, location).subscribe(); + } + + private updateMarkerLocation(data: FormattedData, dsData: FormattedData[]) { + const location = this.dataLayer.dataProcessor.extractLocation(data, dsData); + if (!this.marker.getLatLng().equals(location) && !this.moving) { + this.marker.setLatLng(location); + } + } + + private updateMarkerIcon(data: FormattedData, dsData: FormattedData[]) { + this.dataLayer.dataProcessor.createMarkerIcon(data, dsData).subscribe( + (iconInfo) => { + let icon: L.Icon | L.DivIcon; + const options = deepClone(iconInfo.icon.options); + options.className = this.updateIconClasses(options.className); + if (iconInfo.icon instanceof L.DivIcon) { + icon = L.divIcon(options); + } else { + icon = L.icon(options as L.IconOptions); + } + this.marker.setIcon(icon); + const anchor = options.iconAnchor; + if (anchor && Array.isArray(anchor)) { + this.labelOffset = [iconInfo.size[0] / 2 - anchor[0], 10 - anchor[1]]; + } else { + this.labelOffset = [0, -iconInfo.size[1] * this.dataLayer.markerOffset[1] + 10]; + } + this.updateLabel(data, dsData); + this.editModeUpdated(); + } + ); + } + + private updateIconClasses(className: string, toRemove?: string): string { + const classes: string[] = []; + if (className?.length) { + classes.push(...className.split(' ')); + } + if (toRemove?.length) { + const index = classes.indexOf(toRemove); + if (index !== -1) { + classes.splice(index, 1); + } + } + this.iconClassList.forEach(clazz => { + if (!classes.includes(clazz)) { + classes.push(clazz); + } + }); + return classes.join(' '); + } +} + +export class TbMarkersDataLayer extends TbLatestMapDataLayer { + + public dataProcessor: MarkerDataProcessor; + + public markerOffset: L.LatLngTuple; + public tooltipOffset: L.LatLngTuple; + + private markersClusterContainer: L.MarkerClusterGroup; + private clusterMarkerColorFunction: CompiledTbFunction; + + constructor(protected map: TbMap, + inputSettings: MarkersDataLayerSettings) { + super(map, inputSettings); + } + + public dataLayerType(): MapDataLayerType { + return 'markers'; + } + + public placeItem(item: UnplacedMapDataItem, layer: L.Layer): void { + if (layer instanceof L.Marker) { + const position = layer.getLatLng(); + this.saveMarkerLocation(item.entity, position).subscribe( + (converted) => { + item.entity[this.settings.xKey.label] = converted.x; + item.entity[this.settings.yKey.label] = converted.y; + this.createItemFromUnplaced(item); + } + ); + } else { + console.warn('Unable to place item, layer is not a marker.'); + } + } + + public saveMarkerLocation(data: FormattedData, position: L.LatLng): Observable<{x: number; y: number}> { + const converted = this.map.latLngToLocationData(position); + const locationData = [ + { + dataKey: this.settings.xKey, + value: converted.x + }, + { + dataKey: this.settings.yKey, + value: converted.y + } + ]; + return this.map.saveItemData(data.$datasource, locationData, this.settings.edit?.attributeScope).pipe( + map(() => converted) + ); + } + + protected createDataLayerContainer(): FeatureGroup { + if (this.settings.markerClustering?.enable) { + return this.createMarkersClusterContainer(); + } else { + return super.createDataLayerContainer(); + } + } + + protected setupDatasource(datasource: TbMapDatasource): TbMapDatasource { + datasource.dataKeys.push(this.settings.xKey, this.settings.yKey); + return datasource; + } + + protected defaultBaseSettings(map: TbMap): Partial { + return defaultBaseMarkersDataLayerSettings(map.type()); + } + + protected doSetup(): Observable { + this.markerOffset = [ + isDefined(this.settings.markerOffsetX) ? this.settings.markerOffsetX : 0.5, + isDefined(this.settings.markerOffsetY) ? this.settings.markerOffsetY : 1, + ]; + this.tooltipOffset = [ + isDefined(this.settings.tooltip?.offsetX) ? this.settings.tooltip?.offsetX : 0, + isDefined(this.settings.tooltip?.offsetY) ? this.settings.tooltip?.offsetY : -1, + ]; + this.dataProcessor = new MarkerDataProcessor(this, this.settings, this.markerOffset, this.tooltipOffset); + const setup$: Observable[] = [this.dataProcessor.setup()]; + if (this.settings.markerClustering?.enable && this.settings.markerClustering.useClusterMarkerColorFunction) { + setup$.push( + parseTbFunction(this.getCtx().http, this.settings.markerClustering.clusterMarkerColorFunction, ['data', 'childCount']).pipe( + map((parsed) => { + this.clusterMarkerColorFunction = parsed; + return null; + }) + ) + ); + } + return forkJoin(setup$).pipe(map(() => null)); + } + + protected isValidLayerData(layerData: FormattedData): boolean { + return !!this.dataProcessor.extractLocationData(layerData); + } + + protected createLayerItem(data: FormattedData, dsData: FormattedData[]): TbMarkerDataLayerItem { + return new TbMarkerDataLayerItem(data, dsData, this.settings, this); + } + + protected layerItemsUpdated(updatedItems: TbLatestDataLayerItem[]) { + if (this.settings.markerClustering?.enable) { + this.markersClusterContainer.refreshClusters(updatedItems.map(item => item.getLayer())); + } + super.layerItemsUpdated(updatedItems); + } + + private createMarkersClusterContainer(): L.FeatureGroup { + const markerClusterOptions: L.MarkerClusterGroupOptions = { + spiderfyOnMaxZoom: this.settings.markerClustering?.spiderfyOnMaxZoom, + zoomToBoundsOnClick: this.settings.markerClustering?.zoomOnClick, + showCoverageOnHover: this.settings.markerClustering?.showCoverageOnHover, + removeOutsideVisibleBounds: this.settings.markerClustering?.lazyLoad, + animate: this.settings.markerClustering?.zoomAnimation, + chunkedLoading: this.settings.markerClustering?.chunkedLoad, + snapIgnore: !this.settings.edit?.snappable + }; + if (this.settings.markerClustering?.useClusterMarkerColorFunction) { + markerClusterOptions.iconCreateFunction = (cluster) => { + const childCount = cluster.getChildCount(); + const data = cluster.getAllChildMarkers().map(clusterMarker => clusterMarker.options.tbMarkerData); + const markerColor: string = this.clusterMarkerColorFunction ? + safeExecuteTbFunction(this.clusterMarkerColorFunction, [data, childCount]) : null; + if (isDefinedAndNotNull(markerColor) && tinycolor(markerColor).isValid()) { + const parsedColor = tinycolor(markerColor); + const alpha = parsedColor.getAlpha(); + return L.divIcon({ + html: `
` + + `
` + childCount + '
', + iconSize: new L.Point(40, 40), + className: 'tb-cluster-marker-container' + }); + } else { + let c = ' marker-cluster-'; + if (childCount < 10) { + c += 'small'; + } else if (childCount < 100) { + c += 'medium'; + } else { + c += 'large'; + } + return new L.DivIcon({ + html: '
' + childCount + '
', + className: 'marker-cluster' + c, + iconSize: new L.Point(40, 40) + }); + } + } + } + if (this.settings.markerClustering?.maxClusterRadius && this.settings.markerClustering.maxClusterRadius > 0) { + markerClusterOptions.maxClusterRadius = Math.floor(this.settings.markerClustering.maxClusterRadius); + } + if (this.settings.markerClustering?.maxZoom && this.settings.markerClustering.maxZoom >= 0 && this.settings.markerClustering.maxZoom < 19) { + markerClusterOptions.disableClusteringAtZoom = Math.floor(this.settings.markerClustering.maxZoom); + } + this.markersClusterContainer = new L.MarkerClusterGroup(markerClusterOptions); + return this.markersClusterContainer; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/polygons-data-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/polygons-data-layer.ts new file mode 100644 index 0000000000..36ab097c5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/polygons-data-layer.ts @@ -0,0 +1,435 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + defaultBasePolygonsDataLayerSettings, + isCutPolygon, isJSON, MapDataLayerType, + PolygonsDataLayerSettings, + TbMapDatasource, TbPolyData, TbPolygonCoordinates, TbPolygonRawCoordinates +} from '@shared/models/widget/maps/map.models'; +import L from 'leaflet'; +import { FormattedData } from '@shared/models/widget.models'; +import { TbShapesDataLayer } from '@home/components/widget/lib/maps/data-layer/shapes-data-layer'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { Observable } from 'rxjs'; +import { isNotEmptyStr, isString } from '@core/utils'; +import { + TbLatestDataLayerItem, + UnplacedMapDataItem +} from '@home/components/widget/lib/maps/data-layer/latest-map-data-layer'; +import { map } from 'rxjs/operators'; + +class TbPolygonDataLayerItem extends TbLatestDataLayerItem { + + private polygonContainer: L.FeatureGroup; + private polygon: L.Polygon; + private polygonStyle: L.PathOptions; + private editing = false; + + constructor(data: FormattedData, + dsData: FormattedData[], + protected settings: PolygonsDataLayerSettings, + protected dataLayer: TbPolygonsDataLayer) { + super(data, dsData, settings, dataLayer); + } + + public isEditing() { + return this.editing; + } + + public updateBubblingMouseEvents() { + this.polygon.options.bubblingMouseEvents = !this.dataLayer.isEditMode(); + } + + protected create(data: FormattedData, dsData: FormattedData[]): L.Layer { + const polyData = this.dataLayer.extractPolygonCoordinates(data); + const polyConstructor = isCutPolygon(polyData) || polyData.length !== 2 ? L.polygon : L.rectangle; + this.polygonStyle = this.dataLayer.getShapeStyle(data, dsData); + this.polygon = polyConstructor(polyData as (TbPolygonRawCoordinates & L.LatLngTuple[]), { + ...this.polygonStyle, + snapIgnore: !this.dataLayer.isSnappable(), + bubblingMouseEvents: !this.dataLayer.isEditMode() + }); + + this.polygonContainer = L.featureGroup(); + this.polygon.addTo(this.polygonContainer); + + this.updateLabel(data, dsData); + return this.polygonContainer; + } + + protected unbindLabel() { + this.polygonContainer.unbindTooltip(); + } + + protected bindLabel(content: L.Content): void { + this.polygonContainer.bindTooltip(content, {className: 'tb-polygon-label', permanent: true, direction: 'center'}) + .openTooltip(this.polygonContainer.getBounds().getCenter()); + } + + protected doUpdate(data: FormattedData, dsData: FormattedData[]): void { + this.polygonStyle = this.dataLayer.getShapeStyle(data, dsData); + this.updatePolygonShape(data); + this.updateTooltip(data, dsData); + this.updateLabel(data, dsData); + if (!this.editing || !this.dataLayer.getMap().getMap().pm.globalCutModeEnabled()) { + this.polygon.setStyle(this.polygonStyle); + } + } + + protected doInvalidateCoordinates(data: FormattedData, _dsData: FormattedData[]): void { + this.updatePolygonShape(data); + } + + protected addItemClass(clazz: string): void { + if ((this.polygon as any)._path) { + L.DomUtil.addClass((this.polygon as any)._path, clazz); + } + } + + protected removeItemClass(clazz: string): void { + if ((this.polygon as any)._path) { + L.DomUtil.removeClass((this.polygon as any)._path, clazz); + } + } + + protected enableDrag(): void { + this.polygon.pm.setOptions({ + snappable: this.dataLayer.isSnappable() + }); + this.polygon.pm.enableLayerDrag(); + this.polygon.on('pm:dragstart', () => { + this.editing = true; + }); + this.polygon.on('pm:drag', () => { + if (this.tooltip?.isOpen()) { + this.tooltip.setLatLng(this.polygon.getBounds().getCenter()); + } + }); + this.polygon.on('pm:dragend', () => { + this.savePolygonCoordinates(); + this.editing = false; + }); + } + + protected disableDrag(): void { + this.polygon.pm.disableLayerDrag(); + this.polygon.off('pm:dragstart'); + this.polygon.off('pm:dragend'); + } + + protected onSelected(): L.TB.ToolbarButtonOptions[] { + const buttons: L.TB.ToolbarButtonOptions[] = []; + if (this.dataLayer.isEditEnabled()) { + this.enablePolygonEditMode(); + buttons.push( + { + id: 'cut', + title: this.getDataLayer().getCtx().translate.instant('widgets.maps.data-layer.polygon.cut'), + iconClass: 'tb-cut', + click: (e, button) => { + const map = this.dataLayer.getMap().getMap(); + if (!map.pm.globalCutModeEnabled()) { + this.disablePolygonRotateMode(); + this.disablePolygonEditMode(); + this.enablePolygonCutMode(button); + } else { + this.disablePolygonCutMode(button); + this.enablePolygonEditMode(); + } + } + }, + { + id: 'rotate', + title: this.getDataLayer().getCtx().translate.instant('widgets.maps.data-layer.polygon.rotate'), + iconClass: 'tb-rotate', + click: (e, button) => { + if (!this.polygon.pm.rotateEnabled()) { + this.disablePolygonCutMode(); + this.disablePolygonEditMode(); + this.enablePolygonRotateMode(button); + } else { + this.disablePolygonRotateMode(button); + this.enablePolygonEditMode(); + } + } + } + ); + } + return buttons; + } + + protected onDeselected(): void { + if (this.dataLayer.isEditEnabled()) { + this.disablePolygonEditMode(); + this.disablePolygonCutMode(); + this.disablePolygonRotateMode(); + } + } + + protected canDeselect(cancel = false): boolean { + const map = this.dataLayer.getMap().getMap(); + if (map.pm.globalCutModeEnabled()) { + if (cancel) { + this.disablePolygonCutMode(); + } + return false; + } else if (this.polygon.pm.rotateEnabled()) { + if (cancel) { + this.disablePolygonRotateMode(); + } + return false; + } else if (this.editing) { + return false; + } + return true; + } + + protected removeDataItemTitle(): string { + return this.dataLayer.getCtx().translate.instant('widgets.maps.data-layer.polygon.remove-polygon-for', {entityName: this.data.entityName}); + } + + protected removeDataItem(): Observable { + return this.dataLayer.savePolygonCoordinates(this.data, null); + } + + private enablePolygonEditMode() { + this.polygon.on('pm:markerdragstart', () => this.editing = true); + this.polygon.on('pm:markerdragend', () => setTimeout(() => { + this.editing = false; + }) ); + this.polygon.on('pm:edit', () => this.savePolygonCoordinates()); + this.polygon.pm.enable(); + const map = this.dataLayer.getMap(); + map.getEditToolbar().getButton('remove')?.setDisabled(false); + } + + private disablePolygonEditMode() { + this.polygon.pm.disable(); + this.polygon.off('pm:markerdragstart'); + this.polygon.off('pm:markerdragend'); + this.polygon.off('pm:edit'); + const map = this.dataLayer.getMap(); + map.getEditToolbar().getButton('remove')?.setDisabled(true); + } + + private enablePolygonCutMode(cutButton?: L.TB.ToolbarButton) { + this.polygonContainer.closePopup(); + this.editing = true; + this.polygon.options.bubblingMouseEvents = true; + this.polygon.setStyle({...this.polygonStyle, dashArray: '5 5', weight: 3, + color: '#3388ff', opacity: 1, fillColor: '#3388ff', fillOpacity: 0.2}); + this.addItemClass('tb-cut-mode'); + this.polygon.once('pm:cut', (e) => { + if (e.layer instanceof L.Polygon) { + if (this.polygon instanceof L.Rectangle) { + this.polygonContainer.removeLayer(this.polygon); + this.polygon = L.polygon(e.layer.getLatLngs(), { + ...this.polygonStyle, + snapIgnore: !this.dataLayer.isSnappable(), + bubblingMouseEvents: !this.dataLayer.isEditMode() + }); + this.polygon.addTo(this.polygonContainer); + } else { + this.polygon.setLatLngs(e.layer.getLatLngs()); + } + } + // @ts-ignore + e.layer._pmTempLayer = true; + e.layer.remove(); + this.polygonContainer.removeLayer(this.polygon); + // @ts-ignore + this.polygon._pmTempLayer = false; + this.polygon.addTo(this.polygonContainer); + this.updateSelectedState(); + cutButton?.setActive(false); + this.savePolygonCoordinates() + }); + const map = this.dataLayer.getMap().getMap(); + map.pm.setLang('en', { + tooltips: { + firstVertex: this.getDataLayer().getCtx().translate.instant('widgets.maps.data-layer.polygon.polygon-place-first-point-cut-hint'), + continueLine: this.getDataLayer().getCtx().translate.instant('widgets.maps.data-layer.polygon.continue-polygon-cut-hint'), + finishPoly: this.getDataLayer().getCtx().translate.instant('widgets.maps.data-layer.polygon.finish-polygon-cut-hint') + } + }, 'en'); + map.pm.enableGlobalCutMode({ + // @ts-ignore + layersToCut: [this.polygon] + }); + // @ts-ignore + L.DomUtil.addClass(map.pm.Draw.Cut._hintMarker.getTooltip()._container, 'tb-place-item-label'); + cutButton?.setActive(true); + map.once('pm:globalcutmodetoggled', (e) => { + if (!e.enabled) { + this.disablePolygonCutMode(cutButton); + this.enablePolygonEditMode(); + } + }); + } + + private disablePolygonCutMode(cutButton?: L.TB.ToolbarButton) { + this.editing = false; + this.polygon.options.bubblingMouseEvents = !this.dataLayer.isEditMode(); + this.polygon.setStyle({...this.polygonStyle, dashArray: null}); + this.removeItemClass('tb-cut-mode'); + this.polygon.off('pm:cut'); + const map = this.dataLayer.getMap().getMap(); + map.pm.disableGlobalCutMode(); + cutButton?.setActive(false); + } + + private enablePolygonRotateMode(rotateButton?: L.TB.ToolbarButton) { + this.polygonContainer.closePopup(); + this.editing = true; + this.polygon.on('pm:rotateend', () => { + this.savePolygonCoordinates(); + }); + this.polygon.pm.enableRotate(); + rotateButton?.setActive(true); + this.polygon.on('pm:rotatedisable', () => { + this.disablePolygonRotateMode(rotateButton); + this.enablePolygonEditMode(); + }); + } + + private disablePolygonRotateMode(rotateButton?: L.TB.ToolbarButton) { + this.editing = false; + this.polygon.pm.disableRotate(); + this.polygon.off('pm:rotateend'); + this.polygon.off('pm:rotatedisable'); + rotateButton?.setActive(false); + } + + private savePolygonCoordinates() { + let coordinates: TbPolygonCoordinates = this.polygon.getLatLngs(); + if (coordinates.length === 1) { + coordinates = coordinates[0] as TbPolygonCoordinates; + } + if (this.polygon instanceof L.Rectangle && !isCutPolygon(coordinates)) { + const bounds = this.polygon.getBounds(); + const boundsArray = [bounds.getNorthWest(), bounds.getNorthEast(), bounds.getSouthWest(), bounds.getSouthEast()]; + if (coordinates.every(point => boundsArray.find(boundPoint => boundPoint.equals(point as L.LatLng)) !== undefined)) { + coordinates = [bounds.getNorthWest(), bounds.getSouthEast()]; + } + } + this.dataLayer.savePolygonCoordinates(this.data, coordinates).subscribe(); + } + + private updatePolygonShape(data: FormattedData) { + if (this.editing) { + return; + } + const polyData = this.dataLayer.extractPolygonCoordinates(data) as TbPolyData; + if (isCutPolygon(polyData) || polyData.length !== 2) { + if (this.polygon instanceof L.Rectangle) { + this.polygonContainer.removeLayer(this.polygon); + this.polygon = L.polygon(polyData, { + ...this.polygonStyle, + snapIgnore: !this.dataLayer.isSnappable(), + bubblingMouseEvents: !this.dataLayer.isEditMode() + }); + this.polygon.addTo(this.polygonContainer); + this.editModeUpdated(); + } else { + this.polygon.setLatLngs(polyData); + } + } else if (polyData.length === 2) { + const bounds = new L.LatLngBounds(polyData as L.LatLngTuple[]); + (this.polygon as L.Rectangle).setBounds(bounds); + } + } + +} + +export class TbPolygonsDataLayer extends TbShapesDataLayer { + + constructor(protected map: TbMap, + inputSettings: PolygonsDataLayerSettings) { + super(map, inputSettings); + } + + public dataLayerType(): MapDataLayerType { + return 'polygons'; + } + + public placeItem(item: UnplacedMapDataItem, layer: L.Layer): void { + if (layer instanceof L.Polygon) { + let coordinates: TbPolygonCoordinates; + if (layer instanceof L.Rectangle) { + const bounds = layer.getBounds(); + coordinates = [bounds.getNorthWest(), bounds.getSouthEast()]; + } else { + coordinates = layer.getLatLngs(); + if (coordinates.length === 1) { + coordinates = coordinates[0] as TbPolygonCoordinates; + } + } + this.savePolygonCoordinates(item.entity, coordinates).subscribe( + (converted) => { + item.entity[this.settings.polygonKey.label] = JSON.stringify(converted); + this.createItemFromUnplaced(item); + } + ); + } else { + console.warn('Unable to place item, layer is not a polygon.'); + } + } + + public extractPolygonCoordinates(data: FormattedData): TbPolygonRawCoordinates { + let rawPolyData = data[this.settings.polygonKey.label]; + if (isString(rawPolyData)) { + rawPolyData = JSON.parse(rawPolyData); + } + return this.map.polygonDataToCoordinates(rawPolyData); + } + + public savePolygonCoordinates(data: FormattedData, coordinates: TbPolygonCoordinates): Observable { + const converted = coordinates ? this.map.coordinatesToPolygonData(coordinates) : null; + const polygonData = [ + { + dataKey: this.settings.polygonKey, + value: converted + } + ]; + return this.map.saveItemData(data.$datasource, polygonData, this.settings.edit?.attributeScope).pipe( + map(() => converted) + ); + } + + protected setupDatasource(datasource: TbMapDatasource): TbMapDatasource { + datasource.dataKeys.push(this.settings.polygonKey); + return datasource; + } + + protected defaultBaseSettings(map: TbMap): Partial { + return defaultBasePolygonsDataLayerSettings(map.type()); + } + + protected doSetup(): Observable { + return super.doSetup(); + } + + protected isValidLayerData(layerData: FormattedData): boolean { + return layerData && ((isNotEmptyStr(layerData[this.settings.polygonKey.label]) && !isJSON(layerData[this.settings.polygonKey.label]) + || Array.isArray(layerData[this.settings.polygonKey.label]))); + } + + protected createLayerItem(data: FormattedData, dsData: FormattedData[]): TbPolygonDataLayerItem { + return new TbPolygonDataLayerItem(data, dsData, this.settings, this); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/shapes-data-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/shapes-data-layer.ts new file mode 100644 index 0000000000..ec6c5aba80 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/shapes-data-layer.ts @@ -0,0 +1,54 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ShapeDataLayerSettings, TbMapDatasource } from '@shared/models/widget/maps/map.models'; +import L from 'leaflet'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { forkJoin, Observable } from 'rxjs'; +import { FormattedData } from '@shared/models/widget.models'; +import { TbLatestMapDataLayer } from '@home/components/widget/lib/maps/data-layer/latest-map-data-layer'; +import { DataLayerColorProcessor } from './map-data-layer'; + +export abstract class TbShapesDataLayer> extends TbLatestMapDataLayer { + + public fillColorProcessor: DataLayerColorProcessor; + public strokeColorProcessor: DataLayerColorProcessor; + + protected constructor(protected map: TbMap, + inputSettings: S) { + super(map, inputSettings); + } + + public getShapeStyle(data: FormattedData, dsData: FormattedData[]): L.PathOptions { + const fill = this.fillColorProcessor.processColor(data, dsData); + const stroke = this.strokeColorProcessor.processColor(data, dsData); + return { + fill: true, + fillColor: fill, + color: stroke, + weight: this.settings.strokeWeight, + fillOpacity: 1, + opacity: 1 + }; + } + + protected doSetup(): Observable { + this.fillColorProcessor = new DataLayerColorProcessor(this, this.settings.fillColor); + this.strokeColorProcessor = new DataLayerColorProcessor(this, this.settings.strokeColor); + return forkJoin([this.fillColorProcessor.setup(), this.strokeColorProcessor.setup()]); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/trips-data-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/trips-data-layer.ts new file mode 100644 index 0000000000..d6cbe5e4c9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/data-layer/trips-data-layer.ts @@ -0,0 +1,596 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + calculateInterpolationRatio, + calculateLastPoints, + defaultBaseTripsDataLayerSettings, + findRotationAngle, + interpolateLineSegment, + MapDataLayerType, + TbMapDatasource, + TripsDataLayerSettings +} from '@shared/models/widget/maps/map.models'; +import { forkJoin, Observable } from 'rxjs'; +import { FormattedData, WidgetActionType } from '@shared/models/widget.models'; +import { map } from 'rxjs/operators'; +import L from 'leaflet'; +import { deepClone, isDefined, isUndefined } from '@core/utils'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import moment from 'moment/moment'; +import { createTooltip, updateTooltip } from '@home/components/widget/lib/maps/data-layer/data-layer-utils'; +import _ from 'lodash'; +import { + DataLayerColorProcessor, + DataLayerPatternProcessor, + TbDataLayerItem, + TbMapDataLayer +} from '@home/components/widget/lib/maps/data-layer/map-data-layer'; +import { MarkerDataProcessor } from '@home/components/widget/lib/maps/data-layer/markers-data-layer'; + +type TripRouteData = {[time: number]: FormattedData}; + +interface PointItem { + point: L.CircleMarker; + tooltip?: L.Popup; +} + +class TbTripDataItem extends TbDataLayerItem { + + private tripRouteData: TripRouteData; + + private marker: L.Marker; + private markerTooltip: L.Popup; + private labelOffset: L.PointTuple; + + private polyline: L.Polyline; + private polylineDecorator: L.PolylineDecorator; + private pointsContainer: L.FeatureGroup; + private points = new Map(); + + private currentTime: number; + private currentPositionData: FormattedData; + private pointData: FormattedData; + + constructor(private rawRouteData: FormattedData[], + private latestData: FormattedData, + settings: TripsDataLayerSettings, + dataLayer: TbTripsDataLayer) { + super(settings, dataLayer); + this.tripRouteData = this.prepareTripRouteData(); + this.create(); + } + + public update(rawRouteData: FormattedData[]) { + this.rawRouteData = rawRouteData; + this.tripRouteData = this.prepareTripRouteData(); + this.updateCurrentPosition(true); + this.pointData = this.currentPositionData; + if (this.latestData) { + this.pointData = {...this.pointData, ...this.latestData}; + } + this.updatePath(); + this.updatePoints(); + this.updateMarker(); + } + + public updateLatestData(latestData: FormattedData) { + this.latestData = latestData; + this.pointData = this.currentPositionData; + if (this.latestData) { + this.pointData = {...this.pointData, ...this.latestData}; + } + this.updateAppearance(); + } + + public updateAppearance() { + this.updatePathAppearance(); + const dsData = this.dataLayer.getMap().getData(); + if (this.settings.showMarker && this.settings.tooltip?.show) { + updateTooltip(this.dataLayer.getMap(), this.markerTooltip, + this.settings.tooltip, this.dataLayer.dataLayerTooltipProcessor, this.pointData, dsData); + } + this.updatePoints(); + this.updateMarkerIcon(this.pointData, dsData); + } + + public updateCurrentTime() { + this.updateCurrentPosition(); + this.pointData = this.currentPositionData; + if (this.latestData) { + this.pointData = {...this.pointData, ...this.latestData}; + } + this.updatePath(); + this.updateMarker(); + } + + public invalidateCoordinates(): void { + this.tripRouteData = this.prepareTripRouteData(); + this.updatePath(); + this.updatePoints(); + this.updateMarker(); + } + + public remove() { + if (this.marker) { + this.layer.removeLayer(this.marker); + this.marker.off(); + } + this.points.forEach(pointItem => { + pointItem.point.off(); + }); + this.dataLayer.getDataLayerContainer().removeLayer(this.layer); + this.layer.off(); + } + + public calculateAnchors(): number[] { + const entries = Object.entries(this.tripRouteData); + const dsData = this.dataLayer.getMap().getData(); + return entries.filter(data => this.dataLayer.getMap().getLocationSnapFilterFunction().execute(data[1], dsData)) + .map(data => parseInt(data[0], 10)); + } + + private create() { + this.updateCurrentPosition(); + this.layer = L.featureGroup(); + this.pointData = this.currentPositionData; + if (this.latestData) { + this.pointData = {...this.pointData, ...this.latestData}; + } + this.createPath(); + this.updatePoints(); + this.createMarker(); + try { + this.dataLayer.getDataLayerContainer().addLayer(this.layer); + } catch (e) { + console.warn(e); + } + } + + private createMarker() { + if (this.settings.showMarker) { + const dsData = this.dataLayer.getMap().getData(); + const location = this.dataLayer.dataProcessor.extractLocation(this.pointData, dsData); + this.marker = L.marker(location, { + tbMarkerData: this.pointData, + snapIgnore: true + }); + this.marker.addTo(this.layer); + this.updateMarkerIcon(this.pointData, dsData); + if (this.settings.tooltip?.show) { + this.markerTooltip = createTooltip(this.dataLayer.getMap(), + this.marker, this.settings.tooltip, this.pointData, () => true); + updateTooltip(this.dataLayer.getMap(), this.markerTooltip, + this.settings.tooltip, this.dataLayer.dataLayerTooltipProcessor, this.pointData, dsData); + } + const clickAction = this.settings.click; + if (clickAction && clickAction.type !== WidgetActionType.doNothing) { + this.marker.on('click', (event) => { + this.dataLayer.getMap().dataItemClick(event.originalEvent, clickAction, this.pointData); + }); + } + } + } + + private updateMarker() { + if (this.settings.showMarker) { + const dsData = this.dataLayer.getMap().getData(); + this.marker.options.tbMarkerData = this.pointData; + this.updateMarkerLocation(this.pointData, dsData); + if (this.settings.tooltip.show) { + updateTooltip(this.dataLayer.getMap(), this.markerTooltip, + this.settings.tooltip, this.dataLayer.dataLayerTooltipProcessor, this.pointData, dsData); + } + this.updateMarkerIcon(this.pointData, dsData); + } + } + + private createPath() { + if (this.settings.showPath) { + const formattedRouteData = _.values(this.tripRouteData); + const dsData = this.dataLayer.getMap().getData(); + const locations = formattedRouteData.map(data => this.dataLayer.dataProcessor.extractLocation(data, dsData)); + const pathStyle = this.dataLayer.getPathStyle(this.pointData, dsData); + this.polyline = L.polyline(locations, pathStyle); + this.polyline.addTo(this.layer); + if (this.settings.usePathDecorator) { + this.polylineDecorator = new L.PolylineDecorator(this.polyline, this.dataLayer.getPathDecoratorStyle(pathStyle.color)); + this.polylineDecorator.addTo(this.layer); + } + } + } + + private updatePath() { + if (this.settings.showPath) { + const formattedRouteData = _.values(this.tripRouteData); + const dsData = this.dataLayer.getMap().getData(); + const locations = formattedRouteData.map(data => this.dataLayer.dataProcessor.extractLocation(data, dsData)); + this.polyline.setLatLngs(locations); + if (this.settings.usePathDecorator) { + this.polylineDecorator.setPaths(this.polyline); + } + this.updatePathAppearance(); + } + } + + private updatePoints() { + if (this.settings.showPoints) { + if (!this.pointsContainer) { + this.pointsContainer = L.featureGroup(); + this.pointsContainer.addTo(this.layer); + } + const formattedRouteData = _.values(this.tripRouteData); + const dsData = this.dataLayer.getMap().getData(); + const pointsData = formattedRouteData.map(data => ({ + location: this.dataLayer.dataProcessor.extractLocation(data, dsData), + data + })).filter(pData => !!pData.location); + const toDelete = new Set(Array.from(this.points.keys())); + for (const pData of pointsData) { + let pointData = pData.data; + if (this.latestData) { + pointData = {...pointData, ...this.latestData}; + } + const pointLocation = pData.location; + const pointColor = this.dataLayer.pointColorProcessor.processColor(pointData, dsData); + const pointStyle = { + stroke: false, + fillOpacity: 1, + fillColor: pointColor, + radius: this.settings.pointSize + }; + const pointKey = `${pointLocation.lat}_${pointLocation.lng}`; + let pointItem = this.points.get(pointKey); + if (pointItem) { + pointItem.point.setStyle(pointStyle); + if (this.settings.pointTooltip?.show) { + updateTooltip(this.dataLayer.getMap(), pointItem.tooltip, + this.settings.pointTooltip, this.dataLayer.pointTooltipProcessor, pointData, dsData); + } + } else { + pointItem = { + point: L.circleMarker(pointLocation, pointStyle) + }; + pointItem.point.addTo(this.pointsContainer); + if (this.settings.pointTooltip?.show) { + pointItem.tooltip = createTooltip(this.dataLayer.getMap(), + pointItem.point, this.settings.pointTooltip, pointData, () => true); + updateTooltip(this.dataLayer.getMap(), pointItem.tooltip, + this.settings.pointTooltip, this.dataLayer.pointTooltipProcessor, pointData, dsData); + } + this.points.set(pointKey, pointItem); + } + toDelete.delete(pointKey); + } + toDelete.forEach(pointKey => { + const pointItem = this.points.get(pointKey); + if (pointItem) { + this.pointsContainer.removeLayer(pointItem.point); + pointItem.point.off(); + this.points.delete(pointKey); + } + }); + } + } + + private updatePathAppearance() { + if (this.settings.showPath) { + const dsData = this.dataLayer.getMap().getData(); + const pathStyle = this.dataLayer.getPathStyle(this.pointData, dsData); + this.polyline.setStyle(pathStyle); + if (this.settings.usePathDecorator) { + this.polylineDecorator.setPatterns(this.dataLayer.getPathDecoratorStyle(pathStyle.color).patterns) + } + } + } + + private updateMarkerLocation(data: FormattedData, dsData: FormattedData[]) { + const location = this.dataLayer.dataProcessor.extractLocation(data, dsData); + if (!this.marker.getLatLng().equals(location)) { + this.marker.setLatLng(location); + } + } + + private updateMarkerIcon(data: FormattedData, dsData: FormattedData[]) { + if (this.settings.showMarker) { + this.dataLayer.dataProcessor.createMarkerIcon(data, dsData, data.rotationAngle).subscribe( + (iconInfo) => { + const options = deepClone(iconInfo.icon.options); + this.marker.setIcon(iconInfo.icon); + const anchor = options.iconAnchor; + if (anchor && Array.isArray(anchor)) { + this.labelOffset = [iconInfo.size[0] / 2 - anchor[0], 10 - anchor[1]]; + } else { + this.labelOffset = [0, -iconInfo.size[1] * this.dataLayer.markerOffset[1] + 10]; + } + this.updateMarkerLabel(data, dsData); + } + ); + } + } + + private updateMarkerLabel(data: FormattedData, dsData: FormattedData[]) { + if (this.settings.label.show) { + this.marker.unbindTooltip(); + const label = this.dataLayer.dataLayerLabelProcessor.processPattern(data, dsData); + const labelColor = this.dataLayer.getCtx().widgetConfig.color; + const content: L.Content = `
${label}
`; + this.marker.bindTooltip(content, { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.labelOffset }); + } + } + + private prepareTripRouteData(): TripRouteData { + const result: TripRouteData = {}; + const minTime = this.dataLayer.getMap().getMinTime(); + const maxTime = this.dataLayer.getMap().getMaxTime(); + const timeStep = this.dataLayer.getMap().getTimeStep(); + const timeline = this.dataLayer.getMap().hasTimeline(); + const dsData = this.dataLayer.getMap().getData(); + for (const data of this.rawRouteData) { + const currentTime = data.time; + const normalizeTime = timeline ? (minTime + + Math.ceil((currentTime - minTime) / timeStep) * timeStep) : currentTime; + result[normalizeTime] = { + ...data, + minTime: minTime !== Infinity ? moment(minTime).format('YYYY-MM-DD HH:mm:ss') : '', + maxTime: maxTime !== -Infinity ? moment(maxTime).format('YYYY-MM-DD HH:mm:ss') : '', + rotationAngle: this.settings.rotateMarker ? this.settings.offsetAngle : 0 + }; + } + if (timeline) { + const xKey = this.settings.xKey.label; + const yKey = this.settings.yKey.label; + const timeStamp = Object.keys(result); + for (let i = 0; i < timeStamp.length - 1; i++) { + if (isUndefined(result[timeStamp[i + 1]][xKey]) || isUndefined(result[timeStamp[i + 1]][yKey])) { + for (let j = i + 2; j < timeStamp.length - 1; j++) { + if (isDefined(result[timeStamp[j]][xKey]) || isDefined(result[timeStamp[j]][yKey])) { + const ratio = calculateInterpolationRatio(Number(timeStamp[i]), Number(timeStamp[j]), Number(timeStamp[i + 1])); + result[timeStamp[i + 1]] = { + ...interpolateLineSegment(result[timeStamp[i]], result[timeStamp[j]], xKey, yKey, ratio), + ...result[timeStamp[i + 1]], + }; + break; + } + } + } + if (this.settings.rotateMarker) { + const startPoint = this.dataLayer.dataProcessor.extractLocation(result[timeStamp[i]], dsData); + const endPoint = this.dataLayer.dataProcessor.extractLocation(result[timeStamp[i + 1]], dsData); + result[timeStamp[i]].rotationAngle += findRotationAngle(startPoint, endPoint); + } + } + } + return result; + } + + private updateCurrentPosition(force = false) { + if (this.currentTime !== this.dataLayer.getMap().getCurrentTime() || force) { + this.currentTime = this.dataLayer.getMap().getCurrentTime(); + let currentPosition = this.tripRouteData[this.currentTime]; + if (!currentPosition) { + const timePoints = Object.keys(this.tripRouteData).map(item => parseInt(item, 10)); + for (let i = 1; i < timePoints.length; i++) { + if (timePoints[i - 1] < this.currentTime && timePoints[i] > this.currentTime) { + const beforePosition = this.tripRouteData[timePoints[i - 1]]; + const afterPosition = this.tripRouteData[timePoints[i]]; + const ratio = calculateInterpolationRatio(timePoints[i - 1], timePoints[i], this.currentTime); + currentPosition = { + ...beforePosition, + time: this.currentTime, + ...interpolateLineSegment(beforePosition, afterPosition, this.settings.xKey.label, this.settings.yKey.label, ratio) + }; + break; + } + } + } + if (!currentPosition) { + currentPosition = calculateLastPoints(this.tripRouteData, this.currentTime); + } + this.currentPositionData = currentPosition; + } + } + +} + +export class TbTripsDataLayer extends TbMapDataLayer { + + public dataProcessor: MarkerDataProcessor; + + public markerOffset: L.LatLngTuple; + public tooltipOffset: L.LatLngTuple; + + public pathStrokeColorProcessor: DataLayerColorProcessor; + public pointColorProcessor: DataLayerColorProcessor; + public pointTooltipProcessor: DataLayerPatternProcessor; + + private rawTripsData: FormattedData[][]; + private latestTripsData: FormattedData[]; + + constructor(protected map: TbMap, + inputSettings: TripsDataLayerSettings) { + super(map, inputSettings); + } + + public dataLayerType(): MapDataLayerType { + return 'trips'; + } + + public showMarker(): boolean { + return this.settings.showMarker; + } + + public prepareTripsData(tripsData: FormattedData[][], tripsLatestData: FormattedData[]): {minTime: number; maxTime: number} { + let minTime = Infinity; + let maxTime = -Infinity; + this.rawTripsData = + tripsData.filter(d => !!d.length && d[0].$datasource.mapDataIds.includes(this.mapDataId)).map( + item => this.clearIncorrectFirsLastDatapoint(item)).filter(arr => arr.length); + this.latestTripsData = tripsLatestData.filter(d => d.$datasource.mapDataIds.includes(this.mapDataId)); + this.rawTripsData.forEach((dataSource) => { + minTime = Math.min(dataSource[0].time, minTime); + maxTime = Math.max(dataSource[dataSource.length - 1].time, maxTime); + }); + return {minTime, maxTime}; + } + + public updateTrips() { + const toDelete = new Set(Array.from(this.layerItems.keys())); + this.rawTripsData.forEach(rawTripData => { + const entityId = rawTripData[0].entityId; + let tripItem = this.layerItems.get(entityId); + if (tripItem) { + tripItem.update(rawTripData); + } else { + const latestData = this.latestTripsData.find(d => d.entityId === entityId); + tripItem = new TbTripDataItem(rawTripData, latestData, this.settings, this); + this.layerItems.set(entityId, tripItem); + } + toDelete.delete(entityId); + }); + toDelete.forEach((key) => { + this.removeItem(key); + }); + } + + public updateTripsLatestData(tripsLatestData: FormattedData[]) { + this.latestTripsData = tripsLatestData.filter(d => d.$datasource.mapDataIds.includes(this.mapDataId)); + this.layerItems.forEach((item, entityId) => { + const latestData = this.latestTripsData.find(d => d.entityId === entityId); + item.updateLatestData(latestData); + }); + } + + public updateCurrentTime() { + this.layerItems.forEach(item => { + item.updateCurrentTime(); + }); + } + + public updateAppearance() { + this.layerItems.forEach(item => { + item.updateAppearance(); + }); + } + + public calculateAnchors(): number[] { + let anchors: number[] = []; + this.layerItems.forEach(item => { + const tripAnchors = item.calculateAnchors(); + anchors = [...new Set([...anchors, ...tripAnchors])]; + }); + return anchors; + } + + public getPathStyle(data: FormattedData, dsData: FormattedData[]): L.PolylineOptions { + const pathStroke = this.pathStrokeColorProcessor.processColor(data, dsData); + return { + interactive: false, + color: pathStroke, + opacity: 1, + weight: this.settings.pathStrokeWeight, + pmIgnore: true + }; + } + + public getPathDecoratorStyle(pathStroke: string): L.PolylineDecoratorOptions { + const symbolConstructor = L.Symbol[this.settings.pathDecoratorSymbol]; + return { + patterns: [ + { + offset: this.settings.pathDecoratorOffset, + endOffset: this.settings.pathEndDecoratorOffset, + repeat: this.settings.pathDecoratorRepeat, + symbol: symbolConstructor({ + pixelSize: this.settings.pathDecoratorSymbolSize, + polygon: false, + pathOptions: { + color: this.settings.pathDecoratorSymbolColor ? this.settings.pathDecoratorSymbolColor : pathStroke, + stroke: true + } + }) + } + ] + }; + } + + protected setupDatasource(datasource: TbMapDatasource): TbMapDatasource { + datasource.dataKeys = [this.settings.xKey, this.settings.yKey]; + if (this.settings.additionalDataKeys?.length) { + const tsKeys = this.settings.additionalDataKeys.filter(key => key.type === DataKeyType.timeseries); + const latestKeys = this.settings.additionalDataKeys.filter(key => key.type !== DataKeyType.timeseries); + datasource.dataKeys.push(...tsKeys); + if (latestKeys.length) { + datasource.latestDataKeys = latestKeys; + } + } + return datasource; + } + + protected defaultBaseSettings(map: TbMap): Partial { + return defaultBaseTripsDataLayerSettings(map.type()); + } + + protected doSetup(): Observable { + this.markerOffset = [ + isDefined(this.settings.markerOffsetX) ? this.settings.markerOffsetX : 0.5, + isDefined(this.settings.markerOffsetY) ? this.settings.markerOffsetY : 0.5, + ]; + this.tooltipOffset = [ + isDefined(this.settings.tooltip?.offsetX) ? this.settings.tooltip?.offsetX : 0, + isDefined(this.settings.tooltip?.offsetY) ? this.settings.tooltip?.offsetY : -0.5, + ]; + this.dataProcessor = new MarkerDataProcessor(this, this.settings, this.markerOffset, this.tooltipOffset); + const setup$: Observable[] = [this.dataProcessor.setup()]; + if (this.settings.showPath) { + this.pathStrokeColorProcessor = new DataLayerColorProcessor(this, this.settings.pathStrokeColor); + setup$.push(this.pathStrokeColorProcessor.setup()); + } + if (this.settings.showPoints) { + this.pointColorProcessor = new DataLayerColorProcessor(this, this.settings.pointColor); + setup$.push(this.pointColorProcessor.setup()); + if (this.settings.pointTooltip?.show) { + this.pointTooltipProcessor = new DataLayerPatternProcessor(this, this.settings.pointTooltip); + setup$.push(this.pointTooltipProcessor.setup()); + } + } + return forkJoin(setup$).pipe(map(() => null)); + } + + private clearIncorrectFirsLastDatapoint(dataSource: FormattedData[]): FormattedData[] { + const firstHistoricalDataIndexCoordinate = dataSource.findIndex(this.findFirstHistoricalDataIndexCoordinate); + if (firstHistoricalDataIndexCoordinate === -1) { + return []; + } + let lastIndex = dataSource.length - 1; + for (lastIndex; lastIndex > 0; lastIndex--) { + if (this.findFirstHistoricalDataIndexCoordinate(dataSource[lastIndex])) { + lastIndex++; + break; + } + } + if (firstHistoricalDataIndexCoordinate > 0 || lastIndex < dataSource.length) { + return dataSource.slice(firstHistoricalDataIndexCoordinate, lastIndex); + } + return dataSource; + } + + private findFirstHistoricalDataIndexCoordinate = (item: FormattedData): boolean => { + return isDefined(item[this.settings.xKey.label]) && isDefined(item[this.settings.yKey.label]); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/geo-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/geo-map.ts new file mode 100644 index 0000000000..fd03990d26 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/geo-map.ts @@ -0,0 +1,183 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + DEFAULT_ZOOM_LEVEL, + defaultGeoMapSettings, + GeoMapSettings, + latLngPointToBounds, + MapZoomAction, + TbCircleData, + TbPolygonCoordinate, + TbPolygonCoordinates, + TbPolygonRawCoordinate, + TbPolygonRawCoordinates +} from '@shared/models/widget/maps/map.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { DeepPartial } from '@shared/models/common'; +import { forkJoin, Observable, of } from 'rxjs'; +import L from 'leaflet'; +import { map, tap } from 'rxjs/operators'; +import { TbMapLayer } from '@home/components/widget/lib/maps/map-layer'; +import { TbMap } from '@home/components/widget/lib/maps/map'; + +export class TbGeoMap extends TbMap { + + constructor(protected ctx: WidgetContext, + protected inputSettings: DeepPartial, + protected containerElement: HTMLElement) { + super(ctx, inputSettings, containerElement); + } + + protected defaultSettings(): GeoMapSettings { + return defaultGeoMapSettings; + } + + protected createMap(): Observable { + const map = L.map(this.mapElement, { + scrollWheelZoom: this.settings.zoomActions.includes(MapZoomAction.scroll), + doubleClickZoom: this.settings.zoomActions.includes(MapZoomAction.doubleClick), + zoomControl: this.settings.zoomActions.includes(MapZoomAction.controlButtons), + zoom: this.settings.defaultZoomLevel || DEFAULT_ZOOM_LEVEL, + center: this.defaultCenterPosition + }).setView(this.defaultCenterPosition, this.settings.defaultZoomLevel || DEFAULT_ZOOM_LEVEL); + return of(map); + } + + protected onResize(): void {} + + protected fitBounds(bounds: L.LatLngBounds) { + if (bounds.isValid()) { + if (!this.settings.fitMapBounds && this.settings.defaultZoomLevel) { + this.map.setZoom(this.settings.defaultZoomLevel, { animate: false }); + if (this.settings.useDefaultCenterPosition) { + this.map.panTo(this.defaultCenterPosition, { animate: false }); + } + else { + this.map.panTo(bounds.getCenter()); + } + } else { + this.map.once('zoomend', () => { + let minZoom = this.settings.minZoomLevel; + if (this.settings.defaultZoomLevel) { + minZoom = Math.max(minZoom, this.settings.defaultZoomLevel); + } + if (this.map.getZoom() > minZoom) { + this.map.setZoom(minZoom, { animate: false }); + } + }); + if (this.settings.useDefaultCenterPosition) { + bounds = bounds.extend(this.defaultCenterPosition); + } + this.map.fitBounds(bounds, { padding: [50, 50], animate: false }); + this.map.invalidateSize(); + } + } + } + + protected doSetupControls(): Observable { + return this.loadLayers().pipe( + tap((layers: L.TB.LayerData[]) => { + if (layers.length) { + const layer = layers[0]; + layer.layer.addTo(this.map); + this.map.attributionControl.setPrefix(layer.attributionPrefix); + if (layers.length > 1) { + const sidebar = this.getSidebar(); + L.TB.layers({ + layers, + sidebar, + position: this.settings.controlsPosition, + uiClass: 'tb-layers', + paneTitle: this.ctx.translate.instant('widgets.maps.layer.map-layers'), + buttonTitle: this.ctx.translate.instant('widgets.maps.layer.layers'), + }).addTo(this.map); + } + } + }) + ); + + } + + private loadLayers(): Observable { + const layers = this.settings.layers.map(settings => TbMapLayer.fromSettings(this.ctx, settings)); + return forkJoin(layers.map(layer => layer.loadLayer(this.map))).pipe( + map((layersData) => { + return layersData.filter(l => l !== null); + }) + ); + } + + public locationDataToLatLng(position: {x: number; y: number}): L.LatLng { + return L.latLng(position.x, position.y) as L.LatLng; + } + + public latLngToLocationData(position: L.LatLng): {x: number; y: number} { + position = position ? latLngPointToBounds(position, this.southWest, this.northEast, 0) : {lat: null, lng: null} as L.LatLng; + return { + x: position.lat, + y: position.lng + } + } + + public polygonDataToCoordinates(expression: TbPolygonRawCoordinates): TbPolygonRawCoordinates { + return (expression).map((el: TbPolygonRawCoordinate) => { + if (!Array.isArray(el[0]) && !Array.isArray(el[1]) && el.length === 2) { + return el; + } else if (Array.isArray(el) && el.length) { + return this.polygonDataToCoordinates(el as TbPolygonRawCoordinates) as TbPolygonRawCoordinate; + } else { + return null; + } + }).filter(el => !!el); + } + + public coordinatesToPolygonData(coordinates: TbPolygonCoordinates): TbPolygonRawCoordinates { + if (coordinates.length) { + return coordinates.map((point: TbPolygonCoordinate) => { + if (Array.isArray(point)) { + return this.coordinatesToPolygonData(point) as TbPolygonRawCoordinate; + } else { + const convertPoint = latLngPointToBounds(point, this.southWest, this.northEast); + return [convertPoint.lat, convertPoint.lng]; + } + }); + } + return []; + } + + public circleDataToCoordinates(circle: TbCircleData): TbCircleData { + const centerPoint = latLngPointToBounds(new L.LatLng(circle.latitude, circle.longitude), this.southWest, this.northEast); + circle.latitude = centerPoint.lat; + circle.longitude = centerPoint.lng; + return circle; + } + + public coordinatesToCircleData(center: L.LatLng, radius: number): TbCircleData { + let circleData: TbCircleData = null; + if (center) { + const position = latLngPointToBounds(center, this.southWest, this.northEast); + circleData = { + latitude: position.lat, + longitude: position.lng, + radius + }; + } + return circleData; + } + + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/image-map.ts new file mode 100644 index 0000000000..219f5d63ff --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/image-map.ts @@ -0,0 +1,342 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + calculateNewPointCoordinate, + defaultImageMapSettings, + defaultImageMapSourceSettings, + ImageMapSettings, + imageMapSourceSettingsToDatasource, + ImageSourceType, + loadImageWithAspect, + MapZoomAction, + TbCircleData, TbPolygonCoordinate, TbPolygonCoordinates, TbPolygonRawCoordinate, TbPolygonRawCoordinates +} from '@shared/models/widget/maps/map.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { DeepPartial } from '@shared/models/common'; +import { Observable, of, ReplaySubject, switchMap } from 'rxjs'; +import L from 'leaflet'; +import { TbMap } from '@home/components/widget/lib/maps/map'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { catchError } from 'rxjs/operators'; +import { DataSet, widgetType } from '@shared/models/widget.models'; +import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { isNotEmptyStr } from '@core/utils'; +import { EntityDataPageLink } from '@shared/models/query/query.models'; + +interface ImageLayerData { + imageUrl: string; + aspect: number; + update?: boolean; +} + +export class TbImageMap extends TbMap { + + private maxZoom: number; + private width: number; + private height: number; + private imageLayerData: ImageLayerData; + private initMapSubject: ReplaySubject; + + private imageOverlay: L.ImageOverlay; + + constructor(protected ctx: WidgetContext, + protected inputSettings: DeepPartial, + protected containerElement: HTMLElement) { + super(ctx, inputSettings, containerElement); + } + + protected defaultSettings(): ImageMapSettings { + return defaultImageMapSettings; + } + + protected createMap(): Observable { + this.maxZoom = 4; + this.width = 0; + this.height = 0; + this.imageLayerData = { + imageUrl: null, + aspect: 0 + }; + this.initMapSubject = new ReplaySubject(); + this.loadImageLayerData().subscribe((data) => { + this.imageLayerData = data; + if (this.imageLayerData.update) { + this.onResize(true); + } else { + this.onResize(); + this.initMapSubject.next(this.map); + this.initMapSubject.complete(); + } + }); + return this.initMapSubject.asObservable(); + } + + protected onResize(updateImage?: boolean): void { + let width = this.mapElement.clientWidth; + if (width > 0 && this.imageLayerData.aspect) { + let height = Math.round(width / this.imageLayerData.aspect); + const imageMapHeight = this.mapElement.clientHeight; + if (imageMapHeight > 0 && height > imageMapHeight) { + height = imageMapHeight; + width = Math.round(height * this.imageLayerData.aspect); + } + width *= this.maxZoom; + const prevWidth = this.width; + const prevHeight = this.height; + if (this.width !== width || updateImage) { + this.width = width; + this.height = Math.round(width / this.imageLayerData.aspect); + if (!this.map) { + this.doCreateMap(updateImage); + } else { + const lastCenterPos = this.latLngToPoint(this.map.getCenter()); + lastCenterPos.x /= prevWidth; + lastCenterPos.y /= prevHeight; + this.updateMaxBounds(updateImage, lastCenterPos); + (this.map as any)._enforcingBounds = true; + this.map.invalidateSize(false); + (this.map as any)._enforcingBounds = false; + this.invalidateDataLayersCoordinates(); + } + } + } + } + + protected fitBounds(_bounds: L.LatLngBounds) {} + + public locationDataToLatLng(position: {x: number; y: number}): L.LatLng { + return this.pointToLatLng( + position.x * this.width, + position.y * this.height); + } + + public latLngToLocationData(position: L.LatLng): {x: number; y: number} { + if (!position) { + return { + x: null, + y: null + }; + } + const point = this.latLngToPoint(position); + const posX = calculateNewPointCoordinate(point.x, this.width); + const posY = calculateNewPointCoordinate(point.y, this.height); + return { + x: posX, + y: posY + }; + } + + public polygonDataToCoordinates(expression: TbPolygonRawCoordinates): TbPolygonRawCoordinates { + return expression.map((el: TbPolygonRawCoordinate) => { + if (!Array.isArray(el[0]) && !Array.isArray(el[1]) && el.length === 2) { + const latLng = this.pointToLatLng( + el[0] * this.width, + el[1] * this.height + ); + return [latLng.lat, latLng.lng] as TbPolygonRawCoordinate; + } else if (Array.isArray(el) && el.length) { + return this.polygonDataToCoordinates(el as TbPolygonRawCoordinates) as TbPolygonRawCoordinate; + } else { + return null; + } + }).filter(el => !!el); + } + + public coordinatesToPolygonData(coordinates: TbPolygonCoordinates): TbPolygonRawCoordinates { + if (coordinates.length) { + return coordinates.map((point: TbPolygonCoordinate) => { + if (Array.isArray(point)) { + return this.coordinatesToPolygonData(point) as TbPolygonRawCoordinate; + } else { + const pos = this.latLngToPoint(point); + return [calculateNewPointCoordinate(pos.x, this.width), calculateNewPointCoordinate(pos.y, this.height)]; + } + }); + } else { + return []; + } + } + + public circleDataToCoordinates(circle: TbCircleData): TbCircleData { + const centerPoint = this.pointToLatLng(circle.latitude * this.width, circle.longitude * this.height); + circle.latitude = centerPoint.lat; + circle.longitude = centerPoint.lng; + circle.radius = circle.radius * this.width; + return circle; + } + + public coordinatesToCircleData(center: L.LatLng, radius: number): TbCircleData { + let circleData: TbCircleData = null; + if (center) { + const point = this.latLngToPoint(center); + const posX = calculateNewPointCoordinate(point.x, this.width); + const posY = calculateNewPointCoordinate(point.y, this.height); + const convertedRadius = calculateNewPointCoordinate(radius, this.width); + circleData = { + latitude: posX, + longitude: posY, + radius: convertedRadius + }; + } + return circleData; + } + + public getAspect(): number { + return this.imageLayerData.aspect; + } + + private pointToLatLng(x: number, y: number): L.LatLng { + return L.CRS.Simple.pointToLatLng({ x, y } as L.PointExpression, this.maxZoom - 1); + } + + private latLngToPoint(latLng: L.LatLngLiteral): L.Point { + return L.CRS.Simple.latLngToPoint(latLng, this.maxZoom - 1); + } + + private doCreateMap(updateImage?: boolean) { + if (!this.map && this.imageLayerData.aspect > 0) { + const center = this.pointToLatLng(this.width / 2, this.height / 2); + this.map = L.map(this.mapElement, { + scrollWheelZoom: this.settings.zoomActions.includes(MapZoomAction.scroll), + doubleClickZoom: this.settings.zoomActions.includes(MapZoomAction.doubleClick), + zoomControl: this.settings.zoomActions.includes(MapZoomAction.controlButtons), + minZoom: 1, + maxZoom: this.maxZoom, + zoom: 1, + center, + crs: L.CRS.Simple, + attributionControl: false + }); + this.updateMaxBounds(updateImage); + } + } + + private updateMaxBounds(updateImage?: boolean, lastCenterPos?: L.Point) { + const w = this.width; + const h = this.height; + this.southWest = this.pointToLatLng(0, h); + this.northEast = this.pointToLatLng(w, 0); + const bounds = new L.LatLngBounds(this.southWest, this.northEast); + + if (updateImage && this.imageOverlay) { + this.imageOverlay.remove(); + this.imageOverlay = null; + } + + if (this.imageOverlay) { + this.imageOverlay.setBounds(bounds); + } else { + this.imageOverlay = L.imageOverlay(this.imageLayerData.imageUrl, bounds).addTo(this.map); + } + const padding = 200 * this.maxZoom; + const southWest = this.pointToLatLng(-padding, h + padding); + const northEast = this.pointToLatLng(w + padding, -padding); + const maxBounds = new L.LatLngBounds(southWest, northEast); + (this.map as any)._enforcingBounds = true; + this.map.setMaxBounds(maxBounds); + if (lastCenterPos) { + lastCenterPos.x *= w; + lastCenterPos.y *= h; + const center = this.pointToLatLng(lastCenterPos.x, lastCenterPos.y); + this.map.panTo(center, { animate: false }); + } + (this.map as any)._enforcingBounds = false; + } + + private loadImageLayerData(): Observable { + const imageSource = this.settings.imageSource; + if (imageSource.sourceType === ImageSourceType.image) { + return this.imageFromUrl(imageSource.url); + } else { + const datasource = imageMapSourceSettingsToDatasource(imageSource); + const result = new ReplaySubject<[DataSet, boolean]>(); + let isUpdate = false; + const imageUrlSubscriptionOptions: WidgetSubscriptionOptions = { + datasources: [datasource], + hasDataPageLink: true, + singleEntity: true, + useDashboardTimewindow: false, + type: widgetType.latest, + callbacks: { + onDataUpdated: (subscription) => { + if (isNotEmptyStr(subscription.data[0]?.data[0]?.[1])) { + result.next([subscription.data[0].data, isUpdate]); + } else { + result.next([[[0, imageSource.url]], isUpdate]); + } + isUpdate = true; + } + } + }; + this.ctx.subscriptionApi.createSubscription(imageUrlSubscriptionOptions, true).subscribe((subscription) => { + const pageLink: EntityDataPageLink = { + page: 0, + pageSize: 1, + textSearch: null, + dynamic: true + }; + subscription.subscribeAllForPaginatedData(pageLink, null); + }); + return this.imageFromEntityData(result); + } + } + + private imageFromUrl(url: string, update = false): Observable { + return loadImageWithAspect(this.ctx.$injector.get(ImagePipe), url).pipe( + switchMap( aspectImage => { + if (aspectImage) { + return of({ + imageUrl: aspectImage.url, + aspect: aspectImage.aspect, + update + }); + } else { + return this.imageFromUrl(defaultImageMapSourceSettings.url, update); + } + } + ), + catchError(() => this.imageFromUrl(defaultImageMapSourceSettings.url, update)) + ); + } + + private imageFromEntityData(entityData: Observable<[DataSet, boolean]>): Observable { + return entityData.pipe( + switchMap(res => { + const update = res[1]; + const url = res[0][0][1]; + const layerData: ImageLayerData = { + imageUrl: null, + aspect: null, + update + }; + return loadImageWithAspect(this.ctx.$injector.get(ImagePipe), url).pipe( + switchMap((aspectImage) => { + if (aspectImage) { + layerData.aspect = aspectImage.aspect; + layerData.imageUrl = aspectImage.url; + return of(layerData); + } else { + return this.imageFromUrl(defaultImageMapSourceSettings.url, update); + } + }), + catchError(() => this.imageFromUrl(defaultImageMapSourceSettings.url, update)) + ); + }) + ); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet/leaflet-tb.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet/leaflet-tb.ts new file mode 100644 index 0000000000..df02c46482 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet/leaflet-tb.ts @@ -0,0 +1,679 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import L, { TB } from 'leaflet'; +import { guid, isNotEmptyStr } from '@core/utils'; +import 'leaflet-providers'; +import '@maplibre/maplibre-gl-leaflet'; +import '@geoman-io/leaflet-geoman-free'; +import 'leaflet.markercluster'; +import { MatIconRegistry } from '@angular/material/icon'; +import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; +import { catchError, take } from 'rxjs/operators'; +import { of } from 'rxjs'; + +L.MarkerCluster = L.MarkerCluster.mergeOptions({ pmIgnore: true }); + +class SidebarControl extends L.Control { + + private readonly sidebar: JQuery; + + private current = $(); + private currentButton = $(); + + private map: L.Map; + + private buttonContainer: JQuery; + + constructor(options: TB.SidebarControlOptions) { + super(options); + this.sidebar = $('
'); + this.options.container.append(this.sidebar); + const position = options?.position || 'topleft'; + if (['topleft', 'bottomleft'].includes(position)) { + this.options.container.addClass('tb-sidebar-left'); + } else { + this.options.container.addClass('tb-sidebar-right'); + } + } + + addPane(pane: JQuery, button: JQuery): this { + pane.hide().appendTo(this.sidebar); + button.appendTo(this.buttonContainer); + return this; + } + + togglePane(pane: JQuery, button: JQuery) { + const paneWidth = this.options?.paneWidth || 220; + const position = this.options?.position || 'topleft'; + + this.current.hide().trigger('hide'); + this.currentButton.removeClass('active'); + + + if (this.current === pane) { + if (['topleft', 'bottomleft'].includes(position)) { + this.map.panBy([-paneWidth, 0], { animate: false }); + } + this.sidebar.hide(); + this.current = this.currentButton = $(); + } else { + this.sidebar.show(); + if (['topleft', 'bottomleft'].includes(position) && !this.current.length) { + this.map.panBy([paneWidth, 0], { animate: false }); + } + this.current = pane; + this.currentButton = button || $(); + } + this.current.show().trigger('show'); + this.currentButton.addClass('active'); + this.map.invalidateSize({ pan: false, animate: false }); + } + + onAdd(map: L.Map): HTMLElement { + this.buttonContainer = $("
") + .attr('class', 'leaflet-bar'); + return this.buttonContainer[0]; + } + + addTo(map: L.Map): this { + this.map = map; + return super.addTo(map); + } +} + +class SidebarPaneControl extends L.Control { + + private button: JQuery; + private $ui: JQuery; + + constructor(options: O) { + super(options); + } + + addTo(map: L.Map): this { + + this.button = $("") + .attr('class', 'tb-control-button') + .attr('href', '#') + .attr('role', 'button') + .html('
') + .on('click', (e) => { + this.toggle(e); + }); + if (this.options.buttonTitle) { + this.button.attr('title', this.options.buttonTitle); + } + + this.$ui = $('
') + .attr('class', this.options.uiClass); + + $('
') + .appendTo(this.$ui) + .append($('
') + .text(this.options.paneTitle)) + .append($('
') + .append($('') + .attr('aria-label', 'Close') + .bind('click', (e) => { + this.toggle(e); + }))); + + this.options.sidebar.addPane(this.$ui, this.button); + + this.onAddPane(map, this.button, this.$ui, (e) => { + this.toggle(e); + }); + + return this; + } + + public onAddPane(map: L.Map, button: JQuery, $ui: JQuery, toggle: (e: JQuery.MouseEventBase) => void) {} + + private toggle(e: JQuery.MouseEventBase) { + e.stopPropagation(); + e.preventDefault(); + if (!this.button.hasClass("disabled")) { + this.options.sidebar.togglePane(this.$ui, this.button); + } + } +} + +class LayersControl extends SidebarPaneControl { + constructor(options: TB.LayersControlOptions) { + super(options); + } + + public onAddPane(map: L.Map, button: JQuery, $ui: JQuery, toggle: (e: JQuery.MouseEventBase) => void) { + const paneId = guid(); + const layers = this.options.layers; + const baseSection = $("
") + .attr('class', 'tb-layers-container') + .appendTo($ui); + + layers.forEach((layerData, i) => { + const id = `map-ui-layer-${paneId}-${i}`; + const buttonContainer = $('
') + .appendTo(baseSection); + const mapContainer = $('
') + .appendTo(buttonContainer); + const input = $('') + .prop('id', id) + .prop('checked', map.hasLayer(layerData.layer)) + .appendTo(buttonContainer); + + const item = $('
") + .attr('class', 'tb-layers-container') + .appendTo($ui); + + groups.forEach((groupData, i) => { + const id = `map-group-layer-${paneId}-${i}`; + const checkBoxContainer = $('
') + .appendTo(baseSection); + const input = $('') + .prop('id', id) + .prop('checked', groupData.enabled) + .appendTo(checkBoxContainer); + + $('
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts index 8984e3889f..cccc826ff4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.ts @@ -47,7 +47,8 @@ export class AlarmsTableKeySettingsComponent extends WidgetSettingsComponent { useCellContentFunction: false, cellContentFunction: '', defaultColumnVisibility: 'visible', - columnSelectionToDisplay: 'enabled' + columnSelectionToDisplay: 'enabled', + disableSorting: false }; } @@ -61,6 +62,7 @@ export class AlarmsTableKeySettingsComponent extends WidgetSettingsComponent { cellContentFunction: [settings.cellContentFunction, [Validators.required]], defaultColumnVisibility: [settings.defaultColumnVisibility, []], columnSelectionToDisplay: [settings.columnSelectionToDisplay, []], + disableSorting: [settings.disableSorting, []] }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html index 718edb23d2..ba613ddbfe 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html @@ -94,10 +94,52 @@ {{ 'widgets.table.display-pagination' | translate }} +
+
{{ 'widgets.table.page-step-settings' | translate }}
+
+
widgets.table.page-step-increment
+ + + + warning + + + +
widgets.table.page-step-count
+ + + + warning + + +
+
widgets.table.default-page-size
- - + + + + {{ size }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts index 772bf3937d..72194dcb22 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts @@ -19,6 +19,7 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; +import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widget.models'; @Component({ selector: 'tb-alarms-table-widget-settings', @@ -28,6 +29,7 @@ import { AppState } from '@core/core.state'; export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent { alarmsTableWidgetSettingsForm: UntypedFormGroup; + pageStepSizeValues = []; constructor(protected store: Store, private fb: UntypedFormBuilder) { @@ -56,6 +58,8 @@ export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent displayActivity: true, displayPagination: true, defaultPageSize: 10, + pageStepIncrement: null, + pageStepCount: 3, defaultSortOrder: '-createdTime', useRowStyleFunction: false, rowStyleFunction: '' @@ -80,31 +84,45 @@ export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent displayActivity: [settings.displayActivity, []], displayPagination: [settings.displayPagination, []], defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], + pageStepCount: [settings.pageStepCount ?? 3, [Validators.min(1), Validators.max(100), + Validators.required, Validators.pattern(/^\d*$/)]], + pageStepIncrement: [settings.pageStepIncrement ?? settings.defaultPageSize, + [Validators.min(1), Validators.required, Validators.pattern(/^\d*$/)]], defaultSortOrder: [settings.defaultSortOrder, []], useRowStyleFunction: [settings.useRowStyleFunction, []], rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] }); + this.pageStepSizeValues = buildPageStepSizeValues(this.alarmsTableWidgetSettingsForm.get('pageStepCount').value, + this.alarmsTableWidgetSettingsForm.get('pageStepIncrement').value); } protected validatorTriggers(): string[] { - return ['useRowStyleFunction', 'displayPagination']; + return ['useRowStyleFunction', 'displayPagination', 'pageStepCount', 'pageStepIncrement']; } - protected updateValidators(emitEvent: boolean) { + protected updateValidators(emitEvent: boolean, trigger: string) { + if (trigger === 'pageStepCount' || trigger === 'pageStepIncrement') { + this.alarmsTableWidgetSettingsForm.get('defaultPageSize').reset(); + this.pageStepSizeValues = buildPageStepSizeValues(this.alarmsTableWidgetSettingsForm.get('pageStepCount').value, + this.alarmsTableWidgetSettingsForm.get('pageStepIncrement').value); + return; + } const useRowStyleFunction: boolean = this.alarmsTableWidgetSettingsForm.get('useRowStyleFunction').value; const displayPagination: boolean = this.alarmsTableWidgetSettingsForm.get('displayPagination').value; if (useRowStyleFunction) { - this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').enable(); + this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').enable({emitEvent}); } else { - this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').disable(); + this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').disable({emitEvent}); } if (displayPagination) { - this.alarmsTableWidgetSettingsForm.get('defaultPageSize').enable(); + this.alarmsTableWidgetSettingsForm.get('defaultPageSize').enable({emitEvent}); + this.alarmsTableWidgetSettingsForm.get('pageStepCount').enable({emitEvent: false}); + this.alarmsTableWidgetSettingsForm.get('pageStepIncrement').enable({emitEvent: false}); } else { - this.alarmsTableWidgetSettingsForm.get('defaultPageSize').disable(); + this.alarmsTableWidgetSettingsForm.get('defaultPageSize').disable({emitEvent}); + this.alarmsTableWidgetSettingsForm.get('pageStepCount').disable({emitEvent: false}); + this.alarmsTableWidgetSettingsForm.get('pageStepIncrement').disable({emitEvent: false}); } - this.alarmsTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent}); - this.alarmsTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html index 1dcd4f1f3f..1905888bb6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html @@ -47,6 +47,11 @@
+
+ + {{ 'widgets.table.disable-sorting' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts index 1bc28baf53..ff314d9d2d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.ts @@ -45,7 +45,8 @@ export class TimeseriesTableKeySettingsComponent extends WidgetSettingsComponent useCellContentFunction: false, cellContentFunction: '', defaultColumnVisibility: 'visible', - columnSelectionToDisplay: 'enabled' + columnSelectionToDisplay: 'enabled', + disableSorting: false }; } @@ -57,6 +58,7 @@ export class TimeseriesTableKeySettingsComponent extends WidgetSettingsComponent cellContentFunction: [settings.cellContentFunction, [Validators.required]], defaultColumnVisibility: [settings.defaultColumnVisibility, []], columnSelectionToDisplay: [settings.columnSelectionToDisplay, []], + disableSorting: [settings.disableSorting, []] }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html index 54b2a370e6..684258bea7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html @@ -56,6 +56,11 @@
+
+ + {{ 'widgets.table.disable-sorting' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts index a0ee7e7c70..2e2f414f2c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.ts @@ -46,7 +46,8 @@ export class TimeseriesTableLatestKeySettingsComponent extends WidgetSettingsCom useCellContentFunction: false, cellContentFunction: '', defaultColumnVisibility: 'visible', - columnSelectionToDisplay: 'enabled' + columnSelectionToDisplay: 'enabled', + disableSorting: false }; } @@ -60,6 +61,7 @@ export class TimeseriesTableLatestKeySettingsComponent extends WidgetSettingsCom cellContentFunction: [settings.cellContentFunction, [Validators.required]], defaultColumnVisibility: [settings.defaultColumnVisibility, []], columnSelectionToDisplay: [settings.columnSelectionToDisplay, []], + disableSorting: [settings.disableSorting, []] }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html index 44f33ce42e..61a688f36f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html @@ -61,10 +61,52 @@ {{ 'widgets.table.display-pagination' | translate }} +
+
{{ 'widgets.table.page-step-settings' | translate }}
+
+
widgets.table.page-step-increment
+ + + + warning + + + +
widgets.table.page-step-count
+ + + + warning + + +
+
widgets.table.default-page-size
- - + + + + {{ size }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts index 1e4a5d9660..7826f48dec 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts @@ -19,6 +19,7 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; +import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widget.models'; @Component({ selector: 'tb-timeseries-table-widget-settings', @@ -28,6 +29,7 @@ import { AppState } from '@core/core.state'; export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsComponent { timeseriesTableWidgetSettingsForm: UntypedFormGroup; + pageStepSizeValues = []; constructor(protected store: Store, private fb: UntypedFormBuilder) { @@ -51,6 +53,8 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon displayPagination: true, useEntityLabel: false, defaultPageSize: 10, + pageStepIncrement: null, + pageStepCount: 3, hideEmptyLines: false, disableStickyHeader: false, useRowStyleFunction: false, @@ -77,32 +81,46 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon displayPagination: [settings.displayPagination, []], useEntityLabel: [settings.useEntityLabel, []], defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], + pageStepCount: [settings.pageStepCount ?? 3, [Validators.min(1), Validators.max(100), + Validators.required, Validators.pattern(/^\d*$/)]], + pageStepIncrement: [settings.pageStepIncrement ?? settings.defaultPageSize, + [Validators.min(1), Validators.required, Validators.pattern(/^\d*$/)]], hideEmptyLines: [settings.hideEmptyLines, []], disableStickyHeader: [settings.disableStickyHeader, []], useRowStyleFunction: [settings.useRowStyleFunction, []], rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] }); + this.pageStepSizeValues = buildPageStepSizeValues(this.timeseriesTableWidgetSettingsForm.get('pageStepCount').value, + this.timeseriesTableWidgetSettingsForm.get('pageStepIncrement').value); } protected validatorTriggers(): string[] { - return ['useRowStyleFunction', 'displayPagination']; + return ['useRowStyleFunction', 'displayPagination', 'pageStepCount', 'pageStepIncrement']; } - protected updateValidators(emitEvent: boolean) { + protected updateValidators(emitEvent: boolean, trigger: string) { + if (trigger === 'pageStepCount' || trigger === 'pageStepIncrement') { + this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').reset(); + this.pageStepSizeValues = buildPageStepSizeValues(this.timeseriesTableWidgetSettingsForm.get('pageStepCount').value, + this.timeseriesTableWidgetSettingsForm.get('pageStepIncrement').value); + return; + } const useRowStyleFunction: boolean = this.timeseriesTableWidgetSettingsForm.get('useRowStyleFunction').value; const displayPagination: boolean = this.timeseriesTableWidgetSettingsForm.get('displayPagination').value; if (useRowStyleFunction) { - this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').enable(); + this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').enable({emitEvent}); } else { - this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').disable(); + this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').disable({emitEvent}); } if (displayPagination) { - this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').enable(); + this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').enable({emitEvent}); + this.timeseriesTableWidgetSettingsForm.get('pageStepCount').enable({emitEvent: false}); + this.timeseriesTableWidgetSettingsForm.get('pageStepIncrement').enable({emitEvent: false}); } else { - this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').disable(); + this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').disable({emitEvent}); + this.timeseriesTableWidgetSettingsForm.get('pageStepCount').disable({emitEvent: false}); + this.timeseriesTableWidgetSettingsForm.get('pageStepIncrement').disable({emitEvent: false}); } - this.timeseriesTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent}); - this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts index 1241a76e0d..daaa8c5efe 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts @@ -32,7 +32,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { merge } from 'rxjs'; import { formatValue, isDefinedAndNotNull } from '@core/utils'; -import { DataKeyConfigComponent } from '@home/components/widget/config/data-key-config.component'; +import { DataKeyConfigComponent } from '@home/components/widget/lib/settings/common/key/data-key-config.component'; import { chartLabelPositions, chartLabelPositionTranslations, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html index 67707c58fa..6f1123aa40 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html @@ -16,6 +16,7 @@ -->
@@ -36,7 +39,7 @@ type="button" (click)="applyWidgetAction()" [disabled]="widgetActionFormGroup.invalid || !widgetActionFormGroup.dirty"> - {{ 'action.apply' | translate }} + {{ applyTitle }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts index 7b6cb6ee3c..a9befcf16d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts @@ -15,25 +15,12 @@ /// import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; import { TbPopoverComponent } from '@shared/components/popover.component'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { merge } from 'rxjs'; -import { - DataToValueType, - GetValueAction, - getValueActions, - getValueActionTranslations, - GetValueSettings -} from '@shared/models/action-widget-settings.models'; -import { ValueType } from '@shared/models/constants'; -import { TargetDevice, WidgetAction, widgetType } from '@shared/models/widget.models'; -import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; -import { IAliasController } from '@core/api/widget-api.models'; -import { WidgetService } from '@core/http/widget.service'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { WidgetAction, WidgetActionType, widgetType } from '@shared/models/widget.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-widget-action-settings-panel', @@ -42,7 +29,7 @@ import { WidgetActionCallbacks } from '@home/components/widget/action/manage-wid styleUrls: ['./action-settings-panel.component.scss'], encapsulation: ViewEncapsulation.None }) -export class WidgetActionSettingsPanelComponent extends PageComponent implements OnInit { +export class WidgetActionSettingsPanelComponent implements OnInit { @Input() widgetAction: WidgetAction; @@ -57,7 +44,17 @@ export class WidgetActionSettingsPanelComponent extends PageComponent implements callbacks: WidgetActionCallbacks; @Input() - popover: TbPopoverComponent; + @coerceBoolean() + withName = false; + + @Input() + actionNames: string[]; + + @Input() + applyTitle = this.translate.instant('action.apply'); + + @Input() + additionalWidgetActionTypes: WidgetActionType[]; @Output() widgetActionApplied = new EventEmitter(); @@ -65,8 +62,8 @@ export class WidgetActionSettingsPanelComponent extends PageComponent implements widgetActionFormGroup: UntypedFormGroup; constructor(private fb: UntypedFormBuilder, - protected store: Store) { - super(store); + private translate: TranslateService, + private popover: TbPopoverComponent) { } ngOnInit(): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts index f4b767df28..7731df6407 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts @@ -29,7 +29,12 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; import { TranslateService } from '@ngx-translate/core'; -import { WidgetAction, widgetActionTypeTranslationMap, widgetType } from '@shared/models/widget.models'; +import { + WidgetAction, + WidgetActionType, + widgetActionTypeTranslationMap, + widgetType +} from '@shared/models/widget.models'; import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; import { WidgetActionSettingsPanelComponent @@ -65,6 +70,9 @@ export class WidgetActionSettingsComponent implements OnInit, ControlValueAccess @Input() disabled = false; + @Input() + additionalWidgetActionTypes: WidgetActionType[]; + modelValue: WidgetAction; displayValue: string; @@ -110,7 +118,8 @@ export class WidgetActionSettingsComponent implements OnInit, ControlValueAccess widgetAction: this.modelValue, panelTitle: this.panelTitle, widgetType: this.widgetType, - callbacks: this.callbacks + callbacks: this.callbacks, + additionalWidgetActionTypes: this.additionalWidgetActionTypes }; const widgetActionSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, WidgetActionSettingsPanelComponent, @@ -118,7 +127,6 @@ export class WidgetActionSettingsComponent implements OnInit, ControlValueAccess ctx, {}, {}, {}, true); - widgetActionSettingsPanelPopover.tbComponentRef.instance.popover = widgetActionSettingsPanelPopover; widgetActionSettingsPanelPopover.tbComponentRef.instance.widgetActionApplied.subscribe((widgetAction) => { widgetActionSettingsPanelPopover.hide(); this.modelValue = widgetAction; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html index 948fd947a6..ca2405b8b4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html @@ -16,6 +16,23 @@ -->
+
+
{{'widget-config.action-name' | translate}}*
+ + + + warning + + +
widget-config.action
@@ -241,7 +258,21 @@ helpId="widget/action/custom_action_fn" > - + +
+
{{ 'widget-action.map-item-type' | translate }}
+ + + @for(type of mapItemTypes; track type) { + {{ mapItemTypeTranslationMap.get(type) | translate }} + } + + +
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts index f6dd52002b..932c0e52c3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts @@ -16,16 +16,20 @@ import { ControlValueAccessor, + FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validator, + ValidatorFn, Validators } from '@angular/forms'; import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; import { + MapItemType, + mapItemTypeTranslationMap, WidgetAction, WidgetActionType, widgetActionTypes, @@ -46,6 +50,7 @@ import { CustomActionEditorCompleter, toCustomAction } from '@home/components/widget/lib/settings/common/action/custom-action.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; const stateDisplayTypes = ['normal', 'separateDialog', 'popover'] as const; type stateDisplayTypeTuple = typeof stateDisplayTypes; @@ -89,10 +94,32 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali @Input() callbacks: WidgetActionCallbacks; + @Input() + @coerceBoolean() + withName = false; + + @Input() + actionNames: string[]; + + @Input() + set additionalWidgetActionTypes(value: WidgetActionType[]) { + if (this.widgetActionFormGroup && !widgetActionTypes.includes(this.widgetActionFormGroup.get('type').value)) { + this.widgetActionFormGroup.get('type').setValue(WidgetActionType.doNothing); + } + if (value?.length) { + this.widgetActionTypes = widgetActionTypes.concat(value); + } else { + this.widgetActionTypes = widgetActionTypes; + } + } + widgetActionTypes = widgetActionTypes; widgetActionTypeTranslations = widgetActionTypeTranslationMap; widgetActionType = WidgetActionType; + mapItemTypes = Object.values(MapItemType) as MapItemType[]; + mapItemTypeTranslationMap = mapItemTypeTranslationMap; + allStateDisplayTypes = stateDisplayTypes; allPopoverPlacements = PopoverPlacements; @@ -147,6 +174,10 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali ngOnInit() { this.widgetActionFormGroup = this.fb.group({}); + if (this.withName) { + this.widgetActionFormGroup.addControl('name', + this.fb.control(null, [this.validateActionName(), Validators.required])); + } this.widgetActionFormGroup.addControl('type', this.fb.control(null, [Validators.required])); this.widgetActionFormGroup.get('type').valueChanges.pipe( @@ -159,9 +190,17 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali ).subscribe(() => { this.widgetActionUpdated(); }); + if (this.additionalWidgetActionTypes) { + this.widgetActionTypes = this.widgetActionTypes.concat(this.additionalWidgetActionTypes); + } } writeValue(widgetAction?: WidgetAction): void { + if (this.withName) { + this.widgetActionFormGroup.patchValue({ + name: widgetAction?.name + }, {emitEvent: false}); + } this.widgetActionFormGroup.patchValue({ type: widgetAction?.type }, {emitEvent: false}); @@ -290,6 +329,16 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali this.fb.control(action ? action.url : null, [Validators.required]) ); break; + case WidgetActionType.placeMapItem: + this.actionTypeFormGroup.addControl( + 'mapItemType', + this.fb.control(action?.mapItemType ?? MapItemType.marker, [Validators.required]) + ); + this.actionTypeFormGroup.addControl( + 'customAction', + this.fb.control(toCustomAction(action), [Validators.required]) + ); + break; } } this.actionTypeFormGroupSubscriptions.push( @@ -457,11 +506,35 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali return res; } + private validateActionName(): ValidatorFn { + return (c: FormControl) => { + const newName = c.value; + const valid = this.checkActionName(newName); + return !valid ? { + actionNameNotUnique: true + } : null; + }; + } + + private checkActionName(name: string): boolean { + let actionNameIsUnique = true; + if (this.actionNames?.length) { + actionNameIsUnique = !this.actionNames.includes(name); + } + return actionNameIsUnique; + } + private widgetActionUpdated() { const type: WidgetActionType = this.widgetActionFormGroup.get('type').value; let result: WidgetAction; if (type === WidgetActionType.customPretty) { result = {...this.widgetActionFormGroup.value, ...this.actionTypeFormGroup.get('customAction').value}; + } else if (type === WidgetActionType.placeMapItem) { + result = { + ...this.widgetActionFormGroup.value, + ...this.actionTypeFormGroup.get('customAction').value, + mapItemType: this.actionTypeFormGroup.get('mapItemType').value + }; } else { result = {...this.widgetActionFormGroup.value, ...this.actionTypeFormGroup.value}; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts index e0b301e1ad..45197bea55 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/advanced-range.component.ts @@ -27,7 +27,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { IAliasController } from '@core/api/widget-api.models'; import { AdvancedColorRange } from '@shared/models/widget-settings.models'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { Datasource } from '@shared/models/widget.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; diff --git a/ui-ngx/src/app/modules/home/components/alias/entity-alias-select.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.html similarity index 56% rename from ui-ngx/src/app/modules/home/components/alias/entity-alias-select.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.html index 54a2abbfd6..f2a2ee4b9c 100644 --- a/ui-ngx/src/app/modules/home/components/alias/entity-alias-select.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/alias/entity-alias-select.component.html @@ -15,9 +15,14 @@ limitations under the License. --> - - {{ 'entity.entity-alias' | translate }} - + {{ 'entity.entity-alias' | translate }} + - - - + + + warning + +
+
+ + {{ requiredText | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-input.component.scss similarity index 70% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-input.component.scss index 3e47506dbc..4646d1a5be 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-input.component.scss @@ -14,7 +14,7 @@ * limitations under the License. */ .tb-data-key-input { - .mat-mdc-form-field.tb-inline-field.tb-key-field { + .mat-mdc-form-field.tb-key-field { width: 100%; &.mat-form-field-appearance-fill { .mdc-text-field--filled.mdc-text-field--disabled { @@ -23,14 +23,25 @@ } } } - .mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) { - padding-left: 8px; - padding-right: 0; - + .mat-mdc-text-field-wrapper { + &:not(.mdc-text-field--outlined) { + padding-left: 8px; + padding-right: 0; + .mat-mdc-form-field-infix { + padding-top: 0; + padding-bottom: 6px; + } + } + &.mdc-text-field--outlined { + .mat-mdc-form-field-infix { + padding-top: 12px; + padding-bottom: 12px; + } + .mdc-evolution-chip-set__chips { + margin-left: 0; + } + } .mat-mdc-form-field-infix { - padding-top: 0; - padding-bottom: 6px; - .mdc-evolution-chip-set .mdc-evolution-chip { margin: 0; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-input.component.ts similarity index 96% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-input.component.ts index 71685a182d..30020aec80 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-input.component.ts @@ -50,13 +50,14 @@ import { UtilsService } from '@core/services/utils.service'; import { alarmFields } from '@shared/models/alarm.models'; import { filter, map, mergeMap, publishReplay, refCount, share, tap } from 'rxjs/operators'; import { AggregationType } from '@shared/models/time/time.models'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks } from './data-keys.component.models'; import { IAliasController } from '@core/api/widget-api.models'; +import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; @Component({ selector: 'tb-data-key-input', templateUrl: './data-key-input.component.html', - styleUrls: ['./data-key-input.component.scss', '../../../config/data-keys.component.scss'], + styleUrls: ['./data-key-input.component.scss', './data-keys.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -83,6 +84,19 @@ export class DataKeyInputComponent implements ControlValueAccessor, OnInit, OnCh @Input() disabled: boolean; + @Input() + label: string; + + @Input() + appearance: MatFormFieldAppearance = 'fill'; + + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + + @Input() + @coerceBoolean() + inlineField = true; + @Input() @coerceBoolean() required = false; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.html similarity index 91% rename from ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.html index ec74941cdb..f7d4b8e44b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.html @@ -15,8 +15,12 @@ limitations under the License. --> - - {{placeholder}} + + {{ label ? label : placeholder}}
drag_indicator
-
+
+ + warning + - + {{ requiredText }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.models.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.models.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.scss similarity index 81% rename from ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.scss index 8b377a8456..1fd8a4b117 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.scss @@ -13,6 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +.tb-data-keys { + .mat-mdc-form-field.tb-keys-field { + width: 100%; + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined { + .mat-mdc-form-field-infix { + padding-top: 12px; + padding-bottom: 12px; + .mdc-evolution-chip-set .mdc-evolution-chip { + margin: 0; + } + .mdc-evolution-chip-set__chips { + margin-left: 0; + } + input.mat-mdc-chip-input { + height: 32px; + margin-left: 0; + } + } + .tb-datakeys-container { + gap: 8px; + } + } + } + } +} + .tb-datakeys-container { display: flex; flex-wrap: wrap; @@ -60,6 +88,9 @@ background: rgba(0, 0, 0, 0.04); border-radius: 100px; padding: 2px 10px; + &.tb-transparent { + background: transparent; + } .tb-chip-label { font-weight: normal; font-size: 14px; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts similarity index 95% rename from ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts index ca827e47a1..e60d0298d9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts @@ -19,7 +19,7 @@ import { Component, DestroyRef, ElementRef, - forwardRef, + forwardRef, HostBinding, Input, OnChanges, OnInit, @@ -60,19 +60,19 @@ import { MatDialog } from '@angular/material/dialog'; import { DataKeyConfigDialogComponent, DataKeyConfigDialogData -} from '@home/components/widget/config/data-key-config-dialog.component'; +} from './data-key-config-dialog.component'; import { deepClone, guid, isDefinedAndNotNull, isObject, isUndefined } from '@core/utils'; import { Dashboard } from '@shared/models/dashboard.models'; import { AggregationType } from '@shared/models/time/time.models'; import { DndDropEvent } from 'ngx-drag-drop/lib/dnd-dropzone.directive'; import { moveItemInArray } from '@angular/cdk/drag-drop'; import { coerceBoolean } from '@shared/decorators/coercion'; -import { DatasourceComponent } from '@home/components/widget/config/datasource.component'; import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component'; import { TbPopoverService } from '@shared/components/popover.service'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormProperty } from '@shared/models/dynamic-form.models'; +import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; @Component({ selector: 'tb-data-keys', @@ -98,21 +98,41 @@ import { FormProperty } from '@shared/models/dynamic-form.models'; }) export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChanges, ErrorStateMatcher, Validator { - public get hideDataKeyLabel(): boolean { - return this.datasourceComponent.hideDataKeyLabel; - } + @HostBinding('class') + hostClass = 'tb-data-keys'; - public get hideDataKeyColor(): boolean { - return this.datasourceComponent.hideDataKeyColor; - } + @Input() + label: string; - public get hideDataKeyUnits(): boolean { - return this.datasourceComponent.hideDataKeyUnits; - } + @Input() + appearance: MatFormFieldAppearance = 'fill'; - public get hideDataKeyDecimals(): boolean { - return this.datasourceComponent.hideDataKeyDecimals; - } + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + + @Input() + @coerceBoolean() + inlineField = false; + + @Input() + @coerceBoolean() + hideDataKeyLabel: boolean; + + @Input() + @coerceBoolean() + hideDataKeyColor: boolean; + + @Input() + @coerceBoolean() + hideDataKeyUnits: boolean; + + @Input() + @coerceBoolean() + hideDataKeyDecimals: boolean; + + @Input() + @coerceBoolean() + disableDrag = false; widgetTypes = widgetType; dataKeyTypes = DataKeyType; @@ -138,6 +158,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange } @Input() + @coerceBoolean() optDataKeys: boolean; @Input() @@ -175,6 +196,9 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange @Input() deviceId: string; + @Input() + generateKey: (key: DataKey) => DataKey; + private requiredValue: boolean; get required(): boolean { return this.requiredValue || !this.optDataKeys || this.isCountDatasource; @@ -222,7 +246,6 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange private keysValidator = this._keysValidator.bind(this); constructor(@SkipSelf() private errorStateMatcher: ErrorStateMatcher, - private datasourceComponent: DatasourceComponent, private translate: TranslateService, private utils: UtilsService, private dialog: MatDialog, @@ -463,8 +486,13 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange } private addFromChipValue(chip: DataKey) { - const key = this.callbacks.generateDataKey(chip.name, chip.type, this.dataKeySettingsForm, this.latestDataKeys, - this.datakeySettingsFunction); + let key: DataKey; + if (this.generateKey) { + key = this.generateKey(chip); + } else { + key = this.callbacks.generateDataKey(chip.name, chip.type, this.dataKeySettingsForm, this.latestDataKeys, + this.datakeySettingsFunction); + } this.addKey(key); } @@ -687,7 +715,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange } get dragDisabled(): boolean { - return this.keys.length < 2; + return this.keys.length < 2 || this.disableDrag; } get maxDataKeysSet(): boolean { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.html new file mode 100644 index 0000000000..e512e06a32 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.html @@ -0,0 +1,69 @@ + +
+
widgets.maps.data-layer.color-settings
+
+ + + {{ 'widgets.maps.data-layer.color-type-constant' | translate }} + + + {{ 'widgets.maps.data-layer.color-type-function' | translate }} + + +
+
+
widgets.maps.data-layer.color
+ + +
+
+
+
+ +
+
+ + + +
+
+ + +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.scss new file mode 100644 index 0000000000..b3d0e20de8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.scss @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-data-layer-color-settings-panel { + width: 700px; + max-width: 90vw; + min-height: 300px; + max-height: 90vh; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-xs} { + width: 90vw; + } + .tb-data-layer-color-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-form-row { + height: auto; + } + .tb-data-layer-color-settings-panel-body { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + } + .tb-data-layer-color-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.ts new file mode 100644 index 0000000000..2ce10c25ed --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component.ts @@ -0,0 +1,85 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { DataLayerColorSettings, DataLayerColorType } from '@shared/models/widget/maps/map.models'; + +@Component({ + selector: 'tb-data-layer-color-settings-panel', + templateUrl: './data-layer-color-settings-panel.component.html', + providers: [], + styleUrls: ['./data-layer-color-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class DataLayerColorSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + colorSettings: DataLayerColorSettings; + + @Input() + helpId = 'widget/lib/map/color_fn'; + + @Input() + popover: TbPopoverComponent; + + @Output() + colorSettingsApplied = new EventEmitter(); + + DataLayerColorType = DataLayerColorType; + + colorSettingsFormGroup: UntypedFormGroup; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + protected store: Store, + private destroyRef: DestroyRef) { + super(store); + } + + ngOnInit(): void { + this.colorSettingsFormGroup = this.fb.group( + { + type: [this.colorSettings?.type || DataLayerColorType.constant, []], + color: [this.colorSettings?.color, []], + colorFunction: [this.colorSettings?.colorFunction, []] + } + ); + this.colorSettingsFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + setTimeout(() => {this.popover?.updatePosition();}, 0); + }); + } + + cancel() { + this.popover?.hide(); + } + + applyColorSettings() { + const colorSettings: DataLayerColorSettings = this.colorSettingsFormGroup.value; + this.colorSettingsApplied.emit(colorSettings); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.html new file mode 100644 index 0000000000..81abfd4a7d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.html @@ -0,0 +1,30 @@ + + + +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.ts new file mode 100644 index 0000000000..75cfb9c7bc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-color-settings.component.ts @@ -0,0 +1,117 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ComponentStyle } from '@shared/models/widget-settings.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { DataLayerColorSettings, DataLayerColorType } from '@shared/models/widget/maps/map.models'; +import { + DataLayerColorSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component'; + +@Component({ + selector: 'tb-data-layer-color-settings', + templateUrl: './data-layer-color-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DataLayerColorSettingsComponent), + multi: true + } + ] +}) +export class DataLayerColorSettingsComponent implements ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + helpId = 'widget/lib/map/color_fn'; + + DataLayerColorType = DataLayerColorType; + + modelValue: DataLayerColorSettings; + + colorStyle: ComponentStyle = {}; + + private propagateChange: (v: any) => void = () => { }; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) {} + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + this.updateColorStyle(); + } + + writeValue(value: DataLayerColorSettings): void { + if (value) { + this.modelValue = value; + this.updateColorStyle(); + } + } + + openColorSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + colorSettings: this.modelValue, + helpId: this.helpId + }; + const colorSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, DataLayerColorSettingsPanelComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + colorSettingsPanelPopover.tbComponentRef.instance.popover = colorSettingsPanelPopover; + colorSettingsPanelPopover.tbComponentRef.instance.colorSettingsApplied.subscribe((colorSettings) => { + colorSettingsPanelPopover.hide(); + this.modelValue = colorSettings; + this.updateColorStyle(); + this.propagateChange(this.modelValue); + }); + } + } + + private updateColorStyle() { + if (!this.disabled && this.modelValue) { + if (this.modelValue.type === DataLayerColorType.constant) { + this.colorStyle = {backgroundColor: this.modelValue.color}; + } else { + this.colorStyle = {}; + } + } else { + this.colorStyle = {}; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-pattern-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-pattern-settings.component.html new file mode 100644 index 0000000000..93ec63c128 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-pattern-settings.component.html @@ -0,0 +1,94 @@ + + +
+ + + +
+ + {{ patternTitle ? patternTitle : ((patternType === 'label' ? 'widgets.maps.data-layer.label' : 'widgets.maps.data-layer.tooltip') | translate) }} + + + {{ 'widgets.maps.data-layer.pattern-type-pattern' | translate }} + {{ 'widgets.maps.data-layer.pattern-type-function' | translate }} + +
+
+
+ + + + + + +
+
widgets.maps.data-layer.tooltip-trigger
+ + + + {{ dataLayerTooltipTriggerTranslationMap.get(trigger) | translate }} + + + +
+
+ + {{ 'widgets.maps.data-layer.auto-close-tooltips' | translate }} + +
+
+
widgets.maps.data-layer.tooltip-offset
+
+
+
widgets.maps.data-layer.tooltip-offset-horizontal
+ + + +
+
+
widgets.maps.data-layer.tooltip-offset-vertical
+ + + +
+
+
+ + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-pattern-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-pattern-settings.component.ts new file mode 100644 index 0000000000..681b93da4a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/data-layer-pattern-settings.component.ts @@ -0,0 +1,195 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + Validators +} from '@angular/forms'; +import { merge } from 'rxjs'; +import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + DataLayerPatternSettings, + DataLayerPatternType, + DataLayerTooltipSettings, dataLayerTooltipTriggers, dataLayerTooltipTriggerTranslationMap +} from '@shared/models/widget/maps/map.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; + +@Component({ + selector: 'tb-data-layer-pattern-settings', + templateUrl: './data-layer-pattern-settings.component.html', + styleUrls: ['./../../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DataLayerPatternSettingsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DataLayerPatternSettingsComponent), + multi: true + } + ] +}) +export class DataLayerPatternSettingsComponent implements OnInit, ControlValueAccessor, Validator { + + DataLayerPatternType = DataLayerPatternType; + + dataLayerTooltipTriggers = dataLayerTooltipTriggers; + + dataLayerTooltipTriggerTranslationMap = dataLayerTooltipTriggerTranslationMap; + + settingsExpanded = false; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + @Input() + disabled: boolean; + + @Input() + patternType: 'label' | 'tooltip' = 'label'; + + @Input() + helpId = 'widget/lib/map/label_fn'; + + @Input() + patternTitle: string; + + @Input() + @coerceBoolean() + hasTooltipOffset = false; + + @Input() + @coerceBoolean() + expand = true; + + @Input() + context: MapSettingsContext; + + private modelValue: DataLayerPatternSettings | DataLayerTooltipSettings; + + private propagateChange = null; + + public patternSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + + this.patternSettingsFormGroup = this.fb.group({ + show: [null, []], + type: [null, []], + pattern: [null, [Validators.required]], + patternFunction: [null, [Validators.required]] + }); + if (this.patternType === 'tooltip') { + this.patternSettingsFormGroup.addControl('trigger', this.fb.control(null, [])); + this.patternSettingsFormGroup.addControl('autoclose', this.fb.control(null, [])); + if (this.hasTooltipOffset) { + this.patternSettingsFormGroup.addControl('offsetX', this.fb.control(null, [])); + this.patternSettingsFormGroup.addControl('offsetY', this.fb.control(null, [])); + } + this.patternSettingsFormGroup.addControl('tagActions', this.fb.control(null, [])); + } + this.patternSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + merge(this.patternSettingsFormGroup.get('show').valueChanges, + this.patternSettingsFormGroup.get('type').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.patternSettingsFormGroup.disable({emitEvent: false}); + } else { + this.patternSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: DataLayerPatternSettings | DataLayerTooltipSettings): void { + this.modelValue = value; + this.patternSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + this.settingsExpanded = this.patternSettingsFormGroup.get('show').value && this.expand; + this.patternSettingsFormGroup.get('show').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((show) => { + this.settingsExpanded = show; + }); + } + + public validate(c: UntypedFormControl) { + const valid = this.patternSettingsFormGroup.valid; + return valid ? null : { + [this.patternType]: { + valid: false, + }, + }; + } + + private updateValidators() { + const show: boolean = this.patternSettingsFormGroup.get('show').value; + const type: DataLayerPatternType = this.patternSettingsFormGroup.get('type').value; + if (show) { + this.patternSettingsFormGroup.enable({emitEvent: false}); + if (type === DataLayerPatternType.pattern) { + this.patternSettingsFormGroup.get('pattern').enable({emitEvent: false}); + this.patternSettingsFormGroup.get('patternFunction').disable({emitEvent: false}); + } else { + this.patternSettingsFormGroup.get('pattern').disable({emitEvent: false}); + this.patternSettingsFormGroup.get('patternFunction').enable({emitEvent: false}); + } + } else { + this.patternSettingsFormGroup.disable({emitEvent: false}); + this.patternSettingsFormGroup.get('show').enable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.patternSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/image-map-source-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/image-map-source-settings.component.html new file mode 100644 index 0000000000..fc1ba06ed8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/image-map-source-settings.component.html @@ -0,0 +1,62 @@ + +
+
+
+ {{ 'widgets.maps.image.image-source' | translate }} +
+ + {{ 'widgets.maps.image.image-source-image' | translate }} + {{ 'widgets.maps.image.image-source-entity-key' | translate }} + +
+ + + +
+
widgets.maps.image.source-entity-alias
+ + +
+
+
widgets.maps.image.image-url-key
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/image-map-source-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/image-map-source-settings.component.ts new file mode 100644 index 0000000000..5b43386664 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/image-map-source-settings.component.ts @@ -0,0 +1,157 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + Validators +} from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ImageMapSourceSettings, ImageSourceType } from '@shared/models/widget/maps/map.models'; +import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; + +@Component({ + selector: 'tb-image-map-source-settings', + templateUrl: './image-map-source-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ImageMapSourceSettingsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ImageMapSourceSettingsComponent), + multi: true + } + ] +}) +export class ImageMapSourceSettingsComponent implements OnInit, ControlValueAccessor, Validator { + + ImageSourceType = ImageSourceType; + DatasourceType = DatasourceType; + widgetType = widgetType; + DataKeyType = DataKeyType; + + @Input() + disabled: boolean; + + @Input() + context: MapSettingsContext; + + private modelValue: ImageMapSourceSettings; + + private propagateChange = null; + + public imageMapSourceFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + this.imageMapSourceFormGroup = this.fb.group({ + sourceType: [null, [Validators.required]], + url: [null, [Validators.required]], + entityAliasId: [null, [Validators.required]], + entityKey: [null, [Validators.required]] + }); + this.imageMapSourceFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + this.imageMapSourceFormGroup.get('sourceType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.imageMapSourceFormGroup.disable({emitEvent: false}); + } else { + this.imageMapSourceFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: ImageMapSourceSettings): void { + this.modelValue = value; + this.imageMapSourceFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + } + + editKey() { + const entityKey: DataKey = this.imageMapSourceFormGroup.get('entityKey').value; + this.context.editKey(entityKey, + null, this.imageMapSourceFormGroup.get('entityAliasId').value).subscribe( + (updatedDataKey) => { + if (updatedDataKey) { + this.imageMapSourceFormGroup.get('entityKey').patchValue(updatedDataKey); + } + } + ); + } + + public validate(c: UntypedFormControl) { + const valid = this.imageMapSourceFormGroup.valid; + return valid ? null : { + imageMapSource: { + valid: false, + }, + }; + } + + + private updateValidators() { + const sourceType: ImageSourceType = this.imageMapSourceFormGroup.get('sourceType').value; + if (sourceType === ImageSourceType.image) { + this.imageMapSourceFormGroup.get('url').enable({emitEvent: false}); + this.imageMapSourceFormGroup.get('entityAliasId').disable({emitEvent: false}); + this.imageMapSourceFormGroup.get('entityKey').disable({emitEvent: false}); + } else { + this.imageMapSourceFormGroup.get('url').disable({emitEvent: false}); + this.imageMapSourceFormGroup.get('entityAliasId').enable({emitEvent: false}); + this.imageMapSourceFormGroup.get('entityKey').enable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.imageMapSourceFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-button-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-button-row.component.html new file mode 100644 index 0000000000..8bd57487b0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-button-row.component.html @@ -0,0 +1,47 @@ + +
+ + + + warning + + + + + + +
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-button-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-button-row.component.ts new file mode 100644 index 0000000000..466c6a39d6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-button-row.component.ts @@ -0,0 +1,100 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, forwardRef, Output } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator +} from '@angular/forms'; +import { MapActionButtonSettings } from '@shared/models/widget/maps/map.models'; +import { WidgetAction, WidgetActionType, widgetType } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { isEmptyStr } from '@core/utils'; + +@Component({ + selector: 'tb-map-action-button-row', + templateUrl: 'map-action-button-row.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapActionButtonRowComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MapActionButtonRowComponent), + multi: true + }] +}) +export class MapActionButtonRowComponent implements ControlValueAccessor, Validator { + + @Output() + buttonRemoved = new EventEmitter(); + + mapActionButton = this.fb.group({ + label: [''], + icon: [''], + color: [''], + action: this.fb.control(null) + }, {validators: this.validateButtonConfig()}); + + additionalWidgetActionTypes = [WidgetActionType.placeMapItem]; + readonly widgetType = widgetType; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: FormBuilder) { + this.mapActionButton.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => this.propagateChange(value)) + } + + registerOnChange(fn: any) { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { } + + setDisabledState(isDisabled: boolean) { + if (isDisabled) { + this.mapActionButton.disable({emitEvent: false}); + } else { + this.mapActionButton.enable({emitEvent: false}); + } + } + + validate(): ValidationErrors | null { + return this.mapActionButton.valid ? null : { + mapButtonAction: false + }; + } + + writeValue(value: MapActionButtonSettings) { + this.mapActionButton.patchValue(value, {emitEvent: false}); + } + + private validateButtonConfig() { + return (c: FormGroup) => { + return !c.value.icon && isEmptyStr(c.value.label) ? { + invalidButtonConfig: true + } : null; + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-buttons-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-buttons-settings.component.html new file mode 100644 index 0000000000..b0ad6737d4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-buttons-settings.component.html @@ -0,0 +1,63 @@ + +
+ + + {{ 'widgets.maps.map-action.map-action-buttons' | translate }} + + +
+
+
widgets.maps.map-action.label
+
widgets.maps.map-action.icon
+
widgets.maps.map-action.color
+
widgets.maps.map-action.action
+
+
+
+ @for (mapAction of mapActionButtonsForm.controls; track mapAction) { +
+ + +
+ +
+
+ } @empty { + {{ 'widgets.maps.map-action.no-action-buttons-configured' | translate }} + } +
+
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-buttons-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-buttons-settings.component.ts new file mode 100644 index 0000000000..0260676e03 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-action-buttons-settings.component.ts @@ -0,0 +1,107 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator +} from '@angular/forms'; +import { + defaultMapActionButtonSettings, + MapActionButtonSettings +} from '@shared/models/widget/maps/map.models'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; + +@Component({ + selector: 'tb-map-action-button-settings', + templateUrl: './map-action-buttons-settings.component.html', + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapActionButtonsSettingsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MapActionButtonsSettingsComponent), + multi: true + }] +}) +export class MapActionButtonsSettingsComponent implements ControlValueAccessor, Validator { + + mapActionButtonsForm = this.fb.array([]); + + private propagateChange = (_val: any) => {}; + + constructor(private fb: FormBuilder) { + this.mapActionButtonsForm.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => this.propagateChange(value)); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { } + + setDisabledState(isDisabled: boolean): void { + if (isDisabled) { + this.mapActionButtonsForm.disable({emitEvent: false}); + } else { + this.mapActionButtonsForm.enable({emitEvent: false}); + } + } + + validate(): ValidationErrors | null { + return this.mapActionButtonsForm.valid ? null : { + mapActionButtons: false + }; + } + + writeValue(buttons: MapActionButtonSettings[] = []) { + if (buttons?.length === this.mapActionButtonsForm.length) { + this.mapActionButtonsForm.patchValue(buttons, {emitEvent: false}); + } else { + this.mapActionButtonsForm.clear({emitEvent: false}); + buttons.forEach( + button => this.mapActionButtonsForm.push(this.fb.control(button), {emitEvent: false}) + ); + } + } + + get dragEnabled(): boolean { + return this.mapActionButtonsForm.length > 1; + } + + buttonDrop(event: CdkDragDrop) { + const actionButton = this.mapActionButtonsForm.at(event.previousIndex); + this.mapActionButtonsForm.removeAt(event.previousIndex, {emitEvent: false}); + this.mapActionButtonsForm.insert(event.currentIndex, actionButton); + } + + addButton() { + this.mapActionButtonsForm.push(this.fb.control(defaultMapActionButtonSettings)); + } + + removeButton(index: number) { + this.mapActionButtonsForm.removeAt(index); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html new file mode 100644 index 0000000000..e4ae3a1c58 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.html @@ -0,0 +1,494 @@ + +
+ +

{{dialogTitle}}

+ + +
+
+
+
+
{{ 'widget-config.datasource' | translate }}
+ + {{ datasourceTypesTranslations.get(type) | translate }} + +
+
+ + datasource.label + + + + + + + + +
+
+
+
{{ 'datakey.keys' | translate }}
+
+
+ + + + +
+ + + + + + +
+
+
+
{{ 'widget-config.appearance' | translate }}
+ +
+ + + +
+ @if (dataLayerType === 'markers') { +
{{ 'widgets.maps.data-layer.marker.marker' | translate }}
+ } @else { + +
{{ 'widgets.maps.data-layer.marker.marker' | translate }}
+
+ } + + {{ 'widgets.maps.data-layer.marker.marker-type-shape' | translate }} + {{ 'widgets.maps.data-layer.marker.marker-type-icon' | translate }} + {{ 'widgets.maps.data-layer.marker.marker-type-image' | translate }} + +
+
+
+ +
+
widgets.maps.data-layer.marker.shape
+ +
+
+
widgets.maps.data-layer.marker.icon
+ +
+
+
widgets.maps.data-layer.marker.image
+ +
+
+
widgets.maps.data-layer.marker.marker-offset
+
+
widgets.maps.data-layer.marker.offset-horizontal
+ + + +
widgets.maps.data-layer.marker.offset-vertical
+ + + +
+
+
+ + {{ 'widgets.maps.data-layer.marker.rotate-marker' | translate }} + +
+
widgets.maps.data-layer.marker.offset-angle
+ + +
deg
+
+
+
+ @if (dataLayerType === 'trips') { + + + } +
+
+
+
+
widgets.maps.data-layer.marker.position-conversion
+ + +
+
+ +
+ + + + +
{{ 'widgets.maps.data-layer.path.path' | translate }}
+
+
+
+ +
+
widgets.maps.data-layer.path.path
+
+ + + px + + +
+
+
+ + + + + {{ 'widgets.maps.data-layer.path.path-decorator' | translate }} + + + + +
+
widgets.maps.data-layer.path.decorator-symbol
+
+ + + + {{ pathDecoratorSymbolTranslationMap.get(symbol) | translate }} + + + +
+ + + px + + + +
+
+
+
+
widgets.maps.data-layer.path.decorator-arrangement
+
+
+
widgets.maps.data-layer.path.decorator-offset
+ + +
px
+
+
+
+
widgets.maps.data-layer.path.decorator-end-offset
+ + +
px
+
+
+
+
widgets.maps.data-layer.path.decorator-repeat
+ + +
px
+
+
+
+
+
+
+
+
+
+
+
+ + + + +
{{ 'widgets.maps.data-layer.points.points' | translate }}
+
+
+
+ +
+
widgets.maps.data-layer.points.points
+
+ + + px + + +
+
+ + +
+
+
+
+ +
+
widgets.maps.data-layer.fill-color
+ +
+
+
widgets.maps.data-layer.stroke
+
+ + + px + + +
+
+
+ @if (dataLayerType !== 'trips') { + + } +
+ @if (dataLayerType !== 'trips') { + + } +
+
{{ 'widgets.maps.data-layer.groups' | translate }}
+ + +
+
+
{{ dataLayerEditTitle | translate }}
+
+
widgets.maps.data-layer.edit-instruments
+ + + {{ dataLayerEditActionTranslationMap.get(action) | translate }} + + +
+
+
widgets.maps.data-layer.persist-location-attribute-scope
+ + + {{ telemetryTypeTranslationsShort.get(AttributeScope.SERVER_SCOPE) | translate }} + + + {{ telemetryTypeTranslationsShort.get(AttributeScope.SHARED_SCOPE) | translate }} + + +
+
+ + {{ 'widgets.maps.data-layer.enable-snapping' | translate }} + +
+
+ + +
+
+ + +
+
+ + + + + + + + + + + + +
+
widgets.maps.data-layer.behavior
+
+
widgets.maps.data-layer.on-click
+ + +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.scss new file mode 100644 index 0000000000..37899cda7d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.scss @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-map-data-layer-dialog { + --mdc-outlined-text-field-outline-color: rgba(0,0,0,0.12); + --mat-form-field-trailing-icon-color: rgba(0,0,0,0.38); + .tb-inline-chips { + --mat-form-field-container-vertical-padding: 12px; + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined { + padding-left: 16px; + padding-right: 16px; + .mat-mdc-form-field-infix { + .mdc-evolution-chip-set .mdc-evolution-chip { + margin: 0; + } + .mdc-evolution-chip-set__chips { + gap: 8px; + margin-left: 0; + } + input.mat-mdc-chip-input { + height: 32px; + margin-left: 0; + } + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts new file mode 100644 index 0000000000..dc0123406a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-dialog.component.ts @@ -0,0 +1,513 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, Inject, ViewEncapsulation } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { + CirclesDataLayerSettings, + DataLayerEditAction, + dataLayerEditActions, + dataLayerEditActionTranslationMap, + defaultBaseMapDataLayerSettings, + MapDataLayerSettings, + MapDataLayerType, + MapType, + MarkersDataLayerSettings, + MarkerType, + pathDecoratorSymbols, + pathDecoratorSymbolTranslationMap, + PolygonsDataLayerSettings, + ShapeDataLayerSettings, + TripsDataLayerSettings +} from '@shared/models/widget/maps/map.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { FormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { DataKey, DatasourceType, datasourceTypeTranslationMap, widgetType } from '@shared/models/widget.models'; +import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { EntityType } from '@shared/models/entity-type.models'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; +import { genNextLabelForDataKeys, mergeDeepIgnoreArray } from '@core/utils'; +import { WidgetService } from '@core/http/widget.service'; +import { merge } from 'rxjs'; + +export interface MapDataLayerDialogData { + settings: MapDataLayerSettings; + mapType: MapType; + dataLayerType: MapDataLayerType; + context: MapSettingsContext; +} + +@Component({ + selector: 'tb-map-data-layer-dialog', + templateUrl: './map-data-layer-dialog.component.html', + styleUrls: ['./map-data-layer-dialog.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class MapDataLayerDialogComponent extends DialogComponent { + + DatasourceType = DatasourceType; + + EntityType = EntityType; + + MapType = MapType; + + DataKeyType = DataKeyType; + + AttributeScope = AttributeScope; + telemetryTypeTranslationsShort = telemetryTypeTranslationsShort; + + widgetType = widgetType; + + MarkerType = MarkerType; + + datasourceTypes: Array = []; + datasourceTypesTranslations = datasourceTypeTranslationMap; + + dataLayerEditTitle: string; + dataLayerEditActions: Array = []; + dataLayerEditActionTranslationMap = dataLayerEditActionTranslationMap; + + pathDecoratorSymbols = pathDecoratorSymbols; + pathDecoratorSymbolTranslationMap = pathDecoratorSymbolTranslationMap; + + dataLayerFormGroup: UntypedFormGroup; + + settings = this.data.settings; + mapType = this.data.mapType; + dataLayerType = this.data.dataLayerType; + context = this.data.context; + + generateAdditionalDataKey = this.generateDataKey.bind(this); + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + dialogTitle: string; + + get labelHelpId(): string { + switch (this.dataLayerType) { + case 'trips': + return 'widget/lib/map/label_fn'; + case 'markers': + return 'widget/lib/map/label_fn'; + case 'polygons': + return 'widget/lib/map/polygon_label_fn'; + case 'circles': + return 'widget/lib/map/circle_label_fn'; + default: + return 'widget/lib/map/label_fn'; + } + } + + get tooltipHelpId(): string { + switch (this.dataLayerType) { + case 'trips': + return 'widget/lib/map/tooltip_fn'; + case 'markers': + return 'widget/lib/map/tooltip_fn'; + case 'polygons': + return 'widget/lib/map/polygon_tooltip_fn'; + case 'circles': + return 'widget/lib/map/circle_tooltip_fn'; + default: + return 'widget/lib/map/tooltip_fn'; + } + } + + get showEditAttributeScope(): boolean { + if (this.dataLayerType !== 'trips') { + const editEnabledActions: DataLayerEditAction[] = + this.dataLayerFormGroup.get('edit').get('enabledActions').value; + if (editEnabledActions && editEnabledActions.length) { + switch (this.dataLayerType) { + case 'markers': + const xKey: DataKey = this.dataLayerFormGroup.get('xKey').value; + const yKey: DataKey = this.dataLayerFormGroup.get('yKey').value; + return (xKey?.type === DataKeyType.attribute || yKey?.type === DataKeyType.attribute); + case 'polygons': + const polygonKey: DataKey = this.dataLayerFormGroup.get('polygonKey').value; + return polygonKey?.type === DataKeyType.attribute; + case 'circles': + const circleKey: DataKey = this.dataLayerFormGroup.get('circleKey').value; + return circleKey?.type === DataKeyType.attribute; + } + } else { + return false; + } + } else { + return false; + } + } + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: MapDataLayerDialogData, + public dialogRef: MatDialogRef, + private fb: FormBuilder, + private widgetService: WidgetService, + private destroyRef: DestroyRef) { + super(store, router, dialogRef); + + if (this.context.functionsOnly) { + this.datasourceTypes = [DatasourceType.function]; + } else { + this.datasourceTypes = [DatasourceType.function, DatasourceType.device, DatasourceType.entity]; + } + + this.settings = mergeDeepIgnoreArray({} as MapDataLayerSettings, + defaultBaseMapDataLayerSettings(this.mapType, this.dataLayerType), this.settings); + + this.dataLayerFormGroup = this.fb.group({ + dsType: [this.settings.dsType, [Validators.required]], + dsLabel: [this.settings.dsLabel, []], + dsDeviceId: [this.settings.dsDeviceId, [Validators.required]], + dsEntityAliasId: [this.settings.dsEntityAliasId, [Validators.required]], + dsFilterId: [this.settings.dsFilterId, []], + additionalDataKeys: [this.settings.additionalDataKeys, []], + label: [this.settings.label, []], + tooltip: [this.settings.tooltip, []], + click: [this.settings.click, []], + groups: [this.settings.groups, []] + }); + + if (this.dataLayerType !== 'trips') { + this.dataLayerFormGroup.addControl('edit', this.fb.group({ + enabledActions: [this.settings.edit?.enabledActions, []], + attributeScope: [this.settings.edit?.attributeScope, []], + snappable: [this.settings.edit?.snappable, []] + })); + } + + switch (this.dataLayerType) { + case 'trips': + this.dialogTitle = 'widgets.maps.data-layer.trip.trip-configuration'; + const tripsDataLayer = this.settings as TripsDataLayerSettings; + this.dataLayerFormGroup.addControl('xKey', this.fb.control(tripsDataLayer.xKey, Validators.required)); + this.dataLayerFormGroup.addControl('yKey', this.fb.control(tripsDataLayer.yKey, Validators.required)); + this.dataLayerFormGroup.addControl('showMarker', this.fb.control(tripsDataLayer.showMarker)); + this.dataLayerFormGroup.addControl('markerType', this.fb.control(tripsDataLayer.markerType, Validators.required)); + this.dataLayerFormGroup.addControl('markerShape', this.fb.control(tripsDataLayer.markerShape, Validators.required)); + this.dataLayerFormGroup.addControl('markerIcon', this.fb.control(tripsDataLayer.markerIcon, Validators.required)); + this.dataLayerFormGroup.addControl('markerImage', this.fb.control(tripsDataLayer.markerImage, Validators.required)); + this.dataLayerFormGroup.addControl('markerOffsetX', this.fb.control(tripsDataLayer.markerOffsetX)); + this.dataLayerFormGroup.addControl('markerOffsetY', this.fb.control(tripsDataLayer.markerOffsetY)); + this.dataLayerFormGroup.addControl('rotateMarker', this.fb.control(tripsDataLayer.rotateMarker)); + this.dataLayerFormGroup.addControl('offsetAngle', this.fb.control(tripsDataLayer.offsetAngle, [Validators.min(0), Validators.max(360)])); + if (this.mapType === MapType.image) { + this.dataLayerFormGroup.addControl('positionFunction', this.fb.control(tripsDataLayer.positionFunction)); + } + this.dataLayerFormGroup.addControl('showPath', this.fb.control(tripsDataLayer.showPath)); + this.dataLayerFormGroup.addControl('pathStrokeWeight', this.fb.control(tripsDataLayer.pathStrokeWeight, [Validators.required, Validators.min(0)])); + this.dataLayerFormGroup.addControl('pathStrokeColor', this.fb.control(tripsDataLayer.pathStrokeColor, Validators.required)); + this.dataLayerFormGroup.addControl('usePathDecorator', this.fb.control(tripsDataLayer.usePathDecorator)); + this.dataLayerFormGroup.addControl('pathDecoratorSymbol', this.fb.control(tripsDataLayer.pathDecoratorSymbol, Validators.required)); + this.dataLayerFormGroup.addControl('pathDecoratorSymbolSize', this.fb.control(tripsDataLayer.pathDecoratorSymbolSize, [Validators.required, Validators.min(0)])); + this.dataLayerFormGroup.addControl('pathDecoratorSymbolColor', this.fb.control(tripsDataLayer.pathDecoratorSymbolColor)); + this.dataLayerFormGroup.addControl('pathDecoratorOffset', this.fb.control(tripsDataLayer.pathDecoratorOffset, [Validators.required, Validators.min(0)])); + this.dataLayerFormGroup.addControl('pathEndDecoratorOffset', this.fb.control(tripsDataLayer.pathEndDecoratorOffset, [Validators.required, Validators.min(0)])); + this.dataLayerFormGroup.addControl('pathDecoratorRepeat', this.fb.control(tripsDataLayer.pathDecoratorRepeat, [Validators.required, Validators.min(0)])); + this.dataLayerFormGroup.addControl('showPoints', this.fb.control(tripsDataLayer.showPoints)); + this.dataLayerFormGroup.addControl('pointSize', this.fb.control(tripsDataLayer.pointSize, [Validators.required, Validators.min(0)])); + this.dataLayerFormGroup.addControl('pointColor', this.fb.control(tripsDataLayer.pointColor, Validators.required)); + this.dataLayerFormGroup.addControl('pointTooltip', this.fb.control(tripsDataLayer.pointTooltip)); + merge(this.dataLayerFormGroup.get('showMarker').valueChanges, + this.dataLayerFormGroup.get('markerType').valueChanges, + this.dataLayerFormGroup.get('rotateMarker').valueChanges, + this.dataLayerFormGroup.get('showPath').valueChanges, + this.dataLayerFormGroup.get('usePathDecorator').valueChanges, + this.dataLayerFormGroup.get('showPoints').valueChanges).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => + this.updateValidators() + ); + break; + case 'markers': + this.dialogTitle = 'widgets.maps.data-layer.marker.marker-configuration'; + this.dataLayerEditTitle = 'widgets.maps.data-layer.marker.edit'; + this.dataLayerEditActions = [DataLayerEditAction.add, DataLayerEditAction.move, DataLayerEditAction.remove]; + const markersDataLayer = this.settings as MarkersDataLayerSettings; + this.dataLayerFormGroup.addControl('xKey', this.fb.control(markersDataLayer.xKey, Validators.required)); + this.dataLayerFormGroup.addControl('yKey', this.fb.control(markersDataLayer.yKey, Validators.required)) + this.dataLayerFormGroup.addControl('markerType', this.fb.control(markersDataLayer.markerType, Validators.required)); + this.dataLayerFormGroup.addControl('markerShape', this.fb.control(markersDataLayer.markerShape, Validators.required)); + this.dataLayerFormGroup.addControl('markerIcon', this.fb.control(markersDataLayer.markerIcon, Validators.required)); + this.dataLayerFormGroup.addControl('markerImage', this.fb.control(markersDataLayer.markerImage, Validators.required)); + this.dataLayerFormGroup.addControl('markerOffsetX', this.fb.control(markersDataLayer.markerOffsetX)); + this.dataLayerFormGroup.addControl('markerOffsetY', this.fb.control(markersDataLayer.markerOffsetY)); + if (this.mapType === MapType.image) { + this.dataLayerFormGroup.addControl('positionFunction', this.fb.control(markersDataLayer.positionFunction)); + } + this.dataLayerFormGroup.addControl('markerClustering', this.fb.control(markersDataLayer.markerClustering)); + this.dataLayerFormGroup.get('markerType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => + this.updateValidators() + ); + break; + case 'polygons': + case 'circles': + this.dataLayerEditActions = dataLayerEditActions; + const shapeDataLayer = this.settings as ShapeDataLayerSettings; + this.dataLayerFormGroup.addControl('fillColor', this.fb.control(shapeDataLayer.fillColor, Validators.required)); + this.dataLayerFormGroup.addControl('strokeColor', this.fb.control(shapeDataLayer.strokeColor, Validators.required)); + this.dataLayerFormGroup.addControl('strokeWeight', this.fb.control(shapeDataLayer.strokeWeight, [Validators.required, Validators.min(0)])); + if (this.dataLayerType === 'polygons') { + this.dialogTitle = 'widgets.maps.data-layer.polygon.polygon-configuration'; + this.dataLayerEditTitle = 'widgets.maps.data-layer.polygon.edit'; + const polygonsDataLayer = this.settings as PolygonsDataLayerSettings; + this.dataLayerFormGroup.addControl('polygonKey', this.fb.control(polygonsDataLayer.polygonKey, Validators.required)); + } else { + this.dialogTitle = 'widgets.maps.data-layer.circle.circle-configuration'; + this.dataLayerEditTitle = 'widgets.maps.data-layer.circle.edit'; + const circlesDataLayer = this.settings as CirclesDataLayerSettings; + this.dataLayerFormGroup.addControl('circleKey', this.fb.control(circlesDataLayer.circleKey, Validators.required)); + } + break; + } + this.dataLayerFormGroup.get('dsType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + (newDsType: DatasourceType) => this.onDsTypeChanged(newDsType) + ); + this.updateValidators(); + } + + private onDsTypeChanged(newDsType: DatasourceType) { + switch (this.dataLayerType) { + case 'trips': + case 'markers': + const xKey: DataKey = this.dataLayerFormGroup.get('xKey').value; + if (this.updateDataKeyToNewDsType(xKey, newDsType, this.dataLayerType === 'trips')) { + this.dataLayerFormGroup.get('xKey').patchValue(xKey, {emitEvent: false}); + } + const yKey: DataKey = this.dataLayerFormGroup.get('yKey').value; + if (this.updateDataKeyToNewDsType(yKey, newDsType, this.dataLayerType === 'trips')) { + this.dataLayerFormGroup.get('yKey').patchValue(yKey, {emitEvent: false}); + } + break; + case 'polygons': + const polygonKey: DataKey = this.dataLayerFormGroup.get('polygonKey').value; + if (this.updateDataKeyToNewDsType(polygonKey, newDsType)) { + this.dataLayerFormGroup.get('polygonKey').patchValue(polygonKey, {emitEvent: false}); + } + break; + case 'circles': + const circleKey: DataKey = this.dataLayerFormGroup.get('circleKey').value; + if (this.updateDataKeyToNewDsType(circleKey, newDsType)) { + this.dataLayerFormGroup.get('circleKey').patchValue(circleKey, {emitEvent: false}); + } + break; + } + const additionalDataKeys: DataKey[] = this.dataLayerFormGroup.get('additionalDataKeys').value; + if (additionalDataKeys?.length) { + let updated = false; + for (const key of additionalDataKeys) { + updated = this.updateDataKeyToNewDsType(key, newDsType) || updated; + } + if (updated) { + this.dataLayerFormGroup.get('additionalDataKeys').patchValue(additionalDataKeys, {emitEvent: false}); + } + } + this.updateValidators(); + } + + private updateDataKeyToNewDsType(dataKey: DataKey, newDsType: DatasourceType, timeSeries = false): boolean { + if (newDsType === DatasourceType.function) { + if (dataKey.type !== DataKeyType.function) { + dataKey.type = DataKeyType.function; + return true; + } + } else { + if (dataKey.type === DataKeyType.function) { + dataKey.type = timeSeries ? DataKeyType.timeseries : DataKeyType.attribute; + return true; + } + } + return false; + } + + private updateValidators() { + const dsType: DatasourceType = this.dataLayerFormGroup.get('dsType').value; + if (dsType === DatasourceType.function) { + this.dataLayerFormGroup.get('dsLabel').enable({emitEvent: false}); + this.dataLayerFormGroup.get('dsDeviceId').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); + } else if (dsType === DatasourceType.device) { + this.dataLayerFormGroup.get('dsLabel').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsDeviceId').enable({emitEvent: false}); + this.dataLayerFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); + } else { + this.dataLayerFormGroup.get('dsLabel').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsDeviceId').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsEntityAliasId').enable({emitEvent: false}); + } + if (this.dataLayerType === 'markers') { + this.updateMarkerTypeValidators(); + } + if (this.dataLayerType === 'trips') { + const showMarker: boolean = this.dataLayerFormGroup.get('showMarker').value; + if (showMarker) { + this.dataLayerFormGroup.get('markerType').enable({emitEvent: false}); + this.updateMarkerTypeValidators(); + this.dataLayerFormGroup.get('markerOffsetX').enable({emitEvent: false}); + this.dataLayerFormGroup.get('markerOffsetY').enable({emitEvent: false}); + this.dataLayerFormGroup.get('rotateMarker').enable({emitEvent: false}); + const rotateMarker: boolean = this.dataLayerFormGroup.get('rotateMarker').value; + if (rotateMarker) { + this.dataLayerFormGroup.get('offsetAngle').enable({emitEvent: false}); + } else { + this.dataLayerFormGroup.get('offsetAngle').disable({emitEvent: false}); + } + this.dataLayerFormGroup.get('label').enable({emitEvent: false}); + this.dataLayerFormGroup.get('tooltip').enable({emitEvent: false}); + this.dataLayerFormGroup.get('click').enable({emitEvent: false}); + } else { + this.dataLayerFormGroup.get('markerType').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerShape').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerIcon').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerImage').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerOffsetX').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerOffsetY').disable({emitEvent: false}); + this.dataLayerFormGroup.get('rotateMarker').disable({emitEvent: false}); + this.dataLayerFormGroup.get('offsetAngle').disable({emitEvent: false}); + this.dataLayerFormGroup.get('label').disable({emitEvent: false}); + this.dataLayerFormGroup.get('tooltip').disable({emitEvent: false}); + this.dataLayerFormGroup.get('click').disable({emitEvent: false}); + } + const showPath: boolean = this.dataLayerFormGroup.get('showPath').value; + const usePathDecorator: boolean = this.dataLayerFormGroup.get('usePathDecorator').value; + const showPoints: boolean = this.dataLayerFormGroup.get('showPoints').value; + if (showPath) { + this.dataLayerFormGroup.get('pathStrokeWeight').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pathStrokeColor').enable({emitEvent: false}); + this.dataLayerFormGroup.get('usePathDecorator').enable({emitEvent: false}); + if (usePathDecorator) { + this.dataLayerFormGroup.get('pathDecoratorSymbol').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorSymbolSize').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorSymbolColor').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorOffset').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pathEndDecoratorOffset').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorRepeat').enable({emitEvent: false}); + } else { + this.dataLayerFormGroup.get('pathDecoratorSymbol').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorSymbolSize').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorSymbolColor').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorOffset').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathEndDecoratorOffset').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorRepeat').disable({emitEvent: false}); + } + } else { + this.dataLayerFormGroup.get('pathStrokeWeight').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathStrokeColor').disable({emitEvent: false}); + this.dataLayerFormGroup.get('usePathDecorator').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorSymbol').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorSymbolSize').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorSymbolColor').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorOffset').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathEndDecoratorOffset').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pathDecoratorRepeat').disable({emitEvent: false}); + } + if (showPoints) { + this.dataLayerFormGroup.get('pointSize').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pointColor').enable({emitEvent: false}); + this.dataLayerFormGroup.get('pointTooltip').enable({emitEvent: false}); + } else { + this.dataLayerFormGroup.get('pointSize').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pointColor').disable({emitEvent: false}); + this.dataLayerFormGroup.get('pointTooltip').disable({emitEvent: false}); + } + } + } + + private updateMarkerTypeValidators(): void { + const markerType: MarkerType = this.dataLayerFormGroup.get('markerType').value; + if (markerType === MarkerType.shape) { + this.dataLayerFormGroup.get('markerShape').enable({emitEvent: false}); + this.dataLayerFormGroup.get('markerIcon').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerImage').disable({emitEvent: false}); + } else if (markerType === MarkerType.icon) { + this.dataLayerFormGroup.get('markerShape').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerIcon').enable({emitEvent: false}); + this.dataLayerFormGroup.get('markerImage').disable({emitEvent: false}); + } else { + this.dataLayerFormGroup.get('markerShape').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerIcon').disable({emitEvent: false}); + this.dataLayerFormGroup.get('markerImage').enable({emitEvent: false}); + } + } + + editKey(keyType: 'xKey' | 'yKey' | 'polygonKey' | 'circleKey') { + const targetDataKey: DataKey = this.dataLayerFormGroup.get(keyType).value; + this.context.editKey(targetDataKey, + this.dataLayerFormGroup.get('dsDeviceId').value, this.dataLayerFormGroup.get('dsEntityAliasId').value, + this.dataLayerType === 'trips' ? widgetType.timeseries : widgetType.latest).subscribe( + (updatedDataKey) => { + if (updatedDataKey) { + this.dataLayerFormGroup.get(keyType).patchValue(updatedDataKey); + this.dataLayerFormGroup.markAsDirty(); + } + } + ); + } + + private generateDataKey(key: DataKey): DataKey { + const dataKey = this.context.callbacks.generateDataKey(key.name, key.type, null, false, null); + const dataKeys: DataKey[] = []; + switch (this.dataLayerType) { + case 'trips': + case 'markers': + const xKey: DataKey = this.dataLayerFormGroup.get('xKey').value; + if (xKey) { + dataKeys.push(xKey); + } + const yKey: DataKey = this.dataLayerFormGroup.get('yKey').value; + if (yKey) { + dataKeys.push(yKey); + } + break; + case 'polygons': + const polygonKey: DataKey = this.dataLayerFormGroup.get('polygonKey').value; + if (polygonKey) { + dataKeys.push(polygonKey); + } + break; + case 'circles': + const circleKey: DataKey = this.dataLayerFormGroup.get('circleKey').value; + if (circleKey) { + dataKeys.push(circleKey); + } + break; + } + const additionalKeys: DataKey[] = this.dataLayerFormGroup.get('additionalDataKeys').value; + if (additionalKeys) { + dataKeys.push(...additionalKeys); + } + dataKey.label = genNextLabelForDataKeys(dataKey.label, dataKeys); + return dataKey; + } + + cancel(): void { + this.dialogRef.close(null); + } + + save(): void { + const settings: MapDataLayerSettings = this.dataLayerFormGroup.getRawValue(); + this.dialogRef.close(settings); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.html new file mode 100644 index 0000000000..b03c9ae500 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.html @@ -0,0 +1,138 @@ + +
+
+ + + + {{ datasourceTypesTranslations.get(type) | translate }} + + + + + + + + + + +
+ + + + + + + + +
+ +
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.scss new file mode 100644 index 0000000000..1f17c05f82 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.scss @@ -0,0 +1,74 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-form-table-row.tb-map-data-layer-row { + + .tb-source-field { + flex: 1 1 50%; + display: flex; + gap: 12px; + .tb-ds-type-field, .tb-label-field, .tb-device-field, .tb-entity-alias-field { + flex: 1; + } + } + + .tb-x-pos-field { + flex: 1 1 25%; + min-width: 0; + } + + .tb-y-pos-field { + flex: 1 1 25%; + min-width: 0; + } + + .tb-key-field { + flex: 1 1 50%; + min-width: 0; + } + + .tb-remove-button { + width: 40px; + min-width: 40px; + } + + @media #{$mat-lt-lg} { + .tb-source-field { + flex-direction: column; + flex: 1 1 30%; + } + .tb-x-pos-field, .tb-y-pos-field { + flex: 1 1 35%; + } + .tb-key-field { + flex: 1 1 70%; + } + } + @media screen and (min-width: 450px) and (max-width: 599px) { + .tb-source-field { + flex-direction: row; + } + } + @media #{$mat-xs} { + .tb-x-pos-field, .tb-y-pos-field { + display: none; + } + .tb-key-field { + display: none; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.ts new file mode 100644 index 0000000000..ae47852f33 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layer-row.component.ts @@ -0,0 +1,341 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + DestroyRef, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + CirclesDataLayerSettings, + MapDataLayerSettings, + MapDataLayerType, + MapType, + MarkersDataLayerSettings, + PolygonsDataLayerSettings, + TripsDataLayerSettings +} from '@shared/models/widget/maps/map.models'; +import { DataKey, DatasourceType, datasourceTypeTranslationMap, widgetType } from '@shared/models/widget.models'; +import { EntityType } from '@shared/models/entity-type.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { MapSettingsComponent } from '@home/components/widget/lib/settings/common/map/map-settings.component'; +import { deepClone } from '@core/utils'; +import { MatDialog } from '@angular/material/dialog'; +import { + MapDataLayerDialogComponent, + MapDataLayerDialogData +} from '@home/components/widget/lib/settings/common/map/map-data-layer-dialog.component'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; + +@Component({ + selector: 'tb-map-data-layer-row', + templateUrl: './map-data-layer-row.component.html', + styleUrls: ['./map-data-layer-row.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapDataLayerRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class MapDataLayerRowComponent implements ControlValueAccessor, OnInit { + + DatasourceType = DatasourceType; + DataKeyType = DataKeyType; + + EntityType = EntityType; + + MapType = MapType; + + widgetType = widgetType; + + datasourceTypes: Array = []; + datasourceTypesTranslations = datasourceTypeTranslationMap; + + @Input() + disabled: boolean; + + @Input() + mapType: MapType = MapType.geoMap; + + @Input() + dataLayerType: MapDataLayerType = 'markers'; + + @Input() + context: MapSettingsContext; + + @Output() + dataLayerRemoved = new EventEmitter(); + + dataLayerFormGroup: UntypedFormGroup; + + modelValue: MapDataLayerSettings; + + editDataLayerText: string; + + removeDataLayerText: string; + + private propagateChange = (_val: any) => {}; + + constructor(private mapSettingsComponent: MapSettingsComponent, + private fb: UntypedFormBuilder, + private dialog: MatDialog, + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + if (this.context.functionsOnly) { + this.datasourceTypes = [DatasourceType.function]; + } else { + this.datasourceTypes = [DatasourceType.function, DatasourceType.device, DatasourceType.entity]; + } + this.dataLayerFormGroup = this.fb.group({ + dsType: [null, [Validators.required]], + dsLabel: [null, []], + dsDeviceId: [null, [Validators.required]], + dsEntityAliasId: [null, [Validators.required]] + }); + switch (this.dataLayerType) { + case 'trips': + this.editDataLayerText = 'widgets.maps.data-layer.trip.trip-configuration'; + this.removeDataLayerText = 'widgets.maps.data-layer.trip.remove-trip'; + this.dataLayerFormGroup.addControl('xKey', this.fb.control(null, Validators.required)); + this.dataLayerFormGroup.addControl('yKey', this.fb.control(null, Validators.required)); + break; + case 'markers': + this.editDataLayerText = 'widgets.maps.data-layer.marker.marker-configuration'; + this.removeDataLayerText = 'widgets.maps.data-layer.marker.remove-marker'; + this.dataLayerFormGroup.addControl('xKey', this.fb.control(null, Validators.required)); + this.dataLayerFormGroup.addControl('yKey', this.fb.control(null, Validators.required)); + break; + case 'polygons': + this.editDataLayerText = 'widgets.maps.data-layer.polygon.polygon-configuration'; + this.removeDataLayerText = 'widgets.maps.data-layer.polygon.remove-polygon'; + this.dataLayerFormGroup.addControl('polygonKey', this.fb.control(null, Validators.required)); + break; + case 'circles': + this.editDataLayerText = 'widgets.maps.data-layer.circle.circle-configuration'; + this.removeDataLayerText = 'widgets.maps.data-layer.circle.remove-circle'; + this.dataLayerFormGroup.addControl('circleKey', this.fb.control(null, Validators.required)); + break; + } + this.dataLayerFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => this.updateModel() + ); + this.dataLayerFormGroup.get('dsType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + (newDsType: DatasourceType) => this.onDsTypeChanged(newDsType) + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.dataLayerFormGroup.disable({emitEvent: false}); + } else { + this.dataLayerFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: MapDataLayerSettings): void { + this.modelValue = value; + this.dataLayerFormGroup.patchValue( + { + dsType: value?.dsType, + dsLabel: value?.dsLabel, + dsDeviceId: value?.dsDeviceId, + dsEntityAliasId: value?.dsEntityAliasId + }, {emitEvent: false} + ); + switch (this.dataLayerType) { + case 'trips': + const tripsDataLayer = value as TripsDataLayerSettings; + this.dataLayerFormGroup.patchValue( + { + xKey: tripsDataLayer?.xKey, + yKey: tripsDataLayer?.yKey + }, {emitEvent: false} + ); + break; + case 'markers': + const markersDataLayer = value as MarkersDataLayerSettings; + this.dataLayerFormGroup.patchValue( + { + xKey: markersDataLayer?.xKey, + yKey: markersDataLayer?.yKey + }, {emitEvent: false} + ); + break; + case 'polygons': + const polygonsDataLayer = value as PolygonsDataLayerSettings; + this.dataLayerFormGroup.patchValue( + { + polygonKey: polygonsDataLayer?.polygonKey + }, {emitEvent: false} + ); + break; + case 'circles': + const circlesDataLayer = value as CirclesDataLayerSettings; + this.dataLayerFormGroup.patchValue( + { + circleKey: circlesDataLayer?.circleKey + }, {emitEvent: false} + ); + break; + } + this.updateValidators(); + this.cd.markForCheck(); + } + + editKey(keyType: 'xKey' | 'yKey' | 'polygonKey' | 'circleKey') { + const targetDataKey: DataKey = this.dataLayerFormGroup.get(keyType).value; + this.context.editKey(targetDataKey, + this.dataLayerFormGroup.get('dsDeviceId').value, this.dataLayerFormGroup.get('dsEntityAliasId').value, + this.dataLayerType === 'trips' ? widgetType.timeseries : widgetType.latest).subscribe( + (updatedDataKey) => { + if (updatedDataKey) { + this.dataLayerFormGroup.get(keyType).patchValue(updatedDataKey); + } + } + ); + } + + editDataLayer($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(MapDataLayerDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + settings: deepClone(this.modelValue), + mapType: this.mapType, + dataLayerType: this.dataLayerType, + context: this.context + } + }).afterClosed().subscribe((settings) => { + if (settings) { + this.modelValue = settings; + this.dataLayerFormGroup.patchValue(settings); + this.updateValidators(); + } + }); + } + + private onDsTypeChanged(newDsType: DatasourceType) { + let updateModel = false; + switch (this.dataLayerType) { + case 'trips': + case 'markers': + const xKey: DataKey = this.dataLayerFormGroup.get('xKey').value; + if (this.updateDataKeyToNewDsType(xKey, newDsType, this.dataLayerType === 'trips')) { + this.dataLayerFormGroup.get('xKey').patchValue(xKey, {emitEvent: false}); + updateModel = true; + } + const yKey: DataKey = this.dataLayerFormGroup.get('yKey').value; + if (this.updateDataKeyToNewDsType(yKey, newDsType, this.dataLayerType === 'trips')) { + this.dataLayerFormGroup.get('yKey').patchValue(yKey, {emitEvent: false}); + updateModel = true; + } + break; + case 'polygons': + const polygonKey: DataKey = this.dataLayerFormGroup.get('polygonKey').value; + if (this.updateDataKeyToNewDsType(polygonKey, newDsType)) { + this.dataLayerFormGroup.get('polygonKey').patchValue(polygonKey, {emitEvent: false}); + updateModel = true; + } + break; + case 'circles': + const circleKey: DataKey = this.dataLayerFormGroup.get('circleKey').value; + if (this.updateDataKeyToNewDsType(circleKey, newDsType)) { + this.dataLayerFormGroup.get('circleKey').patchValue(circleKey, {emitEvent: false}); + updateModel = true; + } + break; + } + this.updateValidators(); + if (updateModel) { + this.updateModel(); + } + } + + private updateDataKeyToNewDsType(dataKey: DataKey, newDsType: DatasourceType, timeSeries = false): boolean { + if (newDsType === DatasourceType.function) { + if (dataKey.type !== DataKeyType.function) { + dataKey.type = DataKeyType.function; + return true; + } + } else { + if (dataKey.type === DataKeyType.function) { + dataKey.type = timeSeries ? DataKeyType.timeseries : DataKeyType.attribute; + return true; + } + } + return false; + } + + private updateValidators() { + const dsType: DatasourceType = this.dataLayerFormGroup.get('dsType').value; + if (dsType === DatasourceType.function) { + this.dataLayerFormGroup.get('dsLabel').enable({emitEvent: false}); + this.dataLayerFormGroup.get('dsDeviceId').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); + } else if (dsType === DatasourceType.device) { + this.dataLayerFormGroup.get('dsLabel').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsDeviceId').enable({emitEvent: false}); + this.dataLayerFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); + } else { + this.dataLayerFormGroup.get('dsLabel').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsDeviceId').disable({emitEvent: false}); + this.dataLayerFormGroup.get('dsEntityAliasId').enable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = {...this.modelValue, ...this.dataLayerFormGroup.value}; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.html new file mode 100644 index 0000000000..46a9235970 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.html @@ -0,0 +1,52 @@ + +
+
+
+
widgets.maps.data-layer.source
+
+ {{ (mapType === MapType.geoMap ? 'widgets.maps.data-layer.marker.latitude-key' : 'widgets.maps.data-layer.marker.x-pos-key') | translate }} +
+
+ {{ (mapType === MapType.geoMap ? 'widgets.maps.data-layer.marker.longitude-key' : 'widgets.maps.data-layer.marker.y-pos-key') | translate }} +
+
widgets.maps.data-layer.polygon.polygon-key
+
widgets.maps.data-layer.circle.circle-key
+
+
+
+
+ + +
+
+
+
+ +
+
+ + {{ noDataLayersText | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.scss new file mode 100644 index 0000000000..84262b9810 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.scss @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-map-data-layers { + .tb-form-table-header-cell { + &.tb-source-header { + flex: 1 1 50%; + } + &.tb-x-pos-header { + flex: 1 1 25%; + } + &.tb-y-pos-header { + flex: 1 1 25%; + } + &.tb-key-header { + flex: 1 1 50%; + } + &.tb-actions-header { + width: 80px; + min-width: 80px; + } + @media #{$mat-lt-lg} { + &.tb-source-header { + flex: 1 1 30%; + } + &.tb-x-pos-header, &.tb-y-pos-header { + flex: 1 1 35%; + } + &.tb-key-header { + flex: 1 1 70%; + } + } + @media #{$mat-xs} { + &.tb-x-pos-header, &.tb-y-pos-header { + display: none; + } + &.tb-key-header { + display: none; + } + } + } + + .tb-form-table-body { + tb-map-data-layer-row { + overflow: hidden; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.ts new file mode 100644 index 0000000000..747ef21681 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-layers.component.ts @@ -0,0 +1,181 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator +} from '@angular/forms'; +import { mergeDeep } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + defaultMapDataLayerSettings, + MapDataLayerSettings, + MapDataLayerType, + mapDataLayerValid, + mapDataLayerValidator, + MapType +} from '@shared/models/widget/maps/map.models'; +import { MapSettingsComponent } from '@home/components/widget/lib/settings/common/map/map-settings.component'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; + +@Component({ + selector: 'tb-map-data-layers', + templateUrl: './map-data-layers.component.html', + styleUrls: ['./map-data-layers.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapDataLayersComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MapDataLayersComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class MapDataLayersComponent implements ControlValueAccessor, OnInit, Validator { + + MapType = MapType; + + @Input() + disabled: boolean; + + @Input() + mapType: MapType = MapType.geoMap; + + @Input() + dataLayerType: MapDataLayerType = 'markers'; + + @Input() + context: MapSettingsContext; + + dataLayersFormGroup: UntypedFormGroup; + + addDataLayerText: string; + + noDataLayersText: string; + + private propagateChange = (_val: any) => {}; + + constructor(private mapSettingsComponent: MapSettingsComponent, + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + switch (this.dataLayerType) { + case 'trips': + this.addDataLayerText = 'widgets.maps.data-layer.trip.add-trip'; + this.noDataLayersText = 'widgets.maps.data-layer.trip.no-trips'; + break; + case 'markers': + this.addDataLayerText = 'widgets.maps.data-layer.marker.add-marker'; + this.noDataLayersText = 'widgets.maps.data-layer.marker.no-markers'; + break; + case 'polygons': + this.addDataLayerText = 'widgets.maps.data-layer.polygon.add-polygon'; + this.noDataLayersText = 'widgets.maps.data-layer.polygon.no-polygons'; + break; + case 'circles': + this.addDataLayerText = 'widgets.maps.data-layer.circle.add-circle'; + this.noDataLayersText = 'widgets.maps.data-layer.circle.no-circles'; + break; + } + this.dataLayersFormGroup = this.fb.group({ + dataLayers: [this.fb.array([]), []] + }); + this.dataLayersFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + let layers: MapDataLayerSettings[] = this.dataLayersFormGroup.get('dataLayers').value; + if (layers) { + layers = layers.filter(layer => mapDataLayerValid(layer, this.dataLayerType)); + } + this.propagateChange(layers); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.dataLayersFormGroup.disable({emitEvent: false}); + } else { + this.dataLayersFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: MapDataLayerSettings[] | undefined): void { + const dataLayers: MapDataLayerSettings[] = value || []; + this.dataLayersFormGroup.setControl('dataLayers', this.prepareDataLayersFormArray(dataLayers), {emitEvent: false}); + } + + public validate(c: UntypedFormControl) { + const valid = this.dataLayersFormGroup.valid; + return valid ? null : { + dataLayers: { + valid: false, + }, + }; + } + + dataLayersFormArray(): UntypedFormArray { + return this.dataLayersFormGroup.get('dataLayers') as UntypedFormArray; + } + + trackByDataLayer(index: number, dataLayerControl: AbstractControl): any { + return dataLayerControl; + } + + removeDataLayer(index: number) { + (this.dataLayersFormGroup.get('dataLayers') as UntypedFormArray).removeAt(index); + } + + addDataLayer() { + const dataLayer = mergeDeep({} as MapDataLayerSettings, + defaultMapDataLayerSettings(this.mapType, this.dataLayerType, this.context.functionsOnly)); + const dataLayersArray = this.dataLayersFormGroup.get('dataLayers') as UntypedFormArray; + const dataLayerControl = this.fb.control(dataLayer, [mapDataLayerValidator(this.dataLayerType)]); + dataLayersArray.push(dataLayerControl); + } + + private prepareDataLayersFormArray(dataLayers: MapDataLayerSettings[]): UntypedFormArray { + const dataLayersControls: Array = []; + dataLayers.forEach((dataLayer) => { + dataLayersControls.push(this.fb.control(dataLayer, [mapDataLayerValidator(this.dataLayerType)])); + }); + return this.fb.array(dataLayersControls); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.html new file mode 100644 index 0000000000..a22e6d3cd6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.html @@ -0,0 +1,80 @@ + +
+
+ + + + {{ datasourceTypesTranslations.get(type) | translate }} + + + + + + + + + + +
+ + +
+
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.scss new file mode 100644 index 0000000000..2244f6d14f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.scss @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-form-table-row.tb-map-data-source-row { + + .tb-source-field { + flex: 1 1 50%; + display: flex; + gap: 12px; + .tb-ds-type-field, .tb-label-field, .tb-device-field, .tb-entity-alias-field { + flex: 1; + } + } + + .tb-data-keys-field { + flex: 1 1 50%; + min-width: 0; + } + + .tb-remove-button { + width: 40px; + min-width: 40px; + } + + @media #{$mat-lt-lg} { + .tb-source-field { + flex-direction: column; + flex: 1 1 30%; + } + .tb-data-keys-field { + flex: 1 1 70%; + } + @media #{$mat-lt-md} { + .tb-source-field { + flex: 1 1 50%; + } + .tb-data-keys-field { + flex: 1 1 50%; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.ts new file mode 100644 index 0000000000..a163e8322f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-source-row.component.ts @@ -0,0 +1,207 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + DestroyRef, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { AdditionalMapDataSourceSettings } from '@shared/models/widget/maps/map.models'; +import { DataKey, DatasourceType, datasourceTypeTranslationMap, widgetType } from '@shared/models/widget.models'; +import { EntityType } from '@shared/models/entity-type.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { genNextLabelForDataKeys } from '@core/utils'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; + +@Component({ + selector: 'tb-map-data-source-row', + templateUrl: './map-data-source-row.component.html', + styleUrls: ['./map-data-source-row.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapDataSourceRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class MapDataSourceRowComponent implements ControlValueAccessor, OnInit { + + DatasourceType = DatasourceType; + DataKeyType = DataKeyType; + + EntityType = EntityType; + + widgetType = widgetType; + + datasourceTypes: Array = []; + datasourceTypesTranslations = datasourceTypeTranslationMap; + + @Input() + disabled: boolean; + + @Input() + context: MapSettingsContext; + + @Output() + dataSourceRemoved = new EventEmitter(); + + dataSourceFormGroup: UntypedFormGroup; + + generateAdditionalDataKey = this.generateDataKey.bind(this); + + modelValue: AdditionalMapDataSourceSettings; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + if (this.context.functionsOnly) { + this.datasourceTypes = [DatasourceType.function]; + } else { + this.datasourceTypes = [DatasourceType.function, DatasourceType.device, DatasourceType.entity]; + } + this.dataSourceFormGroup = this.fb.group({ + dsType: [null, [Validators.required]], + dsLabel: [null, []], + dsDeviceId: [null, [Validators.required]], + dsEntityAliasId: [null, [Validators.required]], + dataKeys: [null, [Validators.required]] + }); + this.dataSourceFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => this.updateModel() + ); + this.dataSourceFormGroup.get('dsType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + (newDsType: DatasourceType) => this.onDsTypeChanged(newDsType) + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.dataSourceFormGroup.disable({emitEvent: false}); + } else { + this.dataSourceFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: AdditionalMapDataSourceSettings): void { + this.modelValue = value; + this.dataSourceFormGroup.patchValue( + { + dsType: value?.dsType, + dsLabel: value?.dsLabel, + dsDeviceId: value?.dsDeviceId, + dsEntityAliasId: value?.dsEntityAliasId, + dataKeys: value?.dataKeys + }, {emitEvent: false} + ); + this.updateValidators(); + this.cd.markForCheck(); + } + + private generateDataKey(key: DataKey): DataKey { + const dataKey = this.context.callbacks.generateDataKey(key.name, key.type, null, false, null); + const dataKeys: DataKey[] = this.dataSourceFormGroup.get('dataKeys').value || []; + dataKey.label = genNextLabelForDataKeys(dataKey.label, dataKeys); + return dataKey; + } + + private onDsTypeChanged(newDsType: DatasourceType) { + let updateModel = false; + const dataKeys: DataKey[] = this.dataSourceFormGroup.get('dataKeys').value; + if (dataKeys?.length) { + for (const key of dataKeys) { + updateModel = this.updateDataKeyToNewDsType(key, newDsType) || updateModel; + } + if (updateModel) { + this.dataSourceFormGroup.get('dataKeys').patchValue(dataKeys, {emitEvent: false}); + } + } + this.updateValidators(); + if (updateModel) { + this.updateModel(); + } + } + + private updateDataKeyToNewDsType(dataKey: DataKey, newDsType: DatasourceType): boolean { + if (newDsType === DatasourceType.function) { + if (dataKey.type !== DataKeyType.function) { + dataKey.type = DataKeyType.function; + return true; + } + } else { + if (dataKey.type === DataKeyType.function) { + dataKey.type = DataKeyType.attribute; + return true; + } + } + return false; + } + + private updateValidators() { + const dsType: DatasourceType = this.dataSourceFormGroup.get('dsType').value; + if (dsType === DatasourceType.function) { + this.dataSourceFormGroup.get('dsLabel').enable({emitEvent: false}); + this.dataSourceFormGroup.get('dsDeviceId').disable({emitEvent: false}); + this.dataSourceFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); + } else if (dsType === DatasourceType.device) { + this.dataSourceFormGroup.get('dsLabel').disable({emitEvent: false}); + this.dataSourceFormGroup.get('dsDeviceId').enable({emitEvent: false}); + this.dataSourceFormGroup.get('dsEntityAliasId').disable({emitEvent: false}); + } else { + this.dataSourceFormGroup.get('dsLabel').disable({emitEvent: false}); + this.dataSourceFormGroup.get('dsDeviceId').disable({emitEvent: false}); + this.dataSourceFormGroup.get('dsEntityAliasId').enable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = {...this.modelValue, ...this.dataSourceFormGroup.value}; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.html new file mode 100644 index 0000000000..a4fb89740b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.html @@ -0,0 +1,43 @@ + +
+
+
+
widgets.maps.data-layer.source
+
widgets.maps.data-layer.data-keys
+
+
+
+
+ + +
+
+
+
+ +
+
+ + {{ 'widgets.maps.data-layer.no-datasources' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.scss new file mode 100644 index 0000000000..28788cb4f1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.scss @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-map-data-sources { + .tb-form-table-header-cell { + &.tb-source-header { + flex: 1 1 50%; + } + &.tb-data-keys-header { + flex: 1 1 50%; + } + &.tb-actions-header { + width: 40px; + min-width: 40px; + } + @media #{$mat-lt-lg} { + &.tb-source-header { + flex: 1 1 30%; + } + &.tb-data-keys-header { + flex: 1 1 70%; + } + @media #{$mat-lt-md} { + &.tb-source-header { + flex: 1 1 50%; + } + &.tb-data-keys-header { + flex: 1 1 50%; + } + } + } + } + + .tb-form-table-body { + tb-map-data-source-row { + overflow: hidden; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.ts new file mode 100644 index 0000000000..681a135fed --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-data-sources.component.ts @@ -0,0 +1,147 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator +} from '@angular/forms'; +import { mergeDeep } from '@core/utils'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + AdditionalMapDataSourceSettings, + additionalMapDataSourceValid, + additionalMapDataSourceValidator, + defaultAdditionalMapDataSourceSettings +} from '@shared/models/widget/maps/map.models'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; + +@Component({ + selector: 'tb-map-data-sources', + templateUrl: './map-data-sources.component.html', + styleUrls: ['./map-data-sources.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapDataSourcesComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MapDataSourcesComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class MapDataSourcesComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() + disabled: boolean; + + @Input() + context: MapSettingsContext; + + dataSourcesFormGroup: UntypedFormGroup; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + this.dataSourcesFormGroup = this.fb.group({ + dataSources: [this.fb.array([]), []] + }); + this.dataSourcesFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + let dataSources: AdditionalMapDataSourceSettings[] = this.dataSourcesFormGroup.get('dataSources').value; + if (dataSources) { + dataSources = dataSources.filter(dataSource => additionalMapDataSourceValid(dataSource)); + } + this.propagateChange(dataSources); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.dataSourcesFormGroup.disable({emitEvent: false}); + } else { + this.dataSourcesFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: AdditionalMapDataSourceSettings[] | undefined): void { + const dataSources: AdditionalMapDataSourceSettings[] = value || []; + this.dataSourcesFormGroup.setControl('dataSources', this.prepareDataSourcesFormArray(dataSources), {emitEvent: false}); + } + + public validate(c: UntypedFormControl) { + const valid = this.dataSourcesFormGroup.valid; + return valid ? null : { + dataSources: { + valid: false, + }, + }; + } + + dataSourcesFormArray(): UntypedFormArray { + return this.dataSourcesFormGroup.get('dataSources') as UntypedFormArray; + } + + trackByDataSource(index: number, dataSourceControl: AbstractControl): any { + return dataSourceControl; + } + + removeDataSource(index: number) { + (this.dataSourcesFormGroup.get('dataSources') as UntypedFormArray).removeAt(index); + } + + addDataSource() { + const dataSource = mergeDeep({} as AdditionalMapDataSourceSettings, + defaultAdditionalMapDataSourceSettings(this.context.functionsOnly)); + const dataSourcesArray = this.dataSourcesFormGroup.get('dataSources') as UntypedFormArray; + const dataSourceControl = this.fb.control(dataSource, [additionalMapDataSourceValidator]); + dataSourcesArray.push(dataSourceControl); + } + + private prepareDataSourcesFormArray(dataSources: AdditionalMapDataSourceSettings[]): UntypedFormArray { + const dataSourcesControls: Array = []; + dataSources.forEach((dataSource) => { + dataSourcesControls.push(this.fb.control(dataSource, [additionalMapDataSourceValidator])); + }); + return this.fb.array(dataSourcesControls); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.html new file mode 100644 index 0000000000..721f93f5a9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.html @@ -0,0 +1,84 @@ + +
+ + + + + + + {{ mapProviderTranslationMap.get(provider) | translate }} + + + + + + + {{ openStreetMapLayerTranslationMap.get(layerType) | translate }} + + + + + + + {{ googleMapLayerTranslationMap.get(layerType) | translate }} + + + + + + + {{ hereLayerTranslationMap.get(layerType) | translate }} + + + + + + + {{ tencentLayerTranslationMap.get(layerType) | translate }} + + + + + + +
+ +
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.scss new file mode 100644 index 0000000000..89949ec96e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.scss @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-form-table-row.tb-map-layer-row { + + .tb-label-field { + flex: 1 1 33.33%; + } + + .tb-provider-field { + flex: 1 1 33.33%; + } + + .tb-layer-field { + flex: 1 1 33.33%; + @media #{$mat-xs} { + display: none; + } + } + + .tb-remove-button { + width: 40px; + min-width: 40px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.ts new file mode 100644 index 0000000000..392c9f9565 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-row.component.ts @@ -0,0 +1,231 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + DestroyRef, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + Renderer2, + ViewContainerRef, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + defaultLayerTitle, + defaultMapLayerSettings, + googleMapLayerTranslationMap, + googleMapLayerTypes, + hereLayerTranslationMap, + hereLayerTypes, + MapLayerSettings, + MapProvider, + mapProviders, + mapProviderTranslationMap, + openStreetLayerTypes, + openStreetMapLayerTranslationMap, + tencentLayerTranslationMap, + tencentLayerTypes +} from '@shared/models/widget/maps/map.models'; +import { deepClone } from '@core/utils'; +import { + MapLayerSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/map/map-layer-settings-panel.component'; + +@Component({ + selector: 'tb-map-layer-row', + templateUrl: './map-layer-row.component.html', + styleUrls: ['./map-layer-row.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapLayerRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class MapLayerRowComponent implements ControlValueAccessor, OnInit { + + MapProvider = MapProvider; + + mapProviders = mapProviders; + + mapProviderTranslationMap = mapProviderTranslationMap; + + openStreetLayerTypes = openStreetLayerTypes; + + openStreetMapLayerTranslationMap = openStreetMapLayerTranslationMap; + + googleMapLayerTypes = googleMapLayerTypes; + + googleMapLayerTranslationMap = googleMapLayerTranslationMap; + + hereLayerTypes = hereLayerTypes; + + hereLayerTranslationMap = hereLayerTranslationMap; + + tencentLayerTypes = tencentLayerTypes; + + tencentLayerTranslationMap = tencentLayerTranslationMap; + + @Input() + disabled: boolean; + + @Output() + layerRemoved = new EventEmitter(); + + layerFormGroup: UntypedFormGroup; + + modelValue: MapLayerSettings; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private translate: TranslateService, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + this.layerFormGroup = this.fb.group({ + label: [null, []], + provider: [null, [Validators.required]], + layerType: [null, [Validators.required]], + tileUrl: [null, [Validators.required]], + apiKey: [null, [Validators.required]], + referenceLayer: [null, []] + }); + this.layerFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => this.updateModel() + ); + this.layerFormGroup.get('provider').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((newProvider: MapProvider) => { + this.onProviderChanged(newProvider); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.layerFormGroup.disable({emitEvent: false}); + } else { + this.layerFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: MapLayerSettings): void { + this.modelValue = value; + this.layerFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + this.cd.markForCheck(); + } + + labelPlaceholder(): string { + let translationKey = defaultLayerTitle(this.modelValue); + if (!translationKey) { + translationKey = 'widget-config.set'; + } + return this.translate.instant(translationKey); + } + + editLayer($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + mapLayerSettings: deepClone(this.modelValue) + }; + const mapLayerSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, MapLayerSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + mapLayerSettingsPanelPopover.tbComponentRef.instance.popover = mapLayerSettingsPanelPopover; + mapLayerSettingsPanelPopover.tbComponentRef.instance.mapLayerSettingsApplied.subscribe((layer) => { + mapLayerSettingsPanelPopover.hide(); + this.layerFormGroup.patchValue( + layer, + {emitEvent: false}); + this.updateValidators(); + this.updateModel(); + }); + } + } + + private onProviderChanged(newProvider: MapProvider) { + this.modelValue = {...defaultMapLayerSettings(newProvider), label: this.modelValue.label}; + this.layerFormGroup.patchValue( + this.modelValue, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateValidators() { + const provider: MapProvider = this.layerFormGroup.get('provider').value; + if (provider === MapProvider.custom) { + this.layerFormGroup.get('tileUrl').enable({emitEvent: false}); + this.layerFormGroup.get('layerType').disable({emitEvent: false}); + } else { + this.layerFormGroup.get('tileUrl').disable({emitEvent: false}); + this.layerFormGroup.get('layerType').enable({emitEvent: false}); + } + if ([MapProvider.google, MapProvider.here].includes(provider)) { + this.layerFormGroup.get('apiKey').enable({emitEvent: false}); + } else { + this.layerFormGroup.get('apiKey').disable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.layerFormGroup.value; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.html new file mode 100644 index 0000000000..cf4c4b90c4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.html @@ -0,0 +1,112 @@ + +
+
widgets.maps.layer.layer-settings
+
+
+
widgets.maps.layer.label
+ + + +
+
+
widgets.maps.layer.provider.provider
+ + + + {{ mapProviderTranslationMap.get(provider) | translate }} + + + +
+
+
{{ (MapProvider.custom === layerFormGroup.get('provider').value ? 'widgets.maps.layer.provider.custom.tile-url' : 'widgets.maps.layer.layer') | translate }}
+ + + + {{ openStreetMapLayerTranslationMap.get(layerType) | translate }} + + + + + + + {{ googleMapLayerTranslationMap.get(layerType) | translate }} + + + + + + + {{ hereLayerTranslationMap.get(layerType) | translate }} + + + + + + + {{ tencentLayerTranslationMap.get(layerType) | translate }} + + + + + + +
+
+
widgets.maps.layer.credentials.credentials
+
+
widgets.maps.layer.credentials.api-key
+ + + +
+
+
+
widgets.maps.layer.reference.reference-layer
+ + + {{ 'widgets.maps.layer.reference.no-layer' | translate }} + + {{ referenceLayerTypeTranslationMap.get(layer) | translate }} + + + +
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.scss new file mode 100644 index 0000000000..e45acbb4db --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.scss @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-map-layer-settings-panel { + width: 540px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-map-layer-settings-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + margin: -10px; + padding: 10px; + } + .tb-map-layer-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-map-layer-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.ts new file mode 100644 index 0000000000..1d4adbbb4c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layer-settings-panel.component.ts @@ -0,0 +1,153 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + defaultLayerTitle, + defaultMapLayerSettings, + googleMapLayerTranslationMap, + googleMapLayerTypes, + hereLayerTranslationMap, + hereLayerTypes, + MapLayerSettings, + MapProvider, + mapProviders, + mapProviderTranslationMap, + openStreetLayerTypes, + openStreetMapLayerTranslationMap, referenceLayerTypes, referenceLayerTypeTranslationMap, + tencentLayerTranslationMap, + tencentLayerTypes +} from '@shared/models/widget/maps/map.models'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'tb-map-layer-settings-panel', + templateUrl: './map-layer-settings-panel.component.html', + providers: [], + styleUrls: ['./map-layer-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class MapLayerSettingsPanelComponent implements OnInit { + + MapProvider = MapProvider; + + mapProviders = mapProviders; + + mapProviderTranslationMap = mapProviderTranslationMap; + + openStreetLayerTypes = openStreetLayerTypes; + + openStreetMapLayerTranslationMap = openStreetMapLayerTranslationMap; + + googleMapLayerTypes = googleMapLayerTypes; + + googleMapLayerTranslationMap = googleMapLayerTranslationMap; + + hereLayerTypes = hereLayerTypes; + + hereLayerTranslationMap = hereLayerTranslationMap; + + tencentLayerTypes = tencentLayerTypes; + + tencentLayerTranslationMap = tencentLayerTranslationMap; + + referenceLayerTypes = referenceLayerTypes; + + referenceLayerTypeTranslationMap = referenceLayerTypeTranslationMap; + + @Input() + mapLayerSettings: MapLayerSettings; + + @Input() + popover: TbPopoverComponent; + + @Output() + mapLayerSettingsApplied = new EventEmitter(); + + layerFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private translate: TranslateService, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + this.layerFormGroup = this.fb.group( + { + label: [null, []], + provider: [null, [Validators.required]], + layerType: [null, [Validators.required]], + tileUrl: [null, [Validators.required]], + apiKey: [null, [Validators.required]], + referenceLayer: [null, []] + } + ); + this.layerFormGroup.patchValue( + this.mapLayerSettings, {emitEvent: false} + ); + this.layerFormGroup.get('provider').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((newProvider: MapProvider) => { + this.onProviderChanged(newProvider); + }); + this.updateValidators(); + } + + cancel() { + this.popover?.hide(); + } + + labelPlaceholder(): string { + let translationKey = defaultLayerTitle(this.layerFormGroup.value); + if (!translationKey) { + translationKey = 'widget-config.set'; + } + return this.translate.instant(translationKey); + } + + applyLayerSettings() { + const layerSettings: MapLayerSettings = this.layerFormGroup.value; + this.mapLayerSettingsApplied.emit(layerSettings); + } + + private onProviderChanged(newProvider: MapProvider) { + let modelValue: MapLayerSettings = this.layerFormGroup.value; + modelValue = {...defaultMapLayerSettings(newProvider), label: modelValue.label}; + this.layerFormGroup.patchValue( + modelValue, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateValidators() { + const provider: MapProvider = this.layerFormGroup.get('provider').value; + if (provider === MapProvider.custom) { + this.layerFormGroup.get('tileUrl').enable({emitEvent: false}); + this.layerFormGroup.get('layerType').disable({emitEvent: false}); + } else { + this.layerFormGroup.get('tileUrl').disable({emitEvent: false}); + this.layerFormGroup.get('layerType').enable({emitEvent: false}); + } + if ([MapProvider.google, MapProvider.here].includes(provider)) { + this.layerFormGroup.get('apiKey').enable({emitEvent: false}); + } else { + this.layerFormGroup.get('apiKey').disable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.html new file mode 100644 index 0000000000..1f0c930802 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.html @@ -0,0 +1,57 @@ + +
+
+
+
widgets.maps.layer.label
+
widgets.maps.layer.provider.provider
+
widgets.maps.layer.layer
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+
+ + {{ 'widgets.maps.layer.no-layers' | translate }} + diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.scss similarity index 56% rename from common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.scss index bf81e04b71..e5b035ddd3 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/JsInvokeStats.java +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.scss @@ -13,32 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.actors; +@import '../../../../../../../../../scss/constants'; -public interface JsInvokeStats { - default void incrementRequests() { - incrementRequests(1); +.tb-map-layers { + .tb-form-table-header-cell { + &.tb-label-header { + flex: 1 1 33.33%; } - - void incrementRequests(int amount); - - default void incrementResponses() { - incrementResponses(1); + &.tb-provider-header { + flex: 1 1 33.33%; } - - void incrementResponses(int amount); - - default void incrementFailures() { - incrementFailures(1); + &.tb-layer-header { + flex: 1 1 33.33%; + @media #{$mat-xs} { + display: none; + } } + &.tb-actions-header { + width: 120px; + min-width: 120px; + } + } - void incrementFailures(int amount); - - int getRequests(); - - int getResponses(); - - int getFailures(); - - void reset(); + .tb-form-table-body { + tb-map-layer-row { + overflow: hidden; + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.ts new file mode 100644 index 0000000000..60e9f981fa --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-layers.component.ts @@ -0,0 +1,156 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator +} from '@angular/forms'; +import { mergeDeep } from '@core/utils'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + defaultMapLayerSettings, + MapLayerSettings, + mapLayerValid, + mapLayerValidator, + MapProvider +} from '@shared/models/widget/maps/map.models'; + +@Component({ + selector: 'tb-map-layers', + templateUrl: './map-layers.component.html', + styleUrls: ['./map-layers.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapLayersComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MapLayersComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class MapLayersComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() + disabled: boolean; + + layersFormGroup: UntypedFormGroup; + + get dragEnabled(): boolean { + return this.layersFormArray().controls.length > 1; + } + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + this.layersFormGroup = this.fb.group({ + layers: [this.fb.array([]), []] + }); + this.layersFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + () => { + let layers: MapLayerSettings[] = this.layersFormGroup.get('layers').value; + if (layers) { + layers = layers.filter(layer => mapLayerValid(layer)); + } + this.propagateChange(layers); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.layersFormGroup.disable({emitEvent: false}); + } else { + this.layersFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: MapLayerSettings[] | undefined): void { + const layers: MapLayerSettings[] = value || []; + this.layersFormGroup.setControl('layers', this.prepareLayersFormArray(layers), {emitEvent: false}); + } + + public validate(c: UntypedFormControl) { + const valid = this.layersFormGroup.valid; + return valid ? null : { + layers: { + valid: false, + }, + }; + } + + layerDrop(event: CdkDragDrop) { + const layersArray = this.layersFormGroup.get('layers') as UntypedFormArray; + const layer = layersArray.at(event.previousIndex); + layersArray.removeAt(event.previousIndex); + layersArray.insert(event.currentIndex, layer); + } + + layersFormArray(): UntypedFormArray { + return this.layersFormGroup.get('layers') as UntypedFormArray; + } + + trackByLayer(index: number, layerControl: AbstractControl): any { + return layerControl; + } + + removeLayer(index: number) { + (this.layersFormGroup.get('layers') as UntypedFormArray).removeAt(index); + } + + addLayer() { + const layer = mergeDeep({} as MapLayerSettings, + defaultMapLayerSettings(MapProvider.openstreet)); + const layersArray = this.layersFormGroup.get('layers') as UntypedFormArray; + const layerControl = this.fb.control(layer, [mapLayerValidator]); + layersArray.push(layerControl); + } + + private prepareLayersFormArray(layers: MapLayerSettings[]): UntypedFormArray { + const layersControls: Array = []; + layers.forEach((layer) => { + layersControls.push(this.fb.control(layer, [mapLayerValidator])); + }); + return this.fb.array(layersControls); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.html new file mode 100644 index 0000000000..38a5d13922 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.html @@ -0,0 +1,152 @@ + + +
+
+
+ {{ 'widgets.maps.map-type.type' | translate }} +
+ + {{ 'widgets.maps.map-type.map' | translate }} + {{ 'widgets.maps.map-type.image' | translate }} + +
+ + + + +
+
+
+
+ {{ 'widgets.maps.overlays.overlays' | translate }} +
+ + {{ 'widgets.maps.overlays.trips' | translate }} + {{ 'widgets.maps.overlays.markers' | translate }} + {{ 'widgets.maps.overlays.polygons' | translate }} + {{ 'widgets.maps.overlays.circles' | translate }} + +
+ + + + +
+
+
+ {{ 'widgets.maps.data-layer.additional-datasources' | translate }} +
+ + +
+
+
+ {{ 'widgets.maps.control.map-controls' | translate }} +
+
+
widgets.maps.control.position
+ + + + {{ mapControlsPositionTranslationMap.get(position) | translate }} + + + +
+
+
widgets.maps.control.zoom-actions
+ + + {{ mapZoomActionTranslationMap.get(action) | translate }} + + +
+
+
widgets.maps.control.scale
+ + + {{ mapScaleTranslationMap.get(scale) | translate }} + + +
+
+ + {{ 'widgets.maps.control.switch-to-drag-mode-using-button' | translate }} + +
+ +
+ + +
+
+ {{ 'widgets.maps.common.common-map-settings' | translate }} +
+ +
+ + {{ 'widgets.maps.common.fit-map-bounds' | translate }} + +
+
+ + {{ 'widgets.maps.common.default-map-center-position' | translate }} + + + + +
+
+
widgets.maps.common.default-map-zoom-level
+ + + +
+
+
+
widgets.maps.common.entities-limit
+ + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.models.ts new file mode 100644 index 0000000000..9ce92c5c4a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.models.ts @@ -0,0 +1,29 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { IAliasController } from '@core/api/widget-api.models'; +import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; +import { DataKey, Widget, widgetType } from '@shared/models/widget.models'; +import { Observable } from 'rxjs'; + +export interface MapSettingsContext { + functionsOnly: boolean; + aliasController: IAliasController; + callbacks: WidgetConfigCallbacks; + widget: Widget; + editKey: (key: DataKey, deviceId: string, entityAliasId: string, WidgetType?: widgetType) => Observable; + generateDataKey: (key: DataKey) => DataKey; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.ts new file mode 100644 index 0000000000..f38f2a042c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-settings.component.ts @@ -0,0 +1,312 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + Validators +} from '@angular/forms'; +import { + DataLayerEditAction, + defaultImageMapSourceSettings, + ImageMapSourceSettings, + imageMapSourceSettingsValidator, + mapControlPositions, + mapControlsPositionTranslationMap, + MapDataLayerSettings, + MapDataLayerType, mapScales, mapScaleTranslationMap, + MapSetting, + MapType, + mapZoomActions, + mapZoomActionTranslationMap +} from '@shared/models/widget/maps/map.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { merge, Observable } from 'rxjs'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { IAliasController } from '@core/api/widget-api.models'; +import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; +import { DataKey, DataKeyConfigMode, Widget, widgetType } from '@shared/models/widget.models'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; +import { + DataKeyConfigDialogComponent, + DataKeyConfigDialogData +} from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component'; +import { deepClone, mergeDeep } from '@core/utils'; +import { MatDialog } from '@angular/material/dialog'; + +@Component({ + selector: 'tb-map-settings', + templateUrl: './map-settings.component.html', + styleUrls: ['./../../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapSettingsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MapSettingsComponent), + multi: true + } + ] +}) +export class MapSettingsComponent implements OnInit, ControlValueAccessor, Validator { + + mapControlPositions = mapControlPositions; + + mapControlsPositionTranslationMap = mapControlsPositionTranslationMap; + + mapZoomActions = mapZoomActions; + + mapZoomActionTranslationMap = mapZoomActionTranslationMap; + + mapScales = mapScales; + + mapScaleTranslationMap = mapScaleTranslationMap; + + MapType = MapType; + + @Input() + @coerceBoolean() + trip = false; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + functionsOnly = false; + + @Input() + aliasController: IAliasController; + + @Input() + callbacks: WidgetConfigCallbacks; + + @Input() + widget: Widget; + + context: MapSettingsContext; + + private modelValue: MapSetting; + + private propagateChange = null; + + public mapSettingsFormGroup: UntypedFormGroup; + + dataLayerMode: MapDataLayerType = 'markers'; + + showDragButtonModeButtonSettings = false; + + constructor(private fb: UntypedFormBuilder, + private dialog: MatDialog, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + + if (this.trip) { + this.dataLayerMode = 'trips'; + } + + this.context = { + functionsOnly: this.functionsOnly, + aliasController: this.aliasController, + callbacks: this.callbacks, + widget: this.widget, + editKey: this.editKey.bind(this), + generateDataKey: this.generateDataKey.bind(this) + }; + + this.mapSettingsFormGroup = this.fb.group({ + mapType: [null, []], + layers: [null, []], + imageSource: [null, [imageMapSourceSettingsValidator]], + markers: [null, []], + polygons: [null, []], + circles: [null, []], + additionalDataSources: [null, []], + controlsPosition: [null, []], + zoomActions: [null, []], + scales: [null, []], + dragModeButton: [null, []], + fitMapBounds: [null, []], + useDefaultCenterPosition: [null, []], + defaultCenterPosition: [null, []], + defaultZoomLevel: [null, [Validators.min(0), Validators.max(20)]], + mapPageSize: [null, [Validators.min(1), Validators.required]], + mapActionButtons: [null] + }); + if (this.trip) { + this.mapSettingsFormGroup.addControl('trips', this.fb.control(null)); + this.mapSettingsFormGroup.addControl('tripTimeline', this.fb.control(null)); + } + this.mapSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + merge(this.mapSettingsFormGroup.get('mapType').valueChanges, + this.mapSettingsFormGroup.get('useDefaultCenterPosition').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + }); + this.mapSettingsFormGroup.get('mapType').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((mapType: MapType) => { + this.mapTypeChanged(mapType); + }); + merge(this.mapSettingsFormGroup.get('markers').valueChanges, + this.mapSettingsFormGroup.get('polygons').valueChanges, + this.mapSettingsFormGroup.get('circles').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateDragButtonModeSettings(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.mapSettingsFormGroup.disable({emitEvent: false}); + } else { + this.mapSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: MapSetting): void { + this.modelValue = value; + this.mapSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + this.updateDragButtonModeSettings(); + } + + public validate(_c: UntypedFormControl) { + const valid = this.mapSettingsFormGroup.valid; + return valid ? null : { + mapSettings: { + valid: false, + }, + }; + } + + private updateValidators() { + const mapType: MapType = this.mapSettingsFormGroup.get('mapType').value; + if (mapType === MapType.geoMap) { + this.mapSettingsFormGroup.get('layers').enable({emitEvent: false}); + this.mapSettingsFormGroup.get('imageSource').disable({emitEvent: false}); + this.mapSettingsFormGroup.get('fitMapBounds').enable({emitEvent: false}); + this.mapSettingsFormGroup.get('useDefaultCenterPosition').enable({emitEvent: false}); + const useDefaultCenterPosition: boolean = this.mapSettingsFormGroup.get('useDefaultCenterPosition').value; + if (useDefaultCenterPosition) { + this.mapSettingsFormGroup.get('defaultCenterPosition').enable({emitEvent: false}); + } else { + this.mapSettingsFormGroup.get('defaultCenterPosition').disable({emitEvent: false}); + } + this.mapSettingsFormGroup.get('defaultZoomLevel').enable({emitEvent: false}); + } else { + this.mapSettingsFormGroup.get('layers').disable({emitEvent: false}); + this.mapSettingsFormGroup.get('imageSource').enable({emitEvent: false}); + this.mapSettingsFormGroup.get('fitMapBounds').disable({emitEvent: false}); + this.mapSettingsFormGroup.get('useDefaultCenterPosition').disable({emitEvent: false}); + this.mapSettingsFormGroup.get('defaultCenterPosition').disable({emitEvent: false}); + this.mapSettingsFormGroup.get('defaultZoomLevel').disable({emitEvent: false}); + } + } + + private mapTypeChanged(mapType: MapType): void { + if (mapType === MapType.image) { + let imageSource: ImageMapSourceSettings = this.mapSettingsFormGroup.get('imageSource').value; + if (!imageSource?.sourceType) { + imageSource = mergeDeep({} as ImageMapSourceSettings, defaultImageMapSourceSettings); + this.mapSettingsFormGroup.get('imageSource').patchValue(imageSource); + } + } + } + + private updateDragButtonModeSettings() { + const markers: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('markers').value; + let dragModeButtonSettingsEnabled = markers.some(d => d.edit && d.edit.enabledActions && d.edit.enabledActions.includes(DataLayerEditAction.move)); + if (!dragModeButtonSettingsEnabled) { + const polygons: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('polygons').value; + dragModeButtonSettingsEnabled = polygons.some(d => d.edit && d.edit.enabledActions && d.edit.enabledActions.includes(DataLayerEditAction.move)); + } + if (!dragModeButtonSettingsEnabled) { + const circles: MapDataLayerSettings[] = this.mapSettingsFormGroup.get('circles').value; + dragModeButtonSettingsEnabled = circles.some(d => d.edit && d.edit.enabledActions && d.edit.enabledActions.includes(DataLayerEditAction.move)); + } + this.showDragButtonModeButtonSettings = dragModeButtonSettingsEnabled; + if (dragModeButtonSettingsEnabled) { + this.mapSettingsFormGroup.get('dragModeButton').enable({emitEvent: false}); + } else { + this.mapSettingsFormGroup.get('dragModeButton').disable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.mapSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } + + private editKey(key: DataKey, deviceId: string, entityAliasId: string, _widgetType = widgetType.latest): Observable { + return this.dialog.open(DataKeyConfigDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + dataKey: deepClone(key), + dataKeyConfigMode: DataKeyConfigMode.general, + aliasController: this.aliasController, + widgetType: _widgetType, + deviceId, + entityAliasId, + showPostProcessing: true, + callbacks: this.callbacks, + hideDataKeyColor: true, + hideDataKeyDecimals: true, + hideDataKeyUnits: true, + widget: this.widget, + dashboard: null, + dataKeySettingsForm: null, + dataKeySettingsDirective: null + } + }).afterClosed(); + } + + private generateDataKey(key: DataKey): DataKey { + return this.callbacks.generateDataKey(key.name, key.type, null, false, null); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.html new file mode 100644 index 0000000000..d743f70919 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.html @@ -0,0 +1,67 @@ + +
+
widgets.maps.data-layer.tooltip-tag-actions
+
+ + +
+
+
+ + {{ action.name }} + +
+
+ + +
+
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.scss new file mode 100644 index 0000000000..9e75d6d66c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.scss @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.mat-mdc-chip.mat-mdc-standard-chip.tb-tag-action-chip { + overflow: hidden; + line-height: 20px; + height: 32px; + + &.mdc-evolution-chip--with-trailing-action { + .mdc-evolution-chip__action--primary { + padding-left: 4px; + padding-right: 12px; + } + } + + .tb-chip-labels { + display: flex; + flex-direction: row; + align-items: center; + min-width: 0; + padding: 2px 10px; + .tb-chip-label { + font-weight: normal; + font-size: 14px; + line-height: 20px; + &.tb-chip-text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } + .mat-mdc-chip-remove.mat-mdc-icon-button { + color: inherit; + opacity: inherit; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.ts new file mode 100644 index 0000000000..ba52e41b84 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component.ts @@ -0,0 +1,176 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + Component, + DestroyRef, + forwardRef, + Input, + OnInit, + Renderer2, + ViewContainerRef, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { WidgetAction, WidgetActionType, widgetType } from '@shared/models/widget.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { MapSettingsContext } from '@home/components/widget/lib/settings/common/map/map-settings.component.models'; +import { + WidgetActionSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/action/widget-action-settings-panel.component'; +import { MatButton } from '@angular/material/button'; +import { TranslateService } from '@ngx-translate/core'; +import { deepClone } from '@core/utils'; + +@Component({ + selector: 'tb-map-tooltip-tag-actions-panel', + templateUrl: './map-tooltip-tag-actions.component.html', + styleUrls: ['./map-tooltip-tag-actions.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MapTooltipTagActionsComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class MapTooltipTagActionsComponent implements ControlValueAccessor, OnInit { + + @Input() + disabled: boolean; + + @Input() + context: MapSettingsContext; + + actionsFormGroup: UntypedFormGroup; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private translate: TranslateService, + private destroyRef: DestroyRef) { + } + + ngOnInit() { + this.actionsFormGroup = this.fb.group({ + actions: [null, []] + }); + this.actionsFormGroup.get('actions').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe( + (val) => this.propagateChange(val) + ); + } + + writeValue(actions?: WidgetAction[]): void { + this.actionsFormGroup.get('actions').patchValue(actions || [], {emitEvent: false}); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.actionsFormGroup.disable({emitEvent: false}); + } else { + this.actionsFormGroup.enable({emitEvent: false}); + } + } + + removeAction(index: number): void { + const actions: WidgetAction[] = this.actionsFormGroup.get('actions').value; + if (actions[index]) { + actions.splice(index, 1); + this.actionsFormGroup.get('actions').patchValue(actions); + } + } + + addAction($event: Event, matButton: MatButton): void { + if ($event) { + $event.stopPropagation(); + } + const action: WidgetAction = { + name: '', + type: WidgetActionType.doNothing + }; + const trigger = matButton._elementRef.nativeElement; + const actionNames = (this.actionsFormGroup.get('actions').value as WidgetAction[] || []).map(action => action.name); + this.openActionSettingsPopup(trigger, action, actionNames, true, (added) => { + if (added) { + const actions: WidgetAction[] = this.actionsFormGroup.get('actions').value || []; + actions.push(added); + this.actionsFormGroup.get('actions').patchValue(actions); + } + }); + } + + editAction($event: Event, matButton: MatButton, index: number): void { + if ($event) { + $event.stopPropagation(); + } + const actions: WidgetAction[] = this.actionsFormGroup.get('actions').value; + if (actions[index]) { + const action = deepClone(actions[index]); + const trigger = matButton._elementRef.nativeElement; + const actionNames = actions.filter((_action, current) => current !== index).map(action => action.name); + this.openActionSettingsPopup(trigger, action, actionNames, false, (updated) => { + if (updated) { + actions[index] = updated; + this.actionsFormGroup.get('actions').patchValue(actions); + } + }); + } + } + + private openActionSettingsPopup(trigger: Element, action: WidgetAction, actionNames: string[], isAdd: boolean, callback: (action?: WidgetAction) => void) { + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const title = this.translate.instant(isAdd ? 'widgets.maps.data-layer.add-tooltip-tag-action' : 'widgets.maps.data-layer.edit-tooltip-tag-action'); + const applyTitle = this.translate.instant(isAdd ? 'action.add' : 'action.apply'); + const ctx: any = { + widgetAction: action, + withName: true, + actionNames, + panelTitle: title, + applyTitle, + widgetType: widgetType.latest, + callbacks: this.context.callbacks + }; + const widgetActionSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, WidgetActionSettingsPanelComponent, + ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + widgetActionSettingsPanelPopover.tbComponentRef.instance.widgetActionApplied.subscribe((widgetAction) => { + widgetActionSettingsPanelPopover.hide(); + callback(widgetAction); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-clustering-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-clustering-settings.component.html new file mode 100644 index 0000000000..2a85f8d514 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-clustering-settings.component.html @@ -0,0 +1,98 @@ + +
+ + + + + {{ 'widgets.maps.data-layer.marker.clustering.use-map-markers-clustering' | translate }} + + + + +
+ + {{ 'widgets.maps.data-layer.marker.clustering.zoom-on-cluster-click' | translate }} + +
+
+
widgets.maps.data-layer.marker.clustering.max-zoom
+ + + +
+
+
widgets.maps.data-layer.marker.clustering.max-radius
+ + + px + +
+
+ + {{ 'widgets.maps.data-layer.marker.clustering.zoom-animation' | translate }} + +
+
+ + {{ 'widgets.maps.data-layer.marker.clustering.bounds-on-cluster-mouse-over' | translate }} + +
+
+ + {{ 'widgets.maps.data-layer.marker.clustering.spiderfy-max-zoom-level' | translate }} + +
+
+
widgets.maps.data-layer.marker.clustering.load-optimization
+
+ + {{ 'widgets.maps.data-layer.marker.clustering.chunked-load' | translate }} + +
+
+ + {{ 'widgets.maps.data-layer.marker.clustering.lazy-load' | translate }} + +
+
+
+ + + + + {{ 'widgets.maps.data-layer.marker.clustering.use-cluster-marker-color-function' | translate }} + + + + + + + + +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-clustering-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-clustering-settings.component.ts new file mode 100644 index 0000000000..8240b5d6dd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-clustering-settings.component.ts @@ -0,0 +1,156 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + Validators +} from '@angular/forms'; +import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { MarkerClusteringSettings } from '@shared/models/widget/maps/map.models'; +import { merge } from 'rxjs'; + +@Component({ + selector: 'tb-marker-clustering-settings', + templateUrl: './marker-clustering-settings.component.html', + styleUrls: ['./../../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MarkerClusteringSettingsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MarkerClusteringSettingsComponent), + multi: true + } + ] +}) +export class MarkerClusteringSettingsComponent implements OnInit, ControlValueAccessor, Validator { + + settingsExpanded = false; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + @Input() + disabled: boolean; + + private modelValue: MarkerClusteringSettings; + + private propagateChange = null; + + public clusteringSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + + this.clusteringSettingsFormGroup = this.fb.group({ + enable: [null, []], + zoomOnClick: [null, []], + maxZoom: [null, [Validators.min(0), Validators.max(18)]], + maxClusterRadius: [null, [Validators.min(0)]], + zoomAnimation: [null, []], + showCoverageOnHover: [null, []], + spiderfyOnMaxZoom: [null, []], + chunkedLoad: [null, []], + lazyLoad: [null, []], + useClusterMarkerColorFunction: [null, []], + clusterMarkerColorFunction: [null, []] + }); + this.clusteringSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + merge(this.clusteringSettingsFormGroup.get('enable').valueChanges, + this.clusteringSettingsFormGroup.get('useClusterMarkerColorFunction').valueChanges).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.clusteringSettingsFormGroup.disable({emitEvent: false}); + } else { + this.clusteringSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: MarkerClusteringSettings): void { + this.modelValue = value; + this.clusteringSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.settingsExpanded = this.clusteringSettingsFormGroup.get('enable').value; + this.updateValidators(); + this.clusteringSettingsFormGroup.get('enable').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((enable) => { + this.settingsExpanded = enable; + }); + } + + public validate(c: UntypedFormControl) { + const valid = this.clusteringSettingsFormGroup.valid; + return valid ? null : { + markerClustering: { + valid: false, + }, + }; + } + + private updateValidators() { + const enable: boolean = this.clusteringSettingsFormGroup.get('enable').value; + const useClusterMarkerColorFunction: boolean = this.clusteringSettingsFormGroup.get('useClusterMarkerColorFunction').value; + if (enable) { + this.clusteringSettingsFormGroup.enable({emitEvent: false}); + if (!useClusterMarkerColorFunction) { + this.clusteringSettingsFormGroup.get('clusterMarkerColorFunction').disable({emitEvent: false}); + } + } else { + this.clusteringSettingsFormGroup.disable({emitEvent: false}); + this.clusteringSettingsFormGroup.get('enable').enable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.clusteringSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.html new file mode 100644 index 0000000000..a7e62694df --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.html @@ -0,0 +1,60 @@ + +
+
widgets.maps.data-layer.marker.marker-icon
+ +
widgets.maps.data-layer.marker.marker-appearance
+
+ + @if (containerInfo.iconContainer === iconContainer) { + + } @else { + + } + +
+
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.scss new file mode 100644 index 0000000000..9022db881d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.scss @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-marker-icon-shapes-panel { + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + .tb-marker-icon-shapes-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + button.mat-mdc-button-base.tb-select-shape-button { + width: 42px; + min-width: 42px; + height: 42px; + padding: 4px; + div.tb-marker-shape { + width: 34px; + height: 34px; + object-fit: contain; + } + } + .tb-marker-icon-shapes-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.ts new file mode 100644 index 0000000000..3240ce11dd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-icon-shapes.component.ts @@ -0,0 +1,127 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + createColorMarkerIconElement, + MarkerIconContainer, markerIconContainers, + tripMarkerIconContainers +} from '@shared/models/widget/maps/marker-shape.models'; +import { Observable } from 'rxjs'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { MatIconRegistry } from '@angular/material/icon'; +import tinycolor from 'tinycolor2'; +import { map, share } from 'rxjs/operators'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +export interface MarkerIconInfo { + iconContainer?: MarkerIconContainer; + icon: string; +} + +interface MarkerIconContainerInfo { + iconContainer: MarkerIconContainer; + html$: Observable; +} + +@Component({ + selector: 'tb-marker-icon-shapes', + templateUrl: './marker-icon-shapes.component.html', + providers: [], + styleUrls: ['./marker-icon-shapes.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class MarkerIconShapesComponent extends PageComponent implements OnInit { + + @Input() + icon: string; + + @Input() + iconContainer: MarkerIconContainer; + + @Input() + color: string; + + @Input() + @coerceBoolean() + trip = false; + + @Input() + popover: TbPopoverComponent; + + @Output() + markerIconSelected = new EventEmitter(); + + dirty = false; + + iconContainers: MarkerIconContainerInfo[]; + + constructor(protected store: Store, + private iconRegistry: MatIconRegistry, + private domSanitizer: DomSanitizer) { + super(store); + } + + ngOnInit(): void { + this.updateIconContainers(); + } + + cancel() { + this.popover?.hide(); + } + + selectIcon(icon: string) { + if (this.icon !== icon) { + this.icon = icon; + this.dirty = true; + this.updateIconContainers(); + } + } + + selectIconContainer(iconContainer: MarkerIconContainer) { + if (this.iconContainer !== iconContainer) { + this.iconContainer = iconContainer; + this.dirty = true; + } + } + + apply() { + const iconInfo: MarkerIconInfo = { + iconContainer: this.iconContainer, + icon: this.icon + }; + this.markerIconSelected.emit(iconInfo); + } + + private updateIconContainers() { + const containersList = [...(this.trip ? tripMarkerIconContainers : markerIconContainers),null]; + this.iconContainers = containersList.map((iconContainer) => { + return { + iconContainer, + html$: createColorMarkerIconElement(this.iconRegistry, this.domSanitizer, iconContainer, this.icon, tinycolor(this.color)).pipe( + map((element) => { + return this.domSanitizer.bypassSecurityTrustHtml(element.outerHTML); + }), + share() + ) + }; + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.html new file mode 100644 index 0000000000..ff06899a8b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.html @@ -0,0 +1,72 @@ + +
+
widgets.maps.data-layer.marker.marker-image
+
+ + + {{ 'widgets.maps.data-layer.marker.marker-image-type-image' | translate }} + + + {{ 'widgets.maps.data-layer.marker.marker-image-type-function' | translate }} + + +
+
+
+ +
+
{{ 'widgets.maps.data-layer.marker.custom-marker-image-size' | translate }}
+ + +
px
+
+
+
+
+
+
+ + + + +
+
+
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.scss new file mode 100644 index 0000000000..9a53a50729 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.scss @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-marker-image-settings-panel { + width: 700px; + max-width: 90vw; + min-height: 300px; + max-height: 90vh; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-xs} { + width: 90vw; + } + .tb-marker-image-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-form-row { + height: auto; + } + .tb-marker-image-settings-panel-body { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + } + .tb-marker-image-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.ts new file mode 100644 index 0000000000..72edca5e81 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings-panel.component.ts @@ -0,0 +1,101 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { MarkerImageSettings, MarkerImageType } from '@shared/models/widget/maps/map.models'; + +@Component({ + selector: 'tb-marker-image-settings-panel', + templateUrl: './marker-image-settings-panel.component.html', + providers: [], + styleUrls: ['./marker-image-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class MarkerImageSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + markerImageSettings: MarkerImageSettings; + + @Input() + popover: TbPopoverComponent; + + @Output() + markerImageSettingsApplied = new EventEmitter(); + + MarkerImageType = MarkerImageType; + + markerImageSettingsFormGroup: UntypedFormGroup; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + protected store: Store, + private destroyRef: DestroyRef) { + super(store); + } + + ngOnInit(): void { + this.markerImageSettingsFormGroup = this.fb.group( + { + type: [this.markerImageSettings?.type || MarkerImageType.image, []], + image: [this.markerImageSettings?.image, [Validators.required]], + imageSize: [this.markerImageSettings?.imageSize, [Validators.min(1)]], + imageFunction: [this.markerImageSettings?.imageFunction, [Validators.required]], + images: [this.markerImageSettings?.images, []] + } + ); + this.markerImageSettingsFormGroup.get('type').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + setTimeout(() => {this.popover?.updatePosition();}, 0); + }); + this.updateValidators(); + } + + cancel() { + this.popover?.hide(); + } + + applyMarkerImageSettings() { + const markerImageSettings: MarkerImageSettings = this.markerImageSettingsFormGroup.value; + this.markerImageSettingsApplied.emit(markerImageSettings); + } + + private updateValidators() { + const type: MarkerImageType = this.markerImageSettingsFormGroup.get('type').value; + if (type === MarkerImageType.image) { + this.markerImageSettingsFormGroup.get('image').enable({emitEvent: false}); + this.markerImageSettingsFormGroup.get('imageSize').enable({emitEvent: false}); + this.markerImageSettingsFormGroup.get('imageFunction').disable({emitEvent: false}); + this.markerImageSettingsFormGroup.get('images').disable({emitEvent: false}); + } else { + this.markerImageSettingsFormGroup.get('image').disable({emitEvent: false}); + this.markerImageSettingsFormGroup.get('imageSize').disable({emitEvent: false}); + this.markerImageSettingsFormGroup.get('imageFunction').enable({emitEvent: false}); + this.markerImageSettingsFormGroup.get('images').enable({emitEvent: false}); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.html new file mode 100644 index 0000000000..94f959d91f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.html @@ -0,0 +1,28 @@ + + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts new file mode 100644 index 0000000000..21600392df --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts @@ -0,0 +1,96 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { MarkerImageSettings, MarkerImageType } from '@shared/models/widget/maps/map.models'; +import { + MarkerImageSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/map/marker-image-settings-panel.component'; + +@Component({ + selector: 'tb-marker-image-settings', + templateUrl: './marker-image-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MarkerImageSettingsComponent), + multi: true + } + ] +}) +export class MarkerImageSettingsComponent implements ControlValueAccessor { + + @Input() + disabled: boolean; + + MarkerImageType = MarkerImageType; + + modelValue: MarkerImageSettings; + + private propagateChange: (v: any) => void = () => { }; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private cd: ChangeDetectorRef, + private viewContainerRef: ViewContainerRef) {} + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: MarkerImageSettings): void { + if (value) { + this.modelValue = value; + } + } + + openImageSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + markerImageSettings: this.modelValue, + }; + const markerImageSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, MarkerImageSettingsPanelComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + markerImageSettingsPanelPopover.tbComponentRef.instance.popover = markerImageSettingsPanelPopover; + markerImageSettingsPanelPopover.tbComponentRef.instance.markerImageSettingsApplied.subscribe((markerImageSettings) => { + markerImageSettingsPanelPopover.hide(); + this.modelValue = markerImageSettings; + this.propagateChange(this.modelValue); + this.cd.detectChanges(); + }); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shape-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shape-settings.component.html new file mode 100644 index 0000000000..0a78698f0f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shape-settings.component.html @@ -0,0 +1,34 @@ + +
+ + +
px
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shape-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shape-settings.component.ts new file mode 100644 index 0000000000..14e80e4fd2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shape-settings.component.ts @@ -0,0 +1,219 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + DestroyRef, + forwardRef, + Input, + OnInit, + Renderer2, + ViewContainerRef +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { MarkerIconSettings, MarkerShapeSettings, MarkerType } from '@shared/models/widget/maps/map.models'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Observable } from 'rxjs'; +import { DomSanitizer, SafeHtml, SafeUrl } from '@angular/platform-browser'; +import { MatIconRegistry } from '@angular/material/icon'; +import { + createColorMarkerIconElement, + createColorMarkerShapeURI +} from '@shared/models/widget/maps/marker-shape.models'; +import tinycolor from 'tinycolor2'; +import { map, share } from 'rxjs/operators'; +import { MarkerShapesComponent } from '@home/components/widget/lib/settings/common/map/marker-shapes.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { + MarkerIconShapesComponent +} from '@home/components/widget/lib/settings/common/map/marker-icon-shapes.component'; + +@Component({ + selector: 'tb-marker-shape-settings', + templateUrl: './marker-shape-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MarkerShapeSettingsComponent), + multi: true + } + ] +}) +export class MarkerShapeSettingsComponent implements ControlValueAccessor, OnInit { + + MarkerType = MarkerType; + + @Input() + disabled: boolean; + + @Input() + markerType: MarkerType; + + @Input() + @coerceBoolean() + trip = false; + + modelValue: MarkerShapeSettings | MarkerIconSettings; + + public shapeSettingsFormGroup: UntypedFormGroup; + + public shapePreview$: Observable; + public iconPreview$: Observable; + + private propagateChange: (v: any) => void = () => { }; + + constructor(private popoverService: TbPopoverService, + private fb: UntypedFormBuilder, + private destroyRef: DestroyRef, + private iconRegistry: MatIconRegistry, + private domSanitizer: DomSanitizer, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) {} + + ngOnInit(): void { + this.shapeSettingsFormGroup = this.fb.group({ + size: [null, [Validators.required, Validators.min(1)]], + color: [null, [Validators.required]] + }); + if (this.markerType === MarkerType.shape) { + this.shapeSettingsFormGroup.addControl('shape', this.fb.control(null, [Validators.required])); + } + if (this.markerType === MarkerType.icon) { + this.shapeSettingsFormGroup.addControl('iconContainer', this.fb.control(null, [])); + this.shapeSettingsFormGroup.addControl('icon', this.fb.control(null, [Validators.required])); + } + this.shapeSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.shapeSettingsFormGroup.disable({emitEvent: false}); + } else { + this.shapeSettingsFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: MarkerShapeSettings | MarkerIconSettings): void { + this.modelValue = value; + this.shapeSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updatePreview(); + } + + openShapePopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + if (this.markerType === MarkerType.shape) { + const ctx: any = { + shape: (this.modelValue as MarkerShapeSettings).shape, + color: this.modelValue.color.color, + trip: this.trip + }; + const markerShapesPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, MarkerShapesComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + markerShapesPopover.tbComponentRef.instance.popover = markerShapesPopover; + markerShapesPopover.tbComponentRef.instance.markerShapeSelected.subscribe((shape) => { + markerShapesPopover.hide(); + this.shapeSettingsFormGroup.get('shape').patchValue( + shape + ); + }); + } else if (this.markerType === MarkerType.icon) { + const ctx: any = { + iconContainer: (this.modelValue as MarkerIconSettings).iconContainer, + icon: (this.modelValue as MarkerIconSettings).icon, + color: this.modelValue.color.color, + trip: this.trip + }; + const markerIconShapesPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, MarkerIconShapesComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + markerIconShapesPopover.tbComponentRef.instance.popover = markerIconShapesPopover; + markerIconShapesPopover.tbComponentRef.instance.markerIconSelected.subscribe((iconInfo) => { + markerIconShapesPopover.hide(); + this.shapeSettingsFormGroup.get('iconContainer').patchValue( + iconInfo.iconContainer, {emitEvent: false} + ); + this.shapeSettingsFormGroup.get('icon').patchValue( + iconInfo.icon, {emitEvent: false} + ); + this.updateModel(); + }); + } + } + } + + private updateModel() { + this.modelValue = this.shapeSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + this.updatePreview(); + } + + private updatePreview() { + const color = this.modelValue.color.color; + if (this.markerType === MarkerType.shape) { + const shape = (this.modelValue as MarkerShapeSettings).shape; + this.shapePreview$ = createColorMarkerShapeURI(this.iconRegistry, this.domSanitizer, shape, tinycolor(color)).pipe( + map((url) => { + return this.domSanitizer.bypassSecurityTrustUrl(url); + }), + share() + ); + } else if (this.markerType === MarkerType.icon) { + const iconContainer = (this.modelValue as MarkerIconSettings).iconContainer; + const icon = (this.modelValue as MarkerIconSettings).icon; + this.iconPreview$ = createColorMarkerIconElement(this.iconRegistry, this.domSanitizer, iconContainer, icon, tinycolor(color)).pipe( + map((element) => { + return this.domSanitizer.bypassSecurityTrustHtml(element.outerHTML); + }), + share() + ); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.html new file mode 100644 index 0000000000..43ab2eb448 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.html @@ -0,0 +1,38 @@ + +
+
widgets.maps.data-layer.marker.marker-shapes
+
+ + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.scss new file mode 100644 index 0000000000..dac30dfc7c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.scss @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-marker-shapes-panel { + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + .tb-marker-shapes-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + button.mat-mdc-button-base.tb-select-shape-button { + width: 42px; + min-width: 42px; + height: 42px; + padding: 4px; + img.tb-marker-shape { + width: 34px; + height: 34px; + object-fit: contain; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.ts new file mode 100644 index 0000000000..0e4d9abb97 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-shapes.component.ts @@ -0,0 +1,93 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + createColorMarkerShapeURI, + MarkerShape, markerShapes, + tripMarkerShapes +} from '@shared/models/widget/maps/marker-shape.models'; +import { Observable } from 'rxjs'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { MatIconRegistry } from '@angular/material/icon'; +import tinycolor from 'tinycolor2'; +import { map, share } from 'rxjs/operators'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +interface MarkerShapeInfo { + shape: MarkerShape; + url$: Observable; +} + +@Component({ + selector: 'tb-marker-shapes', + templateUrl: './marker-shapes.component.html', + providers: [], + styleUrls: ['./marker-shapes.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class MarkerShapesComponent extends PageComponent implements OnInit { + + @Input() + shape: MarkerShape; + + @Input() + color: string; + + @Input() + @coerceBoolean() + trip = false; + + @Input() + popover: TbPopoverComponent; + + @Output() + markerShapeSelected = new EventEmitter(); + + shapes: MarkerShapeInfo[]; + + constructor(protected store: Store, + private iconRegistry: MatIconRegistry, + private domSanitizer: DomSanitizer) { + super(store); + } + + ngOnInit(): void { + this.shapes = (this.trip ? tripMarkerShapes : markerShapes).map((shape) => { + return { + shape, + url$: createColorMarkerShapeURI(this.iconRegistry, this.domSanitizer, shape, tinycolor(this.color)).pipe( + map((url) => { + return this.domSanitizer.bypassSecurityTrustUrl(url); + }), + share() + ) + }; + }); + } + + cancel() { + this.popover?.hide(); + } + + selectShape(shape: MarkerShape) { + this.markerShapeSelected.emit(shape); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/trip-timeline-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/trip-timeline-settings.component.html new file mode 100644 index 0000000000..9729c3559d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/trip-timeline-settings.component.html @@ -0,0 +1,81 @@ + + + +
+ + + + + {{ 'widgets.maps.timeline.control-panel' | translate }} + + + + +
+
widgets.maps.timeline.time-step
+ + + ms + +
+
+
widgets.maps.timeline.speed-options
+ + + + {{ speed }} + + + +
+
+ + {{ 'widgets.maps.timeline.timestamp' | translate }} + + +
+
+ + + + + {{ 'widgets.maps.timeline.snap-to-real-location' | translate }} + + + + + + + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/trip-timeline-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/trip-timeline-settings.component.ts new file mode 100644 index 0000000000..5afeefcbe6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/trip-timeline-settings.component.ts @@ -0,0 +1,161 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + Validators +} from '@angular/forms'; +import { merge } from 'rxjs'; +import { WidgetService } from '@core/http/widget.service'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + TripTimelineSettings +} from '@shared/models/widget/maps/map.models'; + +@Component({ + selector: 'tb-trip-timeline-settings', + templateUrl: './trip-timeline-settings.component.html', + styleUrls: ['./../../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TripTimelineSettingsComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => TripTimelineSettingsComponent), + multi: true + } + ] +}) +export class TripTimelineSettingsComponent implements OnInit, ControlValueAccessor, Validator { + + settingsExpanded = false; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + @Input() + disabled: boolean; + + private modelValue: TripTimelineSettings; + + private propagateChange = null; + + public tripTimelineSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + private destroyRef: DestroyRef) { + } + + ngOnInit(): void { + + this.tripTimelineSettingsFormGroup = this.fb.group({ + showTimelineControl: [null], + timeStep: [null, [Validators.required, Validators.min(1)]], + speedOptions: [null, [Validators.required]], + showTimestamp: [null], + timestampFormat: [null, [Validators.required]], + snapToRealLocation: [null], + locationSnapFilter: [null, [Validators.required]] + }); + + this.tripTimelineSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateModel(); + }); + merge(this.tripTimelineSettingsFormGroup.get('showTimelineControl').valueChanges, + this.tripTimelineSettingsFormGroup.get('showTimestamp').valueChanges, + this.tripTimelineSettingsFormGroup.get('snapToRealLocation').valueChanges + ).pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.tripTimelineSettingsFormGroup.disable({emitEvent: false}); + } else { + this.tripTimelineSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: TripTimelineSettings): void { + this.modelValue = value; + this.tripTimelineSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + this.settingsExpanded = this.tripTimelineSettingsFormGroup.get('showTimelineControl').value; + this.tripTimelineSettingsFormGroup.get('showTimelineControl').valueChanges.pipe( + takeUntilDestroyed(this.destroyRef) + ).subscribe((show) => { + this.settingsExpanded = show; + }); + } + + public validate(c: UntypedFormControl) { + const valid = this.tripTimelineSettingsFormGroup.valid; + return valid ? null : { + tripTimelineSettings: { + valid: false, + }, + }; + } + + private updateValidators() { + const showTimelineControl: boolean = this.tripTimelineSettingsFormGroup.get('showTimelineControl').value; + const showTimestamp: boolean = this.tripTimelineSettingsFormGroup.get('showTimestamp').value; + const snapToRealLocation: boolean = this.tripTimelineSettingsFormGroup.get('snapToRealLocation').value; + if (showTimelineControl) { + this.tripTimelineSettingsFormGroup.enable({emitEvent: false}); + if (!showTimestamp) { + this.tripTimelineSettingsFormGroup.get('timestampFormat').disable({emitEvent: false}); + } + if (!snapToRealLocation) { + this.tripTimelineSettingsFormGroup.get('locationSnapFilter').disable({emitEvent: false}); + } + } else { + this.tripTimelineSettingsFormGroup.disable({emitEvent: false}); + this.tripTimelineSettingsFormGroup.get('showTimelineControl').enable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.tripTimelineSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts index 9f36cc7076..6e8f1ea108 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source-data-key.component.ts @@ -29,7 +29,7 @@ import { AppState } from '@core/core.state'; import { IAliasController } from '@core/api/widget-api.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKey, Datasource, DatasourceType } from '@app/shared/models/widget.models'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { ValueSourceConfig, ValueSourceType, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index c73ef5eb57..045ce9a801 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -102,7 +102,7 @@ import { import { TimeSeriesChartThresholdRowComponent } from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component'; -import { DataKeyInputComponent } from '@home/components/widget/lib/settings/common/data-key-input.component'; +import { DataKeyInputComponent } from '@home/components/widget/lib/settings/common/key/data-key-input.component'; import { EntityAliasInputComponent } from '@home/components/widget/lib/settings/common/entity-alias-input.component'; import { TimeSeriesChartThresholdSettingsPanelComponent @@ -183,6 +183,71 @@ import { import { DynamicFormArrayComponent } from '@home/components/widget/lib/settings/common/dynamic-form/dynamic-form-array.component'; +import { MapSettingsComponent } from '@home/components/widget/lib/settings/common/map/map-settings.component'; +import { MapLayersComponent } from '@home/components/widget/lib/settings/common/map/map-layers.component'; +import { MapLayerRowComponent } from '@home/components/widget/lib/settings/common/map/map-layer-row.component'; +import { + MapLayerSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/map/map-layer-settings-panel.component'; +import { MapDataLayersComponent } from '@home/components/widget/lib/settings/common/map/map-data-layers.component'; +import { MapDataLayerRowComponent } from '@home/components/widget/lib/settings/common/map/map-data-layer-row.component'; +import { + EntityAliasSelectComponent +} from '@home/components/widget/lib/settings/common/alias/entity-alias-select.component'; +import { + MapDataLayerDialogComponent +} from '@home/components/widget/lib/settings/common/map/map-data-layer-dialog.component'; +import { FilterSelectComponent } from '@home/components/widget/lib/settings/common/filter/filter-select.component'; +import { DataKeysComponent } from '@home/components/widget/lib/settings/common/key/data-keys.component'; +import { + DataKeyConfigDialogComponent +} from '@home/components/widget/lib/settings/common/key/data-key-config-dialog.component'; +import { DataKeyConfigComponent } from '@home/components/widget/lib/settings/common/key/data-key-config.component'; +import { WidgetSettingsComponent } from '@home/components/widget/lib/settings/common/widget/widget-settings.component'; +import { + DataLayerColorSettingsComponent +} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings.component'; +import { + DataLayerColorSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/map/data-layer-color-settings-panel.component'; +import { + MarkerImageSettingsComponent +} from '@home/components/widget/lib/settings/common/map/marker-image-settings.component'; +import { + MarkerImageSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/map/marker-image-settings-panel.component'; +import { + DataLayerPatternSettingsComponent +} from '@home/components/widget/lib/settings/common/map/data-layer-pattern-settings.component'; +import { + MarkerShapeSettingsComponent +} from '@home/components/widget/lib/settings/common/map/marker-shape-settings.component'; +import { MarkerShapesComponent } from '@home/components/widget/lib/settings/common/map/marker-shapes.component'; +import { + MarkerClusteringSettingsComponent +} from '@home/components/widget/lib/settings/common/map/marker-clustering-settings.component'; +import { MapDataSourcesComponent } from '@home/components/widget/lib/settings/common/map/map-data-sources.component'; +import { + MapDataSourceRowComponent +} from '@home/components/widget/lib/settings/common/map/map-data-source-row.component'; +import { + ImageMapSourceSettingsComponent +} from '@home/components/widget/lib/settings/common/map/image-map-source-settings.component'; +import { + MapTooltipTagActionsComponent +} from '@home/components/widget/lib/settings/common/map/map-tooltip-tag-actions.component'; +import { + MapActionButtonsSettingsComponent +} from '@home/components/widget/lib/settings/common/map/map-action-buttons-settings.component'; +import { + MapActionButtonRowComponent +} from '@home/components/widget/lib/settings/common/map/map-action-button-row.component'; +import { + TripTimelineSettingsComponent +} from '@home/components/widget/lib/settings/common/map/trip-timeline-settings.component'; +import { + MarkerIconShapesComponent +} from '@home/components/widget/lib/settings/common/map/marker-icon-shapes.component'; @NgModule({ declarations: [ @@ -252,7 +317,36 @@ import { DynamicFormSelectItemsComponent, DynamicFormSelectItemRowComponent, DynamicFormComponent, - DynamicFormArrayComponent + DynamicFormArrayComponent, + MapLayerSettingsPanelComponent, + MapLayerRowComponent, + MapLayersComponent, + ImageMapSourceSettingsComponent, + DataLayerColorSettingsComponent, + DataLayerColorSettingsPanelComponent, + MapTooltipTagActionsComponent, + MapActionButtonsSettingsComponent, + MapActionButtonRowComponent, + DataLayerPatternSettingsComponent, + MarkerShapeSettingsComponent, + MarkerShapesComponent, + MarkerIconShapesComponent, + MarkerImageSettingsComponent, + MarkerImageSettingsPanelComponent, + MarkerClusteringSettingsComponent, + MapDataLayerDialogComponent, + MapDataLayerRowComponent, + MapDataLayersComponent, + MapDataSourceRowComponent, + MapDataSourcesComponent, + TripTimelineSettingsComponent, + MapSettingsComponent, + EntityAliasSelectComponent, + FilterSelectComponent, + DataKeysComponent, + DataKeyConfigDialogComponent, + DataKeyConfigComponent, + WidgetSettingsComponent ], imports: [ CommonModule, @@ -326,7 +420,14 @@ import { DynamicFormSelectItemsComponent, DynamicFormSelectItemRowComponent, DynamicFormComponent, - DynamicFormArrayComponent + DynamicFormArrayComponent, + MapSettingsComponent, + EntityAliasSelectComponent, + FilterSelectComponent, + DataKeysComponent, + DataKeyConfigDialogComponent, + DataKeyConfigComponent, + WidgetSettingsComponent ], providers: [ ColorSettingsComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.ts similarity index 96% rename from ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.ts index e6a7a39a1f..fc28c72399 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget/widget-settings.component.ts @@ -74,6 +74,9 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, @Input() callbacks: WidgetConfigCallbacks; + @Input() + functionsOnly: boolean; + @Input() dashboard: Dashboard; @@ -139,6 +142,11 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, this.definedSettingsComponent.dataKeyCallbacks = this.callbacks; } } + if (propName === 'functionsOnly') { + if (this.definedSettingsComponent) { + this.definedSettingsComponent.functionsOnly = this.functionsOnly; + } + } if (propName === 'widgetConfig') { if (this.definedSettingsComponent) { this.definedSettingsComponent.widgetConfig = this.widgetConfig; @@ -228,6 +236,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnDestroy, this.definedSettingsComponent = this.definedSettingsComponentRef.instance; this.definedSettingsComponent.aliasController = this.aliasController; this.definedSettingsComponent.callbacks = this.callbacks; + this.definedSettingsComponent.functionsOnly = this.functionsOnly; this.definedSettingsComponent.dataKeyCallbacks = this.callbacks; this.definedSettingsComponent.dashboard = this.dashboard; this.definedSettingsComponent.widget = this.widget; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.html index 7db75f9968..8c12c81d60 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/device-key-autocomplete.component.html @@ -15,11 +15,10 @@ limitations under the License. --> - {{ (keyType === dataKeyType.attribute ? attributeLabel diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.html index 3a8f80ff2d..b3e0f21b83 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.html @@ -43,15 +43,60 @@ {{ 'widgets.persistent-table.allow-delete-request' | translate }} -
- +
+
widgets.table.pagination
+ {{ 'widgets.table.display-pagination' | translate }} - - widgets.table.default-page-size - - -
+
+
{{ 'widgets.table.page-step-settings' | translate }}
+
+
widgets.table.page-step-increment
+ + + + warning + + + +
widgets.table.page-step-count
+ + + + warning + + +
+
+
+
widgets.table.default-page-size
+ + + + {{ size }} + + + +
+
widgets.table.default-sort-order diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts index 6281332a12..e1cb4f12f4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts @@ -27,6 +27,7 @@ import { MatChipInputEvent, MatChipGrid } from '@angular/material/chips'; import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { map, mergeMap, share, startWith } from 'rxjs/operators'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widget.models'; interface DisplayColumn { name: string; @@ -57,6 +58,8 @@ export class PersistentTableWidgetSettingsComponent extends WidgetSettingsCompon persistentTableWidgetSettingsForm: UntypedFormGroup; + pageStepSizeValues = []; + filteredDisplayColumns: Observable>; columnSearchText = ''; @@ -94,6 +97,8 @@ export class PersistentTableWidgetSettingsComponent extends WidgetSettingsCompon displayPagination: true, defaultPageSize: 10, + pageStepIncrement: null, + pageStepCount: 3, defaultSortOrder: '-createdTime', displayColumns: ['rpcId', 'messageType', 'status', 'method', 'createdTime', 'expirationTime'] @@ -110,9 +115,15 @@ export class PersistentTableWidgetSettingsComponent extends WidgetSettingsCompon displayDetails: [settings.displayDetails, []], displayPagination: [settings.displayPagination, []], defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], + pageStepCount: [settings.pageStepCount ?? 3, [Validators.min(1), Validators.max(100), + Validators.required, Validators.pattern(/^\d*$/)]], + pageStepIncrement: [settings.pageStepIncrement ?? settings.defaultPageSize, + [Validators.min(1), Validators.required, Validators.pattern(/^\d*$/)]], defaultSortOrder: [settings.defaultSortOrder, []], displayColumns: [settings.displayColumns, [Validators.required]] }); + this.pageStepSizeValues = buildPageStepSizeValues(this.persistentTableWidgetSettingsForm.get('pageStepCount').value, + this.persistentTableWidgetSettingsForm.get('pageStepIncrement').value); } public validateSettings(): boolean { @@ -122,17 +133,26 @@ export class PersistentTableWidgetSettingsComponent extends WidgetSettingsCompon } protected validatorTriggers(): string[] { - return ['displayPagination']; + return ['displayPagination', 'pageStepCount', 'pageStepIncrement']; } - protected updateValidators(emitEvent: boolean) { + protected updateValidators(emitEvent: boolean, trigger: string) { + if (trigger === 'pageStepCount' || trigger === 'pageStepIncrement') { + this.persistentTableWidgetSettingsForm.get('defaultPageSize').reset(); + this.pageStepSizeValues = buildPageStepSizeValues(this.persistentTableWidgetSettingsForm.get('pageStepCount').value, + this.persistentTableWidgetSettingsForm.get('pageStepIncrement').value); + return; + } const displayPagination: boolean = this.persistentTableWidgetSettingsForm.get('displayPagination').value; if (displayPagination) { - this.persistentTableWidgetSettingsForm.get('defaultPageSize').enable(); + this.persistentTableWidgetSettingsForm.get('defaultPageSize').enable({emitEvent}); + this.persistentTableWidgetSettingsForm.get('pageStepCount').enable({emitEvent: false}); + this.persistentTableWidgetSettingsForm.get('pageStepIncrement').enable({emitEvent: false}); } else { - this.persistentTableWidgetSettingsForm.get('defaultPageSize').disable(); + this.persistentTableWidgetSettingsForm.get('defaultPageSize').disable({emitEvent}); + this.persistentTableWidgetSettingsForm.get('pageStepCount').disable({emitEvent: false}); + this.persistentTableWidgetSettingsForm.get('pageStepIncrement').disable({emitEvent: false}); } - this.persistentTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); } private fetchColumns(searchText?: string): Observable> { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.html index 76aa5fd2f4..14383b9608 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.html @@ -59,6 +59,11 @@
+
+ + {{ 'widgets.table.disable-sorting' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.ts index 8572a9e348..89393ade34 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.ts @@ -47,7 +47,8 @@ export class EntitiesTableKeySettingsComponent extends WidgetSettingsComponent { useCellContentFunction: false, cellContentFunction: '', defaultColumnVisibility: 'visible', - columnSelectionToDisplay: 'enabled' + columnSelectionToDisplay: 'enabled', + disableSorting: false }; } @@ -61,6 +62,7 @@ export class EntitiesTableKeySettingsComponent extends WidgetSettingsComponent { cellContentFunction: [settings.cellContentFunction, [Validators.required]], defaultColumnVisibility: [settings.defaultColumnVisibility, []], columnSelectionToDisplay: [settings.columnSelectionToDisplay, []], + disableSorting: [settings.disableSorting, []] }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.html index 62ee969a52..b0be9fca0f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.html @@ -91,10 +91,52 @@ {{ 'widgets.table.display-pagination' | translate }} +
+
{{ 'widgets.table.page-step-settings' | translate }}
+
+
widgets.table.page-step-increment
+ + + + warning + + + +
widgets.table.page-step-count
+ + + + warning + + +
+
widgets.table.default-page-size
- - + + + + {{ size }} + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.ts index 9bb6b8c78a..c1bc327c3c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.ts @@ -19,6 +19,7 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; +import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widget.models'; @Component({ selector: 'tb-entities-table-widget-settings', @@ -28,6 +29,7 @@ import { AppState } from '@core/core.state'; export class EntitiesTableWidgetSettingsComponent extends WidgetSettingsComponent { entitiesTableWidgetSettingsForm: UntypedFormGroup; + pageStepSizeValues = []; constructor(protected store: Store, private fb: UntypedFormBuilder) { @@ -54,6 +56,8 @@ export class EntitiesTableWidgetSettingsComponent extends WidgetSettingsComponen displayEntityType: true, displayPagination: true, defaultPageSize: 10, + pageStepIncrement: null, + pageStepCount: 3, defaultSortOrder: 'entityName', useRowStyleFunction: false, rowStyleFunction: '' @@ -76,45 +80,58 @@ export class EntitiesTableWidgetSettingsComponent extends WidgetSettingsComponen displayEntityType: [settings.displayEntityType, []], displayPagination: [settings.displayPagination, []], defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], + pageStepCount: [settings.pageStepCount ?? 3, [Validators.min(1), Validators.max(100), + Validators.required, Validators.pattern(/^\d*$/)]], + pageStepIncrement: [settings.pageStepIncrement ?? settings.defaultPageSize, + [Validators.min(1), Validators.required, Validators.pattern(/^\d*$/)]], defaultSortOrder: [settings.defaultSortOrder, []], useRowStyleFunction: [settings.useRowStyleFunction, []], rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] }); + this.pageStepSizeValues = buildPageStepSizeValues(this.entitiesTableWidgetSettingsForm.get('pageStepCount').value, + this.entitiesTableWidgetSettingsForm.get('pageStepIncrement').value); } protected validatorTriggers(): string[] { - return ['useRowStyleFunction', 'displayPagination', 'displayEntityName', 'displayEntityLabel']; + return ['useRowStyleFunction', 'displayPagination', 'displayEntityName', 'displayEntityLabel', 'pageStepCount', + 'pageStepIncrement']; } - protected updateValidators(emitEvent: boolean) { + protected updateValidators(emitEvent: boolean, trigger: string) { + if (trigger === 'pageStepCount' || trigger === 'pageStepIncrement') { + this.entitiesTableWidgetSettingsForm.get('defaultPageSize').reset(); + this.pageStepSizeValues = buildPageStepSizeValues(this.entitiesTableWidgetSettingsForm.get('pageStepCount').value, + this.entitiesTableWidgetSettingsForm.get('pageStepIncrement').value); + return; + } const useRowStyleFunction: boolean = this.entitiesTableWidgetSettingsForm.get('useRowStyleFunction').value; const displayPagination: boolean = this.entitiesTableWidgetSettingsForm.get('displayPagination').value; const displayEntityName: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityName').value; const displayEntityLabel: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityLabel').value; if (useRowStyleFunction) { - this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').enable(); + this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').enable({emitEvent}); } else { - this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').disable(); + this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').disable({emitEvent}); } if (displayPagination) { - this.entitiesTableWidgetSettingsForm.get('defaultPageSize').enable(); + this.entitiesTableWidgetSettingsForm.get('defaultPageSize').enable({emitEvent}); + this.entitiesTableWidgetSettingsForm.get('pageStepCount').enable({emitEvent: false}); + this.entitiesTableWidgetSettingsForm.get('pageStepIncrement').enable({emitEvent: false}); } else { - this.entitiesTableWidgetSettingsForm.get('defaultPageSize').disable(); + this.entitiesTableWidgetSettingsForm.get('defaultPageSize').disable({emitEvent}); + this.entitiesTableWidgetSettingsForm.get('pageStepCount').disable({emitEvent: false}); + this.entitiesTableWidgetSettingsForm.get('pageStepIncrement').disable({emitEvent: false}); } if (displayEntityName) { - this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').enable(); + this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').enable({emitEvent}); } else { - this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').disable(); + this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').disable({emitEvent}); } if (displayEntityLabel) { - this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').enable(); + this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').enable({emitEvent}); } else { - this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').disable(); + this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').disable({emitEvent}); } - this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').updateValueAndValidity({emitEvent}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts index 31b5fb63f1..dd9ca73b7a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts @@ -22,7 +22,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { IAliasController } from '@core/api/widget-api.models'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { Datasource } from '@shared/models/widget.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/circle-settings.component.html similarity index 97% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/circle-settings.component.html index 49605f4ced..54f9bf4788 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/circle-settings.component.html @@ -66,7 +66,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.circle-label-function' | translate }}" - helpId="widget/lib/map/label_fn"> + helpId="widget/lib/map-legacy/label_fn"> @@ -110,7 +110,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.circle-tooltip-function' | translate }}" - helpId="widget/lib/map/polygon_tooltip_fn"> + helpId="widget/lib/map-legacy/polygon_tooltip_fn"> @@ -146,7 +146,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.circle-fill-color-function' | translate }}" - helpId="widget/lib/map/polygon_color_fn"> + helpId="widget/lib/map-legacy/polygon_color_fn"> @@ -186,7 +186,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.circle-stroke-color-function' | translate }}" - helpId="widget/lib/map/polygon_color_fn"> + helpId="widget/lib/map-legacy/polygon_color_fn"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/circle-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/circle-settings.component.ts index e4e41bf79c..c8761736bc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/circle-settings.component.ts @@ -33,7 +33,7 @@ import { CircleSettings, ShowTooltipAction, showTooltipActionTranslationMap -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { WidgetService } from '@core/http/widget.service'; import { Widget } from '@shared/models/widget.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -41,7 +41,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-circle-settings', templateUrl: './circle-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/common-map-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/common-map-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/common-map-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/common-map-settings.component.ts index 6bd2e937d2..f777fec3b9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/common-map-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/common-map-settings.component.ts @@ -29,14 +29,14 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; -import { CommonMapSettings, MapProviders } from '@home/components/widget/lib/maps/map-models'; +import { CommonMapSettings, MapProviders } from '@home/components/widget/lib/maps-legacy/map-models'; import { Widget } from '@shared/models/widget.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-common-map-settings', templateUrl: './common-map-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/datasources-key-autocomplete.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/datasources-key-autocomplete.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/datasources-key-autocomplete.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/datasources-key-autocomplete.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/datasources-key-autocomplete.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/google-map-provider-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/google-map-provider-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/google-map-provider-settings.component.ts similarity index 97% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/google-map-provider-settings.component.ts index b7d3a78514..d1b3cf2189 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/google-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/google-map-provider-settings.component.ts @@ -33,13 +33,13 @@ import { GoogleMapProviderSettings, GoogleMapType, googleMapTypeProviderTranslationMap -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-google-map-provider-settings', templateUrl: './google-map-provider-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/here-map-provider-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/here-map-provider-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/here-map-provider-settings.component.ts similarity index 97% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/here-map-provider-settings.component.ts index febcaefad8..5fa9a01342 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/here-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/here-map-provider-settings.component.ts @@ -33,14 +33,14 @@ import { HereMapProvider, HereMapProviderSettings, hereMapProviderTranslationMap -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { isDefinedAndNotNull } from '@core/utils'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-here-map-provider-settings', templateUrl: './here-map-provider-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/image-map-provider-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/image-map-provider-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/image-map-provider-settings.component.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/image-map-provider-settings.component.ts index 6ffe958c0a..747a339297 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/image-map-provider-settings.component.ts @@ -28,7 +28,7 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; -import { ImageMapProviderSettings } from '@home/components/widget/lib/maps/map-models'; +import { ImageMapProviderSettings } from '@home/components/widget/lib/maps-legacy/map-models'; import { IAliasController } from '@core/api/widget-api.models'; import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap, publishReplay, refCount, startWith, tap } from 'rxjs/operators'; @@ -40,7 +40,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-image-map-provider-settings', templateUrl: './image-map-provider-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-editor-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-editor-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-editor-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-editor-settings.component.ts index 815768e1ac..b63f3d68c1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-editor-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-editor-settings.component.ts @@ -28,14 +28,14 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; -import { MapEditorSettings } from '@home/components/widget/lib/maps/map-models'; +import { MapEditorSettings } from '@home/components/widget/lib/maps-legacy/map-models'; import { WidgetService } from '@core/http/widget.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-map-editor-settings', templateUrl: './map-editor-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-provider-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-provider-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-provider-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-provider-settings.component.ts index d7cec5f898..23cbec5d51 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-provider-settings.component.ts @@ -41,7 +41,7 @@ import { mapProviderTranslationMap, OpenStreetMapProviderSettings, TencentMapProviderSettings -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { extractType } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -49,7 +49,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-map-provider-settings', templateUrl: './map-provider-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-settings-legacy.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-settings-legacy.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-settings-legacy.component.ts similarity index 94% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-settings-legacy.component.ts index 961423a1ba..8af3d53e00 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-settings-legacy.component.ts @@ -46,30 +46,30 @@ import { PolygonSettings, PolylineSettings, UnitedMapSettings -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { extractType } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; import { Widget } from '@shared/models/widget.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ - selector: 'tb-map-settings', - templateUrl: './map-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + selector: 'tb-map-settings-legacy', + templateUrl: './map-settings-legacy.component.html', + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => MapSettingsComponent), + useExisting: forwardRef(() => MapSettingsLegacyComponent), multi: true }, { provide: NG_VALIDATORS, - useExisting: forwardRef(() => MapSettingsComponent), + useExisting: forwardRef(() => MapSettingsLegacyComponent), multi: true } ] }) -export class MapSettingsComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator { +export class MapSettingsLegacyComponent extends PageComponent implements OnInit, ControlValueAccessor, Validator { @Input() disabled: boolean; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-widget-settings-legacy.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-widget-settings-legacy.component.html new file mode 100644 index 0000000000..717cc48073 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-widget-settings-legacy.component.html @@ -0,0 +1,24 @@ + +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-widget-settings-legacy.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-widget-settings-legacy.component.ts new file mode 100644 index 0000000000..8089313f18 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/map-widget-settings-legacy.component.ts @@ -0,0 +1,63 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps-legacy/map-models'; + +@Component({ + selector: 'tb-map-widget-settings-legacy', + templateUrl: './map-widget-settings-legacy.component.html', + styleUrls: ['./../../widget-settings.scss'] +}) +export class MapWidgetSettingsLegacyComponent extends WidgetSettingsComponent { + + mapWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.mapWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return { + ...defaultMapSettings + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.mapWidgetSettingsForm = this.fb.group({ + mapSettings: [settings.mapSettings, []] + }); + } + + protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { + return { + mapSettings: settings + }; + } + + protected prepareOutputSettings(settings: any): WidgetSettings { + return settings.mapSettings; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/marker-clustering-settings.component.html similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/marker-clustering-settings.component.html index e3201cb4c5..21b4f19e8d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/marker-clustering-settings.component.html @@ -82,7 +82,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'childCount']" functionTitle="{{ 'widgets.maps.marker-color-function' | translate }}" - helpId="widget/lib/map/clustering_color_fn"> + helpId="widget/lib/map-legacy/clustering_color_fn"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/marker-clustering-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/marker-clustering-settings.component.ts index a467ad793d..e317a21ad9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/marker-clustering-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/marker-clustering-settings.component.ts @@ -29,14 +29,14 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; -import { MarkerClusteringSettings } from '@home/components/widget/lib/maps/map-models'; +import { MarkerClusteringSettings } from '@home/components/widget/lib/maps-legacy/map-models'; import { WidgetService } from '@core/http/widget.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-marker-clustering-settings', templateUrl: './marker-clustering-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/markers-settings.component.html similarity index 96% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/markers-settings.component.html index 99b4f0fa29..4111a22987 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/markers-settings.component.html @@ -35,7 +35,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']" functionTitle="{{ 'widgets.maps.position-function' | translate }}" - helpId="widget/lib/map/position_fn"> + helpId="widget/lib/map-legacy/position_fn"> {{ 'widgets.maps.draggable-marker' | translate }} @@ -68,7 +68,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.label-function' | translate }}" - helpId="widget/lib/map/label_fn"> + helpId="widget/lib/map-legacy/label_fn"> @@ -112,7 +112,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}" - helpId="widget/lib/map/tooltip_fn"> + helpId="widget/lib/map-legacy/tooltip_fn">
@@ -151,7 +151,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.color-function' | translate }}" - helpId="widget/lib/map/color_fn"> + helpId="widget/lib/map-legacy/color_fn"> @@ -185,7 +185,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'images', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}" - helpId="widget/lib/map/marker_image_fn"> + helpId="widget/lib/map-legacy/marker_image_fn"> + helpId="widget/lib/map-legacy/label_fn"> @@ -110,7 +110,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.polygon-tooltip-function' | translate }}" - helpId="widget/lib/map/polygon_tooltip_fn"> + helpId="widget/lib/map-legacy/polygon_tooltip_fn"> @@ -146,7 +146,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.polygon-color-function' | translate }}" - helpId="widget/lib/map/polygon_color_fn"> + helpId="widget/lib/map-legacy/polygon_color_fn"> @@ -186,7 +186,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.polygon-stroke-color-function' | translate }}" - helpId="widget/lib/map/polygon_color_fn"> + helpId="widget/lib/map-legacy/polygon_color_fn"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/polygon-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/polygon-settings.component.ts index f68e121393..62e80e0eec 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/polygon-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/polygon-settings.component.ts @@ -33,7 +33,7 @@ import { PolygonSettings, ShowTooltipAction, showTooltipActionTranslationMap -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { WidgetService } from '@core/http/widget.service'; import { Widget } from '@shared/models/widget.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -41,7 +41,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-polygon-settings', templateUrl: './polygon-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-settings.component.ts index 1d8ca0454b..322eb0837e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-settings.component.ts @@ -28,14 +28,14 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; -import { PolylineSettings } from '@home/components/widget/lib/maps/map-models'; +import { PolylineSettings } from '@home/components/widget/lib/maps-legacy/map-models'; import { WidgetService } from '@core/http/widget.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-route-map-settings', templateUrl: './route-map-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-widget-settings.component.html similarity index 92% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-widget-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-widget-settings.component.html index 206933cb91..a6e4dc9293 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-widget-settings.component.html @@ -16,9 +16,9 @@ -->
- - +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-widget-settings.component.ts similarity index 95% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-widget-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-widget-settings.component.ts index 545a0d9c98..0819253feb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/route-map-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/route-map-widget-settings.component.ts @@ -19,12 +19,12 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps/map-models'; +import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps-legacy/map-models'; @Component({ selector: 'tb-route-map-widget-settings', templateUrl: './route-map-widget-settings.component.html', - styleUrls: ['./../widget-settings.scss'] + styleUrls: ['./../../widget-settings.scss'] }) export class RouteMapWidgetSettingsComponent extends WidgetSettingsComponent { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/tencent-map-provider-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/tencent-map-provider-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/tencent-map-provider-settings.component.ts similarity index 97% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/tencent-map-provider-settings.component.ts index ccde0cdafc..ee47caa385 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/tencent-map-provider-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/tencent-map-provider-settings.component.ts @@ -33,13 +33,13 @@ import { TencentMapProviderSettings, TencentMapType, tencentMapTypeProviderTranslationMap -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-tencent-map-provider-settings', templateUrl: './tencent-map-provider-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-common-settings.component.html similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-common-settings.component.html index 1ac03e042a..59393ef944 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-common-settings.component.html @@ -85,7 +85,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.tooltip-function' | translate }}" - helpId="widget/lib/map/tooltip_fn"> + helpId="widget/lib/map-legacy/tooltip_fn"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-common-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-common-settings.component.ts index 1a38c1e041..018b051080 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-common-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-common-settings.component.ts @@ -29,7 +29,7 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; -import { TripAnimationCommonSettings } from '@home/components/widget/lib/maps/map-models'; +import { TripAnimationCommonSettings } from '@home/components/widget/lib/maps-legacy/map-models'; import { Widget } from '@shared/models/widget.models'; import { WidgetService } from '@core/http/widget.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -37,7 +37,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trip-animation-common-settings', templateUrl: './trip-animation-common-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-marker-settings.component.html similarity index 97% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-marker-settings.component.html index b869d12fb8..dfb2dd8ce5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-marker-settings.component.html @@ -50,7 +50,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.label-function' | translate }}" - helpId="widget/lib/map/label_fn"> + helpId="widget/lib/map-legacy/label_fn"> @@ -84,7 +84,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'images', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}" - helpId="widget/lib/map/marker_image_fn"> + helpId="widget/lib/map-legacy/marker_image_fn"> + helpId="widget/lib/map-legacy/path_color_fn"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-path-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-path-settings.component.ts index f540c2cd21..8936d9b1aa 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-path-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-path-settings.component.ts @@ -32,14 +32,14 @@ import { PolylineDecoratorSymbol, polylineDecoratorSymbolTranslationMap, PolylineSettings -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { WidgetService } from '@core/http/widget.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trip-animation-path-settings', templateUrl: './trip-animation-path-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-point-settings.component.html similarity index 96% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-point-settings.component.html index ea7b2d77ca..d98a88f6f1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-point-settings.component.html @@ -59,7 +59,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.point-color-function' | translate }}" - helpId="widget/lib/map/path_point_color_fn"> + helpId="widget/lib/map-legacy/path_point_color_fn"> @@ -80,7 +80,7 @@ [globalVariables]="functionScopeVariables" [functionArgs]="['data', 'dsData', 'dsIndex']" functionTitle="{{ 'widgets.maps.point-as-anchor-function' | translate }}" - helpId="widget/lib/map/trip_point_as_anchor_fn"> + helpId="widget/lib/map-legacy/trip_point_as_anchor_fn"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-point-settings.component.ts similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-point-settings.component.ts index 02d8601bb6..fe0fc91ba5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-point-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-point-settings.component.ts @@ -33,14 +33,14 @@ import { PolylineDecoratorSymbol, polylineDecoratorSymbolTranslationMap, PolylineSettings -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { WidgetService } from '@core/http/widget.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'tb-trip-animation-point-settings', templateUrl: './trip-animation-point-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-widget-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-widget-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-widget-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-widget-settings.component.ts similarity index 97% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-widget-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-widget-settings.component.ts index c33ebc6be7..85be370ada 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/legacy/trip-animation-widget-settings.component.ts @@ -35,13 +35,13 @@ import { PolylineSettings, TripAnimationCommonSettings, TripAnimationMarkerSettings -} from 'src/app/modules/home/components/widget/lib/maps/map-models'; +} from 'src/app/modules/home/components/widget/lib/maps-legacy/map-models'; import { extractType } from '@core/utils'; @Component({ selector: 'tb-trip-animation-widget-settings', templateUrl: './trip-animation-widget-settings.component.html', - styleUrls: ['./../widget-settings.scss'] + styleUrls: ['./../../widget-settings.scss'] }) export class TripAnimationWidgetSettingsComponent extends WidgetSettingsComponent { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.html index 0065c0d0a1..f58e1d65ab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.html @@ -15,10 +15,27 @@ limitations under the License. --> -
- + + -
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.ts index f5bb9b64d0..221f820d68 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-widget-settings.component.ts @@ -19,7 +19,9 @@ import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.m import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { defaultMapSettings } from 'src/app/modules/home/components/widget/lib/maps/map-models'; +import { isDefinedAndNotNull, mergeDeepIgnoreArray } from '@core/utils'; +import { mapWidgetDefaultSettings, MapWidgetSettings } from '@home/components/widget/lib/maps/map-widget.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; @Component({ selector: 'tb-map-widget-settings', @@ -30,6 +32,8 @@ export class MapWidgetSettingsComponent extends WidgetSettingsComponent { mapWidgetSettingsForm: UntypedFormGroup; + trip = false; + constructor(protected store: Store, private fb: UntypedFormBuilder) { super(store); @@ -39,25 +43,26 @@ export class MapWidgetSettingsComponent extends WidgetSettingsComponent { return this.mapWidgetSettingsForm; } + protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + if (isDefinedAndNotNull(params.trip)) { + this.trip = params.trip === true; + } + } + protected defaultSettings(): WidgetSettings { - return { - ...defaultMapSettings - }; + return mergeDeepIgnoreArray({} as MapWidgetSettings, mapWidgetDefaultSettings); } protected onSettingsSet(settings: WidgetSettings) { this.mapWidgetSettingsForm = this.fb.group({ - mapSettings: [settings.mapSettings, []] + mapSettings: [settings, []], + background: [settings.background, []], + padding: [settings.padding, []] }); } - protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { - return { - mapSettings: settings - }; - } - protected prepareOutputSettings(settings: any): WidgetSettings { - return settings.mapSettings; + return {...settings.mapSettings, background: settings.background, padding: settings.padding}; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index dfc606a380..fbf9b2eec2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -210,52 +210,52 @@ import { } from '@home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component'; import { OpenStreetMapProviderSettingsComponent -} from '@home/components/widget/lib/settings/map/openstreet-map-provider-settings.component'; -import { MapProviderSettingsComponent } from '@home/components/widget/lib/settings/map/map-provider-settings.component'; -import { MapSettingsComponent } from '@home/components/widget/lib/settings/map/map-settings.component'; -import { MapWidgetSettingsComponent } from '@home/components/widget/lib/settings/map/map-widget-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/openstreet-map-provider-settings.component'; +import { MapProviderSettingsComponent } from '@home/components/widget/lib/settings/map/legacy/map-provider-settings.component'; +import { MapSettingsLegacyComponent } from '@home/components/widget/lib/settings/map/legacy/map-settings-legacy.component'; +import { MapWidgetSettingsLegacyComponent } from '@home/components/widget/lib/settings/map/legacy/map-widget-settings-legacy.component'; import { GoogleMapProviderSettingsComponent -} from '@home/components/widget/lib/settings/map/google-map-provider-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/google-map-provider-settings.component'; import { HereMapProviderSettingsComponent -} from '@home/components/widget/lib/settings/map/here-map-provider-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/here-map-provider-settings.component'; import { TencentMapProviderSettingsComponent -} from '@home/components/widget/lib/settings/map/tencent-map-provider-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/tencent-map-provider-settings.component'; import { ImageMapProviderSettingsComponent -} from '@home/components/widget/lib/settings/map/image-map-provider-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/image-map-provider-settings.component'; import { DatasourcesKeyAutocompleteComponent -} from '@home/components/widget/lib/settings/map/datasources-key-autocomplete.component'; -import { CommonMapSettingsComponent } from '@home/components/widget/lib/settings/map/common-map-settings.component'; -import { MarkersSettingsComponent } from '@home/components/widget/lib/settings/map/markers-settings.component'; -import { PolygonSettingsComponent } from '@home/components/widget/lib/settings/map/polygon-settings.component'; -import { CircleSettingsComponent } from '@home/components/widget/lib/settings/map/circle-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/datasources-key-autocomplete.component'; +import { CommonMapSettingsComponent } from '@home/components/widget/lib/settings/map/legacy/common-map-settings.component'; +import { MarkersSettingsComponent } from '@home/components/widget/lib/settings/map/legacy/markers-settings.component'; +import { PolygonSettingsComponent } from '@home/components/widget/lib/settings/map/legacy/polygon-settings.component'; +import { CircleSettingsComponent } from '@home/components/widget/lib/settings/map/legacy/circle-settings.component'; import { MarkerClusteringSettingsComponent -} from '@home/components/widget/lib/settings/map/marker-clustering-settings.component'; -import { MapEditorSettingsComponent } from '@home/components/widget/lib/settings/map/map-editor-settings.component'; -import { RouteMapSettingsComponent } from '@home/components/widget/lib/settings/map/route-map-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/marker-clustering-settings.component'; +import { MapEditorSettingsComponent } from '@home/components/widget/lib/settings/map/legacy/map-editor-settings.component'; +import { RouteMapSettingsComponent } from '@home/components/widget/lib/settings/map/legacy/route-map-settings.component'; import { RouteMapWidgetSettingsComponent -} from '@home/components/widget/lib/settings/map/route-map-widget-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/route-map-widget-settings.component'; import { TripAnimationWidgetSettingsComponent -} from '@home/components/widget/lib/settings/map/trip-animation-widget-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/trip-animation-widget-settings.component'; import { TripAnimationCommonSettingsComponent -} from '@home/components/widget/lib/settings/map/trip-animation-common-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/trip-animation-common-settings.component'; import { TripAnimationMarkerSettingsComponent -} from '@home/components/widget/lib/settings/map/trip-animation-marker-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/trip-animation-marker-settings.component'; import { TripAnimationPathSettingsComponent -} from '@home/components/widget/lib/settings/map/trip-animation-path-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/trip-animation-path-settings.component'; import { TripAnimationPointSettingsComponent -} from '@home/components/widget/lib/settings/map/trip-animation-point-settings.component'; +} from '@home/components/widget/lib/settings/map/legacy/trip-animation-point-settings.component'; import { GatewayLogsSettingsComponent } from '@home/components/widget/lib/settings/gateway/gateway-logs-settings.component'; @@ -374,6 +374,7 @@ import { import { ValueStepperWidgetSettingsComponent } from '@home/components/widget/lib/settings/control/value-stepper-widget-settings.component'; +import { MapWidgetSettingsComponent } from '@home/components/widget/lib/settings/map/map-widget-settings.component'; @NgModule({ declarations: [ @@ -461,12 +462,12 @@ import { MarkerClusteringSettingsComponent, MapEditorSettingsComponent, RouteMapSettingsComponent, - MapSettingsComponent, + MapSettingsLegacyComponent, TripAnimationCommonSettingsComponent, TripAnimationMarkerSettingsComponent, TripAnimationPathSettingsComponent, TripAnimationPointSettingsComponent, - MapWidgetSettingsComponent, + MapWidgetSettingsLegacyComponent, RouteMapWidgetSettingsComponent, GatewayLogsSettingsComponent, GatewayServiceRPCSettingsComponent, @@ -506,7 +507,8 @@ import { LabelCardWidgetSettingsComponent, LabelValueCardWidgetSettingsComponent, UnreadNotificationWidgetSettingsComponent, - ScadaSymbolWidgetSettingsComponent + ScadaSymbolWidgetSettingsComponent, + MapWidgetSettingsComponent ], imports: [ CommonModule, @@ -599,12 +601,12 @@ import { MarkerClusteringSettingsComponent, MapEditorSettingsComponent, RouteMapSettingsComponent, - MapSettingsComponent, + MapSettingsLegacyComponent, TripAnimationCommonSettingsComponent, TripAnimationMarkerSettingsComponent, TripAnimationPathSettingsComponent, TripAnimationPointSettingsComponent, - MapWidgetSettingsComponent, + MapWidgetSettingsLegacyComponent, RouteMapWidgetSettingsComponent, GatewayLogsSettingsComponent, GatewayServiceRPCSettingsComponent, @@ -644,7 +646,8 @@ import { LabelCardWidgetSettingsComponent, LabelValueCardWidgetSettingsComponent, UnreadNotificationWidgetSettingsComponent, - ScadaSymbolWidgetSettingsComponent + ScadaSymbolWidgetSettingsComponent, + MapWidgetSettingsComponent ] }) export class WidgetSettingsModule { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts index 47ae0b8cc8..8aadc62ee4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts @@ -16,7 +16,7 @@ import { EntityId } from '@shared/models/id/entity-id'; import { DataKey, FormattedData, WidgetActionDescriptor, WidgetConfig } from '@shared/models/widget.models'; -import { getDescendantProp, isDefined, isNotEmptyStr } from '@core/utils'; +import { getDescendantProp, isDefined, isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; import { AlarmDataInfo, alarmFields } from '@shared/models/alarm.models'; import tinycolor from 'tinycolor2'; import { Direction } from '@shared/models/page/sort-order'; @@ -47,6 +47,8 @@ export interface TableWidgetSettings { enableStickyHeader: boolean; displayPagination: boolean; defaultPageSize: number; + pageStepIncrement: number; + pageStepCount: number; useRowStyleFunction: boolean; rowStyleFunction?: TbFunction; reserveSpaceForHiddenAction?: boolean; @@ -61,6 +63,7 @@ export interface TableWidgetDataKeySettings { cellContentFunction?: TbFunction; defaultColumnVisibility?: ColumnVisibilityOptions; columnSelectionToDisplay?: ColumnSelectionOptions; + disableSorting?: boolean; } export type ShowCellButtonActionFunction = (ctx: WidgetContext, data: EntityData | AlarmDataInfo | FormattedData) => boolean; @@ -145,7 +148,7 @@ export function entityDataSortOrderFromString(strSortOrder: string, columns: Ent if (!column) { column = findColumnByName(property, columns); } - if (column && column.entityKey) { + if (column && column.entityKey && column.sortable) { return {key: column.entityKey, direction}; } return null; @@ -558,3 +561,14 @@ export function getHeaderTitle(dataKey: DataKey, keySettings: TableWidgetDataKey } return dataKey.label; } + +export function buildPageStepSizeValues(pageStepCount: number, pageStepIncrement: number): Array { + const pageSteps: Array = []; + if (isDefinedAndNotNull(pageStepCount) && pageStepCount > 0 && pageStepCount <= 100 && + isDefinedAndNotNull(pageStepIncrement) && pageStepIncrement > 0) { + for (let i = 1; i <= pageStepCount; i++) { + pageSteps.push(pageStepIncrement * i); + } + } + return pageSteps; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index 0fda3b9afe..9544947060 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -173,7 +173,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI public enableStickyHeader = true; public enableStickyAction = true; public showCellActionsMenu = true; - public pageSizeOptions; + public pageSizeOptions = []; public textSearchMode = false; public hidePageSize = false; public sources: TimeseriesTableSource[]; @@ -192,7 +192,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI private latestData: Array; private datasources: Array; - private defaultPageSize = 10; + private defaultPageSize; private defaultSortOrder = '-0'; private hideEmptyLines = false; public showTimestamp = true; @@ -352,10 +352,25 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI this.rowStylesInfo = getRowStyleInfo(this.ctx, this.settings, 'rowData, ctx'); const pageSize = this.settings.defaultPageSize; + let pageStepIncrement = this.settings.pageStepIncrement; + let pageStepCount = this.settings.pageStepCount; + if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { this.defaultPageSize = pageSize; } - this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + + if (!this.defaultPageSize) { + this.defaultPageSize = pageStepIncrement ?? 10; + } + + if (!isDefinedAndNotNull(pageStepIncrement) || !isDefinedAndNotNull(pageStepCount)) { + pageStepIncrement = this.defaultPageSize; + pageStepCount = 3; + } + + for (let i = 1; i <= pageStepCount; i++) { + this.pageSizeOptions.push(pageStepIncrement * i); + } this.noDataDisplayMessageText = noDataMessage(this.widgetConfig.noDataDisplayMessage, 'widget.no-data-found', this.utils, this.translate); @@ -518,8 +533,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI const latestDataKeys = datasource.latestDataKeys; let header: TimeseriesHeader[] = []; dataKeys.forEach((dataKey, index) => { - const sortable = !dataKey.usePostProcessing; const keySettings: TableWidgetDataKeySettings = dataKey.settings; + const sortable = !keySettings.disableSorting && !dataKey.usePostProcessing; const styleInfo = getCellStyleInfo(this.ctx, keySettings, 'value, rowData, ctx'); const contentFunctionInfo = getCellContentFunctionInfo(this.ctx, keySettings, 'value, rowData, ctx'); const columnDefaultVisibility = getColumnDefaultVisibility(keySettings, this.ctx); @@ -544,8 +559,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI if (latestDataKeys) { latestDataKeys.forEach((dataKey, latestIndex) => { const index = dataKeys.length + latestIndex; - const sortable = !dataKey.usePostProcessing; const keySettings: TimeseriesWidgetLatestDataKeySettings = dataKey.settings; + const sortable = !keySettings.disableSorting && !dataKey.usePostProcessing; const styleInfo = getCellStyleInfo(this.ctx, keySettings, 'value, rowData, ctx'); const contentFunctionInfo = getCellContentFunctionInfo(this.ctx, keySettings, 'value, rowData, ctx'); const columnDefaultVisibility = getColumnDefaultVisibility(keySettings, this.ctx); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts index 7bd6e226f0..d351cabbde 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts @@ -31,7 +31,7 @@ import { defaultTripAnimationSettings, MapProviders, WidgetUnitedTripAnimationSettings -} from '@home/components/widget/lib/maps/map-models'; +} from '@home/components/widget/lib/maps-legacy/map-models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { @@ -39,7 +39,7 @@ import { getRatio, interpolateOnLineSegment, parseWithTranslation -} from '@home/components/widget/lib/maps/common-maps-utils'; +} from '@home/components/widget/lib/maps-legacy/common-maps-utils'; import { FormattedData, WidgetConfig } from '@shared/models/widget.models'; import moment from 'moment'; import { @@ -51,7 +51,7 @@ import { parseTbFunction, safeExecuteTbFunction } from '@core/utils'; -import { MapWidgetInterface } from '@home/components/widget/lib/maps/map-widget.interface'; +import { MapWidgetInterface } from '@home/components/widget/lib/maps-legacy/map-widget.interface'; import { firstValueFrom, from } from 'rxjs'; interface DataMap { @@ -123,7 +123,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy } ngAfterViewInit() { - import('@home/components/widget/lib/maps/map-widget2').then( + import('@home/components/widget/lib/maps-legacy/map-widget2').then( (mod) => { this.mapWidget = new mod.MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement, false, () => { @@ -378,4 +378,4 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy } } -export let TbTripAnimationWidget = TripAnimationComponent; +export const TbTripAnimationWidget = TripAnimationComponent; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index 22e0d725e5..1a397d3a1f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -40,11 +40,12 @@ import { migrateWidgetTypeToDynamicForms, Widget, widgetActionSources, + WidgetActionType, WidgetControllerDescriptor, WidgetType } from '@shared/models/widget.models'; import { catchError, map, mergeMap, switchMap, tap } from 'rxjs/operators'; -import { isFunction, isUndefined } from '@core/utils'; +import { isDefinedAndNotNull, isFunction, isUndefined } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { DynamicWidgetComponent } from '@home/components/widget/dynamic-widget.component'; import { WidgetComponentsModule } from '@home/components/widget/widget-components.module'; @@ -185,7 +186,7 @@ export class WidgetComponentService { (window as any).TbCanvasDigitalGauge = mod.TbCanvasDigitalGauge; })) ); - widgetModulesTasks.push(from(import('@home/components/widget/lib/maps/map-widget2')).pipe( + widgetModulesTasks.push(from(import('@home/components/widget/lib/maps-legacy/map-widget2')).pipe( tap((mod) => { (window as any).TbMapWidgetV2 = mod.TbMapWidgetV2; })) @@ -633,6 +634,9 @@ export class WidgetComponentService { if (isUndefined(result.typeParameters.overflowVisible)) { result.typeParameters.overflowVisible = false; } + if (isUndefined(result.typeParameters.hideDataTab)) { + result.typeParameters.hideDataTab = false; + } if (isUndefined(result.typeParameters.hideDataSettings)) { result.typeParameters.hideDataSettings = false; } @@ -651,6 +655,13 @@ export class WidgetComponentService { if (isUndefined(result.typeParameters.targetDeviceOptional)) { result.typeParameters.targetDeviceOptional = false; } + if (isDefinedAndNotNull(result.typeParameters.additionalWidgetActionTypes)) { + if (Array.isArray(result.typeParameters.additionalWidgetActionTypes)) { + result.typeParameters.additionalWidgetActionTypes = result.typeParameters.additionalWidgetActionTypes.filter(type => WidgetActionType[type]); + } else { + result.typeParameters.additionalWidgetActionTypes = null; + } + } if (isFunction(widgetTypeInstance.actionSources)) { result.actionSources = widgetTypeInstance.actionSources(); } else { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index fba18edd4f..cdc829a96f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -39,7 +39,7 @@ import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges- import { JsonInputWidgetComponent } from '@home/components/widget/lib/json-input-widget.component'; import { QrCodeWidgetComponent } from '@home/components/widget/lib/qrcode-widget.component'; import { MarkdownWidgetComponent } from '@home/components/widget/lib/markdown-widget.component'; -import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component'; +import { SelectEntityDialogComponent } from '@home/components/widget/lib/maps-legacy/dialogs/select-entity-dialog.component'; import { HomePageWidgetsModule } from '@home/components/widget/lib/home-page/home-page-widgets.module'; import { WIDGET_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { FlotWidgetComponent } from '@home/components/widget/lib/flot-widget.component'; @@ -89,6 +89,11 @@ import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component'; import { TwoSegmentButtonWidgetComponent } from '@home/components/widget/lib/button/two-segment-button-widget.component'; import { ValueStepperWidgetComponent } from '@home/components/widget/lib/rpc/value-stepper-widget.component'; +import { MapWidgetComponent } from '@home/components/widget/lib/maps/map-widget.component'; +import { + SelectMapEntityPanelComponent +} from '@home/components/widget/lib/maps/panels/select-map-entity-panel.component'; +import { MapTimelinePanelComponent } from '@home/components/widget/lib/maps/panels/map-timeline-panel.component'; @NgModule({ declarations: [ @@ -143,7 +148,10 @@ import { ValueStepperWidgetComponent } from '@home/components/widget/lib/rpc/val LabelValueCardWidgetComponent, UnreadNotificationWidgetComponent, NotificationTypeFilterPanelComponent, - ScadaSymbolWidgetComponent + ScadaSymbolWidgetComponent, + SelectMapEntityPanelComponent, + MapTimelinePanelComponent, + MapWidgetComponent ], imports: [ CommonModule, @@ -205,7 +213,8 @@ import { ValueStepperWidgetComponent } from '@home/components/widget/lib/rpc/val LabelValueCardWidgetComponent, UnreadNotificationWidgetComponent, NotificationTypeFilterPanelComponent, - ScadaSymbolWidgetComponent + ScadaSymbolWidgetComponent, + MapWidgetComponent ], providers: [ {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule}, diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index 9e5a5b7bfc..ae4e35aa9c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -26,7 +26,7 @@
-
+
@@ -164,6 +164,7 @@ [callbacks]="widgetConfigCallbacks" [widgetType] = "modelValue.widgetType" [actionSources]="modelValue.actionSources" + [additionalWidgetActionTypes]="modelValue.typeParameters.additionalWidgetActionTypes" formControlName="actions">
@@ -278,6 +279,12 @@
+ + + +
widget-config.data-settings
@@ -303,6 +310,7 @@ = new EventEmitter(); hovered = false; - isReferenceWidget = false; get widgetEditActionsEnabled(): boolean { return (this.isEditActionEnabled || this.isRemoveActionEnabled || this.isExportActionEnabled) && !this.widget?.isFullscreen; @@ -204,7 +204,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O } onMouseDown(event: MouseEvent) { - if (event) { + if (event && this.isEdit) { event.stopPropagation(); } this.widgetComponentAction.emit({ @@ -295,6 +295,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O theme: ['tb-widget-edit-actions-tooltip'], interactive: true, trigger: 'custom', + ignoreCloseOnScroll: true, triggerOpen: { mouseenter: true }, @@ -305,6 +306,9 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O trackOrigin: true, trackerInterval: 25, content: '', + checkOverflowY: (geo: ITooltipsterGeoHelper, bcr: DOMRect) => { + return geo.origin.windowOffset.top < bcr.top || geo.origin.windowOffset.bottom < bcr.bottom; + }, functionPosition: (instance, helper, position) => { const clientRect = helper.origin.getBoundingClientRect(); const container = parent.getBoundingClientRect(); @@ -314,6 +318,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O return position; }, functionReady: (_instance, helper) => { + this.editWidgetActionsTooltip.__scrollHandler({}); const tooltipEl = $(helper.tooltip); tooltipEl.on('mouseenter', () => { this.hovered = true; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index f4b2cd7e06..da56004ae2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -248,6 +248,8 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, this.widgetContext.isPreview = this.isPreview; this.widgetContext.isMobile = this.isMobile; this.widgetContext.toastTargetId = this.toastTargetId; + this.widgetContext.renderer = this.renderer; + this.widgetContext.widgetContentContainer = this.widgetContentContainer; this.widgetContext.subscriptionApi = { createSubscription: this.createSubscription.bind(this), @@ -271,7 +273,8 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, click: this.click.bind(this), getActiveEntityInfo: this.getActiveEntityInfo.bind(this), openDashboardStateInSeparateDialog: this.openDashboardStateInSeparateDialog.bind(this), - openDashboardStateInPopover: this.openDashboardStateInPopover.bind(this) + openDashboardStateInPopover: this.openDashboardStateInPopover.bind(this), + placeMapItem: () => {} }; this.widgetContext.customHeaderActions = []; @@ -1147,45 +1150,16 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, ) } break; - case WidgetActionType.customPretty: - const customPrettyFunction = descriptor.customFunction; - const customHtml = descriptor.customHtml; - const customCss = descriptor.customCss; - const customResources = descriptor.customResources; - const actionNamespace = `custom-action-pretty-${guid()}`; - let htmlTemplate = ''; - if (isDefined(customHtml) && customHtml.length > 0) { - htmlTemplate = customHtml; - } - this.loadCustomActionResources(actionNamespace, customCss, customResources, descriptor).subscribe({ - next: () => { - if (isNotEmptyTbFunction(customPrettyFunction)) { - compileTbFunction(this.http, customPrettyFunction, '$event', 'widgetContext', 'entityId', - 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel').subscribe( - { - next: (compiled) => { - try { - if (!additionalParams) { - additionalParams = {}; - } - this.widgetContext.customDialog.setAdditionalImports(descriptor.customImports); - compiled.execute($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel); - } catch (e) { - console.error(e); - } - }, - error: (err) => { - console.error(err); - } - } - ) - } - }, - error: (errorMessages: string[]) => { - this.processResourcesLoadErrors(errorMessages); - } + case WidgetActionType.placeMapItem: + this.widgetContext.actionsApi.placeMapItem({ + action: descriptor, + afterPlaceItemCallback: this.executeCustomPrettyAction.bind(this), + additionalParams: additionalParams }); break; + case WidgetActionType.customPretty: + this.executeCustomPrettyAction($event, descriptor, entityId, entityName, additionalParams, entityLabel); + break; case WidgetActionType.mobileAction: const mobileAction = descriptor.mobileAction; this.handleMobileAction($event, mobileAction, entityId, entityName, additionalParams, entityLabel); @@ -1556,6 +1530,45 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, this.handleWidgetAction($event, action, entityId, entityName, null, entityLabel); } + private executeCustomPrettyAction($event: Event, descriptor: WidgetAction, entityId?: EntityId, + entityName?: string, additionalParams?: any, entityLabel?: string) { + const customPrettyFunction = descriptor.customFunction; + const customHtml = descriptor.customHtml; + const customCss = descriptor.customCss; + const customResources = descriptor.customResources; + const actionNamespace = `custom-action-pretty-${guid()}`; + let htmlTemplate = ''; + if (isDefined(customHtml) && customHtml.length > 0) { + htmlTemplate = customHtml; + } + this.loadCustomActionResources(actionNamespace, customCss, customResources, descriptor).subscribe({ + next: () => { + if (isNotEmptyTbFunction(customPrettyFunction)) { + compileTbFunction(this.http, customPrettyFunction, '$event', 'widgetContext', 'entityId', + 'entityName', 'htmlTemplate', 'additionalParams', 'entityLabel').subscribe({ + next: (compiled) => { + try { + if (!additionalParams) { + additionalParams = {}; + } + this.widgetContext.customDialog.setAdditionalImports(descriptor.customImports); + compiled.execute($event, this.widgetContext, entityId, entityName, htmlTemplate, additionalParams, entityLabel); + } catch (e) { + console.error(e); + } + }, + error: (err) => { + console.error(err); + } + }); + } + }, + error: (errorMessages: string[]) => { + this.processResourcesLoadErrors(errorMessages); + } + }); + } + private loadCustomActionResources(actionNamespace: string, customCss: string, customResources: Array, actionDescriptor: WidgetAction): Observable { const resourceTasks: Observable[] = []; diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index b55ec2fd54..66bddabd62 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -46,7 +46,15 @@ import { WidgetActionsApi, WidgetSubscriptionApi } from '@core/api/widget-api.models'; -import { ChangeDetectorRef, InjectionToken, Injector, NgZone, TemplateRef, Type } from '@angular/core'; +import { + ChangeDetectorRef, + InjectionToken, + Injector, + NgZone, Renderer2, + TemplateRef, + Type, + ViewContainerRef +} from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { RafService } from '@core/services/raf.service'; import { WidgetTypeId } from '@shared/models/id/widget-type-id'; @@ -102,7 +110,7 @@ import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time- import { SharedTelemetrySubscriber, TelemetrySubscriber } from '@shared/models/telemetry/telemetry.models'; import { UserId } from '@shared/models/id/user-id'; import { UserSettingsService } from '@core/http/user-settings.service'; -import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeySettingsFunction } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { UtilsService } from '@core/services/utils.service'; import { CompiledTbFunction } from '@shared/models/js-function.models'; import { FormProperty } from '@shared/models/dynamic-form.models'; @@ -215,6 +223,8 @@ export class WidgetContext { http: HttpClient; sanitizer: DomSanitizer; router: Router; + renderer: Renderer2; + widgetContentContainer: ViewContainerRef; private changeDetectorValue: ChangeDetectorRef; private containerChangeDetectorValue: ChangeDetectorRef; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.html index 16840f3394..f1ac4d1213 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.html @@ -43,6 +43,22 @@ mobile.page-name + + warning + + + warning +
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.ts index 04583c1b6e..5e01758939 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/default-mobile-page-panel.component.ts @@ -17,7 +17,7 @@ import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { DefaultMobilePage, defaultMobilePageMap, hideDefaultMenuItems } from '@shared/models/mobile-app.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; -import { FormBuilder } from '@angular/forms'; +import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ @@ -43,7 +43,7 @@ export class DefaultMobilePagePanelComponent implements OnInit { mobilePageFormGroup = this.fb.group({ visible: [true], icon: [''], - label: [''], + label: ['', [Validators.pattern(/\S/), Validators.maxLength(255)]], }); isCleanupEnabled = false; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html index 653a9ebeab..149be8dd15 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html @@ -50,6 +50,14 @@ class="tb-error"> warning + + warning + diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index 789d96b5cb..33be223815 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -99,7 +99,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, mobilePageRowForm = this.fb.group({ visible: [true, []], icon: ['', []], - label: ['', [Validators.pattern(/\S/)]], + label: ['', [Validators.pattern(/\S/), Validators.maxLength(255)]], type: [MobilePageType.DEFAULT] }); diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html index d1e8107108..fec242ecc6 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html @@ -233,8 +233,8 @@ - {{descriptionInput.value?.length || 0}}/255 + rows="2" maxlength="1024"> + {{descriptionInput.value?.length || 0}}/1024 - - {{ label | translate }} + {{ label | translate }} {{ displayEntityFn(selectEntityFormGroup.get('entity').value) }}
- - warning - + + warning + @@ -72,7 +75,7 @@
- + {{ requiredErrorText | translate }} diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts index 5e4ddae6a0..848550a94b 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts @@ -15,8 +15,6 @@ /// import { - AfterViewInit, - booleanAttribute, Component, ElementRef, EventEmitter, @@ -116,6 +114,9 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit @Input() requiredText: string; + @Input() + placeholder: string; + @Input() @coerceBoolean() useFullEntityId: boolean; @@ -123,6 +124,10 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit @Input() appearance: MatFormFieldAppearance = 'fill'; + @Input() + @coerceBoolean() + inlineField: boolean; + @Input() @coerceBoolean() required: boolean; @@ -135,12 +140,6 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit @coerceBoolean() allowCreateNew: boolean; - @Input({ transform: booleanAttribute }) hideLabel = false; - - @Input({ transform: booleanAttribute }) inlineField = false; - - @Input() placeholder: string; - @Input() subscriptSizing: SubscriptSizing = 'fixed'; diff --git a/ui-ngx/src/app/shared/components/material-icons.component.html b/ui-ngx/src/app/shared/components/material-icons.component.html index 1df91fc9be..d4d9fe93a6 100644 --- a/ui-ngx/src/app/shared/components/material-icons.component.html +++ b/ui-ngx/src/app/shared/components/material-icons.component.html @@ -16,7 +16,7 @@ -->
-
icon.icons
+
icon.icons
search diff --git a/ui-ngx/src/app/shared/components/material-icons.component.ts b/ui-ngx/src/app/shared/components/material-icons.component.ts index 22360affa7..ece1a99108 100644 --- a/ui-ngx/src/app/shared/components/material-icons.component.ts +++ b/ui-ngx/src/app/shared/components/material-icons.component.ts @@ -58,7 +58,11 @@ export class MaterialIconsComponent extends PageComponent implements OnInit { iconClearButton = false; @Input() - popover: TbPopoverComponent; + @coerceBoolean() + showTitle = true; + + @Input() + popover: TbPopoverComponent; @Output() iconSelected = new EventEmitter(); diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.html b/ui-ngx/src/app/shared/components/string-items-list.component.html index 4362b5e315..b50e0ecce8 100644 --- a/ui-ngx/src/app/shared/components/string-items-list.component.html +++ b/ui-ngx/src/app/shared/components/string-items-list.component.html @@ -16,7 +16,7 @@ --> diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.scss b/ui-ngx/src/app/shared/components/string-items-list.component.scss new file mode 100644 index 0000000000..726344037c --- /dev/null +++ b/ui-ngx/src/app/shared/components/string-items-list.component.scss @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-string-items-list { + .mat-mdc-standard-chip { + .mdc-evolution-chip__cell--primary, .mat-mdc-chip-action-label { + overflow: hidden; + } + } +} diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.ts b/ui-ngx/src/app/shared/components/string-items-list.component.ts index d12b34fa9e..a2fa28d0ff 100644 --- a/ui-ngx/src/app/shared/components/string-items-list.component.ts +++ b/ui-ngx/src/app/shared/components/string-items-list.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; import { AbstractControl, ControlValueAccessor, @@ -38,14 +38,15 @@ export interface StringItemsOption { @Component({ selector: 'tb-string-items-list', templateUrl: './string-items-list.component.html', - styleUrls: [], + styleUrls: ['./string-items-list.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => StringItemsListComponent), multi: true } - ] + ], + encapsulation: ViewEncapsulation.None }) export class StringItemsListComponent implements ControlValueAccessor, OnInit { diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts index b209a30ace..cbe0fc7700 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.ts @@ -17,7 +17,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; import { interval } from 'rxjs'; import { filter } from 'rxjs/operators'; -import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models'; +import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps-legacy/map-models'; @Component({ selector: 'tb-history-selector', diff --git a/ui-ngx/src/app/shared/import-export/import-export.service.ts b/ui-ngx/src/app/shared/import-export/import-export.service.ts index 022a844ceb..6109275d6d 100644 --- a/ui-ngx/src/app/shared/import-export/import-export.service.ts +++ b/ui-ngx/src/app/shared/import-export/import-export.service.ts @@ -339,23 +339,23 @@ export class ImportExportService { } } return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo); } )); } else { return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo); } } ) ); } else { return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo); } } else { return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize, widgetItem.widgetExportInfo); } } }), @@ -1075,11 +1075,11 @@ export class ImportExportService { filtersInfo: FiltersInfo, onAliasesUpdateFunction: () => void, onFiltersUpdateFunction: () => void, - originalColumns: number, originalSize: WidgetSize): Observable { + originalColumns: number, originalSize: WidgetSize, widgetExportInfo: any): Observable { return targetLayoutFunction().pipe( mergeMap((targetLayout) => this.itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout, widget, aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, - originalColumns, originalSize, -1, -1).pipe( + originalColumns, originalSize, -1, -1, 'default', widgetExportInfo).pipe( map(() => ({widget, layoutId: targetLayout} as ImportWidgetResult)) ) )); diff --git a/ui-ngx/src/app/shared/legacy/flex-layout.models.ts b/ui-ngx/src/app/shared/legacy/flex-layout.models.ts deleted file mode 100644 index 4038fee802..0000000000 --- a/ui-ngx/src/app/shared/legacy/flex-layout.models.ts +++ /dev/null @@ -1,41 +0,0 @@ -/// -/// Copyright © 2016-2025 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Observable } from 'rxjs/internal/Observable'; -import { from, of } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; -import { Type } from '@angular/core'; - -let flexLayoutModule: any; - -export function getFlexLayout(): Observable { - if (flexLayoutModule) { - return of(flexLayoutModule); - } else { - return from(import('@angular/flex-layout')).pipe( - tap((module) => { - module.DEFAULT_CONFIG.addFlexToParent = false; - flexLayoutModule = module; - }) - ); - } -} - -export function getFlexLayoutModule(): Observable> { - return getFlexLayout().pipe( - map(module => module.FlexLayoutModule) - ); -} diff --git a/ui-ngx/src/app/shared/models/alias.models.ts b/ui-ngx/src/app/shared/models/alias.models.ts index 4005e451af..bc325a0692 100644 --- a/ui-ngx/src/app/shared/models/alias.models.ts +++ b/ui-ngx/src/app/shared/models/alias.models.ts @@ -18,6 +18,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { EntityId } from '@shared/models/id/entity-id'; import { EntitySearchDirection, RelationEntityTypeFilter } from '@shared/models/relation.models'; import { EntityFilter } from '@shared/models/query/query.models'; +import { guid, isEqual } from '@core/utils'; export enum AliasFilterType { singleEntity = 'singleEntity', @@ -210,3 +211,41 @@ export interface EntityAliasFilterResult { entityFilter: EntityFilter; entityParamName?: string; } + +export const getEntityAliasId = (entityAliases: EntityAliases, aliasInfo: EntityAliasInfo): string => { + let newAliasId: string; + for (const aliasId of Object.keys(entityAliases)) { + if (isEntityAliasEqual(entityAliases[aliasId], aliasInfo)) { + newAliasId = aliasId; + break; + } + } + if (!newAliasId) { + const newAliasName = createEntityAliasName(entityAliases, aliasInfo.alias); + newAliasId = guid(); + entityAliases[newAliasId] = {id: newAliasId, alias: newAliasName, filter: aliasInfo.filter}; + } + return newAliasId; +} + +const isEntityAliasEqual = (alias1: EntityAliasInfo, alias2: EntityAliasInfo): boolean => { + return isEqual(alias1.filter, alias2.filter); +} + +const createEntityAliasName = (entityAliases: EntityAliases, alias: string): string => { + let c = 0; + let newAlias = alias; + let unique = false; + while (!unique) { + unique = true; + for (const entAliasId of Object.keys(entityAliases)) { + const entAlias = entityAliases[entAliasId]; + if (newAlias === entAlias.alias) { + c++; + newAlias = alias + c; + unique = false; + } + } + } + return newAlias; +} diff --git a/ui-ngx/src/app/shared/models/query/query.models.ts b/ui-ngx/src/app/shared/models/query/query.models.ts index 98c0cce970..65128fe1eb 100644 --- a/ui-ngx/src/app/shared/models/query/query.models.ts +++ b/ui-ngx/src/app/shared/models/query/query.models.ts @@ -23,6 +23,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { DataKey, Datasource, DatasourceType } from '@shared/models/widget.models'; import { PageData } from '@shared/models/page/page-data'; import { + guid, isArraysEqualIgnoreUndefined, isDefined, isDefinedAndNotNull, @@ -921,3 +922,42 @@ export function updateDatasourceFromEntityInfo(datasource: Datasource, entity: E } } } + +export const getFilterId = (filters: Filters, filterInfo: FilterInfo): string => { + let newFilterId: string; + for (const filterId of Object.keys(filters)) { + if (isFilterEqual(filters[filterId], filterInfo)) { + newFilterId = filterId; + break; + } + } + if (!newFilterId) { + const newFilterName = createFilterName(filters, filterInfo.filter); + newFilterId = guid(); + filters[newFilterId] = {id: newFilterId, filter: newFilterName, + keyFilters: filterInfo.keyFilters, editable: filterInfo.editable}; + } + return newFilterId; +} + +const isFilterEqual = (filter1: FilterInfo, filter2: FilterInfo): boolean => { + return isEqual(filter1.keyFilters, filter2.keyFilters); +} + +const createFilterName = (filters: Filters, filter: string): string => { + let c = 0; + let newFilter = filter; + let unique = false; + while (!unique) { + unique = true; + for (const entFilterId of Object.keys(filters)) { + const entFilter = filters[entFilterId]; + if (newFilter === entFilter.filter) { + c++; + newFilter = filter + c; + unique = false; + } + } + } + return newFilter; +} diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index c7f3327f68..415d400523 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -42,7 +42,7 @@ import { WidgetConfigComponentData } from '@home/models/widget-component.models' import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { EntityInfoData, HasTenantId, HasVersion } from '@shared/models/entity.models'; -import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/lib/settings/common/key/data-keys.component.models'; import { WidgetConfigCallbacks } from '@home/components/widget/config/widget-config.component.models'; import { TbFunction } from '@shared/models/js-function.models'; import { FormProperty, jsonFormSchemaToFormProperties } from '@shared/models/dynamic-form.models'; @@ -185,12 +185,14 @@ export interface WidgetTypeParameters { previewHeight?: string; embedTitlePanel?: boolean; overflowVisible?: boolean; + hideDataTab?: boolean; hideDataSettings?: boolean; defaultDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; defaultLatestDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; dataKeySettingsFunction?: DataKeySettingsFunction; displayRpcMessageToast?: boolean; targetDeviceOptional?: boolean; + additionalWidgetActionTypes?: WidgetActionType[]; } export interface WidgetControllerDescriptor { @@ -510,8 +512,8 @@ export const datasourcesHasOnlyComparisonAggregation = (datasources?: Array { + $datasource: D; entityName: string; deviceName: string; entityId: string; @@ -575,7 +577,8 @@ export enum WidgetActionType { custom = 'custom', customPretty = 'customPretty', mobileAction = 'mobileAction', - openURL = 'openURL' + openURL = 'openURL', + placeMapItem = 'placeMapItem' } export enum WidgetMobileActionType { @@ -589,7 +592,15 @@ export enum WidgetMobileActionType { takeScreenshot = 'takeScreenshot' } -export const widgetActionTypes = Object.keys(WidgetActionType) as WidgetActionType[]; +export enum MapItemType { + marker = 'marker', + polygon = 'polygon', + rectangle = 'rectangle', + circle = 'circle' +} + +export const widgetActionTypes = Object.keys(WidgetActionType) + .filter(value => value !== WidgetActionType.placeMapItem) as WidgetActionType[]; export const widgetActionTypeTranslationMap = new Map( [ @@ -600,7 +611,8 @@ export const widgetActionTypeTranslationMap = new Map( [ WidgetActionType.custom, 'widget-action.custom' ], [ WidgetActionType.customPretty, 'widget-action.custom-pretty' ], [ WidgetActionType.mobileAction, 'widget-action.mobile-action' ], - [ WidgetActionType.openURL, 'widget-action.open-URL' ] + [ WidgetActionType.openURL, 'widget-action.open-URL' ], + [ WidgetActionType.placeMapItem, 'widget-action.place-map-item' ], ] ); @@ -617,6 +629,15 @@ export const widgetMobileActionTypeTranslationMap = new Map( + [ + [ MapItemType.marker, 'widget-action.map-item.marker' ], + [ MapItemType.polygon, 'widget-action.map-item.polygon' ], + [ MapItemType.rectangle, 'widget-action.map-item.rectangle' ], + [ MapItemType.circle, 'widget-action.map-item.circle' ], + ] +) + export interface MobileLaunchResult { launched: boolean; } @@ -692,6 +713,7 @@ export interface CustomActionDescriptor { } export interface WidgetAction extends CustomActionDescriptor { + name?: string; type: WidgetActionType; targetDashboardId?: string; targetDashboardStateId?: string; @@ -713,6 +735,7 @@ export interface WidgetAction extends CustomActionDescriptor { stateEntityParamName?: string; mobileAction?: WidgetMobileActionDescriptor; url?: string; + mapItemType?: MapItemType; } export interface WidgetActionDescriptor extends WidgetAction { @@ -861,6 +884,7 @@ export interface IWidgetSettingsComponent { aliasController: IAliasController; callbacks: WidgetConfigCallbacks; dataKeyCallbacks: DataKeysCallbacks; + functionsOnly: boolean; dashboard: Dashboard; widget: Widget; widgetConfig: WidgetConfigComponentData; @@ -882,6 +906,8 @@ export abstract class WidgetSettingsComponent extends PageComponent implements dataKeyCallbacks: DataKeysCallbacks; + functionsOnly: boolean; + dashboard: Dashboard; widget: Widget; diff --git a/ui-ngx/src/app/shared/models/widget/maps/map-export.models.ts b/ui-ngx/src/app/shared/models/widget/maps/map-export.models.ts new file mode 100644 index 0000000000..f7750db731 --- /dev/null +++ b/ui-ngx/src/app/shared/models/widget/maps/map-export.models.ts @@ -0,0 +1,151 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { EntityAliases, EntityAliasInfo, getEntityAliasId } from '@shared/models/alias.models'; +import { FilterInfo, Filters, getFilterId } from '@shared/models/query/query.models'; +import { Dashboard } from '@shared/models/dashboard.models'; +import { DatasourceType, Widget } from '@shared/models/widget.models'; +import { BaseMapSettings, MapDataSourceSettings, MapType } from '@shared/models/widget/maps/map.models'; +import { WidgetExportDefinition } from '@shared/models/widget/widget-export.models'; + +interface ExportDataSourceInfo { + aliases: {[dataLayerIndex: number]: EntityAliasInfo}; + filters: {[dataLayerIndex: number]: FilterInfo}; +} + +interface MapDatasourcesInfo { + trips?: ExportDataSourceInfo; + markers?: ExportDataSourceInfo; + polygons?: ExportDataSourceInfo; + circles?: ExportDataSourceInfo; + additionalDataSources?: ExportDataSourceInfo; +} + +export const MapExportDefinition: WidgetExportDefinition = { + testWidget(widget: Widget): boolean { + if (widget?.config?.settings) { + const settings = widget.config.settings; + if (settings.mapType && [MapType.image, MapType.geoMap].includes(settings.mapType)) { + if (settings.trips && Array.isArray(settings.trips)) { + return true; + } + if (settings.markers && Array.isArray(settings.markers)) { + return true; + } + if (settings.polygons && Array.isArray(settings.polygons)) { + return true; + } + if (settings.circles && Array.isArray(settings.circles)) { + return true; + } + } + } + return false; + }, + prepareExportInfo(dashboard: Dashboard, widget: Widget): MapDatasourcesInfo { + const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; + const info: MapDatasourcesInfo = {}; + if (settings.trips?.length) { + info.trips = prepareExportDataSourcesInfo(dashboard, settings.trips); + } + if (settings.markers?.length) { + info.markers = prepareExportDataSourcesInfo(dashboard, settings.markers); + } + if (settings.polygons?.length) { + info.polygons = prepareExportDataSourcesInfo(dashboard, settings.polygons); + } + if (settings.circles?.length) { + info.circles = prepareExportDataSourcesInfo(dashboard, settings.circles); + } + if (settings.additionalDataSources?.length) { + info.additionalDataSources = prepareExportDataSourcesInfo(dashboard, settings.additionalDataSources); + } + return info; + }, + updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: MapDatasourcesInfo): void { + const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; + if (info?.trips) { + updateMapDatasourceFromExportInfo(entityAliases, filters, settings.trips, info.trips); + } + if (info?.markers) { + updateMapDatasourceFromExportInfo(entityAliases, filters, settings.markers, info.markers); + } + if (info?.polygons) { + updateMapDatasourceFromExportInfo(entityAliases, filters, settings.polygons, info.polygons); + } + if (info?.circles) { + updateMapDatasourceFromExportInfo(entityAliases, filters, settings.circles, info.circles); + } + if (info?.additionalDataSources) { + updateMapDatasourceFromExportInfo(entityAliases, filters, settings.additionalDataSources, info.additionalDataSources); + } + } +}; + +const updateMapDatasourceFromExportInfo = (entityAliases: EntityAliases, + filters: Filters, settings: MapDataSourceSettings[], info: ExportDataSourceInfo): void => { + if (info.aliases) { + for (const dsIndexStr of Object.keys(info.aliases)) { + const dsIndex = Number(dsIndexStr); + if (settings[dsIndex] && settings[dsIndex].dsType === DatasourceType.entity) { + const aliasInfo = info.aliases[dsIndex]; + settings[dsIndex].dsEntityAliasId = getEntityAliasId(entityAliases, aliasInfo); + } + } + } + if (info.filters) { + for (const dsIndexStr of Object.keys(info.filters)) { + const dsIndex = Number(dsIndexStr); + if (settings[dsIndex] && settings[dsIndex].dsType === DatasourceType.entity) { + const filterInfo = info.filters[dsIndex]; + settings[dsIndex].dsFilterId = getFilterId(filters, filterInfo); + } + } + } +} + +const prepareExportDataSourcesInfo = (dashboard: Dashboard, settings: MapDataSourceSettings[]): ExportDataSourceInfo => { + const info: ExportDataSourceInfo = { + aliases: {}, + filters: {} + }; + settings.forEach((dsSettings, index) => { + prepareExportDataSourceInfo(dashboard, info, dsSettings, index); + }); + return info; +} + +const prepareExportDataSourceInfo = (dashboard: Dashboard, info: ExportDataSourceInfo, settings: MapDataSourceSettings, index: number): void => { + if (settings.dsType === DatasourceType.entity) { + const entityAlias = dashboard.configuration.entityAliases[settings.dsEntityAliasId]; + if (entityAlias) { + info.aliases[index] = { + alias: entityAlias.alias, + filter: entityAlias.filter + }; + } + if (settings.dsFilterId && dashboard.configuration.filters) { + const filter = dashboard.configuration.filters[settings.dsFilterId]; + if (filter) { + info.filters[index] = { + filter: filter.filter, + keyFilters: filter.keyFilters, + editable: filter.editable + }; + } + } + } +} diff --git a/ui-ngx/src/app/shared/models/widget/maps/map.models.ts b/ui-ngx/src/app/shared/models/widget/maps/map.models.ts new file mode 100644 index 0000000000..5dc4d52ad4 --- /dev/null +++ b/ui-ngx/src/app/shared/models/widget/maps/map.models.ts @@ -0,0 +1,1363 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + DataKey, + Datasource, + DatasourceType, + FormattedData, + WidgetAction, + WidgetActionType +} from '@shared/models/widget.models'; +import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + guid, + hashCode, + isDefinedAndNotNull, + isNotEmptyStr, + isNumber, + isString, + isUndefinedOrNull, + mergeDeep +} from '@core/utils'; +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { materialColors } from '@shared/models/material.models'; +import type L from 'leaflet'; +import { TbFunction } from '@shared/models/js-function.models'; +import { Observable, Observer, of, switchMap } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { MarkerIconContainer, MarkerShape } from '@shared/models/widget/maps/marker-shape.models'; +import { DateFormatSettings, simpleDateFormat } from '@shared/models/widget-settings.models'; + +export enum MapType { + geoMap = 'geoMap', + image = 'image' +} + +export interface MapDataSourceSettings { + dsType: DatasourceType; + dsLabel?: string; + dsDeviceId?: string; + dsEntityAliasId?: string; + dsFilterId?: string; +} + +export interface TbMapDatasource extends Datasource { + mapDataIds: string[]; +} + +export const mapDataSourceSettingsToDatasource = (settings: MapDataSourceSettings): TbMapDatasource => { + return { + type: settings.dsType, + name: settings.dsLabel, + deviceId: settings.dsDeviceId, + entityAliasId: settings.dsEntityAliasId, + filterId: settings.dsFilterId, + dataKeys: [], + mapDataIds: [guid()] + }; +}; + + +export enum DataLayerPatternType { + pattern = 'pattern', + function = 'function' +} + +export interface DataLayerPatternSettings { + show: boolean; + type: DataLayerPatternType; + pattern?: string; + patternFunction?: TbFunction; +} + +export enum DataLayerTooltipTrigger { + click = 'click', + hover = 'hover' +} + +export const dataLayerTooltipTriggers = Object.keys(DataLayerTooltipTrigger) as DataLayerTooltipTrigger[]; + +export const dataLayerTooltipTriggerTranslationMap = new Map( + [ + [DataLayerTooltipTrigger.click, 'widgets.maps.data-layer.tooltip-trigger-click'], + [DataLayerTooltipTrigger.hover, 'widgets.maps.data-layer.tooltip-trigger-hover'] + ] +); + +export interface DataLayerTooltipSettings extends DataLayerPatternSettings { + trigger: DataLayerTooltipTrigger; + autoclose: boolean; + offsetX: number; + offsetY: number; + tagActions?: WidgetAction[]; +} + +export enum DataLayerEditAction { + add = 'add', + edit = 'edit', + move = 'move', + remove = 'remove' +} + +export const dataLayerEditActions = Object.keys(DataLayerEditAction) as DataLayerEditAction[]; + +export const dataLayerEditActionTranslationMap = new Map( + [ + [DataLayerEditAction.add, 'widgets.maps.data-layer.action-add'], + [DataLayerEditAction.edit, 'widgets.maps.data-layer.action-edit'], + [DataLayerEditAction.move, 'widgets.maps.data-layer.action-move'], + [DataLayerEditAction.remove, 'widgets.maps.data-layer.action-remove'] + ] +); + +export interface DataLayerEditSettings { + enabledActions: DataLayerEditAction[]; + attributeScope: AttributeScope; + snappable: boolean; +} + +export interface MapDataLayerSettings extends MapDataSourceSettings { + additionalDataKeys?: DataKey[]; + label: DataLayerPatternSettings; + tooltip: DataLayerTooltipSettings; + click: WidgetAction; + groups?: string[]; + edit: DataLayerEditSettings; +} + +export const defaultBaseDataLayerSettings = (mapType: MapType): Partial => ({ + label: { + show: true, + type: DataLayerPatternType.pattern, + pattern: '${entityName}' + }, + tooltip: { + show: true, + trigger: DataLayerTooltipTrigger.click, + autoclose: true, + type: DataLayerPatternType.pattern, + pattern: mapType === MapType.geoMap ? + '${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See tooltip settings for details' + : '${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See tooltip settings for details', + offsetX: 0, + offsetY: -1 + }, + click: { + type: WidgetActionType.doNothing + }, + edit: { + enabledActions: [], + attributeScope: AttributeScope.SERVER_SCOPE, + snappable: false + } +}) + +export type MapDataLayerType = 'trips' | 'markers' | 'polygons' | 'circles'; + +export const mapDataLayerTypes: MapDataLayerType[] = ['trips', 'markers', 'polygons', 'circles']; + +export const mapDataLayerValid = (dataLayer: MapDataLayerSettings, type: MapDataLayerType): boolean => { + if (!dataLayer.dsType || ![DatasourceType.function, DatasourceType.device, DatasourceType.entity].includes(dataLayer.dsType)) { + return false; + } + switch (dataLayer.dsType) { + case DatasourceType.function: + break; + case DatasourceType.device: + if (!dataLayer.dsDeviceId) { + return false; + } + break; + case DatasourceType.entity: + if (!dataLayer.dsEntityAliasId) { + return false; + } + break; + } + switch (type) { + case 'markers': + const markersDataLayer = dataLayer as MarkersDataLayerSettings; + if (!markersDataLayer.xKey?.type || !markersDataLayer.xKey?.name || + !markersDataLayer.yKey?.type || !markersDataLayer.xKey?.name) { + return false; + } + break; + case 'polygons': + const polygonsDataLayer = dataLayer as PolygonsDataLayerSettings; + if (!polygonsDataLayer.polygonKey?.type || !polygonsDataLayer.polygonKey?.name) { + return false; + } + break; + case 'circles': + const circlesDataLayer = dataLayer as CirclesDataLayerSettings; + if (!circlesDataLayer.circleKey?.type || !circlesDataLayer.circleKey?.name) { + return false; + } + break; + } + return true; +}; + +export const mapDataLayerValidator = (type: MapDataLayerType): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const layer: MapDataLayerSettings = control.value; + if (!mapDataLayerValid(layer, type)) { + return { + layer: true + }; + } + return null; + }; +}; + +export enum MarkerType { + shape = 'shape', + icon = 'icon', + image = 'image' +} + +export enum DataLayerColorType { + constant = 'constant', + function = 'function' +} + +export interface DataLayerColorSettings { + type: DataLayerColorType; + color: string; + colorFunction?: TbFunction; +} + +export enum MarkerImageType { + image = 'image', + function = 'function' +} + +export interface MarkerImageSettings { + type: MarkerImageType; + image?: string; + imageSize?: number; + imageFunction?: TbFunction; + images?: string[]; +} + +export interface BaseMarkerShapeSettings { + size: number; + color: DataLayerColorSettings; +} + +export interface MarkerShapeSettings extends BaseMarkerShapeSettings { + shape: MarkerShape; +} + +export interface MarkerIconSettings extends BaseMarkerShapeSettings { + iconContainer?: MarkerIconContainer; + icon: string; +} +export interface MarkerClusteringSettings { + enable: boolean; + zoomOnClick: boolean; + maxZoom: number; + maxClusterRadius: number; + zoomAnimation: boolean; + showCoverageOnHover: boolean; + spiderfyOnMaxZoom: boolean; + chunkedLoad: boolean; + lazyLoad: boolean; + useClusterMarkerColorFunction: boolean; + clusterMarkerColorFunction: TbFunction; +} + + +export interface MarkersDataLayerSettings extends MapDataLayerSettings { + xKey: DataKey; + yKey: DataKey; + markerType: MarkerType; + markerShape?: MarkerShapeSettings; + markerIcon?: MarkerIconSettings; + markerImage?: MarkerImageSettings; + markerOffsetX: number; + markerOffsetY: number; + positionFunction?: TbFunction; + markerClustering: MarkerClusteringSettings; +} + +const defaultMarkerLatitudeFunction = 'var value = prevValue || 15.833293;\n' + + 'if (time % 500 < 500) {\n' + + ' value += Math.random() * 0.05 - 0.025;\n' + + '}\n' + + 'return value;'; + +const defaultMarkerLongitudeFunction = 'var value = prevValue || -90.454350;\n' + + 'if (time % 500 < 500) {\n' + + ' value += Math.random() * 0.05 - 0.025;\n' + + '}\n' + + 'return value;'; + +const defaultMarkerXPosFunction = 'var value = prevValue || 0.2;\n' + + 'if (time % 500 < 500) {\n' + + ' value += Math.random() * 0.05 - 0.025;\n' + + '}\n' + + 'return value;'; + +const defaultMarkerYPosFunction = 'var value = prevValue || 0.3;\n' + + 'if (time % 500 < 500) {\n' + + ' value += Math.random() * 0.05 - 0.025;\n' + + '}\n' + + 'return value;'; + +const defaultMarkersDataSourceSettings = (mapType: MapType, timeSeries = false, functionsOnly = false): Partial => ({ + dsType: functionsOnly ? DatasourceType.function : DatasourceType.entity, + dsLabel: functionsOnly ? 'First point' : '', + xKey: { + name: functionsOnly ? 'f(x)' : (MapType.geoMap === mapType ? 'latitude' : 'xPos'), + label: MapType.geoMap === mapType ? 'latitude' : 'xPos', + type: functionsOnly ? DataKeyType.function : (timeSeries ? DataKeyType.timeseries : DataKeyType.attribute), + funcBody: functionsOnly ? (MapType.geoMap === mapType ? defaultMarkerLatitudeFunction : defaultMarkerXPosFunction) : undefined, + settings: {}, + color: materialColors[0].value + }, + yKey: { + name: functionsOnly ? 'f(x)' : (MapType.geoMap === mapType ? 'longitude' : 'yPos'), + label: MapType.geoMap === mapType ? 'longitude' : 'yPos', + type: functionsOnly ? DataKeyType.function : (timeSeries ? DataKeyType.timeseries : DataKeyType.attribute), + funcBody: functionsOnly ? (MapType.geoMap === mapType ? defaultMarkerLongitudeFunction : defaultMarkerYPosFunction) : undefined, + settings: {}, + color: materialColors[0].value + } +}); + +export const defaultMarkersDataLayerSettings = (mapType: MapType, functionsOnly = false): MarkersDataLayerSettings => mergeDeep( + defaultMarkersDataSourceSettings(mapType, false, functionsOnly) as MarkersDataLayerSettings, + defaultBaseMarkersDataLayerSettings(mapType) as MarkersDataLayerSettings); + +export const defaultBaseMarkersDataLayerSettings = (mapType: MapType): Partial => mergeDeep({ + markerType: MarkerType.shape, + markerShape: { + shape: MarkerShape.markerShape1, + size: 34, + color: { + type: DataLayerColorType.constant, + color: '#307FE5', + } + }, + markerIcon: { + iconContainer: MarkerIconContainer.iconContainer1, + icon: 'mdi:lightbulb-on', + size: 48, + color: { + type: DataLayerColorType.constant, + color: '#307FE5', + } + }, + markerImage: { + type: MarkerImageType.image, + image: '/assets/markers/shape1.svg', + imageSize: 34 + }, + markerOffsetX: 0.5, + markerOffsetY: 1, + positionFunction: 'return {x: origXPos, y: origYPos};', + markerClustering: { + enable: false, + zoomOnClick: true, + maxZoom: null, + maxClusterRadius: 80, + zoomAnimation: true, + showCoverageOnHover: true, + spiderfyOnMaxZoom: false, + chunkedLoad: false, + lazyLoad: true, + useClusterMarkerColorFunction: false, + clusterMarkerColorFunction: null + } +} as MarkersDataLayerSettings, defaultBaseDataLayerSettings(mapType)); + +export enum PathDecoratorSymbol { + arrowHead = 'arrowHead', + dash = 'dash' +} + +export const pathDecoratorSymbols = Object.keys(PathDecoratorSymbol) as PathDecoratorSymbol[]; + +export const pathDecoratorSymbolTranslationMap = new Map( + [ + [PathDecoratorSymbol.arrowHead, 'widgets.maps.data-layer.path.decorator-symbol-arrow-head'], + [PathDecoratorSymbol.dash, 'widgets.maps.data-layer.path.decorator-symbol-dash'] + ] +); + +export interface TripsDataLayerSettings extends MarkersDataLayerSettings { + showMarker: boolean; + rotateMarker: boolean; + offsetAngle: number; + showPath: boolean; + pathStrokeWeight?: number; + pathStrokeColor?: DataLayerColorSettings; + usePathDecorator?: boolean; + pathDecoratorSymbol?: PathDecoratorSymbol; + pathDecoratorSymbolSize?: number; + pathDecoratorSymbolColor?: string; + pathDecoratorOffset?: number; + pathEndDecoratorOffset?: number; + pathDecoratorRepeat?: number; + showPoints: boolean; + pointSize?: number; + pointColor?: DataLayerColorSettings; + pointTooltip?: DataLayerTooltipSettings; +} + +export const defaultTripsDataLayerSettings = (mapType: MapType, functionsOnly = false): TripsDataLayerSettings => mergeDeep( + defaultMarkersDataSourceSettings(mapType, true, functionsOnly) as TripsDataLayerSettings, + defaultBaseTripsDataLayerSettings(mapType) as TripsDataLayerSettings); + +export const defaultBaseTripsDataLayerSettings = (mapType: MapType): Partial => mergeDeep( + defaultBaseMarkersDataLayerSettings(mapType), + { + showMarker: true, + tooltip: { + offsetY: -0.5, + pattern: mapType === MapType.geoMap ? + '${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}' + : '${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
End Time: ${maxTime}
Start Time: ${minTime}', + }, + rotateMarker: true, + offsetAngle: 0, + markerShape: { + shape: MarkerShape.tripMarkerShape2 + }, + markerIcon: { + iconContainer: MarkerIconContainer.tripIconContainer1, + icon: 'arrow_forward' + }, + markerImage: { + image: '/assets/markers/tripShape2.svg' + }, + markerOffsetX: 0.5, + markerOffsetY: 0.5, + showPath: true, + pathStrokeWeight: 4, + pathStrokeColor: { + type: DataLayerColorType.constant, + color: '#307FE5', + }, + usePathDecorator: false, + pathDecoratorSymbol: PathDecoratorSymbol.arrowHead, + pathDecoratorSymbolSize: 10, + pathDecoratorSymbolColor: '#307FE5', + pathDecoratorOffset: 20, + pathEndDecoratorOffset: 20, + pathDecoratorRepeat: 20, + showPoints: false, + pointSize: 10, + pointColor: { + type: DataLayerColorType.constant, + color: '#307FE5', + }, + pointTooltip: { + show: true, + trigger: DataLayerTooltipTrigger.click, + autoclose: true, + type: DataLayerPatternType.pattern, + pattern: mapType === MapType.geoMap ? + '${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}' + : '${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
End Time: ${maxTime}
Start Time: ${minTime}', + offsetX: 0, + offsetY: -1 + }, + } as TripsDataLayerSettings); + +export interface ShapeDataLayerSettings extends MapDataLayerSettings { + fillColor: DataLayerColorSettings; + strokeColor: DataLayerColorSettings; + strokeWeight: number; +} + +export interface PolygonsDataLayerSettings extends ShapeDataLayerSettings { + polygonKey: DataKey; +} + +export const defaultPolygonsDataLayerSettings = (mapType: MapType, functionsOnly = false): PolygonsDataLayerSettings => mergeDeep({ + dsType: functionsOnly ? DatasourceType.function : DatasourceType.entity, + dsLabel: functionsOnly ? 'First polygon' : '', + polygonKey: { + name: functionsOnly ? 'f(x)' : 'perimeter', + label: 'perimeter', + type: functionsOnly ? DataKeyType.function : DataKeyType.attribute, + settings: {}, + color: materialColors[0].value + } +} as PolygonsDataLayerSettings, defaultBasePolygonsDataLayerSettings(mapType) as PolygonsDataLayerSettings); + +export const defaultBasePolygonsDataLayerSettings = (mapType: MapType): Partial => mergeDeep({ + fillColor: { + type: DataLayerColorType.constant, + color: 'rgba(51,136,255,0.2)', + }, + strokeColor: { + type: DataLayerColorType.constant, + color: '#3388ff', + }, + strokeWeight: 3 +} as Partial, defaultBaseDataLayerSettings(mapType), + {label: {show: false}, tooltip: {show: false, pattern: '${entityName}

TimeStamp: ${ts:7}'}} as Partial) + +export interface CirclesDataLayerSettings extends ShapeDataLayerSettings { + circleKey: DataKey; +} + +export const defaultCirclesDataLayerSettings = (mapType: MapType, functionsOnly = false): CirclesDataLayerSettings => mergeDeep({ + dsType: functionsOnly ? DatasourceType.function : DatasourceType.entity, + dsLabel: functionsOnly ? 'First circle' : '', + circleKey: { + name: functionsOnly ? 'f(x)' : 'perimeter', + label: 'perimeter', + type: functionsOnly ? DataKeyType.function : DataKeyType.attribute, + settings: {}, + color: materialColors[0].value + } +} as CirclesDataLayerSettings, defaultBaseCirclesDataLayerSettings(mapType) as CirclesDataLayerSettings); + +export const defaultBaseCirclesDataLayerSettings = (mapType: MapType): Partial => mergeDeep({ + fillColor: { + type: DataLayerColorType.constant, + color: 'rgba(51,136,255,0.2)', + }, + strokeColor: { + type: DataLayerColorType.constant, + color: '#3388ff', + }, + strokeWeight: 3 +} as Partial, defaultBaseDataLayerSettings(mapType), + {label: {show: false}, tooltip: {show: false, pattern: '${entityName}

TimeStamp: ${ts:7}'}} as Partial) + +export const defaultMapDataLayerSettings = (mapType: MapType, dataLayerType: MapDataLayerType, functionsOnly = false): MapDataLayerSettings => { + switch (dataLayerType) { + case 'trips': + return defaultTripsDataLayerSettings(mapType, functionsOnly); + case 'markers': + return defaultMarkersDataLayerSettings(mapType, functionsOnly); + case 'polygons': + return defaultPolygonsDataLayerSettings(mapType, functionsOnly); + case 'circles': + return defaultCirclesDataLayerSettings(mapType, functionsOnly); + } +}; + +export const defaultBaseMapDataLayerSettings = (mapType: MapType, dataLayerType: MapDataLayerType): T => { + switch (dataLayerType) { + case 'trips': + return defaultBaseTripsDataLayerSettings(mapType) as T; + case 'markers': + return defaultBaseMarkersDataLayerSettings(mapType) as T; + case 'polygons': + return defaultBasePolygonsDataLayerSettings(mapType) as T; + case 'circles': + return defaultBaseCirclesDataLayerSettings(mapType) as T; + } +} + +export interface AdditionalMapDataSourceSettings extends MapDataSourceSettings { + dataKeys: DataKey[]; +} + +export const additionalMapDataSourcesToDatasources = (additionalMapDataSources: AdditionalMapDataSourceSettings[]): TbMapDatasource[] => { + return additionalMapDataSources.map(addDs => { + const res = mapDataSourceSettingsToDatasource(addDs); + res.dataKeys = addDs.dataKeys; + return res; + }); +}; + +export const additionalMapDataSourceValid = (dataSource: AdditionalMapDataSourceSettings): boolean => { + if (!dataSource.dsType || ![DatasourceType.function, DatasourceType.device, DatasourceType.entity].includes(dataSource.dsType)) { + return false; + } + return !!dataSource.dataKeys?.length; +}; + +export const additionalMapDataSourceValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + const dataSource: AdditionalMapDataSourceSettings = control.value; + if (!additionalMapDataSourceValid(dataSource)) { + return { + dataSource: true + }; + } + return null; +}; + +export const defaultAdditionalMapDataSourceSettings = (functionsOnly = false): AdditionalMapDataSourceSettings => { + return { + dsType: functionsOnly ? DatasourceType.function : DatasourceType.entity, + dsLabel: functionsOnly ? 'Additional data' : '', + dataKeys: [] + }; +}; + +export enum MapControlsPosition { + topleft = 'topleft', + topright = 'topright', + bottomleft = 'bottomleft', + bottomright = 'bottomright' +} + +export const mapControlPositions = Object.keys(MapControlsPosition) as MapControlsPosition[]; + +export const mapControlsPositionTranslationMap = new Map( + [ + [MapControlsPosition.topleft, 'widgets.maps.control.position-topleft'], + [MapControlsPosition.topright, 'widgets.maps.control.position-topright'], + [MapControlsPosition.bottomleft, 'widgets.maps.control.position-bottomleft'], + [MapControlsPosition.bottomright, 'widgets.maps.control.position-bottomright'] + ] +); + +export enum MapZoomAction { + scroll = 'scroll', + doubleClick = 'doubleClick', + controlButtons = 'controlButtons' +} + +export const mapZoomActions = Object.keys(MapZoomAction) as MapZoomAction[]; + +export const mapZoomActionTranslationMap = new Map( + [ + [MapZoomAction.scroll, 'widgets.maps.control.zoom-scroll'], + [MapZoomAction.doubleClick, 'widgets.maps.control.zoom-double-click'], + [MapZoomAction.controlButtons, 'widgets.maps.control.zoom-control-buttons'] + ] +); + +export enum MapScale { + metric = 'metric', + imperial = 'imperial' +} + +export const mapScales = Object.keys(MapScale) as MapScale[]; + +export const mapScaleTranslationMap = new Map( + [ + [MapScale.metric, 'widgets.maps.control.scale-metric'], + [MapScale.imperial, 'widgets.maps.control.scale-imperial'], + ] +); + +export interface MapActionButtonSettings { + label?: string; + icon?: string; + color: string; + action: WidgetAction; +} + +export interface TripTimelineSettings { + showTimelineControl: boolean; + timeStep: number; + speedOptions: number[]; + showTimestamp: boolean; + timestampFormat: DateFormatSettings; + snapToRealLocation: boolean; + locationSnapFilter: TbFunction; +} + +export interface BaseMapSettings { + mapType: MapType; + trips?: TripsDataLayerSettings[]; + markers: MarkersDataLayerSettings[]; + polygons: PolygonsDataLayerSettings[]; + circles: CirclesDataLayerSettings[]; + additionalDataSources: AdditionalMapDataSourceSettings[]; + controlsPosition: MapControlsPosition; + zoomActions: MapZoomAction[]; + scales: MapScale[]; + dragModeButton: boolean; + fitMapBounds: boolean; + useDefaultCenterPosition: boolean; + defaultCenterPosition?: string; + defaultZoomLevel: number; + minZoomLevel: number; + mapPageSize: number; + mapActionButtons: MapActionButtonSettings[]; + tripTimeline?: TripTimelineSettings; +} + +export const DEFAULT_MAP_PAGE_SIZE = 16384; +export const DEFAULT_ZOOM_LEVEL = 8; + +export const defaultBaseMapSettings: BaseMapSettings = { + mapType: MapType.geoMap, + trips: [], + markers: [], + polygons: [], + circles: [], + additionalDataSources: [], + controlsPosition: MapControlsPosition.topleft, + zoomActions: [MapZoomAction.scroll, MapZoomAction.doubleClick, MapZoomAction.controlButtons], + scales: [], + dragModeButton: false, + fitMapBounds: true, + useDefaultCenterPosition: false, + defaultCenterPosition: '0,0', + defaultZoomLevel: null, + minZoomLevel: 16, + mapPageSize: DEFAULT_MAP_PAGE_SIZE, + mapActionButtons: [], + tripTimeline: { + showTimelineControl: false, + timeStep: 1000, + speedOptions: [1,5,10,15,25], + showTimestamp: true, + timestampFormat: simpleDateFormat('yyyy-MM-dd HH:mm:ss'), + snapToRealLocation: false, + locationSnapFilter: 'return true;' + } +}; + +export const defaultMapActionButtonSettings: MapActionButtonSettings = { + label: '', + icon: 'add', + color: '#0000008a', + action: { + type: WidgetActionType.doNothing + } +} + +export enum MapProvider { + openstreet = 'openstreet', + google = 'google', + here = 'here', + tencent = 'tencent', + custom = 'custom' +} + +export const mapProviders = Object.keys(MapProvider) as MapProvider[]; + +export const mapProviderTranslationMap = new Map( + [ + [MapProvider.openstreet, 'widgets.maps.layer.provider.openstreet.title'], + [MapProvider.google, 'widgets.maps.layer.provider.google.title'], + [MapProvider.here, 'widgets.maps.layer.provider.here.title'], + [MapProvider.tencent, 'widgets.maps.layer.provider.tencent.title'], + [MapProvider.custom, 'widgets.maps.layer.provider.custom.title'] + ] +); + +export enum ReferenceLayerType { + openstreetmap_hybrid = 'openstreetmap_hybrid', + world_edition_hybrid = 'world_edition_hybrid', +} + +export const referenceLayerTypes = Object.keys(ReferenceLayerType) as ReferenceLayerType[]; + +export const referenceLayerTypeTranslationMap = new Map( + [ + [ReferenceLayerType.openstreetmap_hybrid, 'widgets.maps.layer.reference.openstreetmap-hybrid'], + [ReferenceLayerType.world_edition_hybrid, 'widgets.maps.layer.reference.world-edition-hybrid'] + ] +); + +export interface MapLayerSettings { + label?: string; + provider: MapProvider; + referenceLayer?: ReferenceLayerType; +} + +export const mapLayerValid = (layer: MapLayerSettings): boolean => { + if (!layer.provider) { + return false; + } + switch (layer.provider) { + case MapProvider.openstreet: + const openStreetLayer = layer as OpenStreetMapLayerSettings; + return !!openStreetLayer.layerType; + case MapProvider.google: + const googleLayer = layer as GoogleMapLayerSettings; + return !!googleLayer.layerType; + case MapProvider.here: + const hereLayer = layer as HereMapLayerSettings; + return !!hereLayer.layerType; + case MapProvider.tencent: + const tencentLayer = layer as TencentMapLayerSettings; + return !!tencentLayer.layerType; + case MapProvider.custom: + const customLayer = layer as CustomMapLayerSettings; + return !!customLayer.tileUrl; + } +}; + +export const mapLayerValidator = (control: AbstractControl): ValidationErrors | null => { + const layer: MapLayerSettings = control.value; + if (!mapLayerValid(layer)) { + return { + layer: true + }; + } + return null; +}; + +export const defaultLayerTitle = (layer: MapLayerSettings): string => { + if (!layer.provider) { + return null; + } + switch (layer.provider) { + case MapProvider.openstreet: + const openStreetLayer = layer as OpenStreetMapLayerSettings; + return openStreetMapLayerTranslationMap.get(openStreetLayer.layerType); + case MapProvider.google: + const googleLayer = layer as GoogleMapLayerSettings; + return googleMapLayerTranslationMap.get(googleLayer.layerType); + case MapProvider.here: + const hereLayer = layer as HereMapLayerSettings; + return hereLayerTranslationMap.get(hereLayer.layerType); + case MapProvider.tencent: + const tencentLayer = layer as TencentMapLayerSettings; + return tencentLayerTranslationMap.get(tencentLayer.layerType); + case MapProvider.custom: + return 'widgets.maps.layer.provider.custom.title'; + } +} + +export enum OpenStreetLayerType { + openStreetMapnik = 'OpenStreetMap.Mapnik', + openStreetHot = 'OpenStreetMap.HOT', + esriWorldStreetMap = 'Esri.WorldStreetMap', + esriWorldTopoMap = 'Esri.WorldTopoMap', + esriWorldImagery = 'Esri.WorldImagery', + cartoDbPositron = 'CartoDB.Positron', + cartoDbDarkMatter = 'CartoDB.DarkMatter' +} + +export const openStreetLayerTypes = Object.values(OpenStreetLayerType) as OpenStreetLayerType[]; + +export const openStreetMapLayerTranslationMap = new Map( + [ + [OpenStreetLayerType.openStreetMapnik, 'widgets.maps.layer.provider.openstreet.mapnik'], + [OpenStreetLayerType.openStreetHot, 'widgets.maps.layer.provider.openstreet.hot'], + [OpenStreetLayerType.esriWorldStreetMap, 'widgets.maps.layer.provider.openstreet.esri-street'], + [OpenStreetLayerType.esriWorldTopoMap, 'widgets.maps.layer.provider.openstreet.esri-topo'], + [OpenStreetLayerType.esriWorldImagery, 'widgets.maps.layer.provider.openstreet.esri-imagery'], + [OpenStreetLayerType.cartoDbPositron, 'widgets.maps.layer.provider.openstreet.cartodb-positron'], + [OpenStreetLayerType.cartoDbDarkMatter, 'widgets.maps.layer.provider.openstreet.cartodb-dark-matter'] + ] +); + +export interface OpenStreetMapLayerSettings extends MapLayerSettings { + provider: MapProvider.openstreet; + layerType: OpenStreetLayerType; +} + +export const defaultOpenStreetMapLayerSettings: OpenStreetMapLayerSettings = { + provider: MapProvider.openstreet, + layerType: OpenStreetLayerType.openStreetMapnik +} + +export enum GoogleLayerType { + roadmap = 'roadmap', + satellite = 'satellite', + hybrid = 'hybrid', + terrain = 'terrain' +} + +export const googleMapLayerTypes = Object.values(GoogleLayerType) as GoogleLayerType[]; + +export const googleMapLayerTranslationMap = new Map( + [ + [GoogleLayerType.roadmap, 'widgets.maps.layer.provider.google.roadmap'], + [GoogleLayerType.satellite, 'widgets.maps.layer.provider.google.satellite'], + [GoogleLayerType.hybrid, 'widgets.maps.layer.provider.google.hybrid'], + [GoogleLayerType.terrain, 'widgets.maps.layer.provider.google.terrain'] + ] +); + +export interface GoogleMapLayerSettings extends MapLayerSettings { + provider: MapProvider.google; + layerType: GoogleLayerType; + apiKey: string; +} + +export const defaultGoogleMapLayerSettings: GoogleMapLayerSettings = { + provider: MapProvider.google, + layerType: GoogleLayerType.roadmap, + apiKey: 'AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q' +}; + +export enum HereLayerType { + hereNormalDay = 'HEREv3.normalDay', + hereNormalNight = 'HEREv3.normalNight', + hereHybridDay = 'HEREv3.hybridDay', + hereTerrainDay = 'HEREv3.terrainDay' +} + +export const hereLayerTypes = Object.values(HereLayerType) as HereLayerType[]; + +export const hereLayerTranslationMap = new Map( + [ + [HereLayerType.hereNormalDay, 'widgets.maps.layer.provider.here.normal-day'], + [HereLayerType.hereNormalNight, 'widgets.maps.layer.provider.here.normal-night'], + [HereLayerType.hereHybridDay, 'widgets.maps.layer.provider.here.hybrid-day'], + [HereLayerType.hereTerrainDay, 'widgets.maps.layer.provider.here.terrain-day'] + ] +); + +export interface HereMapLayerSettings extends MapLayerSettings { + provider: MapProvider.here; + layerType: HereLayerType; + apiKey: string; +} + +export const defaultHereMapLayerSettings: HereMapLayerSettings = { + provider: MapProvider.here, + layerType: HereLayerType.hereNormalDay, + apiKey: 'kVXykxAfZ6LS4EbCTO02soFVfjA7HoBzNVVH9u7nzoE' +} + +export enum TencentLayerType { + tencentNormal = 'Tencent.Normal', + tencentSatellite = 'Tencent.Satellite', + tencentTerrain = 'Tencent.Terrain' +} + +export const tencentLayerTypes = Object.values(TencentLayerType) as TencentLayerType[]; + +export const tencentLayerTranslationMap = new Map( + [ + [TencentLayerType.tencentNormal, 'widgets.maps.layer.provider.tencent.normal'], + [TencentLayerType.tencentSatellite, 'widgets.maps.layer.provider.tencent.satellite'], + [TencentLayerType.tencentTerrain, 'widgets.maps.layer.provider.tencent.terrain'] + ] +); + +export interface TencentMapLayerSettings extends MapLayerSettings { + provider: MapProvider.tencent; + layerType: TencentLayerType +} + +export const defaultTencentMapLayerSettings: TencentMapLayerSettings = { + provider: MapProvider.tencent, + layerType: TencentLayerType.tencentNormal +} + +export interface CustomMapLayerSettings extends MapLayerSettings { + provider: MapProvider.custom; + tileUrl: string; +} + +export const defaultCustomMapLayerSettings: CustomMapLayerSettings = { + provider: MapProvider.custom, + tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' +} + +export const defaultMapLayerSettings = (provider: MapProvider): MapLayerSettings => { + switch (provider) { + case MapProvider.openstreet: + return defaultOpenStreetMapLayerSettings; + case MapProvider.google: + return defaultGoogleMapLayerSettings; + case MapProvider.here: + return defaultHereMapLayerSettings; + case MapProvider.tencent: + return defaultTencentMapLayerSettings; + case MapProvider.custom: + return defaultCustomMapLayerSettings; + } +}; + +export const defaultMapLayers: MapLayerSettings[] = [ + { + label: '{i18n:widgets.maps.layer.roadmap}', + provider: MapProvider.openstreet, + layerType: OpenStreetLayerType.openStreetMapnik, + } as OpenStreetMapLayerSettings, + { + label: '{i18n:widgets.maps.layer.satellite}', + provider: MapProvider.openstreet, + layerType: OpenStreetLayerType.esriWorldImagery, + } as OpenStreetMapLayerSettings, + { + label: '{i18n:widgets.maps.layer.hybrid}', + provider: MapProvider.openstreet, + layerType: OpenStreetLayerType.esriWorldImagery, + referenceLayer: ReferenceLayerType.openstreetmap_hybrid + } as OpenStreetMapLayerSettings +]; + +export interface GeoMapSettings extends BaseMapSettings { + layers?: MapLayerSettings[]; +} + +export const defaultGeoMapSettings: GeoMapSettings = { + mapType: MapType.geoMap, + layers: mergeDeep([], defaultMapLayers), + ...mergeDeep({} as BaseMapSettings, defaultBaseMapSettings) +}; + +export enum ImageSourceType { + image = 'image', + entityKey = 'entityKey' +} + +export interface ImageMapSourceSettings { + sourceType: ImageSourceType; + url?: string; + entityAliasId?: string; + entityKey?: DataKey; +} + +export const imageMapSourceSettingsValid = (imageSource: ImageMapSourceSettings): boolean => { + if (!imageSource?.sourceType) { + return false; + } else if (imageSource.sourceType === ImageSourceType.image) { + return isNotEmptyStr(imageSource.url); + } else { + return isNotEmptyStr(imageSource.entityAliasId) && !!imageSource.entityKey; + } +} + +export const imageMapSourceSettingsValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + const imageSource: ImageMapSourceSettings = control.value; + if (!imageMapSourceSettingsValid(imageSource)) { + return { + imageMapSource: true + }; + } + return null; +}; + +export const defaultImageMapSourceSettings: ImageMapSourceSettings = { + sourceType: ImageSourceType.image, + url: 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==', +}; + +export const imageMapSourceSettingsToDatasource = (settings: ImageMapSourceSettings): Datasource => { + return { + type: DatasourceType.entity, + name: '', + entityAliasId: settings.entityAliasId, + dataKeys: [settings.entityKey] + }; +}; + +export interface ImageMapSettings extends BaseMapSettings { + imageSource?: ImageMapSourceSettings; +} + +export const defaultImageMapSettings: ImageMapSettings = { + mapType: MapType.image, + imageSource: mergeDeep({} as ImageMapSourceSettings, defaultImageMapSourceSettings), + ...mergeDeep({} as BaseMapSettings, defaultBaseMapSettings) +} + +export type MapSetting = GeoMapSettings & ImageMapSettings; + +export const defaultMapSettings: MapSetting = defaultGeoMapSettings; + +export interface MarkerImageInfo { + url: string; + size: number; + markerOffset?: [number, number]; + tooltipOffset?: [number, number]; +} + +export interface MarkerIconInfo { + icon: L.Icon; + size: [number, number]; +} + +export type MapStringFunction = (data: FormattedData, + dsData: FormattedData[]) => string; + +export type MapBooleanFunction = (data: FormattedData, + dsData: FormattedData[]) => boolean; + +export type MarkerImageFunction = (data: FormattedData, markerImages: string[], + dsData: FormattedData[]) => MarkerImageInfo; + +export type ClusterMarkerColorFunction = (data: FormattedData[], childCount: number) => string; + +export type MarkerPositionFunction = (origXPos: number, origYPos: number, data: FormattedData, + dsData: FormattedData[], aspect: number) => { x: number, y: number }; + +export type TbPolygonRawCoordinate = L.LatLngTuple | L.LatLngTuple[] | L.LatLngTuple[][]; +export type TbPolygonRawCoordinates = TbPolygonRawCoordinate[]; +export type TbPolyData = L.LatLngTuple[] | L.LatLngTuple[][] | L.LatLngTuple[][][]; +export type TbPolygonCoordinate = L.LatLng | L.LatLng[] | L.LatLng[][]; +export type TbPolygonCoordinates = TbPolygonCoordinate[]; + +export interface TbCircleData { + latitude: number; + longitude: number; + radius: number; +} + +export type DataKeyValuePair = { + dataKey: DataKey; + value: any; +} + +export const isJSON = (data: string): boolean => { + try { + const parseData = JSON.parse(data); + return !Array.isArray(parseData); + } catch (e) { + return false; + } +} + +export const isValidLatitude = (latitude: any): boolean => + isDefinedAndNotNull(latitude) && + !isString(latitude) && + !isNaN(latitude) && isFinite(latitude) && Math.abs(latitude) <= 90; + +export const isValidLongitude = (longitude: any): boolean => + isDefinedAndNotNull(longitude) && + !isString(longitude) && + !isNaN(longitude) && isFinite(longitude) && Math.abs(longitude) <= 180; + +export const isValidLatLng = (latitude: any, longitude: any): boolean => + isValidLatitude(latitude) && isValidLongitude(longitude); + +export const isCutPolygon = (data: TbPolygonCoordinates | TbPolygonRawCoordinates): boolean => { + return data.length > 1 && Array.isArray(data[0]) && (Array.isArray(data[0][0]) || (isNumber((data[0][0] as any).lat) && isNumber((data[0][0] as any).lng)) ); +} + +export const parseCenterPosition = (position: string | [number, number]): [number, number] => { + if (typeof (position) === 'string') { + const parts = position.split(','); + if (parts.length === 2) { + return [Number(parts[0]), Number(parts[1])]; + } + } + if (typeof (position) === 'object') { + return position; + } + return [0, 0]; +} + +export const mergeMapDatasources = (target: TbMapDatasource[], source: TbMapDatasource[]): TbMapDatasource[] => { + const appendDatasources: TbMapDatasource[] = []; + for (const sourceDs of source) { + let merged = false; + for (let i = 0; i < target.length; i++) { + const targetDs = target[i]; + if (mapDatasourceIsSame(targetDs, sourceDs)) { + target[i] = mergeMapDatasource(targetDs, sourceDs); + merged = true; + break; + } + } + if (!merged) { + appendDatasources.push(sourceDs); + } + } + target.push(...appendDatasources); + return target; +}; + +const mapDatasourceIsSame = (ds1: TbMapDatasource, ds2: TbMapDatasource): boolean => { + if (ds1.type === ds2.type) { + switch (ds1.type) { + case DatasourceType.function: + return ds1.name === ds2.name; + case DatasourceType.device: + case DatasourceType.entity: + if (ds1.filterId === ds2.filterId) { + if (ds1.type === DatasourceType.device) { + return ds1.deviceId === ds2.deviceId; + } else { + return ds1.entityAliasId === ds2.entityAliasId; + } + } + } + } + return false; +} + +const mergeMapDatasource = (target: TbMapDatasource, source: TbMapDatasource): TbMapDatasource => { + target.mapDataIds.push(...source.mapDataIds); + const appendKeys: DataKey[] = []; + for (const sourceKey of source.dataKeys) { + const found = + target.dataKeys.find(key => key.type === sourceKey.type && key.name === sourceKey.name && key.label === sourceKey.label); + if (!found) { + appendKeys.push(sourceKey); + } + } + target.dataKeys.push(...appendKeys); + return target; +} + +const imageAspectMap: {[key: string]: ImageWithAspect} = {}; + +const imageLoader = (imageUrl: string): Observable => new Observable((observer: Observer) => { + const image = document.createElement('img'); // support IE + image.style.position = 'absolute'; + image.style.left = '-99999px'; + image.style.top = '-99999px'; + image.onload = () => { + observer.next(image); + document.body.removeChild(image); + observer.complete(); + }; + image.onerror = err => { + observer.error(err); + document.body.removeChild(image); + observer.complete(); + }; + document.body.appendChild(image); + image.src = imageUrl; +}); + +const loadImageAspect = (imageUrl: string): Observable => + imageLoader(imageUrl).pipe(map(image => image.width / image.height)); + +export interface ImageWithAspect { + url: string; + aspect: number; +} + +export const loadImageWithAspect = (imagePipe: ImagePipe, imageUrl: string): Observable => { + if (imageUrl?.length) { + const hash = hashCode(imageUrl); + let imageWithAspect = imageAspectMap[hash]; + if (imageWithAspect) { + return of(imageWithAspect); + } else { + return imagePipe.transform(imageUrl, {asString: true, ignoreLoadingImage: true}).pipe( + switchMap((res) => { + const url = res as string; + return loadImageAspect(url).pipe( + map((aspect) => { + imageWithAspect = {url, aspect}; + imageAspectMap[hash] = imageWithAspect; + return imageWithAspect; + }) + ); + }) + ); + } + } else { + return of(null); + } +}; + +const linkActionRegex = /([^<]*)<\/link-act>/g; +const buttonActionRegex = /([^<]*)<\/button-act>/g; + +const createTooltipLinkElement = (actionName: string, actionText: string): string => { + return `
${actionText}`; +} + +const creatTooltipButtonElement = (actionName: string, actionText: string): string => { + return ``; +} + +export const processTooltipTemplate = (template: string): string => { + let actionTags: string; + let actionText: string; + let actionName: string; + let action: string; + + let match = linkActionRegex.exec(template); + while (match !== null) { + [actionTags, actionName, actionText] = match; + action = createTooltipLinkElement(actionName, actionText); + template = template.replace(actionTags, action); + match = linkActionRegex.exec(template); + } + + match = buttonActionRegex.exec(template); + while (match !== null) { + [actionTags, actionName, actionText] = match; + action = creatTooltipButtonElement(actionName, actionText); + template = template.replace(actionTags, action); + match = buttonActionRegex.exec(template); + } + + return template; +} + +export const calculateNewPointCoordinate = (coordinate: number, imageSize: number): number => { + let pointCoordinate = coordinate / imageSize; + if (pointCoordinate < 0) { + pointCoordinate = 0; + } else if (pointCoordinate > 1) { + pointCoordinate = 1; + } + return pointCoordinate; +} + +export const latLngPointToBounds = (point: L.LatLng, southWest: L.LatLng, northEast: L.LatLng, offset = 0): L.LatLng => { + const maxLngMap = northEast.lng - offset; + const minLngMap = southWest.lng + offset; + const maxLatMap = northEast.lat - offset; + const minLatMap = southWest.lat + offset; + if (point.lng > maxLngMap) { + point.lng = maxLngMap; + } else if (point.lng < minLngMap) { + point.lng = minLngMap; + } + if (point.lat > maxLatMap) { + point.lat = maxLatMap; + } else if (point.lat < minLatMap) { + point.lat = minLatMap; + } + return point; +} + +export type TripRouteData = {[time: number]: FormattedData}; + +export const calculateInterpolationRatio = (firsMoment: number, secondMoment: number, intermediateMoment: number): number => { + return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); +} + +export const interpolateLineSegment = ( + pointA: FormattedData, + pointB: FormattedData, + xKey: string, + yKey: string, + ratio: number +): { [key: string]: number } => { + return { + [xKey]: (pointA[xKey] + (pointB[xKey] - pointA[xKey]) * ratio), + [yKey]: (pointA[yKey] + (pointB[yKey] - pointA[yKey]) * ratio) + }; +} + +export const findRotationAngle = (startPoint: L.LatLng, endPoint: L.LatLng): number => { + if (isUndefinedOrNull(startPoint) || isUndefinedOrNull(endPoint)) { + return 0; + } + let angle = -Math.atan2(endPoint.lat - startPoint.lat, endPoint.lng - startPoint.lng); + angle = angle * 180 / Math.PI; + return parseInt(angle.toFixed(2), 10); +} + +export const calculateLastPoints = (routeData: TripRouteData, time: number): FormattedData => { + const timeArr = Object.keys(routeData); + let index = timeArr.findIndex((dtime) => { + return Number(dtime) >= time; + }); + + if (index !== -1) { + if (Number(timeArr[index]) !== time && index !== 0) { + index--; + } + } else { + index = timeArr.length - 1; + } + + return routeData[timeArr[index]]; +} diff --git a/ui-ngx/src/app/shared/models/widget/maps/marker-shape.models.ts b/ui-ngx/src/app/shared/models/widget/maps/marker-shape.models.ts new file mode 100644 index 0000000000..29e04dbaa1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/widget/maps/marker-shape.models.ts @@ -0,0 +1,344 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import tinycolor from 'tinycolor2'; +import { MatIconRegistry } from '@angular/material/icon'; +import { DomSanitizer } from '@angular/platform-browser'; +import { Observable, of, shareReplay, switchMap } from 'rxjs'; +import { catchError, map, take } from 'rxjs/operators'; +import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; +import { Element, G, SVG, Text } from '@svgdotjs/svg.js'; +import { guid } from '@core/utils'; + +export enum MarkerShape { + markerShape1 = 'markerShape1', + markerShape2 = 'markerShape2', + markerShape3 = 'markerShape3', + markerShape4 = 'markerShape4', + markerShape5 = 'markerShape5', + markerShape6 = 'markerShape6', + markerShape7 = 'markerShape7', + markerShape8 = 'markerShape8', + markerShape9 = 'markerShape9', + markerShape10 = 'markerShape10', + tripMarkerShape1 = 'tripMarkerShape1', + tripMarkerShape2 = 'tripMarkerShape2', + tripMarkerShape3 = 'tripMarkerShape3', + tripMarkerShape4 = 'tripMarkerShape4', + tripMarkerShape5 = 'tripMarkerShape5', + tripMarkerShape6 = 'tripMarkerShape6', + tripMarkerShape7 = 'tripMarkerShape7', + tripMarkerShape8 = 'tripMarkerShape8', + tripMarkerShape9 = 'tripMarkerShape9', + tripMarkerShape10 = 'tripMarkerShape10' +} + +export enum MarkerIconContainer { + iconContainer1 = 'iconContainer1', + iconContainer2 = 'iconContainer2', + iconContainer3 = 'iconContainer3', + tripIconContainer1 = 'tripIconContainer1', + tripIconContainer2 = 'tripIconContainer2', + tripIconContainer3 = 'tripIconContainer3' +} + +const markerShapeMap = new Map( + [ + [MarkerShape.markerShape1, '/assets/markers/shape1.svg'], + [MarkerShape.markerShape2, '/assets/markers/shape2.svg'], + [MarkerShape.markerShape3, '/assets/markers/shape3.svg'], + [MarkerShape.markerShape4, '/assets/markers/shape4.svg'], + [MarkerShape.markerShape5, '/assets/markers/shape5.svg'], + [MarkerShape.markerShape6, '/assets/markers/shape6.svg'], + [MarkerShape.markerShape7, '/assets/markers/shape7.svg'], + [MarkerShape.markerShape8, '/assets/markers/shape8.svg'], + [MarkerShape.markerShape9, '/assets/markers/shape9.svg'], + [MarkerShape.markerShape10, '/assets/markers/shape10.svg'], + [MarkerShape.tripMarkerShape1, '/assets/markers/tripShape1.svg'], + [MarkerShape.tripMarkerShape2, '/assets/markers/tripShape2.svg'], + [MarkerShape.tripMarkerShape3, '/assets/markers/tripShape3.svg'], + [MarkerShape.tripMarkerShape4, '/assets/markers/tripShape4.svg'], + [MarkerShape.tripMarkerShape5, '/assets/markers/tripShape5.svg'], + [MarkerShape.tripMarkerShape6, '/assets/markers/tripShape6.svg'], + [MarkerShape.tripMarkerShape7, '/assets/markers/tripShape7.svg'], + [MarkerShape.tripMarkerShape8, '/assets/markers/tripShape8.svg'], + [MarkerShape.tripMarkerShape9, '/assets/markers/tripShape9.svg'], + [MarkerShape.tripMarkerShape10, '/assets/markers/tripShape10.svg'] + ] +); + +const markerIconContainerMap = new Map( + [ + [MarkerIconContainer.iconContainer1, '/assets/markers/iconContainer1.svg'], + [MarkerIconContainer.iconContainer2, '/assets/markers/iconContainer2.svg'], + [MarkerIconContainer.iconContainer3, '/assets/markers/iconContainer3.svg'], + [MarkerIconContainer.tripIconContainer1, '/assets/markers/tripIconContainer1.svg'], + [MarkerIconContainer.tripIconContainer2, '/assets/markers/tripIconContainer2.svg'], + [MarkerIconContainer.tripIconContainer3, '/assets/markers/tripIconContainer3.svg'] + ] +); + +interface MarkerIconContainerDefinition { + iconSize: number; + iconColor: (color: tinycolor.Instance) => tinycolor.Instance; + iconAlpha: (color: tinycolor.Instance) => number; + appendIcon?: (svgElement: SVGElement, iconElement: Element) => void; +} + +const emptyIconContainerDefinition: MarkerIconContainerDefinition = { + iconSize: 24, + iconColor: (color) => color, + iconAlpha: color => color.getAlpha() +} + +const defaultIconContainerDefinition: MarkerIconContainerDefinition = { + iconSize: 12, + iconColor: (color) => tinycolor.mix(color.clone().setAlpha(1), tinycolor('rgba(0,0,0,0.38)')), + iconAlpha: color => color.getAlpha() +} + +const defaultTripIconContainerDefinition: MarkerIconContainerDefinition = { + iconSize: 24, + iconColor: () => tinycolor('#000'), + iconAlpha: () => 1, + appendIcon: (svgElement, iconElement) => { + const box = iconElement.bbox(); + let elements = svgElement.getElementsByClassName('icon-mask-exclude'); + if (elements.length) { + elements = elements[0].getElementsByClassName('marker-icon-container'); + if (elements.length) { + const iconContainer = new G(elements[0] as SVGGElement); + iconContainer.add(iconElement.clone().fill('#000').translate(-box.cx, -box.cy)); + } + } + elements = svgElement.getElementsByClassName('icon-mask-overlay'); + if (elements.length) { + elements = elements[0].getElementsByClassName('marker-icon-container'); + if (elements.length) { + const iconContainer = new G(elements[0] as SVGGElement); + iconContainer.add(iconElement.clone().fill('#fff').translate(-box.cx, -box.cy)); + } + } + } +} + +const markerIconContainerDefinitionMap = new Map( + [ + [MarkerIconContainer.iconContainer1, defaultIconContainerDefinition], + [MarkerIconContainer.iconContainer2, defaultIconContainerDefinition], + [MarkerIconContainer.iconContainer3, defaultIconContainerDefinition], + [MarkerIconContainer.tripIconContainer1, defaultTripIconContainerDefinition], + [MarkerIconContainer.tripIconContainer2, {...defaultTripIconContainerDefinition, iconSize: 16}], + [MarkerIconContainer.tripIconContainer3, {...defaultTripIconContainerDefinition, iconSize: 16}] + ] +); + +export const markerShapes = [ + MarkerShape.markerShape1, + MarkerShape.markerShape2, + MarkerShape.markerShape3, + MarkerShape.markerShape4, + MarkerShape.markerShape5, + MarkerShape.markerShape6, + MarkerShape.markerShape7, + MarkerShape.markerShape8, + MarkerShape.markerShape9, + MarkerShape.markerShape10 +]; + +export const tripMarkerShapes = [ + MarkerShape.tripMarkerShape1, + MarkerShape.tripMarkerShape2, + MarkerShape.tripMarkerShape3, + MarkerShape.tripMarkerShape4, + MarkerShape.tripMarkerShape5, + MarkerShape.tripMarkerShape6, + MarkerShape.tripMarkerShape7, + MarkerShape.tripMarkerShape8, + MarkerShape.tripMarkerShape9, + MarkerShape.tripMarkerShape10 +]; + +export const markerIconContainers = [ + MarkerIconContainer.iconContainer1, + MarkerIconContainer.iconContainer2, + MarkerIconContainer.iconContainer3 +]; + +export const tripMarkerIconContainers = [ + MarkerIconContainer.tripIconContainer1, + MarkerIconContainer.tripIconContainer2, + MarkerIconContainer.tripIconContainer3 +]; + +const generateElementId = () => { + const id = guid(); + const firstChar = id.charAt(0); + if (firstChar >= '0' && firstChar <= '9') { + return 'a' + id; + } else { + return id; + } +}; + +const prepareSvgIds = (element: SVGElement): SVGElement => { + let svgContent = element.outerHTML; + const regexp = /\sid="([^"]*)"[\s>]/g; + const unique_id_suffix = '_' + generateElementId(); + const ids: string[] = []; + let match = regexp.exec(svgContent); + while (match !== null) { + ids.push(match[1]); + match = regexp.exec(svgContent); + } + for (const id of ids) { + const newId = id + unique_id_suffix; + svgContent = svgContent.replace(new RegExp('id="'+id+'"', 'g'), 'id="'+newId+'"'); + svgContent = svgContent.replace(new RegExp('url\\(#'+id+'\\)', 'g'), 'url(#'+newId+')'); + } + return SVG(svgContent).node; +}; + +const createColorMarkerShape = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer, assetUrl: string, color: tinycolor.Instance): Observable => { + const safeUrl = domSanitizer.bypassSecurityTrustResourceUrl(assetUrl); + return iconRegistry.getSvgIconFromUrl(safeUrl).pipe( + map((svgElement) => { + const colorElements = Array.from(svgElement.getElementsByClassName('marker-color')); + if (svgElement.classList.contains('marker-color')) { + colorElements.push(svgElement); + } + colorElements.forEach(el => { + el.setAttribute('fill', '#'+color.toHex()); + el.setAttribute('fill-opacity', `${color.getAlpha()}`); + }); + const opacityElements = Array.from(svgElement.getElementsByClassName('marker-opacity')); + if (svgElement.classList.contains('marker-opacity')) { + opacityElements.push(svgElement); + } + opacityElements.forEach(el => { + el.setAttribute('opacity', `${color.getAlpha()}`); + }); + return prepareSvgIds(svgElement); + }) + ); +} + + +export const createColorMarkerShapeURI = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer, shape: MarkerShape, color: tinycolor.Instance): Observable => { + const assetUrl = markerShapeMap.get(shape); + return createColorMarkerShape(iconRegistry, domSanitizer, assetUrl, color).pipe( + map((svgElement) => { + const svg = svgElement.outerHTML; + return 'data:image/svg+xml;base64,' + btoa(svg); + }) + ); +} + +const createIconElement = (iconRegistry: MatIconRegistry, icon: string, size: number, iconColor: tinycolor.Instance, iconAlpha: number): Observable => { + const isSvg = isSvgIcon(icon); + if (isSvg) { + const [namespace, iconName] = splitIconName(icon); + return iconRegistry + .getNamedSvgIcon(iconName, namespace) + .pipe( + take(1), + map((svgElement) => { + const element = new Element(svgElement.firstChild); + element.fill('#'+iconColor.toHex()); + element.attr('fill-opacity', iconAlpha); + const scale = size / 24; + element.scale(scale); + return element; + }), + catchError(() => of(null)) + ); + } else { + const iconName = splitIconName(icon)[1]; + const textElement = new Text(document.createElementNS('http://www.w3.org/2000/svg', 'text')); + const fontSetClasses = ( + iconRegistry.getDefaultFontSetClass() + ).filter(className => className.length > 0); + fontSetClasses.forEach(className => textElement.addClass(className)); + textElement.font({size: `${size}px`}); + textElement.attr({ + style: `font-size: ${size}px`, + 'text-anchor': 'start' + }); + textElement.fill('#'+iconColor.toHex()); + textElement.attr('fill-opacity', iconAlpha); + const tspan = textElement.tspan(iconName); + tspan.attr({ + 'dominant-baseline': 'hanging' + }); + return of(textElement); + } +} + +export const createColorMarkerIconElement = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer, + iconContainer: MarkerIconContainer, icon: string, color: tinycolor.Instance): Observable => { + const markerShape$: Observable = iconContainer ? + createColorMarkerShape(iconRegistry, domSanitizer, markerIconContainerMap.get(iconContainer), color) : of(null); + return markerShape$.pipe( + switchMap((svgElement) => { + const definition = iconContainer ? markerIconContainerDefinitionMap.get(iconContainer) : emptyIconContainerDefinition; + const iconSize = definition.iconSize; + const iconColor = definition.iconColor(color); + const iconAlpha = definition.iconAlpha(color); + return createIconElement(iconRegistry, icon, iconSize, iconColor, iconAlpha).pipe( + map((iconElement) => { + if (svgElement) { + if (iconElement) { + if (definition.appendIcon) { + definition.appendIcon(svgElement, iconElement); + } else { + const elements = svgElement.getElementsByClassName('marker-icon-container'); + if (elements.length) { + const iconContainer = new G(elements[0] as SVGGElement); + iconContainer.add(iconElement); + const box = iconElement.bbox(); + iconElement.translate(-box.cx, -box.cy); + } + } + } + return svgElement; + } else { + const svg = SVG(); + svg.viewbox(0,0,iconSize,iconSize); + const iconContainer = new G(); + iconContainer.translate(iconSize/2,iconSize/2); + iconContainer.add(iconElement); + const box = iconElement.bbox(); + iconElement.translate(-box.cx, -box.cy); + svg.add(iconContainer); + return svg.node; + } + }) + ); + }) + ); +} + +let placeItemIconURI$: Observable; + +export const createPlaceItemIcon = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer): Observable => { + if (placeItemIconURI$) { + return placeItemIconURI$; + } + placeItemIconURI$ = createColorMarkerShapeURI(iconRegistry, domSanitizer, MarkerShape.markerShape1, tinycolor('rgba(255,255,255,0.75)')).pipe( + shareReplay({refCount: true, bufferSize: 1}) + ); + return placeItemIconURI$; +} diff --git a/ui-ngx/src/app/shared/models/widget/widget-export.models.ts b/ui-ngx/src/app/shared/models/widget/widget-export.models.ts new file mode 100644 index 0000000000..5b47010285 --- /dev/null +++ b/ui-ngx/src/app/shared/models/widget/widget-export.models.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Widget } from '@shared/models/widget.models'; +import { Dashboard } from '@shared/models/dashboard.models'; +import { EntityAliases } from '@shared/models/alias.models'; +import { Filters } from '@shared/models/query/query.models'; +import { MapExportDefinition } from '@shared/models/widget/maps/map-export.models'; + +export interface WidgetExportDefinition { + testWidget(widget: Widget): boolean; + prepareExportInfo(dashboard: Dashboard, widget: Widget): T; + updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: T): void; +} + +const widgetExportDefinitions: WidgetExportDefinition[] = [ + MapExportDefinition +]; + +export const getWidgetExportDefinition = (widget: Widget): WidgetExportDefinition => { + return widgetExportDefinitions.find(def => def.testWidget(widget)); +} diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index f8173dc528..f46923ebd3 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -1 +1,400 @@ - +## Calculated Field TBEL Script Function + +The **calculate()** function is a user-defined script that enables custom calculations using [TBEL](\${siteBaseUrl}/docs\${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data. +It receives arguments configured in the calculated field setup, along with an additional `ctx` object that provides access to all arguments. + + +### Function Signature + +```javascript +function calculate(ctx, arg1, arg2, ...): object | object[] +``` + +### Supported Arguments + +There are three types of arguments supported in the calculated field configuration: + +#### Attribute and Latest Telemetry Arguments + +These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON. + +Attribute and Latest telemetry are single value arguments that may be one of: boolean, int64 (long), double, string and JSON. + +**Example: Convert Temperature from Fahrenheit to Celsius** + +```javascript +var temperatureC = (temperatureF - 32) / 1.8; +return { + "temperatureC": toFixed(temperatureC, 2) +} +``` + +Alternatively, using `ctx` to access the argument as an object: + +```json +{ + "temperatureF": { + "ts": 1740644636669, + "value": 36.6 + } +} +``` + +You may notice that the object includes both the `value` of an argument and its timestamp as `ts`. +Let's modify the function that converts Fahrenheit to Celsius to also return the timestamp information: + +```javascript +var temperatureC = (temperatureF - 32) / 1.8; +return { + "ts": ctx.args.temperatureF.ts, + "values": { "temperatureC": toFixed(temperatureC, 2) } +}; +``` + +#### Time Series Rolling Arguments + +These contain time series data within a defined time window. Example format: + +```json +{ + "temperature": { + "timeWindow": { + "startTs": 1740643762896, + "endTs": 1740644662896 + }, + "values": [ + { "ts": 1740644350000, "value": 72.32 }, + { "ts": 1740644360000, "value": 72.86 }, + { "ts": 1740644370000, "value": 73.58 }, + { "ts": 1740644380000, "value": "NaN" } + ] + } +} +``` + +The values are always converted to type `double`, and `NaN` is used when conversion fails. One may use `isNaN(double): boolean` function to check that the value is a valid number. + +**Example: Accessing time series rolling argument data** + +```javascript +var startOfInterval = temperature.timeWindow.startTs; +var endOfInterval = temperature.timeWindow.endTs; +var firstItem = temperature.values[0]; +var firstItemTs = firstItem.ts; +var firstItemValue = firstItem.value; +var sum = 0.0; +// iterate through all values and calculate the sum using foreach: +foreach(t: temperature) { + if(!isNaN(t.value)) { // check that the value is a valid number; + sum += t.value; + } +} +// iterate through all values and calculate the sum using for loop: +sum = 0.0; +for(var i = 0; i < temperature.values.size; i++) { + sum += temperature.values[i].value; +} +// use built-in function to calculate the sum +sum = t.sum(); +``` + +##### Built-in Methods for Rolling Arguments + +Time series rolling arguments support built-in functions for calculations. These functions accept an optional `ignoreNaN` boolean parameter. + +| Method | Default Behavior (`ignoreNaN = true`) | Alternative (`ignoreNaN = false`) | +|------------|-----------------------------------------------------|---------------------------------------------| +| `max()` | Returns the highest value, ignoring NaN values. | Returns NaN if any NaN values exist. | +| `min()` | Returns the lowest value, ignoring NaN values. | Returns NaN if any NaN values exist. | +| `mean()` | Computes the average value, ignoring NaN values. | Returns NaN if any NaN values exist. | +| `std()` | Calculates the standard deviation, ignoring NaN. | Returns NaN if any NaN values exist. | +| `median()` | Returns the median value, ignoring NaN values. | Returns NaN if any NaN values exist. | +| `count()` | Counts values, ignoring NaN values. | Counts all values, including NaN. | +| `last()` | Returns the most recent value, skipping NaN values. | Returns the last value, even if it is NaN. | +| `first()` | Returns the oldest value, skipping NaN values. | Returns the first value, even if it is NaN. | +| `sum()` | Computes the total sum, ignoring NaN values. | Returns NaN if any NaN values exist. | + + +Usage example: + +```javascript +var avgTemp = temperature.mean(); // Returns 72.92 +var tempMax = temperature.max(); // Returns 73.58 +var valueCount = temperature.count(); // Returns 3 + +var avgTempNaN = temperature.mean(false); // Returns NaN +var tempMaxNaN = temperature.max(false); // Returns NaN +var valueCountNaN = temperature.count(false); // Returns 4 +``` + +This function calculates air density using `altitude` (single value) and `temperature` (time series rolling argument). + +```javascript +function calculate(ctx, altitude, temperature) { + var avgTemperature = temperature.mean(); // Get average temperature + var temperatureK = (avgTemperature - 32) * (5 / 9) + 273.15; // Convert Fahrenheit to Kelvin + + // Estimate air pressure based on altitude + var pressure = 101325 * Math.pow((1 - 2.25577e-5 * altitude), 5.25588); + + // Air density formula + var airDensity = pressure / (287.05 * temperatureK); + + return { + "airDensity": airDensity + }; +} +``` + +##### Merging Time Series Arguments + +Time series rolling arguments can be **merged** to align timestamps across multiple datasets. + +| Method | Description | Parameters | Returns | +|:-----------------------------|:--------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------| +| `merge(other, settings)` | Merges with another rolling argument. Aligns timestamps and filling missing values with the previous available value. |
  • `other` (another rolling argument)
  • `settings` (optional) - configuration object, supports:

    • `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored.
    • `timeWindow` (object, default {}) - defines a custom time window for filtering merged values.
| Merged object with timeWindow and aligned values. | +| `mergeAll(others, settings)` | Merges with multiple rolling arguments. Aligns timestamps and filling missing values with the previous available value. |
  • `others` (array of rolling arguments)
  • `settings` (optional) - configuration object, supports:

    • `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored.
    • `timeWindow` (object, default {}) - defines a custom time window for filtering merged values.
| Merged object with timeWindow and aligned values.| + +Assuming the following arguments and their values: + +```json +{ + "humidity": { + "timeWindow": { + "startTs": 1741356332086, + "endTs": 1741357232086 + }, + "values": [{ + "ts": 1741356882759, + "value": 43 + }, { + "ts": 1741356918779, + "value": 46 + }] + }, + "pressure": { + "timeWindow": { + "startTs": 1741356332086, + "endTs": 1741357232086 + }, + "values": [{ + "ts": 1741357047945, + "value": 1023 + }, { + "ts": 1741357056144, + "value": 1026 + }, { + "ts": 1741357147391, + "value": 1025 + }] + }, + "temperature": { + "timeWindow": { + "startTs": 1741356332086, + "endTs": 1741357232086 + }, + "values": [{ + "ts": 1741356874943, + "value": 76 + }, { + "ts": 1741357063689, + "value": 77 + }] + } +} +``` + +**Usage:** + +```javascript +var mergedData = temperature.merge(humidity, { ignoreNaN: false }); +``` + +**Output:** + +```json +{ + "mergedData": { + "timeWindow": { + "startTs": 1741356332086, + "endTs": 1741357232086 + }, + "values": [{ + "ts": 1741356874943, + "values": [76.0, "NaN"] + }, { + "ts": 1741356882759, + "values": [76.0, 43.0] + }, { + "ts": 1741356918779, + "values": [76.0, 46.0] + }, { + "ts": 1741357063689, + "values": [77.0, 46.0] + }] + } +} +``` + +**Usage:** + +```javascript +var mergedData = temperature.mergeAll([humidity, pressure], { ignoreNaN: true }); +``` + +**Output:** + +```json +{ + "mergedData": { + "timeWindow": { + "startTs": 1741356332086, + "endTs": 1741357232086 + }, + "values": [{ + "ts": 1741357047945, + "values": [76.0, 46.0, 1023.0] + }, { + "ts": 1741357056144, + "values": [76.0, 46.0, 1026.0] + }, { + "ts": 1741357063689, + "values": [77.0, 46.0, 1026.0] + }, { + "ts": 1741357147391, + "values": [77.0, 46.0, 1025.0] + }] + } +} +``` + +**Example: Freezer temperature analysis** + +This function merges `temperature` data with the fridge's `defrost` status. It then analyzes the merged data to identify instances where the fridge is not in defrost mode, yet the internal air temperature is too high ( > -5° C). + +```javascript +function calculate(ctx, temperature, defrost) { + var merged = temperature.merge(defrost); + var result = []; + + foreach(item: merged) { + if (item.v1 > -5.0 && item.v2 == 0) { + result.add({ + ts: item.ts, + values: { + issue: { + temperature: item.v1, + defrostState: false + } + } + }); + } + } + + return result; +} +``` + +The result is a list of issues that may be used to configure alarm rules: + +```json +[{ + "ts": 1741613833843, + "values": { + "issue": { + "temperature": -3.12, + "defrostState": false + } + } +}, { + "ts": 1741613923848, + "values": { + "issue": { + "temperature": -4.16, + "defrostState": false + } + } +}] +``` + +### Function return format + +The return format depends on the output type configured in the calculated field settings (default: **Time Series**). + +##### Time Series Output + +The function must return a JSON object or array with or without a timestamp. +Examples below return 5 data points: airDensity (double), humidity (integer), hvacEnabled (boolean), hvacState (string) and configuration (JSON): + +Without timestamp: + +```json +{ + "airDensity": 1.06, + "humidity": 70, + "hvacEnabled": true, + "hvacState": "IDLE", + "configuration": { + "someNumber": 42, + "someArray": [1,2,3], + "someNestedObject": {"key": "value"} + } +} +``` + +With timestamp: + +```json +{ + "ts": 1740644636669, + "values": { + "airDensity": 1.06, + "humidity": 70, + "hvacEnabled": true, + "hvacState": "IDLE", + "configuration": { + "someNumber": 42, + "someArray": [1,2,3], + "someNestedObject": {"key": "value"} + } + } +} +``` + +Array containing multiple timestamps and different values of the `airDensity` : + +```json +[ + { + "ts": 1740644636669, + "values": { + "airDensity": 1.06 + } + }, + { + "ts": 1740644636670, + "values": { + "airDensity": 1.07 + } + } +] +``` + +##### Attribute Output + +The function must return a JSON object **without timestamp** information. +Example below return 5 data points: airDensity (double), humidity (integer), hvacEnabled (boolean), hvacState (string) and configuration (JSON): + +```json +{ + "airDensity": 1.06, + "humidity": 70, + "hvacEnabled": true, + "hvacState": "IDLE", + "configuration": { + "someNumber": 42, + "someArray": [1,2,3], + "someNestedObject": {"key": "value"} + } +} +``` diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/clustering_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/clustering_color_fn.md new file mode 100644 index 0000000000..ba58fa3cd3 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/clustering_color_fn.md @@ -0,0 +1,65 @@ +#### Clustering marker function + +
+
+ +*function (data, childCount): string* + +A JavaScript function used to compute clustering marker color. + +**Parameters:** + +
    +
  • data: FormattedData[] + - the array of total markers contained within each cluster.
    + Represents basic entity properties (ex. entityId, entityName)
    and provides access to other entity attributes/timeseries declared in widget datasource configuration. +
  • +
  • childCount: number - the total number of markers contained within that cluster +
  • +
+ +**Returns:** + +Should return string value presenting color of the marker. + +In case no data is returned, color value from **Color** settings field will be used. + +
+ +##### Examples + +
    +
  • +Calculate color depending on temperature telemetry value: +
  • + + +```javascript +let customColor; +for (let markerData of data) { + if (markerData.temperature > 40) { + customColor = 'red' + } +} +return customColor ? customColor : 'green'; +{:copy-code} +``` + +
  • +Calculate color depending on childCount: +
  • + +```javascript +if (childCount < 10) { + return 'green'; +} else if (childCount < 100) { + return 'yellow'; +} else { + return 'red'; +} +{:copy-code} +``` + +
+
+
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/color_fn.md new file mode 100644 index 0000000000..2f461e28a6 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/color_fn.md @@ -0,0 +1,42 @@ +#### Marker color function + +
+
+ +*function (data, dsData, dsIndex): string* + +A JavaScript function used to compute color of the marker. + +**Parameters:** + +
    + {% include widget/lib/map-legacy/map_fn_args %} +
+ +**Returns:** + +Should return string value presenting color of the marker. + +In case no data is returned, color value from **Color** settings field will be used. + +
+ +##### Examples + +* Calculate color depending on `temperature` telemetry value for `colorpin` device type: + +```javascript +var type = data['Type']; +if (type == 'colorpin') { + var temperature = data['temperature']; + if (typeof temperature !== undefined) { + var percent = (temperature + 60)/120 * 100; + return tinycolor.mix('blue', 'red', percent).toHexString(); + } + return 'blue'; +} +{:copy-code} +``` + +
+
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/label_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/label_fn.md new file mode 100644 index 0000000000..adfdcc287c --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/label_fn.md @@ -0,0 +1,40 @@ +#### Marker label function + +
+
+ +*function (data, dsData, dsIndex): string* + +A JavaScript function used to compute text or HTML code of the marker label. + +**Parameters:** + +
    + {% include widget/lib/map-legacy/map_fn_args %} +
+ +**Returns:** + +Should return string value presenting text or HTML of the marker label. + +
+ +##### Examples + +* Display styled label with corresponding latest telemetry data for `energy meter` or `thermometer` device types: + +```javascript +var deviceType = data['Type']; +if (typeof deviceType !== undefined) { + if (deviceType == "energy meter") { + return '${entityName}, ${energy:2} kWt'; + } else if (deviceType == "thermometer") { + return '${entityName}, ${temperature:2} °C'; + } +} +return data.entityName; +{:copy-code} +``` + +
+
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/map_fn_args.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/map_fn_args.md new file mode 100644 index 0000000000..7a4f24f0ac --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/map_fn_args.md @@ -0,0 +1,10 @@ +
  • data: FormattedData - A FormattedData object associated with marker or data point of the route.
    + Represents basic entity properties (ex. entityId, entityName)
    and provides access to other entity attributes/timeseries declared in widget datasource configuration. +
  • +
  • dsData: FormattedData[] - All available data associated with markers or routes data points as array of FormattedData objects
    + resolved from configured datasources. Each object represents basic entity properties (ex. entityId, entityName)
    + and provides access to other entity attributes/timeseries declared in widget datasource configuration. +
  • +
  • dsIndex number - index of the current marker data or route data point in dsData array.
    + Note: The data argument is equivalent to dsData[dsIndex] expression. +
  • diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/marker_image_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/marker_image_fn.md new file mode 100644 index 0000000000..2ef0f4d645 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/marker_image_fn.md @@ -0,0 +1,62 @@ +#### Marker image function + +
    +
    + +*function (data, images, dsData, dsIndex): {url: string, size: number}* + +A JavaScript function used to compute marker image. + +**Parameters:** + +
      + {% include widget/lib/map-legacy/map_fn_args %} +
    + +**Returns:** + +Should return marker image data having the following structure: + +```typescript +{ + url: string, + size: number +} +``` + +- *url* - marker image url; +- *size* - marker image size; + +In case no data is returned, default marker image will be used. + +
    + +##### Examples + +
      +
    • +Calculate image url depending on temperature telemetry value for thermometer device type.
      +Let's assume 4 images are defined in Marker images section. Each image corresponds to particular temperature level: +
    • +
    + +```javascript +var type = data['Type']; +if (type == 'thermometer') { + var res = { + url: images[0], + size: 40 + } + var temperature = data['temperature']; + if (typeof temperature !== undefined) { + var percent = (temperature + 60)/120; + var index = Math.min(3, Math.floor(4 * percent)); + res.url = images[index]; + } + return res; +} +{:copy-code} +``` + +
    +
    diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/path_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/path_color_fn.md new file mode 100644 index 0000000000..01d3d09ea7 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/path_color_fn.md @@ -0,0 +1,42 @@ +#### Path color function + +
    +
    + +*function (data, dsData, dsIndex): string* + +A JavaScript function used to compute color of the trip path. + +**Parameters:** + +
      + {% include widget/lib/map-legacy/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting color of the trip path. + +In case no data is returned, color value from **Path color** settings field will be used. + +
    + +##### Examples + +* Calculate color depending on `temperature` telemetry value for `colorpin` device type: + +```javascript +var type = data['Type']; +if (type == 'colorpin') { + var temperature = data['temperature']; + if (typeof temperature !== undefined) { + var percent = (temperature + 60)/120 * 100; + return tinycolor.mix('blue', 'red', percent).toHexString(); + } + return 'blue'; +} +{:copy-code} +``` + +
    +
    diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/path_point_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/path_point_color_fn.md new file mode 100644 index 0000000000..83a4af5bc4 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/path_point_color_fn.md @@ -0,0 +1,43 @@ +#### Path point color function + +
    +
    + +*function (data, dsData, dsIndex): string* + +A JavaScript function used to compute color of the trip path point. + +**Parameters:** + +
      + {% include widget/lib/map-legacy/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting color of the trip path point. + +In case no data is returned, color value from **Point color** settings field will be used. + +
    + +##### Examples + +* Calculate color depending on `temperature` telemetry value for `colorpin` device type: + +```javascript +var type = data['Type']; +if (type == 'colorpin') { + var temperature = data['temperature']; + if (typeof temperature !== undefined) { + var percent = (temperature + 60)/120 * 100; + return tinycolor.mix('blue', 'red', percent).toHexString(); + } + return 'blue'; +} +{:copy-code} +``` + +
    +
    + diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/polygon_color_fn.md similarity index 94% rename from ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_color_fn.md rename to ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/polygon_color_fn.md index b4cc0c5223..45edf0d2a5 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/polygon_color_fn.md @@ -10,7 +10,7 @@ A JavaScript function used to compute color of the polygon. **Parameters:**
      - {% include widget/lib/map/map_fn_args %} + {% include widget/lib/map-legacy/map_fn_args %}
    **Returns:** diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/polygon_tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/polygon_tooltip_fn.md new file mode 100644 index 0000000000..b583e93228 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/polygon_tooltip_fn.md @@ -0,0 +1,40 @@ +#### Polygon tooltip function + +
    +
    + +*function (data, dsData, dsIndex): string* + +A JavaScript function used to compute text or HTML code to be displayed in the polygon tooltip. + +**Parameters:** + +
      + {% include widget/lib/map-legacy/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML for the polygon tooltip. + +
    + +##### Examples + +* Display details with corresponding telemetry data for `energy meter` or `thermostat` device types: + +```javascript +var deviceType = data['Type']; +if (typeof deviceType !== undefined) { + if (deviceType == "energy meter") { + return '${entityName}
    Energy: ${energy:2} kWt
    '; + } else if (deviceType == "thermostat") { + return '${entityName}
    Temperature: ${temperature:2} °C
    '; + } +} +return data.entityName; +{:copy-code} +``` + +
    +
    diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/position_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/position_fn.md new file mode 100644 index 0000000000..fa33714fbd --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/position_fn.md @@ -0,0 +1,65 @@ +#### Position conversion function + +
    +
    + +*function (origXPos, origYPos, data, dsData, dsIndex, aspect): {x: number, y: number}* + +A JavaScript function used to convert original relative x, y coordinates of the marker. + +**Parameters:** + +
      +
    • origXPos: number - original relative x coordinate as double from 0 to 1.
    • +
    • origYPos: number - original relative y coordinate as double from 0 to 1.
    • + {% include widget/lib/map-legacy/map_fn_args %} +
    • aspect: number - image map aspect ratio.
    • +
    + +**Returns:** + +Should return position data having the following structure: + +```typescript +{ + x: number, + y: number +} +``` + +- *x* - new relative x coordinate as double from 0 to 1; +- *y* - new relative y coordinate as double from 0 to 1; + +
    + +##### Examples + +* Scale the coordinates to half the original: + +```javascript +return {x: origXPos / 2, y: origYPos / 2}; +{:copy-code} +``` + +* Detect markers with same positions and place them with minimum overlap: + +```javascript +var xPos = data.xPos; +var yPos = data.yPos; +var locationGroup = dsData.filter((item) => item.xPos === xPos && item.yPos === yPos); +if (locationGroup.length > 1) { + const count = locationGroup.length; + const index = locationGroup.indexOf(data); + const radius = 0.035; + const angle = (360 / count) * index - 45; + const x = xPos + radius * Math.sin(angle*Math.PI/180) / aspect; + const y = yPos + radius * Math.cos(angle*Math.PI/180); + return {x: x, y: y}; +} else { + return {x: xPos, y: yPos}; +} +{:copy-code} +``` + +
    +
    diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/tooltip_fn.md new file mode 100644 index 0000000000..8b290c2215 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/tooltip_fn.md @@ -0,0 +1,40 @@ +#### Marker tooltip function + +
    +
    + +*function (data, dsData, dsIndex): string* + +A JavaScript function used to compute text or HTML code to be displayed in the marker, point or polygon tooltip. + +**Parameters:** + +
      + {% include widget/lib/map-legacy/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML for the tooltip. + +
    + +##### Examples + +* Display details with corresponding telemetry data for `thermostat` device type: + +```javascript +var deviceType = data['Type']; +if (typeof deviceType !== undefined) { + if (deviceType == "energy meter") { + return '${entityName}
    Energy: ${energy:2} kWt
    '; + } else if (deviceType == "thermometer") { + return '${entityName}
    Temperature: ${temperature:2} °C
    '; + } +} +return data.entityName; +{:copy-code} +``` + +
    +
    diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/trip_point_as_anchor_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/trip_point_as_anchor_fn.md new file mode 100644 index 0000000000..eb11261f3d --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map-legacy/trip_point_as_anchor_fn.md @@ -0,0 +1,34 @@ +#### Point as anchor function + +
    +
    + +*function (data, dsData, dsIndex): boolean* + +A JavaScript function evaluating whether to use trip point as time anchor used in time selector. + +**Parameters:** + +
      + {% include widget/lib/map-legacy/map_fn_args %} +
    + +**Returns:** + +`true` if the point should be decided as anchor, `false` otherwise. + +In case no data is returned, the point is not used as anchor. + +
    + +##### Examples + +* Make anchors with 5 seconds step interval: + +```javascript +return data.time % 5000 < 1000; +{:copy-code} +``` + +
    +
    diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_fill_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_fill_color_fn.md new file mode 100644 index 0000000000..9a7a383876 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_fill_color_fn.md @@ -0,0 +1,24 @@ +#### Circle fill color function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute fill color of the circle. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting fill color of the circle. + +In case no data is returned, color value from **Color** settings field will be used. + +
    + +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_label_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_label_fn.md new file mode 100644 index 0000000000..1b73148876 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_label_fn.md @@ -0,0 +1,22 @@ +#### Circle label function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute text or HTML code of the circle label. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML of the circle label. + +
    + +{% include widget/lib/map/label_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_stroke_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_stroke_color_fn.md new file mode 100644 index 0000000000..31d0a0f82a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_stroke_color_fn.md @@ -0,0 +1,24 @@ +#### Circle stroke color function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute stroke color of the circle. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting stroke color of the circle. + +In case no data is returned, color value from **Color** settings field will be used. + +
    + +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_tooltip_fn.md new file mode 100644 index 0000000000..1ea037f084 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/circle_tooltip_fn.md @@ -0,0 +1,22 @@ +#### Circle tooltip function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute text or HTML code to be displayed in the circle tooltip. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML for the tooltip. + +
    + +{% include widget/lib/map/tooltip_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/clustering_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/clustering_color_fn.md index ba58fa3cd3..33d4df2f18 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/clustering_color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/clustering_color_fn.md @@ -10,9 +10,9 @@ A JavaScript function used to compute clustering marker color. **Parameters:**
      -
    • data: FormattedData[] +
    • data: FormattedData[] - the array of total markers contained within each cluster.
      - Represents basic entity properties (ex. entityId, entityName)
      and provides access to other entity attributes/timeseries declared in widget datasource configuration. + Represents basic entity properties (ex. entityId, entityName)
      and provides access to other entity attributes/timeseries declared in datasource of the data layer configuration.
    • childCount: number - the total number of markers contained within that cluster
    • @@ -22,7 +22,7 @@ A JavaScript function used to compute clustering marker color. Should return string value presenting color of the marker. -In case no data is returned, color value from **Color** settings field will be used. +In case no data is returned, default colors will be used depending on number of markers within that cluster.
      diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md index ede068d68f..fdecf9596c 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md @@ -3,7 +3,7 @@

      -*function (data, dsData, dsIndex): string* +*function (data, dsData): string* A JavaScript function used to compute color of the marker. @@ -21,22 +21,4 @@ In case no data is returned, color value from **Color** settings field will be u
      -##### Examples - -* Calculate color depending on `temperature` telemetry value for `colorpin` device type: - -```javascript -var type = data['Type']; -if (type == 'colorpin') { - var temperature = data['temperature']; - if (typeof temperature !== undefined) { - var percent = (temperature + 60)/120 * 100; - return tinycolor.mix('blue', 'red', percent).toHexString(); - } - return 'blue'; -} -{:copy-code} -``` - -
      -
      +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn_examples.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn_examples.md new file mode 100644 index 0000000000..8c3631bde7 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn_examples.md @@ -0,0 +1,19 @@ +##### Examples + +* Calculate color depending on `temperature` telemetry value for `thermostat` device type: + +```javascript +var type = data.Type; +if (type == 'thermostat') { + var temperature = data.temperature; + if (typeof temperature !== undefined) { + var percent = (temperature + 60)/120 * 100; + return tinycolor.mix('blue', 'red', percent).toHexString(); + } + return 'blue'; +} +{:copy-code} +``` + +
      +
      diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/label_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/label_fn.md index eb3e6f981e..0028d77b82 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/label_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/label_fn.md @@ -3,7 +3,7 @@

      -*function (data, dsData, dsIndex): string* +*function (data, dsData): string* A JavaScript function used to compute text or HTML code of the marker label. @@ -19,22 +19,4 @@ Should return string value presenting text or HTML of the marker label.
      -##### Examples - -* Display styled label with corresponding latest telemetry data for `energy meter` or `thermometer` device types: - -```javascript -var deviceType = data['Type']; -if (typeof deviceType !== undefined) { - if (deviceType == "energy meter") { - return '${entityName}, ${energy:2} kWt'; - } else if (deviceType == "thermometer") { - return '${entityName}, ${temperature:2} °C'; - } -} -return data.entityName; -{:copy-code} -``` - -
      -
      +{% include widget/lib/map/label_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/label_fn_examples.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/label_fn_examples.md new file mode 100644 index 0000000000..2e2ecfabec --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/label_fn_examples.md @@ -0,0 +1,19 @@ +##### Examples + +* Display styled label with corresponding latest telemetry data for `energy meter` or `thermometer` device types: + +```javascript +var deviceType = data.Type; +if (typeof deviceType !== undefined) { + if (deviceType == "energy meter") { + return '${entityName}, ${energy:2} kWt'; + } else if (deviceType == "thermometer") { + return '${entityName}, ${temperature:2} °C'; + } +} +return data.entityName; +{:copy-code} +``` + +
      +
      diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/map_fn_args.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/map_fn_args.md index 7a4f24f0ac..a627e1867f 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/map_fn_args.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/map_fn_args.md @@ -1,10 +1,8 @@ -
    • data: FormattedData - A FormattedData object associated with marker or data point of the route.
      - Represents basic entity properties (ex. entityId, entityName)
      and provides access to other entity attributes/timeseries declared in widget datasource configuration. +
    • data: FormattedData object associated with data layer (markers/polygons/circles) or data point of the route (trips data layer).
      + Represents basic entity properties (ex. entityId, entityName)
      and provides access to other entity attributes/timeseries declared in datasource of the data layer configuration.
    • -
    • dsData: FormattedData[] - All available data associated with markers or routes data points as array of FormattedData objects
      +
    • dsData: FormattedData[] - All available data associated with data layers including additional datasources as array of FormattedData objects
      resolved from configured datasources. Each object represents basic entity properties (ex. entityId, entityName)
      - and provides access to other entity attributes/timeseries declared in widget datasource configuration. -
    • -
    • dsIndex number - index of the current marker data or route data point in dsData array.
      - Note: The data argument is equivalent to dsData[dsIndex] expression. + and provides access to other entity attributes/timeseries declared in datasources of data layers configuration including additional datasources of the map configuration.
    • + diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/marker_image_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/marker_image_fn.md index 565de0147a..ed8b82d4cc 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/marker_image_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/marker_image_fn.md @@ -3,14 +3,14 @@

      -*function (data, images, dsData, dsIndex): {url: string, size: number}* +*function (data, images, dsData): {url: string, size: number}* A JavaScript function used to compute marker image. **Parameters:**
        - {% include widget/lib/map/map_fn_args %} + {% include widget/lib/map/marker_image_fn_args %}
      **Returns:** @@ -18,14 +18,18 @@ A JavaScript function used to compute marker image. Should return marker image data having the following structure: ```typescript -{ - url: string, - size: number +{ + url: string; + size: number; + markerOffset?: [number, number]; + tooltipOffset?: [number, number]; } ``` - *url* - marker image url; - *size* - marker image size; +- *markerOffset* - optional array of two numbers presenting relative horizontal and vertical offset of the marker image; +- *tooltipOffset* - optional array of two numbers presenting relative horizontal and vertical offset of the marker image tooltip; In case no data is returned, default marker image will be used. @@ -41,13 +45,13 @@ Let's assume 4 images are defined in Marker images section. Each image co
    ```javascript -var type = data['Type']; +var type = data.Type; if (type == 'thermometer') { var res = { url: images[0], size: 40 } - var temperature = data['temperature']; + var temperature = data.temperature; if (typeof temperature !== undefined) { var percent = (temperature + 60)/120; var index = Math.min(3, Math.floor(4 * percent)); diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/marker_image_fn_args.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/marker_image_fn_args.md new file mode 100644 index 0000000000..e86bd3106f --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/marker_image_fn_args.md @@ -0,0 +1,10 @@ +
  • data: FormattedData object associated with data layer (markers/polygons/circles) or data point of the route (trips data layer).
    + Represents basic entity properties (ex. entityId, entityName)
    and provides access to other entity attributes/timeseries declared in datasource of the data layer configuration. +
  • +
  • images: string[] - array of image urls configured in the Marker images section. +
  • +
  • dsData: FormattedData[] - All available data associated with data layers including additional datasources as array of FormattedData objects
    + resolved from configured datasources. Each object represents basic entity properties (ex. entityId, entityName)
    + and provides access to other entity attributes/timeseries declared in datasources of data layers configuration including additional datasources of the map configuration. +
  • + diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md index c4df0a99a9..7f90b9c8f7 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md @@ -3,7 +3,7 @@

    -*function (data, dsData, dsIndex): string* +*function (data, dsData): string* A JavaScript function used to compute color of the trip path. @@ -17,26 +17,8 @@ A JavaScript function used to compute color of the trip path. Should return string value presenting color of the trip path. -In case no data is returned, color value from **Path color** settings field will be used. +In case no data is returned, color value from **Color** settings field will be used.
    -##### Examples - -* Calculate color depending on `temperature` telemetry value for `colorpin` device type: - -```javascript -var type = data['Type']; -if (type == 'colorpin') { - var temperature = data['temperature']; - if (typeof temperature !== undefined) { - var percent = (temperature + 60)/120 * 100; - return tinycolor.mix('blue', 'red', percent).toHexString(); - } - return 'blue'; -} -{:copy-code} -``` - -
    -
    +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md index de092704c3..be95f136ad 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md @@ -3,7 +3,7 @@

    -*function (data, dsData, dsIndex): string* +*function (data, dsData): string* A JavaScript function used to compute color of the trip path point. @@ -17,27 +17,8 @@ A JavaScript function used to compute color of the trip path point. Should return string value presenting color of the trip path point. -In case no data is returned, color value from **Point color** settings field will be used. +In case no data is returned, color value from **Color** settings field will be used.
    -##### Examples - -* Calculate color depending on `temperature` telemetry value for `colorpin` device type: - -```javascript -var type = data['Type']; -if (type == 'colorpin') { - var temperature = data['temperature']; - if (typeof temperature !== undefined) { - var percent = (temperature + 60)/120 * 100; - return tinycolor.mix('blue', 'red', percent).toHexString(); - } - return 'blue'; -} -{:copy-code} -``` - -
    -
    - +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_tooltip_fn.md new file mode 100644 index 0000000000..4ce996626a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_tooltip_fn.md @@ -0,0 +1,22 @@ +#### Path point tooltip function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute text or HTML code to be displayed in the trip path point tooltip. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML for the tooltip. + +
    + +{% include widget/lib/map/tooltip_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_fill_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_fill_color_fn.md new file mode 100644 index 0000000000..ef1ca8c54c --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_fill_color_fn.md @@ -0,0 +1,24 @@ +#### Polygon fill color function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute fill color of the polygon. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting fill color of the polygon. + +In case no data is returned, color value from **Color** settings field will be used. + +
    + +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_label_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_label_fn.md new file mode 100644 index 0000000000..00bc0ee50b --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_label_fn.md @@ -0,0 +1,22 @@ +#### Polygon label function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute text or HTML code of the polygon label. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting text or HTML of the polygon label. + +
    + +{% include widget/lib/map/label_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_stroke_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_stroke_color_fn.md new file mode 100644 index 0000000000..4419ab991c --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_stroke_color_fn.md @@ -0,0 +1,24 @@ +#### Polygon stroke color function + +
    +
    + +*function (data, dsData): string* + +A JavaScript function used to compute stroke color of the polygon. + +**Parameters:** + +
      + {% include widget/lib/map/map_fn_args %} +
    + +**Returns:** + +Should return string value presenting stroke color of the polygon. + +In case no data is returned, color value from **Color** settings field will be used. + +
    + +{% include widget/lib/map/color_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_tooltip_fn.md index 86de41c185..9c3a5c8e5e 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_tooltip_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_tooltip_fn.md @@ -3,7 +3,7 @@

    -*function (data, dsData, dsIndex): string* +*function (data, dsData): string* A JavaScript function used to compute text or HTML code to be displayed in the polygon tooltip. @@ -15,26 +15,8 @@ A JavaScript function used to compute text or HTML code to be displayed in the p **Returns:** -Should return string value presenting text or HTML for the polygon tooltip. +Should return string value presenting text or HTML for the tooltip.
    -##### Examples - -* Display details with corresponding telemetry data for `energy meter` or `thermostat` device types: - -```javascript -var deviceType = data['Type']; -if (typeof deviceType !== undefined) { - if (deviceType == "energy meter") { - return '${entityName}
    Energy: ${energy:2} kWt
    '; - } else if (deviceType == "thermostat") { - return '${entityName}
    Temperature: ${temperature:2} °C
    '; - } -} -return data.entityName; -{:copy-code} -``` - -
    -
    +{% include widget/lib/map/tooltip_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/position_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/position_fn.md index 5687e4fe84..f85c691cdd 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/position_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/position_fn.md @@ -3,7 +3,7 @@

    -*function (origXPos, origYPos, data, dsData, dsIndex, aspect): {x: number, y: number}* +*function (origXPos, origYPos, data, dsData, aspect): {x: number, y: number}* A JavaScript function used to convert original relative x, y coordinates of the marker. @@ -22,8 +22,8 @@ Should return position data having the following structure: ```typescript { - x: number, - y: number + x: number; + y: number; } ``` diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/tooltip_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/tooltip_fn.md index d4d97d99b6..61fb00e7fd 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/tooltip_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/tooltip_fn.md @@ -3,9 +3,9 @@

    -*function (data, dsData, dsIndex): string* +*function (data, dsData): string* -A JavaScript function used to compute text or HTML code to be displayed in the marker, point or polygon tooltip. +A JavaScript function used to compute text or HTML code to be displayed in the marker tooltip. **Parameters:** @@ -19,22 +19,4 @@ Should return string value presenting text or HTML for the tooltip.
    -##### Examples - -* Display details with corresponding telemetry data for `thermostat` device type: - -```javascript -var deviceType = data['Type']; -if (typeof deviceType !== undefined) { - if (deviceType == "energy meter") { - return '${entityName}
    Energy: ${energy:2} kWt
    '; - } else if (deviceType == "thermometer") { - return '${entityName}
    Temperature: ${temperature:2} °C
    '; - } -} -return data.entityName; -{:copy-code} -``` - -
    -
    +{% include widget/lib/map/tooltip_fn_examples %} diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/tooltip_fn_examples.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/tooltip_fn_examples.md new file mode 100644 index 0000000000..34f8e06e0f --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/tooltip_fn_examples.md @@ -0,0 +1,19 @@ +##### Examples + +* Display details with corresponding telemetry data for `energy meter` or `thermometer` device types: + +```javascript +var deviceType = data.Type; +if (typeof deviceType !== undefined) { + if (deviceType == "energy meter") { + return '${entityName}
    Energy: ${energy:2} kWt
    '; + } else if (deviceType == "thermometer") { + return '${entityName}
    Temperature: ${temperature:2} °C
    '; + } +} +return data.entityName; +{:copy-code} +``` + +
    +
    diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/trip_point_as_anchor_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/trip_point_as_anchor_fn.md index e938532b78..167fb0a123 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/trip_point_as_anchor_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/trip_point_as_anchor_fn.md @@ -1,9 +1,9 @@ -#### Point as anchor function +#### Location snap filter function

    -*function (data, dsData, dsIndex): boolean* +*function (data, dsData): boolean* A JavaScript function evaluating whether to use trip point as time anchor used in time selector. diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 85e4daf264..30c481610c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1447,6 +1447,7 @@ "general": "General", "advanced": "Advanced", "key": "Key", + "keys": "Keys", "label": "Label", "color": "Color", "units": "Special symbol to show next to value", @@ -2430,6 +2431,7 @@ "alias-required": "Entity alias is required.", "remove-alias": "Remove entity alias", "add-alias": "Add entity alias", + "edit-alias": "Edit entity alias", "entity-list": "Entity list", "entity-type": "Entity type", "entity-types": "Entity types", @@ -3363,6 +3365,8 @@ "left-bottom-connector": "Left bottom connector", "top-left-connector": "Top left connector", "top-right-connector": "Top right connector", + "top-connector": "Top connector", + "bottom-connector": "Bottom connector", "running-color": "Running color", "stopped-color": "Stopped color", "stopped": "Stopped", @@ -3435,8 +3439,20 @@ "arrow-presence": "Arrow presence", "arrow-presence-hint": "Indicates whether arrow is present in connector.", "arrow-present": "Arrow present", - "arrow-direction": "Arrow direction", + "arrow-direction": "Arrow/Animation direction", "arrow-direction-hint": "Indicates flow direction.", + "animation-direction": "Flow animation direction", + "animation-direction-hint": "Indicates animation flow direction.", + "flow-animation": "Flow animation", + "flow-animation-hint": "Indicates whether animation is present in connector.", + "flow": "Flow", + "flow-style": "Flow style", + "flow-dash-cap": "Flow dash cap", + "dash-cap-butt": "Butt", + "dash-cap-round": "Round", + "dash-cap-square": "Square", + "dash": "Dash", + "gap": "Gap", "main-line": "Main line", "line": "Line", "line-color": "Line color", @@ -4838,10 +4854,6 @@ "min-buffer-memory-message": "Only 0 minimum buffer size is allowed.", "memory-buffer-size-range": "Memory buffer size must be between 0 and {{max}} KB", "acks": "Number of acknowledgments", - "key-serializer": "Key serializer", - "key-serializer-required": "Key serializer is required", - "value-serializer": "Value serializer", - "value-serializer-required": "Value serializer is required", "topic-arn-pattern": "Topic ARN pattern", "topic-arn-pattern-required": "Topic ARN pattern is required", "aws-access-key-id": "AWS Access Key ID", @@ -6570,7 +6582,15 @@ "take-screenshot": "Take screenshot" }, "custom-action-function": "Custom action function", - "custom-pretty-function": "Custom action (with HTML template) function" + "custom-pretty-function": "Custom action (with HTML template) function", + "map-item-type": "Map item type", + "map-item": { + "marker": "Marker", + "polygon": "Polygon", + "rectangle": "Rectangle", + "circle": "Circle" + }, + "place-map-item": "Place map item" }, "widgets-bundle": { "current": "Current bundle", @@ -7817,6 +7837,299 @@ "max-value": "Maximum value" }, "maps": { + "map-type": { + "type": "Map type", + "map": "Map", + "image": "Image" + }, + "image": { + "image-source": "Image source", + "image-source-image": "Image", + "image-source-entity-key": "Entity key", + "source-entity-alias": "Source entity alias", + "image-url-key": "Image URL key", + "image-url-key-required": "Image URL key is required" + }, + "control": { + "map-controls": "Map controls", + "position": "Position", + "position-topleft": "Top-left", + "position-topright": "Top-right", + "position-bottomleft": "Bottom-left", + "position-bottomright": "Bottom-right", + "zoom-actions": "Zoom actions", + "zoom-scroll": "Scroll", + "zoom-double-click": "Double click", + "zoom-control-buttons": "Control buttons", + "scale": "Scale", + "scale-metric": "Metric", + "scale-imperial": "Imperial", + "switch-to-drag-mode-using-button": "Switch to drag mode using button" + }, + "timeline": { + "control-panel": "Timeline control panel", + "time-step": "Time step", + "speed-options": "Speed options", + "timestamp": "Timestamp", + "snap-to-real-location": "Snap to real location", + "location-snap-filter-function": "Location snap filter function", + "no-trips-data-available": "No trips data available" + }, + "map-action": { + "map-action-buttons": "Map action buttons", + "label": "Label", + "icon": "Icon", + "color": "Color", + "action": "Action", + "add-button": "Add button", + "no-action-buttons-configured": "No action buttons configured", + "remove-action-button": "Remove action button", + "map-action-button": "Map action button", + "button-requires": "Button requires label or icon" + }, + "common": { + "common-map-settings": "Common map settings", + "fit-map-bounds": "Fit map bounds to cover all markers", + "default-map-center-position": "Default map center position", + "default-map-zoom-level": "Default map zoom level", + "entities-limit": "Limit of entities to load" + }, + "layer": { + "label": "Label", + "layer": "Layer", + "layers": "Layers", + "map-layers": "Map layers", + "add-layer": "Add layer", + "layer-settings": "Layer settings", + "remove-layer": "Remove layer", + "no-layers": "No layers configured", + "roadmap": "Roadmap", + "satellite": "Satellite", + "hybrid": "Hybrid", + "reference": { + "reference-layer": "Reference layer", + "no-layer": "No layer", + "openstreetmap-hybrid": "OpenStreetMap Hybrid", + "world-edition-hybrid": "World Edition Hybrid" + }, + "provider": { + "provider": "Provider", + "openstreet": { + "title": "OpenStreet", + "mapnik": "Mapnik", + "hot": "HOT", + "esri-street": "WorldStreetMap", + "esri-topo": "WorldTopoMap", + "esri-imagery": "WorldImagery", + "cartodb-positron": "Positron", + "cartodb-dark-matter": "DarkMatter" + }, + "google": { + "title": "Google", + "roadmap": "Roadmap", + "satellite": "Satellite", + "hybrid": "Hybrid", + "terrain": "Terrain" + }, + "here": { + "title": "HERE", + "normal-day": "Normal day", + "normal-night": "Normal night", + "hybrid-day": "Hybrid day", + "terrain-day": "Terrain day" + }, + "tencent": { + "title": "Tencent", + "normal": "Normal", + "satellite": "Satellite", + "terrain": "Terrain" + }, + "custom": { + "title": "Custom", + "tile-url": "Tile URL" + } + }, + "credentials": { + "credentials": "Credentials", + "api-key": "API Key" + } + }, + "overlays": { + "overlays": "Overlays", + "trips": "Trips", + "markers": "Markers", + "polygons": "Polygons", + "circles": "Circles" + }, + "data-layer": { + "source": "Source", + "additional-data-keys": "Additional data keys", + "additional-datasources": "Additional datasources", + "data-keys": "Data keys", + "add-datasource": "Add datasource", + "no-datasources": "No datasources configured", + "remove-datasource": "Remove datasource", + "behavior": "Behavior", + "on-click": "On click", + "on-click-hint": "Action invoked when user clicks on the map item.", + "groups": "Groups", + "color": "Color", + "fill-color": "Fill color", + "stroke": "Stroke", + "color-settings": "Color settings", + "color-type-constant": "Constant", + "color-type-function": "Function", + "color-function": "Color function", + "label": "Label", + "tooltip": "Tooltip", + "pattern-type-pattern": "Pattern", + "pattern-type-function": "Function", + "label-pattern": "Label (pattern examples: '${entityName}', '${entityName}: (Text ${keyName} units.)' )", + "label-function": "Label function", + "tooltip-pattern": "Tooltip (for ex. 'Text ${keyName} units.' or Link text')", + "tooltip-function": "Tooltip function", + "tooltip-trigger": "Tooltip trigger", + "tooltip-trigger-click": "Show tooltip on click", + "tooltip-trigger-hover": "Show tooltip on hover", + "auto-close-tooltips": "Auto-close tooltips", + "tooltip-offset": "Tooltip offset", + "tooltip-offset-horizontal": "Horizontal", + "tooltip-offset-vertical": "Vertical", + "tooltip-tag-actions": "Tag actions", + "add-tooltip-tag-action": "Add tag action", + "edit-tooltip-tag-action": "Edit tag action", + "remove-tooltip-tag-action": "Remove tag action", + "action-add": "Add", + "action-edit": "Edit", + "action-move": "Move", + "action-remove": "Remove", + "edit-instruments": "Instruments", + "persist-location-attribute-scope": "Scope of the attribute to persist location", + "enable-snapping": "Enable snapping to other vertices for precision drawing", + "drag-drop-mode": "Drag-drop mode", + "trip": { + "no-trips": "No trips configured", + "add-trip": "Add trip", + "trip-configuration": "Trip configuration", + "remove-trip": "Remove trip" + }, + "marker": { + "marker": "Marker", + "latitude-key": "Latitude key", + "longitude-key": "Longitude key", + "x-pos-key": "X position key", + "y-pos-key": "Y position key", + "latitude-key-required": "Latitude key required", + "longitude-key-required": "Longitude key required", + "x-pos-key-required": "X position key required", + "y-pos-key-required": "Y position key required", + "no-markers": "No markers configured", + "add-marker": "Add marker", + "marker-configuration": "Marker configuration", + "remove-marker": "Remove marker", + "marker-type": "Marker type", + "marker-type-shape": "Shape", + "marker-type-icon": "Icon", + "marker-type-image": "Image", + "shape": "Shape", + "icon": "Icon", + "image": "Image", + "marker-shapes": "Marker shapes", + "marker-icon": "Marker icon", + "marker-appearance": "Marker appearance", + "marker-image": "Marker image", + "marker-image-type-image": "Image", + "marker-image-type-function": "Function", + "custom-marker-image-size": "Custom marker image size", + "marker-image-function": "Marker image function", + "marker-images": "Marker images", + "marker-offset": "Marker offset", + "offset-horizontal": "Horizontal", + "offset-vertical": "Vertical", + "rotate-marker": "Rotate marker", + "offset-angle": "Offset angle", + "position-conversion": "Position conversion", + "position-conversion-function": "Position conversion function, should return x,y coordinates as double from 0 to 1 each", + "clustering": { + "use-map-markers-clustering": "Use map markers clustering", + "zoom-on-cluster-click": "Zoom when clicking on a cluster", + "max-zoom": "The maximum zoom level when a marker can be part of a cluster (0 - 18)", + "max-radius": "Maximum radius that a cluster will cover", + "zoom-animation": "Animation on markers when zooming", + "bounds-on-cluster-mouse-over": "Bounds of markers when mouse over a cluster", + "spiderfy-max-zoom-level": "Spiderfy at the max zoom level (to see all cluster markers)", + "load-optimization": "Load optimization", + "chunked-load": "Use chunks for adding markers so that the page does not freeze", + "lazy-load": "Use lazy load for adding markers", + "use-cluster-marker-color-function": "Use cluster markers color function", + "marker-color-function": "Marker color function" + }, + "edit": "Edit marker", + "remove-marker-for": "Remove marker for '{{entityName}}'", + "place-marker": "Place marker", + "place-marker-hint": "Click to place marker", + "place-marker-hint-with-entity": "Click to place '{{entityName}}' entity" + }, + "path": { + "path": "Path", + "path-decorator": "Path decorator", + "decorator-symbol": "Decorator symbol", + "decorator-symbol-arrow-head": "Arrow", + "decorator-symbol-dash": "Dash", + "decorator-arrangement": "Decorator arrangement", + "decorator-offset": "Start", + "decorator-end-offset": "End", + "decorator-repeat": "Repeat" + }, + "points": { + "points": "Points", + "point-tooltip": "Point tooltip" + }, + "polygon": { + "polygon-key": "Polygon key", + "polygon-key-required": "Polygon key required", + "no-polygons": "No polygons configured", + "add-polygon": "Add polygon", + "polygon-configuration": "Polygon configuration", + "remove-polygon": "Remove polygon", + "edit": "Edit polygon", + "remove-polygon-for": "Remove polygon for '{{entityName}}'", + "cut": "Cut polygon area", + "rotate": "Rotate polygon", + "draw-rectangle": "Draw rectangle", + "draw-polygon": "Draw polygon", + "polygon-place-first-point-cut-hint": "Click to place first point", + "continue-polygon-cut-hint": "Click to continue drawing", + "finish-polygon-cut-hint": "Click first marker to finish and save", + "polygon-place-first-point-hint": "Polygon: click to place first point", + "polygon-place-first-point-hint-with-entity": "Polygon for '{{entityName}}': click to place first point", + "continue-polygon-hint": "Polygon: click to continue drawing", + "continue-polygon-hint-with-entity": "Polygon for '{{entityName}}': click to continue drawing", + "finish-polygon-hint": "Polygon: click first marker to finish drawing", + "finish-polygon-hint-with-entity": "Polygon for '{{entityName}}': click first marker to finish and save", + "rectangle-place-first-point-hint": "Rectangle: click to place first point", + "rectangle-place-first-point-hint-with-entity": "Rectangle for '{{entityName}}': click to place first point", + "finish-rectangle-hint": "Rectangle: click to finish drawing", + "finish-rectangle-hint-with-entity": "Rectangle for '{{entityName}}': click to finish and save" + }, + "circle": { + "circle-key": "Circle key", + "circle-key-required": "Circle key required", + "no-circles": "No circles configured", + "add-circle": "Add circle", + "circle-configuration": "Circle configuration", + "remove-circle": "Remove circle", + "edit": "Edit circle", + "remove-circle-for": "Remove circle for '{{entityName}}'", + "draw-circle": "Draw circle", + "place-circle-center-hint-with-entity": "Circle for '{{entityName}}': click to place circle center", + "place-circle-center-hint": "Circle: click to place circle center", + "finish-circle-hint-with-entity": "Circle for '{{entityName}}': click to finish and save circle", + "finish-circle-hint": "Circle: click to finish drawing" + }, + "select-entity": "Select entity", + "select-entity-hint": "Hint: after selection click at the map to set position" + }, "select-entity": "Select entity", "select-entity-hint": "Hint: after selection click at the map to set position", "tooltips": { @@ -8257,6 +8570,11 @@ "display-timestamp": "Timestamp", "display-pagination": "Display pagination", "default-page-size": "Default page size", + "page-step-settings": "Page step settings", + "page-step-count": "Number of steps", + "page-step-increment": "Step increment", + "page-step-count-format-message": "Should be an integer value, in the range from 1 to 100.", + "page-step-increment-format-message": "Should be an integer value, greater or equal to 1.", "use-entity-label-tab-name": "Use entity label in tab name", "hide-empty-lines": "Hide empty lines", "row-style": "Row style", @@ -8310,7 +8628,8 @@ "timeseries-column-error": "At least one time series column should be specified", "alarm-column-error": "At least one alarm column should be specified", "table-tabs": "Table tabs", - "show-cell-actions-menu-mobile": "Show cell actions dropdown menu in mobile mode" + "show-cell-actions-menu-mobile": "Show cell actions dropdown menu in mobile mode", + "disable-sorting": "Disable sorting" }, "latest-chart": { "total": "Total", diff --git a/ui-ngx/src/assets/map/openstreetmap_hybrid_reference_style.json b/ui-ngx/src/assets/map/openstreetmap_hybrid_reference_style.json new file mode 100644 index 0000000000..b40e5a3838 --- /dev/null +++ b/ui-ngx/src/assets/map/openstreetmap_hybrid_reference_style.json @@ -0,0 +1,25803 @@ +{ + "version": 8, + "sprite": "https://cdn.arcgis.com/sharing/rest/content/items/f240fe360b434afc87dd989bf0c0b825/resources/sprites/sprite", + "glyphs": "https://basemaps.arcgis.com/arcgis/rest/services/OpenStreetMap_v2/VectorTileServer/resources/fonts/{fontstack}/{range}.pbf", + "sources": { + "esri": { + "type": "vector", + "url": "https://basemaps.arcgis.com/arcgis/rest/services/OpenStreetMap_v2/VectorTileServer", + "attribution": "Map data (c) OpenStreetMap contributors, Microsoft, Facebook, Google, Esri Community Maps contributors, Map layer by Esri", + "tiles": [ + "https://basemaps.arcgis.com/arcgis/rest/services/OpenStreetMap_v2/VectorTileServer/tile/{z}/{y}/{x}.pbf" + ] + } + }, + "layers": [ + { + "id": "country boundary (small scale)/casing", + "type": "line", + "source": "esri", + "source-layer": "country boundary (small scale)", + "minzoom": 2, + "maxzoom": 3, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "stops": [ + [ + 2, + "#000000" + ], + [ + 10, + "#4e4e4e" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 2, + 0.55 + ], + [ + 4, + 0.7 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 2, + 3.33 + ], + [ + 6, + 5 + ] + ] + } + } + }, + { + "id": "country boundary (small scale)/line", + "type": "line", + "source": "esri", + "source-layer": "country boundary (small scale)", + "minzoom": 1, + "maxzoom": 3, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 1, + 0.55 + ], + [ + 4, + 0.7 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 1, + 1.33 + ], + [ + 6, + 2 + ] + ] + } + } + }, + { + "id": "track (tunnel)/casing", + "type": "line", + "source": "esri", + "source-layer": "track (tunnel)", + "minzoom": 13, + "layout": {}, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 21 + ], + [ + 22, + 36 + ] + ] + } + } + }, + { + "id": "track (tunnel)/line", + "type": "line", + "source": "esri", + "source-layer": "track (tunnel)", + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#fdfdfd", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 18 + ], + [ + 22, + 33 + ] + ] + } + } + }, + { + "id": "service road (tunnel)/minor/casing", + "type": "line", + "source": "esri", + "source-layer": "service road (tunnel)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 15, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "service road (tunnel)/minor/line", + "type": "line", + "source": "esri", + "source-layer": "service road (tunnel)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 15, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "service road (tunnel)/normal/casing", + "type": "line", + "source": "esri", + "source-layer": "service road (tunnel)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "service road (tunnel)/normal/line", + "type": "line", + "source": "esri", + "source-layer": "service road (tunnel)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road (tunnel)/other/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 13, + "layout": {}, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road (tunnel)/other/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road (tunnel)/living street/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 13, + "layout": {}, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road (tunnel)/living street/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road (tunnel)/residential, unclassified/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 13, + "layout": {}, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road (tunnel)/residential, unclassified/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/tertiary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road (tunnel)/tertiary/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": {}, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/tertiary/line", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road (tunnel)/tertiary/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/secondary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 17 + ], + [ + 22, + 32 + ] + ] + } + } + }, + { + "id": "road (tunnel)/secondary/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 27 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/primary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 19 + ], + [ + 22, + 34 + ] + ] + } + } + }, + { + "id": "road (tunnel)/primary/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 29 + ], + [ + 22, + 44 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/trunk/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road (tunnel)/trunk/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 7, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 31 + ], + [ + 22, + 46 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road (tunnel)/motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 7, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 33 + ], + [ + 22, + 48 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/secondary/line", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 14 + ], + [ + 22, + 29 + ] + ] + } + } + }, + { + "id": "road (tunnel)/secondary/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 24 + ], + [ + 22, + 39 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/primary/line", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 16 + ], + [ + 22, + 31 + ] + ] + } + } + }, + { + "id": "road (tunnel)/primary/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 26 + ], + [ + 22, + 41 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/trunk/line", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road (tunnel)/trunk/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 28 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "road link (tunnel)/motorway/line", + "type": "line", + "source": "esri", + "source-layer": "road link (tunnel)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road (tunnel)/motorway/line", + "type": "line", + "source": "esri", + "source-layer": "road (tunnel)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 30 + ], + [ + 22, + 45 + ] + ] + } + } + }, + { + "id": "Daylight road/track/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 21 + ], + [ + 22, + 36 + ] + ] + } + } + }, + { + "id": "track/casing", + "type": "line", + "source": "esri", + "source-layer": "track", + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 21 + ], + [ + 22, + 36 + ] + ] + } + } + }, + { + "id": "Daylight road/tertiary link/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road link/tertiary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "Daylight road/secondary link/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 17 + ], + [ + 22, + 32 + ] + ] + } + } + }, + { + "id": "road link/secondary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 17 + ], + [ + 22, + 32 + ] + ] + } + } + }, + { + "id": "Daylight road/primary link/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 19 + ], + [ + 22, + 34 + ] + ] + } + } + }, + { + "id": "road link/primary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 19 + ], + [ + 22, + 34 + ] + ] + } + } + }, + { + "id": "Daylight road/trunk link/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road link/trunk/casing", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "Daylight road/motorway link/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road link/motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "Daylight road/service/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#D4CFC3", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.5 + ], + [ + 14, + 3.33 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "service road/minor/casing", + "type": "line", + "source": "esri", + "source-layer": "service road", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 15, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "service road/normal/casing", + "type": "line", + "source": "esri", + "source-layer": "service road", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road/other/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "Daylight road/living street/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road/living street/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "Daylight road/residential, unclassified/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road/residential, unclassified/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "Daylight road/tertiary/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road/tertiary/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "Daylight road/secondary/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 27 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "road/secondary/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 27 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "Daylight road/primary/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 29 + ], + [ + 22, + 44 + ] + ] + } + } + }, + { + "id": "road/primary/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 29 + ], + [ + 22, + 44 + ] + ] + } + } + }, + { + "id": "Daylight road/trunk/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 31 + ], + [ + 22, + 46 + ] + ] + } + } + }, + { + "id": "road/trunk/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 31 + ], + [ + 22, + 46 + ] + ] + } + } + }, + { + "id": "Daylight road/motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 33 + ], + [ + 22, + 48 + ] + ] + } + } + }, + { + "id": "road/motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 33 + ], + [ + 22, + 48 + ] + ] + } + } + }, + { + "id": "Daylight road/track/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#fdfdfd", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 18 + ], + [ + 22, + 33 + ] + ] + } + } + }, + { + "id": "track/line", + "type": "line", + "source": "esri", + "source-layer": "track", + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#fdfdfd", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 18 + ], + [ + 22, + 33 + ] + ] + } + } + }, + { + "id": "Daylight road/tertiary link/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road link/tertiary/line", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "Daylight road/secondary link/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 14 + ], + [ + 22, + 29 + ] + ] + } + } + }, + { + "id": "road link/secondary/line", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 14 + ], + [ + 22, + 29 + ] + ] + } + } + }, + { + "id": "Daylight road/primary link/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 16 + ], + [ + 22, + 31 + ] + ] + } + } + }, + { + "id": "road link/primary/line", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 16 + ], + [ + 22, + 31 + ] + ] + } + } + }, + { + "id": "Daylight road/trunk link/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road link/trunk/line", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "Daylight road/motorway link/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road link/motorway/line", + "type": "line", + "source": "esri", + "source-layer": "road link", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "Daylight road/service/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "service road/minor/line", + "type": "line", + "source": "esri", + "source-layer": "service road", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 15, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "service road/normal/line", + "type": "line", + "source": "esri", + "source-layer": "service road", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road/other/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "Daylight road/living street/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road/living street/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "Daylight road/residential, unclassified/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road/residential, unclassified/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "Daylight road/tertiary/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road/tertiary/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "Daylight road/secondary/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 24 + ], + [ + 22, + 39 + ] + ] + } + } + }, + { + "id": "road/secondary/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 24 + ], + [ + 22, + 39 + ] + ] + } + } + }, + { + "id": "Daylight road/primary/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 26 + ], + [ + 22, + 41 + ] + ] + } + } + }, + { + "id": "road/primary/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 26 + ], + [ + 22, + 41 + ] + ] + } + } + }, + { + "id": "Daylight road/trunk/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 28 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "road/trunk/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 28 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "Daylight road/motorway/line", + "type": "line", + "source": "esri", + "source-layer": "Daylight road", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 30 + ], + [ + 22, + 45 + ] + ] + } + } + }, + { + "id": "road/motorway/line", + "type": "line", + "source": "esri", + "source-layer": "road", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 30 + ], + [ + 22, + 45 + ] + ] + } + } + }, + { + "id": "track (bridge)/casing", + "type": "line", + "source": "esri", + "source-layer": "track (bridge)", + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 21 + ], + [ + 22, + 36 + ] + ] + } + } + }, + { + "id": "service road (bridge)/minor/casing", + "type": "line", + "source": "esri", + "source-layer": "service road (bridge)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 15, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "service road (bridge)/normal/casing", + "type": "line", + "source": "esri", + "source-layer": "service road (bridge)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road (bridge)/other/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road (bridge)/living street/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road (bridge)/residential, unclassified/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road link (bridge)/tertiary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road (bridge)/tertiary/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "road link (bridge)/secondary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 17 + ], + [ + 22, + 32 + ] + ] + } + } + }, + { + "id": "road (bridge)/secondary/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 27 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "road link (bridge)/primary/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 19 + ], + [ + 22, + 34 + ] + ] + } + } + }, + { + "id": "road (bridge)/primary/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 29 + ], + [ + 22, + 44 + ] + ] + } + } + }, + { + "id": "road link (bridge)/trunk/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road (bridge)/trunk/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 7, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 31 + ], + [ + 22, + 46 + ] + ] + } + } + }, + { + "id": "road link (bridge)/motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "road (bridge)/motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 7, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 33 + ], + [ + 22, + 48 + ] + ] + } + } + }, + { + "id": "track (bridge)/line", + "type": "line", + "source": "esri", + "source-layer": "track (bridge)", + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#fdfdfd", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 18 + ], + [ + 22, + 33 + ] + ] + } + } + }, + { + "id": "service road (bridge)/minor/line", + "type": "line", + "source": "esri", + "source-layer": "service road (bridge)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 15, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "service road (bridge)/normal/line", + "type": "line", + "source": "esri", + "source-layer": "service road (bridge)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road (bridge)/other/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road (bridge)/living street/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road (bridge)/residential, unclassified/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road link (bridge)/tertiary/line", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road (bridge)/tertiary/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "road link (bridge)/secondary/line", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 14 + ], + [ + 22, + 29 + ] + ] + } + } + }, + { + "id": "road link (bridge)/primary/line", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 16 + ], + [ + 22, + 31 + ] + ] + } + } + }, + { + "id": "road link (bridge)/trunk/line", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road link (bridge)/motorway/line", + "type": "line", + "source": "esri", + "source-layer": "road link (bridge)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "road (bridge)/secondary/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 24 + ], + [ + 22, + 39 + ] + ] + } + } + }, + { + "id": "road (bridge)/primary/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 26 + ], + [ + 22, + 41 + ] + ] + } + } + }, + { + "id": "road (bridge)/trunk/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 28 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "road (bridge)/motorway/line", + "type": "line", + "source": "esri", + "source-layer": "road (bridge)", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 30 + ], + [ + 22, + 45 + ] + ] + } + } + }, + { + "id": "administrative boundary/level 6", + "type": "line", + "source": "esri", + "source-layer": "administrative boundary", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-opacity": 0.55, + "line-dasharray": [ + 2, + 2 + ], + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.5 + ], + [ + 13, + 2 + ], + [ + 18, + 2.75 + ] + ] + } + } + }, + { + "id": "administrative boundary/level 5", + "type": "line", + "source": "esri", + "source-layer": "administrative boundary", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-opacity": 0.55, + "line-dasharray": [ + 2, + 2 + ], + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.5 + ], + [ + 13, + 2 + ], + [ + 18, + 2.75 + ] + ] + } + } + }, + { + "id": "administrative boundary/level 4", + "type": "line", + "source": "esri", + "source-layer": "administrative boundary", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 3, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-dasharray": [ + 4, + 3 + ], + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 3, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.0, + "stops": [ + [ + 3, + 1.33 + ], + [ + 6, + 1.75 + ], + [ + 18, + 4 + ] + ] + } + } + }, + { + "id": "administrative boundary/level 3", + "type": "line", + "source": "esri", + "source-layer": "administrative boundary", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 3, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-dasharray": [ + 4, + 3 + ], + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 3, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.0, + "stops": [ + [ + 3, + 1.33 + ], + [ + 6, + 1.75 + ], + [ + 18, + 4 + ] + ] + } + } + }, + { + "id": "administrative boundary/level 2/casing", + "type": "line", + "source": "esri", + "source-layer": "administrative boundary", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 3, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "stops": [ + [ + 2, + "#000000" + ], + [ + 10, + "#4e4e4e" + ], + [ + 11, + "#cccccc" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 2, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 2, + 3.33 + ], + [ + 6, + 5 + ], + [ + 18, + 12 + ] + ] + } + } + }, + { + "id": "administrative boundary/level 2/line", + "type": "line", + "source": "esri", + "source-layer": "administrative boundary", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 3, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 1, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 1, + 1.33 + ], + [ + 6, + 2 + ], + [ + 18, + 4 + ] + ] + } + } + }, + { + "id": "station area/aerialway station, halt", + "type": "symbol", + "source": "esri", + "source-layer": "station area", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 12, + "layout": { + "icon-image": "station area/aerialway station, halt", + "icon-size": { + "stops": [ + [ + 12, + 0.67 + ], + [ + 14, + 1 + ] + ] + }, + "icon-allow-overlap": true + } + }, + { + "id": "station area/tram stop", + "type": "symbol", + "source": "esri", + "source-layer": "station area", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 13, + "layout": { + "icon-image": "station area/tram stop", + "icon-size": { + "stops": [ + [ + 13, + 0.67 + ], + [ + 14, + 1 + ] + ] + }, + "icon-allow-overlap": true + } + }, + { + "id": "Daylight address area/label/Number/Unit", + "type": "symbol", + "source": "esri", + "source-layer": "Daylight address area/label", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 19, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 16, + 8 + ], + [ + 19, + 9 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 16, + 3 + ], + [ + 19, + 3.3 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 16, + 1 + ], + [ + 19, + 1.1 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM address area/label/Number/Name", + "type": "symbol", + "source": "esri", + "source-layer": "OSM address area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 19, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 16, + 8 + ], + [ + 19, + 9 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 16, + 3 + ], + [ + 19, + 3.3 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 16, + 1 + ], + [ + 19, + 1.1 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM address area/label/Number/Unit/Name", + "type": "symbol", + "source": "esri", + "source-layer": "OSM address area/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 19, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 16, + 8 + ], + [ + 19, + 9 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 16, + 3 + ], + [ + 19, + 3.3 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 16, + 1 + ], + [ + 19, + 1.1 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM address area/label/Number/Flat/Name", + "type": "symbol", + "source": "esri", + "source-layer": "OSM address area/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 19, + 11 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 16, + 8 + ], + [ + 19, + 9 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 16, + 3 + ], + [ + 19, + 3.3 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 16, + 1 + ], + [ + 19, + 1.1 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM address point/Number/Name", + "type": "symbol", + "source": "esri", + "source-layer": "OSM address point", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 19, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 16, + 8 + ], + [ + 19, + 9 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 16, + 3 + ], + [ + 19, + 3.3 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 16, + 1 + ], + [ + 19, + 1.1 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM address point/Number/Unit/Name", + "type": "symbol", + "source": "esri", + "source-layer": "OSM address point", + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 19, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 16, + 8 + ], + [ + 19, + 9 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 16, + 3 + ], + [ + 19, + 3.3 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 16, + 1 + ], + [ + 19, + 1.1 + ] + ] + }, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM address point/Number/Flat/Name", + "type": "symbol", + "source": "esri", + "source-layer": "OSM address point", + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 19, + 11 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 16, + 8 + ], + [ + 19, + 9 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 16, + 3 + ], + [ + 19, + 3.3 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 16, + 1 + ], + [ + 19, + 1.1 + ] + ] + }, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Daylight building/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Daylight building/label", + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 12 + ] + ] + }, + "text-letter-spacing": 0.08, + "text-max-width": { + "stops": [ + [ + 15, + 5.5 + ], + [ + 16, + 6 + ] + ] + }, + "text-field": "{_name}", + "text-padding": 3, + "text-line-height": 1, + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM major building/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "OSM major building/label", + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 14, + 5 + ], + [ + 16, + 6 + ] + ] + }, + "text-field": "{_name}", + "text-padding": 3, + "text-line-height": 1, + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "OSM building/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "OSM building/label", + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 12 + ] + ] + }, + "text-letter-spacing": 0.08, + "text-max-width": { + "stops": [ + [ + 15, + 5.5 + ], + [ + 16, + 6 + ] + ] + }, + "text-field": "{_name}", + "text-padding": 3, + "text-line-height": 1, + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity area/parking street side", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 108 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/parking street side", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/parking", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 104 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/parking", + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 17, + 1 + ] + ] + }, + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/motorcycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 103 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/motorcycle parking", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/bicycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 102 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/bicycle parking", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/obelisk", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 98 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/obelisk", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/vehicle inspection", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 95 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/vehicle inspection", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/bicycle repair station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 94 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/bicycle repair station", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/office", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 91 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/office", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/casino", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 86 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/casino", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/boat rental", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 84 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/boat rental", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/atm", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 76 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/atm", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/bank", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 75 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/bank", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/bar", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 74 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/bar", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/bicycle rental", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 72 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/bicycle rental", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/bus stop", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 71 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/bus stop", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/bus station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 70 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/bus station", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/taxi", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 69 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/taxi", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/traffic signal", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 68 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/traffic signal", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/cafe", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 67 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/cafe", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/camp site", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 66 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/camp site", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/caravan site", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 65 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/caravan site", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/car rental", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 64 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/car rental", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/car wash", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 63 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/car wash", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/cinema", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 61 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/cinema", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/nightclub", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 59 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/nightclub", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/fire station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 58 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/fire station", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/charging station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 56 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/charging station", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/fuel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 55 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/fuel", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/hospital", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 52 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 14, + "layout": { + "icon-image": "amenity area/hospital", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/hostel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 51 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/hostel", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/hotel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 50 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/hotel", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/motel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 49 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/motel", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/ice cream", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 48 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/ice cream", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/library", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 46 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/library", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/courthouse", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 45 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/courthouse", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/community centre", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 44 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/community centre", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/townhall", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 41 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/townhall", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/museum", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 40 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/museum", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/pharmacy", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 39 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/pharmacy", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/clinic", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 38 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/clinic", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/dentist", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 36 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/dentist", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/police", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 34 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/police", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/post office", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 32 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/post office", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/pub", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 31 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/pub", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/food court", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 29 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/food court", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/restaurant", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 28 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/restaurant", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/fast food", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 27 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/fast food", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/theatre", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 25 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/theatre", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/arts centre", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 24 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/arts centre", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/prison", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 22 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity area/prison", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/viewpoint", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 21 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/viewpoint", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/monument", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 19 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity area/monument", + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 17, + 1 + ] + ] + }, + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/marketplace", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 16 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/marketplace", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/fitness", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 14 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity area/fitness", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/golf course", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 12 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 14, + "layout": { + "icon-image": "amenity area/golf course", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/helipad", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity area/helipad", + "icon-allow-overlap": true + } + }, + { + "id": "amenity area/ferry terminal", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area", + "filter": [ + "all", + [ + "==", + "_symbol", + 5 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 14, + "layout": { + "icon-image": "amenity area/ferry terminal", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/fabric", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 70 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/fabric", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/massage", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 66 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/massage", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/video games", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 58 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/video games", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/variety store", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 57 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/variety store", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/tyres", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 56 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/tyres", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/supermarket", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 51 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "shop area/supermarket", + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 17, + 1 + ] + ] + }, + "icon-allow-overlap": true + } + }, + { + "id": "shop area/sports", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 49 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/sports", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/shoes", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 48 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/shoes", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/pet", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 45 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/pet", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/outdoor", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 43 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/outdoor", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/optician", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 41 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/optician", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/motorcycle", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 37 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/motorcycle", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/mobile phone", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 36 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/mobile phone", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/laundry", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 33 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/laundry", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/jewelry", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 32 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/jewelry", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/hardware", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 30 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/hardware", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/hairdresser", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 29 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/hairdresser", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/gift", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 27 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/gift", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/furniture", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 25 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/furniture", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/florist", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 24 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/florist", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/clothes", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 23 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/clothes", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/electronics", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 22 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/electronics", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/department store", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 21 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 15, + "layout": { + "icon-image": "shop area/department store", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/convenience", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 17 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/convenience", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/computer", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 16 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/computer", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/car repair", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 12 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/car repair", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/car parts", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 11 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/car parts", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/car", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 10 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/car", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/books", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 8 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/books", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/bicycle", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 7 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/bicycle", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/beverages", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/beverages", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/beauty", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 4 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/beauty", + "icon-allow-overlap": true + } + }, + { + "id": "shop area/bakery", + "type": "symbol", + "source": "esri", + "source-layer": "shop area", + "filter": [ + "all", + [ + "==", + "_symbol", + 3 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "shop area/bakery", + "icon-allow-overlap": true + } + }, + { + "id": "private access area/charging station", + "type": "symbol", + "source": "esri", + "source-layer": "private access area", + "filter": [ + "all", + [ + "==", + "_symbol", + 5 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "private access area/charging station", + "icon-allow-overlap": true + } + }, + { + "id": "private access area/parking street side", + "type": "symbol", + "source": "esri", + "source-layer": "private access area", + "filter": [ + "all", + [ + "==", + "_symbol", + 17 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "private access area/parking street side", + "icon-allow-overlap": true + } + }, + { + "id": "private access area/parking", + "type": "symbol", + "source": "esri", + "source-layer": "private access area", + "filter": [ + "all", + [ + "==", + "_symbol", + 4 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "private access area/parking", + "icon-allow-overlap": true + } + }, + { + "id": "private access area/motorcycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "private access area", + "filter": [ + "all", + [ + "==", + "_symbol", + 3 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "private access area/motorcycle parking", + "icon-allow-overlap": true + } + }, + { + "id": "private access area/bicycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "private access area", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 17, + "layout": { + "icon-image": "private access area/bicycle parking", + "icon-allow-overlap": true + } + }, + { + "id": "private access area/bicycle repair station", + "type": "symbol", + "source": "esri", + "source-layer": "private access area", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "private access area/bicycle repair station", + "icon-allow-overlap": true + } + }, + { + "id": "aerodrome area", + "type": "symbol", + "source": "esri", + "source-layer": "aerodrome area", + "filter": [ + "==", + "$type", + "Point" + ], + "minzoom": 9, + "maxzoom": 17, + "layout": { + "icon-image": "aerodrome area", + "icon-size": { + "stops": [ + [ + 9, + 0.7 + ], + [ + 12, + 1 + ] + ] + }, + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/other", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 9 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/other", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Taoist", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 8 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Taoist", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Sikh", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 7 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Sikh", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Shinto", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Shinto", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Muslim", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 5 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Muslim", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Jewish", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 4 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Jewish", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Hindu", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 3 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Hindu", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Christian - other", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Christian - other", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Christian - Jehovah's Witness", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Christian - Jehovah's Witness", + "icon-allow-overlap": true + } + }, + { + "id": "place of worship area/Buddhist", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship area/Buddhist", + "icon-allow-overlap": true + } + }, + { + "id": "vending machine area/public transport tickets", + "type": "symbol", + "source": "esri", + "source-layer": "vending machine area", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "vending machine area/public transport tickets", + "icon-allow-overlap": true + } + }, + { + "id": "vending machine area/parking tickets", + "type": "symbol", + "source": "esri", + "source-layer": "vending machine area", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "==", + "$type", + "Point" + ] + ], + "minzoom": 18, + "layout": { + "icon-image": "vending machine area/parking tickets", + "icon-allow-overlap": true + } + }, + { + "id": "amenity line/slipway", + "type": "symbol", + "source": "esri", + "source-layer": "amenity line", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 16, + "layout": { + "symbol-placement": "line", + "icon-image": "amenity line/slipway", + "icon-rotation-alignment": "map", + "icon-allow-overlap": false + } + }, + { + "id": "amenity line/ford", + "type": "symbol", + "source": "esri", + "source-layer": "amenity line", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "icon-image": "amenity line/ford", + "icon-rotation-alignment": "map", + "icon-allow-overlap": false + } + }, + { + "id": "vending machine point/public transport tickets", + "type": "symbol", + "source": "esri", + "source-layer": "vending machine point", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 18, + "layout": { + "icon-image": "vending machine point/public transport tickets", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "vending machine point/parking tickets", + "type": "symbol", + "source": "esri", + "source-layer": "vending machine point", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 18, + "layout": { + "icon-image": "vending machine point/parking tickets", + "icon-allow-overlap": false + } + }, + { + "id": "junction point/label/exit name and number", + "type": "symbol", + "source": "esri", + "source-layer": "junction point", + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 12 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name1}", + "icon-image": "junction point/exit name and number/{_len1}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport", + "text-optional": true + }, + "paint": { + "text-color": "#ffa878", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "track (tunnel)/label/alternate name", + "type": "symbol", + "source": "esri", + "source-layer": "track (tunnel)/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "track (tunnel)/label/name", + "type": "symbol", + "source": "esri", + "source-layer": "track (tunnel)/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "service road (tunnel)/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "service road (tunnel)/label", + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/living street", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 12 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/residential, unclassified shield text", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 10 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/residential, unclassified name", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 11 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": { + "stops": [ + [ + 14, + 300 + ], + [ + 16, + 400 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/tertiary name", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 9 + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 13, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/secondary name", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 8 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/primary name", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 7 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/trunk name", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 6 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/motorway name", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (tunnel)/label/tertiary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (tunnel)/tertiary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (tunnel)/label/secondary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (tunnel)/secondary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (tunnel)/label/primary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 10, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 10, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (tunnel)/primary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (tunnel)/label/trunk shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 10, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 10, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (tunnel)/trunk shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (tunnel)/label/motorway shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (tunnel)/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 9, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 9, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (tunnel)/motorway shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "track/label/alternate name", + "type": "symbol", + "source": "esri", + "source-layer": "track/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-max-angle": 30, + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "track/label/name", + "type": "symbol", + "source": "esri", + "source-layer": "track/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-max-angle": 30, + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "service road/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "service road/label", + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-max-angle": 30, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/living street", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 12 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-max-angle": 30, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/residential, unclassified shield text", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 10 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/residential, unclassified name", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 11 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": { + "stops": [ + [ + 14, + 300 + ], + [ + 16, + 400 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-max-angle": 30, + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/tertiary name", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 9 + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-max-angle": 30, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 13, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/secondary name", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 8 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/primary name", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 7 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/trunk name", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 6 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/motorway name", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road/label/tertiary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road/tertiary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road/label/secondary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road/secondary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road/label/primary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 10, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 10, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road/primary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road/label/trunk shield", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 10, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 10, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road/trunk shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road/label/motorway shield", + "type": "symbol", + "source": "esri", + "source-layer": "road/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 9, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "icon-size": { + "stops": [ + [ + 9, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-size": { + "stops": [ + [ + 9, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road/motorway shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "track (bridge)/label/alternate name", + "type": "symbol", + "source": "esri", + "source-layer": "track (bridge)/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "track (bridge)/label/name", + "type": "symbol", + "source": "esri", + "source-layer": "track (bridge)/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "service road (bridge)/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "service road (bridge)/label", + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/living street", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 12 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/residential, unclassified shield text", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 10 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/residential, unclassified name", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 11 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": { + "stops": [ + [ + 14, + 300 + ], + [ + 16, + 400 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 8 + ], + [ + 15, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/tertiary name", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 9 + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 13, + 9 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/secondary name", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 8 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/primary name", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 7 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/trunk name", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 6 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/motorway name", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 300, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 12, + 8 + ], + [ + 13, + 9 + ], + [ + 14, + 10 + ], + [ + 16, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "road (bridge)/label/tertiary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (bridge)/tertiary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (bridge)/label/secondary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (bridge)/secondary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (bridge)/label/primary shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 18 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 12, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (bridge)/primary shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (bridge)/label/trunk shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (bridge)/trunk shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "road (bridge)/label/motorway shield", + "type": "symbol", + "source": "esri", + "source-layer": "road (bridge)/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 760, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 15, + 10 + ], + [ + 17, + 12 + ] + ] + }, + "icon-size": { + "stops": [ + [ + 11, + 1 + ], + [ + 15, + 1.2 + ], + [ + 17, + 1.6 + ] + ] + }, + "text-field": "{_name}", + "icon-image": "road (bridge)/motorway shield/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "station area/label/aerialway station", + "type": "symbol", + "source": "esri", + "source-layer": "station area/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "station area/label/tram stop", + "type": "symbol", + "source": "esri", + "source-layer": "station area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "waterway/label/dock", + "type": "symbol", + "source": "esri", + "source-layer": "waterway/label", + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 8, + 10 + ], + [ + 12, + 11 + ], + [ + 16, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 8, + 7 + ], + [ + 12, + 8 + ], + [ + 16, + 9 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 8, + 1.13 + ], + [ + 12, + 1.16 + ], + [ + 16, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water area/label/water, basin, reservoir", + "type": "symbol", + "source": "esri", + "source-layer": "water area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 7, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 7, + 8 + ], + [ + 8, + 10 + ], + [ + 12, + 11 + ], + [ + 16, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 7, + 6 + ], + [ + 8, + 7 + ], + [ + 12, + 8 + ], + [ + 16, + 9 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 7, + 1 + ], + [ + 8, + 1.13 + ], + [ + 12, + 1.16 + ], + [ + 16, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "landcover/label/forest", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 31 + ], + "minzoom": 9, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 7, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 7, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 7, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/park", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 29 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.1 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/sand", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 26 + ], + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 7, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/vineyard, orchard", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 23 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/farmland", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 22 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/farmyard", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 21 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/industrial", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 19 + ], + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/garages", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 16 + ], + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 9 + ], + [ + 16, + 10 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 14, + 1.15 + ], + [ + 16, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/allotments", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 15 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/education", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 12 + ], + "minzoom": 10, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/commercial", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 14 + ], + "minzoom": 10, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/retail", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 13 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/religious", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 32 + ], + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 12, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "cemetery/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "cemetery/label", + "minzoom": 12, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 13, + 11 + ], + [ + 16, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 13, + 1.15 + ], + [ + 16, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/danger area", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 11 + ], + "minzoom": 8, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 8, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 8, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 8, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/highway services", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 10 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/power station", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 9 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/power substation", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 8 + ], + "minzoom": 12, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 10 + ], + [ + 14, + 11 + ], + [ + 16, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 12, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 12, + 1 + ], + [ + 14, + 1.15 + ], + [ + 16, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/apron", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 7 + ], + "minzoom": 9, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/camping", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 6 + ], + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 11 + ], + [ + 15, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/sports", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/pitch, track", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/dog park, fitness station", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/swimming pool, marina", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 13, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 13, + 5 + ], + [ + 15, + 7 + ], + [ + 17, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 13, + 1 + ], + [ + 15, + 1.15 + ], + [ + 17, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "landcover/label/military", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 7, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 7, + 10 + ], + [ + 10, + 11 + ], + [ + 13, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 7, + 5 + ], + [ + 11, + 7 + ], + [ + 14, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 7, + 1 + ], + [ + 10, + 1.15 + ], + [ + 13, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/tourist attraction", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 33 + ], + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 12, + "text-max-width": 6, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "landcover/label/amusement park", + "type": "symbol", + "source": "esri", + "source-layer": "landcover/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 13, + 10 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "beach/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "beach/label", + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 12, + 11 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 9, + 5 + ], + [ + 14, + 7 + ], + [ + 16, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 9, + 1 + ], + [ + 12, + 1.15 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "transportation area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "transportation area/label", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "aboriginal land/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "aboriginal land/label", + "minzoom": 7, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 7, + 10 + ], + [ + 18, + 14 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-max-width": 6, + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "protected area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "protected area/label", + "minzoom": 7, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 7, + 10 + ], + [ + 18, + 14 + ] + ] + }, + "text-field": "{_name}", + "text-letter-spacing": 0.05, + "text-max-width": 7, + "text-optional": true + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "island/label/islet", + "type": "symbol", + "source": "esri", + "source-layer": "island/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 9 + ], + [ + 12, + 10 + ], + [ + 14, + 12 + ], + [ + 18, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 11, + 7 + ], + [ + 14, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 10, + 1 + ], + [ + 14, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "island/label/island", + "type": "symbol", + "source": "esri", + "source-layer": "island/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 11, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 10, + 11 + ], + [ + 13, + 12 + ], + [ + 18, + 15 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 11, + 7 + ], + [ + 14, + 8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 10, + 1.15 + ], + [ + 13, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "amenity area/label/aeroway gate", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 31 + ], + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1, + "text-field": "{_name}", + "text-letter-spacing": 0.05, + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity area/label/parking", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 30 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity area/label/places to eat/drink", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 28 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/all other office", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 27 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/medium office", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 26 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/transportation", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 19 + ], + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity area/label/marketplace", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 18 + ], + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/viewpoint", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 17 + ], + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/golf course", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 12 + ], + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#84d435", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/atm", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 9 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/hospital", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#FF9393", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/medical", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#FF9393", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity area/label/helipad", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "bottom", + "text-offset": [ + 0, + -1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity area/label/ferry terminal", + "type": "symbol", + "source": "esri", + "source-layer": "amenity area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "shop area/label/massage", + "type": "symbol", + "source": "esri", + "source-layer": "shop area/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop area/label/supermarket, department store", + "type": "symbol", + "source": "esri", + "source-layer": "shop area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "private access area/label/charging station", + "type": "symbol", + "source": "esri", + "source-layer": "private access area/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "private access area/label/bicycle repair station", + "type": "symbol", + "source": "esri", + "source-layer": "private access area/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "private access area/label/parking", + "type": "symbol", + "source": "esri", + "source-layer": "private access area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "aerodrome area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "aerodrome area/label", + "minzoom": 10, + "maxzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "place of worship area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship area/label", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "vending machine area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "vending machine area/label", + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "station area/label/halt", + "type": "symbol", + "source": "esri", + "source-layer": "station area/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 5, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "aeroway/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "aeroway/label", + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 750, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "water line (intermittent)/label/ditch, drain", + "type": "symbol", + "source": "esri", + "source-layer": "water line (intermittent)/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water line (intermittent)/label/stream", + "type": "symbol", + "source": "esri", + "source-layer": "water line (intermittent)/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water line (intermittent)/label/canal", + "type": "symbol", + "source": "esri", + "source-layer": "water line (intermittent)/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water line (intermittent)/label/river", + "type": "symbol", + "source": "esri", + "source-layer": "water line (intermittent)/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water line (perennial)/label/ditch, drain", + "type": "symbol", + "source": "esri", + "source-layer": "water line (perennial)/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water line (perennial)/label/stream", + "type": "symbol", + "source": "esri", + "source-layer": "water line (perennial)/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water line (perennial)/label/canal", + "type": "symbol", + "source": "esri", + "source-layer": "water line (perennial)/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "water line (perennial)/label/river", + "type": "symbol", + "source": "esri", + "source-layer": "water line (perennial)/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-field": "{_name}", + "text-offset": [ + 0, + -0.5 + ], + "text-max-angle": 30, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "station point/subway entrance", + "type": "symbol", + "source": "esri", + "source-layer": "station point", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 17, + "layout": { + "icon-image": "station point/subway entrance", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name3}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 17.5, + 0 + ], + [ + 17.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "station point/aerialway station", + "type": "symbol", + "source": "esri", + "source-layer": "station point", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 12, + "layout": { + "icon-image": "station point/aerialway station, halt", + "icon-size": { + "stops": [ + [ + 12, + 0.67 + ], + [ + 14, + 1 + ] + ] + }, + "icon-allow-overlap": false, + "text-max-width": 6, + "text-line-height": 1.1, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 13, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 12.5, + 0 + ], + [ + 12.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "station point/tram stop", + "type": "symbol", + "source": "esri", + "source-layer": "station point", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 13, + "layout": { + "icon-image": "station point/tram stop", + "icon-size": { + "stops": [ + [ + 13, + 0.67 + ], + [ + 14, + 1 + ] + ] + }, + "icon-allow-overlap": false, + "text-max-width": 6, + "text-line-height": 1.1, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 14.5, + 0 + ], + [ + 14.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "station point/halt", + "type": "symbol", + "source": "esri", + "source-layer": "station point", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 12, + "layout": { + "icon-image": "station point/aerialway station, halt", + "icon-size": { + "stops": [ + [ + 12, + 0.67 + ], + [ + 14, + 1 + ] + ] + }, + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 13.5, + 0 + ], + [ + 13.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/education", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 7, + "text-field": "{_name40}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/attraction", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 7, + "text-field": "{_name39}", + "text-optional": true + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/aeroway gate", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1, + "text-field": "{_name37}", + "text-letter-spacing": 0.05, + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "parking entrance multi-storey", + "type": "symbol", + "source": "esri", + "source-layer": "parking entrance multi-storey", + "minzoom": 18, + "layout": { + "icon-image": "parking entrance multi-storey", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/parking entrance underground", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 117 + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity point/parking entrance underground", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name36}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/parking street side", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 121 + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity point/parking street side", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 0.8 + ], + "text-field": "{_name35}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/parking", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 116 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/parking", + "icon-allow-overlap": false, + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 17, + 1 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name35}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/motorcycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 115 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/motorcycle parking", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name35}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/bicycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 114 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/bicycle parking", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name35}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/obelisk", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 110 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/obelisk", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/vehicle inspection", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 107 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/vehicle inspection", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/bicycle repair station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 106 + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity point/bicycle repair station", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name16}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/all other office", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 103 + ], + "minzoom": 18, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1 + ], + "text-field": "{_name32}", + "text-allow-overlap": true, + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/medium office", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 103 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/office", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1 + ], + "text-field": "{_name31}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/casino", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 97 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/casino", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/boat rental", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 95 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/boat rental", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/bay, strait", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 12, + "text-max-width": 6, + "text-line-height": 1.1, + "text-field": "{_name10}", + "text-letter-spacing": 0.1, + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "amenity point/atm", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 86 + ], + "minzoom": 18, + "layout": { + "icon-image": "amenity point/atm", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name9}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/bank", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 85 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/bank", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/bar", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 84 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/bar", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name33}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 16.5, + 0 + ], + [ + 16.6, + 1 + ] + ] + }, + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/bicycle rental", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 82 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/bicycle rental", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/bus stop", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 81 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/bus stop", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/bus station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 80 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/bus station", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/taxi", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 79 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/taxi", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/traffic signal", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 78 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/traffic signal", + "icon-allow-overlap": false + } + }, + { + "id": "amenity point/toll booth", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 77 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/toll booth", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/cafe", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 76 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/cafe", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name33}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 16.5, + 0 + ], + [ + 16.6, + 1 + ] + ] + }, + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/camp site", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 75 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/camp site", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name26}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/ford", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 74 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/ford", + "icon-allow-overlap": false + } + }, + { + "id": "amenity point/caravan site", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 73 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/caravan site", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name26}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/car rental", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 72 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/car rental", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/car wash", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 71 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/car wash", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/cinema", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 69 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/cinema", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/nightclub", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 67 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/nightclub", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/fire station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 66 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/fire station", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/charging station", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 64 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/charging station", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/fuel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 63 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/fuel", + "icon-allow-overlap": false, + "icon-size": { + "stops": [ + [ + 16, + 0.8 + ], + [ + 17, + 1 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/hospital", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 60 + ], + "minzoom": 14, + "layout": { + "icon-image": "amenity point/hospital", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name3}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 14.5, + 0 + ], + [ + 14.6, + 1 + ] + ] + }, + "text-color": "#FF9393", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/hostel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 59 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/hostel", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name26}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/hotel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 58 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/hotel", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name26}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/motel", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 57 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/motel", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name26}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/ice cream", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 56 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/ice cream", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name33}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 16.5, + 0 + ], + [ + 16.6, + 1 + ] + ] + }, + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/library", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 54 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/library", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/courthouse", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 53 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/courthouse", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/community centre", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 52 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/community centre", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/townhall", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 49 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/townhall", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/museum", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 48 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/museum", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/pharmacy", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 47 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/pharmacy", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-color": "#FF9393", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/clinic", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 46 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/clinic", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#FF9393", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/dentist", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 44 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/dentist", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-color": "#FF9393", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/police", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 40 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/police", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/post office", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 38 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/post office", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/pub", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 37 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/pub", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name33}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 16.5, + 0 + ], + [ + 16.6, + 1 + ] + ] + }, + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/food court", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 35 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/food court", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name33}", + "text-optional": true + }, + "paint": { + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/restaurant", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 34 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/restaurant", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name33}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 16.5, + 0 + ], + [ + 16.6, + 1 + ] + ] + }, + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/fast food", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 33 + ], + "minzoom": 17, + "layout": { + "icon-image": "amenity point/fast food", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name33}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 16.5, + 0 + ], + [ + 16.6, + 1 + ] + ] + }, + "text-color": "#7ceae5", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/theatre", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/theatre", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 15.5, + 0 + ], + [ + 15.6, + 1 + ] + ] + }, + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/arts centre", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/arts centre", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/prison", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 28 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/prison", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name25}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/viewpoint", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/viewpoint", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name19}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/monument", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/monument", + "icon-allow-overlap": false, + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 17, + 1 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name15}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/marketplace", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/marketplace", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name23}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/fitness", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/fitness", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name12}", + "text-optional": true + }, + "paint": { + "text-color": "#84d435", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/dog park", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/dog park", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name12}", + "text-optional": true + }, + "paint": { + "text-color": "#84d435", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/golf course", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 14, + "layout": { + "icon-image": "amenity point/golf course", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name13}", + "text-optional": true + }, + "paint": { + "text-color": "#84d435", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "amenity point/slipway", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/slipway", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name24}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/helipad", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 15, + "layout": { + "icon-image": "amenity point/helipad", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "bottom", + "text-offset": [ + 0, + -1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "amenity point/ferry terminal", + "type": "symbol", + "source": "esri", + "source-layer": "amenity point", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 14, + "layout": { + "icon-image": "amenity point/ferry terminal", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "shop point/fabric", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 70 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/fabric", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/massage", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 66 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/massage", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/video games", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 58 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/video games", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/variety store", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 57 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/variety store", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/tyres", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 56 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/tyres", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/supermarket", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 51 + ], + "minzoom": 15, + "layout": { + "icon-image": "shop point/supermarket", + "icon-allow-overlap": false, + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 17, + 1 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/sports", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 49 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/sports", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/shoes", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 48 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/shoes", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/pet", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 45 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/pet", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/outdoor", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 44 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/outdoor", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/other", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 43 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/other", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/optician", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 42 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/optician", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/motorcycle", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 39 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/motorcycle", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/mobile phone", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 38 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/mobile phone", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/mall", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 36 + ], + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "center", + "text-field": "{_name1}", + "text-letter-spacing": 0.08, + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/laundry", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 35 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/laundry", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/jewelry", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 33 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/jewelry", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/hardware", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/hardware", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/hairdresser", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/hairdresser", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/gift", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 29 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/gift", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/furniture", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/furniture", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/florist", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/florist", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/electronics", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/electronics", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/department store", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 15, + "layout": { + "icon-image": "shop point/department store", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/convenience", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/convenience", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/computer", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/computer", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/clothes", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/clothes", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/car repair", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/car repair", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/car parts", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/car parts", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/car", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/car", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/books", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/books", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/bicycle", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/bicycle", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/beverages", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/beverages", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/beauty", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/beauty", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "shop point/bakery", + "type": "symbol", + "source": "esri", + "source-layer": "shop point", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 17, + "layout": { + "icon-image": "shop point/bakery", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffc420", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "private access point/charging station", + "type": "symbol", + "source": "esri", + "source-layer": "private access point", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 16, + "layout": { + "icon-image": "amenity point/charging station", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "private access point/parking street side", + "type": "symbol", + "source": "esri", + "source-layer": "private access point", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 18, + "layout": { + "icon-image": "private access point/parking street side", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 0.8 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "private access point/parking", + "type": "symbol", + "source": "esri", + "source-layer": "private access point", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 16, + "layout": { + "icon-image": "private access point/parking", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "private access point/motorcycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "private access point", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 17, + "layout": { + "icon-image": "private access point/motorcycle parking", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "private access point/bicycle parking", + "type": "symbol", + "source": "esri", + "source-layer": "private access point", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 17, + "layout": { + "icon-image": "private access point/bicycle parking", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "private access point/bicycle repair station", + "type": "symbol", + "source": "esri", + "source-layer": "private access point", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 18, + "layout": { + "icon-image": "private access point/bicycle repair station", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.08, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "aerodrome point", + "type": "symbol", + "source": "esri", + "source-layer": "aerodrome point", + "minzoom": 9, + "maxzoom": 17, + "layout": { + "icon-image": "aerodrome point", + "icon-allow-overlap": false, + "icon-size": { + "stops": [ + [ + 9, + 0.7 + ], + [ + 12, + 1 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 12, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 9.5, + 0 + ], + [ + 9.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "place of worship point/other", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/other", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Taoist", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Taoist", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Sikh", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Sikh", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Shinto", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Shinto", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Muslim", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Muslim", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Jewish", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Jewish", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Hindu", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Hindu", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Christian - other", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Christian - other", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Christian - Jehovah's Witness", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Christian - Jehovah's Witness", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "place of worship point/Buddhist", + "type": "symbol", + "source": "esri", + "source-layer": "place of worship point", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 16, + "layout": { + "icon-image": "place of worship point/Buddhist", + "icon-allow-overlap": false, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 16, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 6, + "text-line-height": 1.1, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#dbaef2", + "text-halo-color": "#000000", + "text-halo-width": 1.1 + } + }, + { + "id": "junction area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "junction area/label", + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "junction point/traffic signal", + "type": "symbol", + "source": "esri", + "source-layer": "junction point", + "minzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 10, + "text-max-width": 6, + "text-anchor": "top", + "text-offset": [ + 0, + -0.9 + ], + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "railway station area", + "type": "symbol", + "source": "esri", + "source-layer": "railway station area", + "filter": [ + "==", + "$type", + "Point" + ], + "minzoom": 13, + "layout": { + "icon-image": "railway station area", + "icon-size": { + "stops": [ + [ + 13, + 0.44 + ], + [ + 14, + 0.67 + ], + [ + 15, + 1 + ] + ] + }, + "icon-allow-overlap": true + } + }, + { + "id": "railway station area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "railway station area/label", + "minzoom": 13, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 13, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 5, + "text-line-height": { + "stops": [ + [ + 13, + 1 + ], + [ + 14, + 1.1 + ] + ] + }, + "text-field": "{_name}", + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.05, + "text-optional": false + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "railway station point", + "type": "symbol", + "source": "esri", + "source-layer": "railway station point", + "minzoom": 13, + "layout": { + "icon-image": "railway station point", + "icon-size": { + "stops": [ + [ + 13, + 0.44 + ], + [ + 14, + 0.67 + ], + [ + 15, + 1 + ] + ] + }, + "icon-allow-overlap": true, + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 13, + 10 + ], + [ + 18, + 11 + ] + ] + }, + "text-max-width": 5, + "text-line-height": { + "stops": [ + [ + 13, + 1 + ], + [ + 14, + 1.1 + ] + ] + }, + "text-anchor": "top", + "text-offset": [ + 0, + 1.3 + ], + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-optional": false + }, + "paint": { + "text-opacity": { + "stops": [ + [ + 13.5, + 0 + ], + [ + 13.6, + 1 + ] + ] + }, + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "place (small)/locality", + "type": "symbol", + "source": "esri", + "source-layer": "place (small)", + "minzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 16, + 12 + ] + ] + }, + "text-max-width": 7, + "text-padding": { + "stops": [ + [ + 15, + 7 + ], + [ + 16, + 8.4 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 15, + 1.1 + ], + [ + 16, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name6}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "place (small)/neighborhood", + "type": "symbol", + "source": "esri", + "source-layer": "place (small)", + "minzoom": 14, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": 7, + "text-padding": { + "stops": [ + [ + 14, + 7 + ], + [ + 15, + 8.4 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 14, + 1.1 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name5}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "place (small)/quarter", + "type": "symbol", + "source": "esri", + "source-layer": "place (small)", + "minzoom": 13, + "maxzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 13, + 11 + ], + [ + 14, + 12 + ], + [ + 15, + 14 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 13, + 5 + ], + [ + 14, + 8 + ], + [ + 15, + 15 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 13, + 7.7 + ], + [ + 14, + 8.4 + ], + [ + 15, + 9.8 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 13, + 1 + ], + [ + 14, + 1.1 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name4}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "place (small)/square", + "type": "symbol", + "source": "esri", + "source-layer": "place (small)", + "minzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": 11, + "text-max-width": 6, + "text-line-height": 1.1, + "text-field": "{_name3}", + "text-letter-spacing": 0.05, + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "place (small)/hamlet", + "type": "symbol", + "source": "esri", + "source-layer": "place (small)", + "minzoom": 13, + "maxzoom": 17, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 15, + 12 + ] + ] + }, + "text-max-width": 7, + "text-padding": { + "stops": [ + [ + 14, + 7 + ], + [ + 15, + 8.4 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 14, + 1.1 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name2}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "place (small)/village", + "type": "symbol", + "source": "esri", + "source-layer": "place (small)", + "minzoom": 11, + "maxzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 10 + ], + [ + 12, + 11 + ], + [ + 13, + 13 + ], + [ + 14, + 14 + ], + [ + 15, + 15 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 11, + 11 + ], + [ + 12, + 12 + ], + [ + 13, + 13 + ], + [ + 14, + 14 + ], + [ + 15, + 15 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 11, + 7 + ], + [ + 12, + 7.7 + ], + [ + 13, + 9.1 + ], + [ + 14, + 9.8 + ], + [ + 15, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 11, + 1 + ], + [ + 12, + 1.12 + ], + [ + 13, + 1.15 + ], + [ + 14, + 1.17 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name1}", + "text-optional": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 11, + "#ffffff" + ], + [ + 14, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "place (small)/suburb", + "type": "symbol", + "source": "esri", + "source-layer": "place (small)", + "minzoom": 11, + "maxzoom": 16, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 11 + ], + [ + 12, + 12 + ], + [ + 13, + 14 + ], + [ + 15, + 15 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 11, + 12 + ], + [ + 12, + 13 + ], + [ + 13, + 14 + ], + [ + 15, + 15 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 11, + 7.7 + ], + [ + 12, + 8.4 + ], + [ + 13, + 9.8 + ], + [ + 15, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 11, + 1 + ], + [ + 12, + 1.12 + ], + [ + 13, + 1.15 + ], + [ + 14, + 1.17 + ], + [ + 15, + 1.2 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 11, + "#ffffff" + ], + [ + 14, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "place (low importance)", + "type": "symbol", + "source": "esri", + "source-layer": "place (low importance)", + "minzoom": 8, + "maxzoom": 15, + "layout": { + "text-font": [ + "Arial Unicode MS Regular" + ], + "text-size": { + "stops": [ + [ + 8, + 10 + ], + [ + 10, + 11 + ], + [ + 11, + 13 + ], + [ + 13, + 15 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 8, + 9 + ], + [ + 10, + 10 + ], + [ + 11, + 11 + ], + [ + 13, + 12 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 8, + 7 + ], + [ + 10, + 7.7 + ], + [ + 11, + 8.4 + ], + [ + 13, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 8, + 1 + ], + [ + 10, + 1.13 + ], + [ + 11, + 1.16 + ], + [ + 13, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 8, + "#ffffff" + ], + [ + 12, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "place (medium importance)", + "type": "symbol", + "source": "esri", + "source-layer": "place (medium importance)", + "minzoom": 4, + "maxzoom": 14, + "layout": { + "icon-image": "place (medium importance)", + "icon-allow-overlap": false, + "symbol-avoid-edges": true, + "text-font": [ + "Arial Unicode MS Regular" + ], + "icon-size": { + "stops": [ + [ + 5.5, + 0.8 + ], + [ + 5.6, + 1 + ] + ] + }, + "text-size": { + "stops": [ + [ + 4, + 10 + ], + [ + 8, + 12 + ], + [ + 9, + 13 + ], + [ + 10, + 14 + ], + [ + 13, + 15 + ] + ] + }, + "text-max-width": 8, + "text-padding": { + "stops": [ + [ + 4, + 7 + ], + [ + 8, + 8.4 + ], + [ + 9, + 9.1 + ], + [ + 10, + 9.8 + ], + [ + 13, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 4, + 1 + ], + [ + 7, + 1.12 + ], + [ + 8, + 1.14 + ], + [ + 9, + 1.16 + ], + [ + 10, + 1.2 + ] + ] + }, + "text-anchor": { + "stops": [ + [ + 9.5, + "bottom" + ], + [ + 9.6, + "center" + ] + ] + }, + "text-offset": { + "stops": [ + [ + 9.5, + [ + 0, + -0.3 + ] + ], + [ + 9.6, + [ + 0, + 0 + ] + ] + ] + }, + "text-justify": "left", + "text-field": "{_name}" + }, + "paint": { + "icon-opacity": { + "stops": [ + [ + 9.5, + 1 + ], + [ + 9.6, + 0 + ] + ] + }, + "text-color": { + "stops": [ + [ + 4, + "#ffffff" + ], + [ + 8, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "administrative label/county level 6", + "type": "symbol", + "source": "esri", + "source-layer": "administrative label/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 10, + "layout": { + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": 11, + "text-max-width": { + "stops": [ + [ + 10, + 14 + ], + [ + 12, + 15 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 10, + 9 + ], + [ + 12, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 10, + 1.15 + ], + [ + 12, + 1.17 + ] + ] + }, + "text-letter-spacing": 0.2, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "administrative label/county level 5", + "type": "symbol", + "source": "esri", + "source-layer": "administrative label/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 10, + "layout": { + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": 12.5, + "text-max-width": { + "stops": [ + [ + 10, + 14 + ], + [ + 12, + 15 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 10, + 9 + ], + [ + 12, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 10, + 1.15 + ], + [ + 12, + 1.17 + ] + ] + }, + "text-letter-spacing": 0.2, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "administrative label/state", + "type": "symbol", + "source": "esri", + "source-layer": "administrative label/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 4, + "maxzoom": 10, + "layout": { + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 4, + 10 + ], + [ + 6, + 11 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 4, + 8 + ], + [ + 6, + 10 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 4, + 7 + ], + [ + 6, + 7.7 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 4, + 1 + ], + [ + 6, + 1.2 + ] + ] + }, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "place (high importance)", + "type": "symbol", + "source": "esri", + "source-layer": "place (high importance)", + "minzoom": 3, + "maxzoom": 13, + "layout": { + "icon-image": "place (high importance)", + "icon-allow-overlap": false, + "symbol-avoid-edges": true, + "text-font": [ + "Arial Unicode MS Bold" + ], + "icon-size": { + "stops": [ + [ + 3, + 0.8 + ], + [ + 6, + 1 + ] + ] + }, + "text-size": { + "stops": [ + [ + 3, + 11 + ], + [ + 5, + 12 + ], + [ + 7, + 13 + ], + [ + 9, + 14 + ], + [ + 10, + 15 + ] + ] + }, + "text-max-width": 7, + "text-padding": { + "stops": [ + [ + 3, + 7.7 + ], + [ + 5, + 8.4 + ], + [ + 7, + 9.1 + ], + [ + 9, + 9.8 + ], + [ + 10, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 3, + 1 + ], + [ + 4, + 1.12 + ], + [ + 5, + 1.14 + ], + [ + 7, + 1.16 + ], + [ + 9, + 1.18 + ], + [ + 10, + 1.2 + ] + ] + }, + "text-anchor": { + "stops": [ + [ + 9.5, + "bottom" + ], + [ + 9.6, + "center" + ] + ] + }, + "text-offset": { + "stops": [ + [ + 9.5, + [ + 0, + -0.4 + ] + ], + [ + 9.6, + [ + 0, + 0 + ] + ] + ] + }, + "text-justify": "left", + "text-field": "{_name}" + }, + "paint": { + "icon-opacity": { + "stops": [ + [ + 9.5, + 1 + ], + [ + 9.6, + 0 + ] + ] + }, + "text-color": { + "stops": [ + [ + 3, + "#ffffff" + ], + [ + 6, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.3 + } + }, + { + "id": "capital", + "type": "symbol", + "source": "esri", + "source-layer": "capital", + "minzoom": 3, + "maxzoom": 15, + "layout": { + "icon-image": "capital", + "icon-allow-overlap": false, + "symbol-avoid-edges": true, + "icon-size": { + "stops": [ + [ + 3, + 0.8 + ], + [ + 5, + 1 + ] + ] + }, + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 3, + 11 + ], + [ + 5, + 12 + ], + [ + 7, + 13 + ], + [ + 9, + 14 + ], + [ + 10, + 15 + ] + ] + }, + "text-max-width": 6, + "text-padding": { + "stops": [ + [ + 3, + 7.7 + ], + [ + 5, + 8.4 + ], + [ + 7, + 9.1 + ], + [ + 9, + 9.8 + ], + [ + 10, + 10.5 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 3, + 1 + ], + [ + 4, + 1.12 + ], + [ + 5, + 1.14 + ], + [ + 7, + 1.16 + ], + [ + 9, + 1.18 + ], + [ + 10, + 1.2 + ] + ] + }, + "text-anchor": { + "stops": [ + [ + 9.5, + "bottom" + ], + [ + 9.6, + "center" + ] + ] + }, + "text-offset": { + "stops": [ + [ + 9.5, + [ + 0, + -0.4 + ] + ], + [ + 9.6, + [ + 0, + 0 + ] + ] + ] + }, + "text-justify": "left", + "text-field": "{_name}" + }, + "paint": { + "icon-opacity": { + "stops": [ + [ + 9.5, + 1 + ], + [ + 9.6, + 0 + ] + ] + }, + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.3 + } + }, + { + "id": "administrative label/country", + "type": "symbol", + "source": "esri", + "source-layer": "administrative label/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "text-font": [ + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 2, + 9.5 + ], + [ + 3, + 10 + ], + [ + 4, + 11 + ], + [ + 6, + 12 + ], + [ + 9, + 13 + ] + ] + }, + "text-max-width": { + "stops": [ + [ + 2, + 6 + ], + [ + 3, + 7 + ], + [ + 4, + 8 + ], + [ + 6, + 9 + ], + [ + 9, + 10 + ] + ] + }, + "text-padding": { + "stops": [ + [ + 2, + 7 + ], + [ + 3, + 7.7 + ], + [ + 4, + 8.4 + ], + [ + 6, + 9.1 + ] + ] + }, + "text-line-height": { + "stops": [ + [ + 2, + 1.2 + ], + [ + 3, + 1.3 + ], + [ + 4, + 1.4 + ], + [ + 6, + 1.6 + ], + [ + 9, + 1.8 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-field": "{_name}", + "text-transform": "uppercase", + "text-optional": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 2, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.3 + } + } + ] +} diff --git a/ui-ngx/src/assets/map/world_edition_hybrid_reference_style.json b/ui-ngx/src/assets/map/world_edition_hybrid_reference_style.json new file mode 100644 index 0000000000..852d0455a0 --- /dev/null +++ b/ui-ngx/src/assets/map/world_edition_hybrid_reference_style.json @@ -0,0 +1,57840 @@ +{ + "version": 8, + "sprite": "https://cdn.arcgis.com/sharing/rest/content/items/30d6b8271e1849cd9c3042060001f425/resources/sprites/sprite", + "glyphs": "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/resources/fonts/{fontstack}/{range}.pbf", + "sources": { + "esri": { + "type": "vector", + "url": "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer", + "attribution": "Sources: Esri, TomTom, Garmin, FAO, NOAA, USGS, © OpenStreetMap contributors, and the GIS User Community", + "tiles": [ + "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/{z}/{y}/{x}.pbf" + ] + } + }, + "layers": [ + { + "id": "Railroad/casing", + "type": "line", + "source": "esri", + "source-layer": "Railroad", + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.4 + ], + [ + 16, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.3 + ], + [ + 17, + 5.33 + ] + ] + } + } + }, + { + "id": "Railroad/line", + "type": "line", + "source": "esri", + "source-layer": "Railroad", + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#dbdbd0", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.4 + ], + [ + 16, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.3 + ], + [ + 17, + 2.7 + ] + ] + } + } + }, + { + "id": "Railroad/symbol", + "type": "symbol", + "source": "esri", + "source-layer": "Railroad", + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "icon-image": "Railroad/0", + "icon-rotate": 90, + "symbol-spacing": 20, + "icon-rotation-alignment": "map", + "icon-allow-overlap": true, + "icon-padding": 1 + }, + "paint": {} + }, + { + "id": "Ferry/Rail ferry/casing", + "type": "line", + "source": "esri", + "source-layer": "Ferry", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.4 + ], + [ + 16, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.3 + ], + [ + 17, + 5.33 + ] + ] + } + } + }, + { + "id": "Ferry/Rail ferry/line", + "type": "line", + "source": "esri", + "source-layer": "Ferry", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#dbdbd0", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.4 + ], + [ + 16, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.3 + ], + [ + 17, + 2.7 + ] + ] + } + } + }, + { + "id": "Ferry/Rail ferry/symbol", + "type": "symbol", + "source": "esri", + "source-layer": "Ferry", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "icon-image": "Railroad/0", + "symbol-spacing": 20, + "icon-rotation-alignment": "map", + "icon-allow-overlap": true, + "icon-rotate": 90, + "icon-padding": 1 + }, + "paint": {} + }, + { + "id": "Road/4WD/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 10 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 21 + ], + [ + 22, + 36 + ] + ] + } + } + }, + { + "id": "Road/Service/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 8 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "Road/Local/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 7 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.33 + ], + [ + 15, + 4 + ], + [ + 16, + 4.67 + ], + [ + 17, + 7.3 + ], + [ + 18, + 25 + ], + [ + 22, + 40 + ] + ] + } + } + }, + { + "id": "Road/4WD/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 10 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#fdfdfd", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 18 + ], + [ + 22, + 33 + ] + ] + } + } + }, + { + "id": "Road/Service/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 8 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "Road/Local/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 7 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#FDFDFD", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 13, + 0.25 + ], + [ + 17, + 0.2 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 1.33 + ], + [ + 15, + 2 + ], + [ + 16, + 2.67 + ], + [ + 17, + 5.3 + ], + [ + 18, + 22 + ], + [ + 22, + 37 + ] + ] + } + } + }, + { + "id": "Road/Minor/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 5 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 27 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "Road/Minor, ramp or traffic circle/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 17 + ], + [ + 22, + 32 + ] + ] + } + } + }, + { + "id": "Road/Minor/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 5 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 24 + ], + [ + 22, + 39 + ] + ] + } + } + }, + { + "id": "Road/Minor, ramp or traffic circle/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 14 + ], + [ + 22, + 29 + ] + ] + } + } + }, + { + "id": "Road/Major/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 3 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 29 + ], + [ + 22, + 44 + ] + ] + } + } + }, + { + "id": "Road/Major, ramp or traffic circle/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 4 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 19 + ], + [ + 22, + 34 + ] + ] + } + } + }, + { + "id": "Road/Major/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 3 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 26 + ], + [ + 22, + 41 + ] + ] + } + } + }, + { + "id": "Road/Major, ramp or traffic circle/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 4 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 10, + 0.2 + ], + [ + 11, + 0.3 + ], + [ + 12, + 0.45 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.3 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 16 + ], + [ + 22, + 31 + ] + ] + } + } + }, + { + "id": "Road/Highway/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 31 + ], + [ + 22, + 46 + ] + ] + } + } + }, + { + "id": "Road/Freeway Motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 33 + ], + [ + 22, + 48 + ] + ] + } + } + }, + { + "id": "Road/Freeway Motorway Highway, ramp or traffic circle/casing", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 6, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "Road/Highway/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 28 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "Road/Freeway Motorway/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.15 + ], + [ + 9, + 0.2 + ], + [ + 12, + 0.23 + ], + [ + 13, + 0.35 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 30 + ], + [ + 22, + 45 + ] + ] + } + } + }, + { + "id": "Road/Freeway Motorway Highway, ramp or traffic circle/line", + "type": "line", + "source": "esri", + "source-layer": "Road", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 6, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 6, + "#ffb994" + ], + [ + 10, + "#ff9961" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 6, + 0.1 + ], + [ + 13, + 0.12 + ], + [ + 17, + 0.25 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 6, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "Road tunnel/Minor/casing", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 5 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 27 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "Road tunnel/Minor, ramp or traffic circle/casing", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 17 + ], + [ + 22, + 32 + ] + ] + } + } + }, + { + "id": "Road tunnel/Minor/line", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 5 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 24 + ], + [ + 22, + 39 + ] + ] + } + } + }, + { + "id": "Road tunnel/Minor, ramp or traffic circle/line", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 14 + ], + [ + 22, + 29 + ] + ] + } + } + }, + { + "id": "Road tunnel/Major/casing", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 3 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 29 + ], + [ + 22, + 44 + ] + ] + } + } + }, + { + "id": "Road tunnel/Major, ramp or traffic circle/casing", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 4 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#191919", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 14, + 4 + ], + [ + 15, + 4.67 + ], + [ + 16, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 19 + ], + [ + 22, + 34 + ] + ] + } + } + }, + { + "id": "Road tunnel/Major/line", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 3 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 26 + ], + [ + 22, + 41 + ] + ] + } + } + }, + { + "id": "Road tunnel/Major, ramp or traffic circle/line", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 4 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "base": 1.2, + "stops": [ + [ + 12, + "#fffaf0" + ], + [ + 13, + "#ffdb8c" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.33 + ], + [ + 14, + 2 + ], + [ + 15, + 2.67 + ], + [ + 16, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 16 + ], + [ + 22, + 31 + ] + ] + } + } + }, + { + "id": "Road tunnel/Highway/casing", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 8, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 31 + ], + [ + 22, + 46 + ] + ] + } + } + }, + { + "id": "Road tunnel/Freeway Motorway/casing", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 8, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 33 + ], + [ + 22, + 48 + ] + ] + } + } + }, + { + "id": "Road tunnel/Freeway Motorway Highway, ramp or traffic circle/casing", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#4d0800", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3.33 + ], + [ + 13, + 4 + ], + [ + 15, + 6 + ], + [ + 17, + 10 + ], + [ + 18, + 23 + ], + [ + 22, + 38 + ] + ] + } + } + }, + { + "id": "Road tunnel/Highway/line", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 8, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#ff9961", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 28 + ], + [ + 22, + 42 + ] + ] + } + } + }, + { + "id": "Road tunnel/Freeway Motorway/line", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 8, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#ff9961", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 30 + ], + [ + 22, + 45 + ] + ] + } + } + }, + { + "id": "Road tunnel/Freeway Motorway Highway, ramp or traffic circle/line", + "type": "line", + "source": "esri", + "source-layer": "Road tunnel", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#ff9961", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 12, + 0.35 + ], + [ + 13, + 0.25 + ], + [ + 17, + 0.15 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.33 + ], + [ + 13, + 2 + ], + [ + 15, + 4 + ], + [ + 17, + 8 + ], + [ + 18, + 20 + ], + [ + 22, + 35 + ] + ] + } + } + }, + { + "id": "Boundary line/Disputed admin2", + "type": "line", + "source": "esri", + "source-layer": "Boundary line", + "filter": [ + "all", + [ + "==", + "_symbol", + 8 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-opacity": 0.55, + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.6 + ], + [ + 11, + 2.5 + ], + [ + 18, + 4 + ] + ] + }, + "line-dasharray": [ + 6, + 3 + ] + } + }, + { + "id": "Boundary line/Disputed admin1", + "type": "line", + "source": "esri", + "source-layer": "Boundary line", + "filter": [ + "all", + [ + "==", + "_symbol", + 7 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 3, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-dasharray": [ + 6.0, + 3.0 + ], + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 3, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 3, + 1.6 + ], + [ + 11, + 2.5 + ], + [ + 18, + 4 + ] + ] + } + } + }, + { + "id": "Boundary line/Disputed admin0", + "type": "line", + "source": "esri", + "source-layer": "Boundary line", + "filter": [ + "all", + [ + "==", + "_symbol", + 6 + ], + [ + "!in", + "Viz", + 3 + ], + [ + "!in", + "DisputeID", + 8, + 16, + 90, + 96, + 0 + ] + ], + "minzoom": 1, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": { + "stops": [ + [ + 1, + "#ffffff" + ], + [ + 2, + "#000000" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 1, + 1.33 + ], + [ + 2, + 1.6 + ], + [ + 11, + 2.5 + ], + [ + 18, + 4 + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 1, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-dasharray": [ + 6, + 3 + ] + } + }, + { + "id": "Boundary line/Admin0/casing", + "type": "line", + "source": "esri", + "source-layer": "Boundary line", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 2, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": { + "stops": [ + [ + 2, + "#000000" + ], + [ + 10, + "#4e4e4e" + ], + [ + 11, + "#cccccc" + ] + ] + }, + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 2, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 2, + 3.33 + ], + [ + 6, + 5 + ], + [ + 18, + 12 + ] + ] + } + } + }, + { + "id": "Boundary line/Admin2", + "type": "line", + "source": "esri", + "source-layer": "Boundary line", + "filter": [ + "all", + [ + "==", + "_symbol", + 2 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-opacity": 0.55, + "line-dasharray": [ + 2, + 2 + ], + "line-width": { + "base": 1.2, + "stops": [ + [ + 10, + 1.5 + ], + [ + 13, + 2 + ], + [ + 18, + 2.75 + ] + ] + } + } + }, + { + "id": "Boundary line/Admin1", + "type": "line", + "source": "esri", + "source-layer": "Boundary line", + "filter": [ + "all", + [ + "==", + "_symbol", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 3, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-dasharray": [ + 4, + 3 + ], + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 3, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.0, + "stops": [ + [ + 3, + 1.33 + ], + [ + 6, + 1.75 + ], + [ + 18, + 4 + ] + ] + } + } + }, + { + "id": "Boundary line/Admin0/line", + "type": "line", + "source": "esri", + "source-layer": "Boundary line", + "filter": [ + "all", + [ + "==", + "_symbol", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 1, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#ffffff", + "line-opacity": { + "base": 1.0, + "stops": [ + [ + 1, + 0.55 + ], + [ + 4, + 0.7 + ], + [ + 10, + 0.55 + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 1, + 1.33 + ], + [ + 6, + 2 + ], + [ + 18, + 4 + ] + ] + } + } + }, + { + "id": "Water point/Stream or river", + "type": "symbol", + "source": "esri", + "source-layer": "Water point", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 9, + "layout": { + "icon-image": "Water point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": { + "stops": [ + [ + 9, + 7 + ], + [ + 15, + 11.3 + ] + ] + }, + "text-anchor": "center", + "text-letter-spacing": 0.01, + "text-max-width": 5, + "text-field": "{_name_global}", + "text-padding": 15 + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water point/Lake or reservoir", + "type": "symbol", + "source": "esri", + "source-layer": "Water point", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 9, + "layout": { + "icon-image": "Water point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": { + "stops": [ + [ + 9, + 7 + ], + [ + 15, + 11.3 + ] + ] + }, + "text-anchor": "center", + "text-letter-spacing": 0.01, + "text-max-width": 5, + "text-field": "{_name_global}", + "text-padding": 15 + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water point/Bay or inlet", + "type": "symbol", + "source": "esri", + "source-layer": "Water point", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 9, + "layout": { + "icon-image": "Water point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": { + "stops": [ + [ + 9, + 7 + ], + [ + 15, + 12 + ] + ] + }, + "text-anchor": "center", + "text-letter-spacing": 0.15, + "text-max-width": 5, + "text-field": "{_name_global}", + "text-padding": 15 + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water point/Sea or ocean", + "type": "symbol", + "source": "esri", + "source-layer": "Water point", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 9, + "layout": { + "icon-image": "Water point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 15, + "text-anchor": "center", + "text-letter-spacing": 0.1, + "text-max-width": 7, + "text-field": "{_name_global}", + "text-padding": 15 + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water point/Canal or ditch", + "type": "symbol", + "source": "esri", + "source-layer": "Water point", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": "Water point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": { + "stops": [ + [ + 11, + 7 + ], + [ + 15, + 10 + ] + ] + }, + "text-anchor": "center", + "text-letter-spacing": 0.01, + "text-max-width": 5, + "text-field": "{_name_global}", + "text-padding": 15 + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water point/Island", + "type": "symbol", + "source": "esri", + "source-layer": "Water point", + "filter": [ + "==", + "_label_class", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": "Water point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": { + "stops": [ + [ + 11, + 7 + ], + [ + 15, + 11.3 + ] + ] + }, + "text-anchor": "center", + "text-letter-spacing": 0.05, + "text-max-width": 5, + "text-field": "{_name_global}", + "text-padding": 15 + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Water line/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Water line/label", + "minzoom": 14, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 10, + "text-letter-spacing": 0.07, + "text-max-width": 8, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Marine park/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Marine park/label", + "minzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 6, + 10 + ], + [ + 7, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + }, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#d9fff2", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area/label/Canal or ditch", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 10, + "text-letter-spacing": 0.01, + "text-max-width": 5, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area/label/Small river", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 7 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11.33, + "text-letter-spacing": 0.01, + "text-max-width": 5, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area/label/Large river", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 13.33, + "text-letter-spacing": 0.01, + "text-max-width": 7, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area/label/Small lake or reservoir", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 6 + ], + "minzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11.33, + "text-letter-spacing": 0.01, + "text-max-width": 5, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area/label/Large lake or reservoir", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 13.33, + "text-letter-spacing": 0.01, + "text-max-width": 7, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area/label/Bay or inlet", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 12, + "text-letter-spacing": 0.15, + "text-max-width": 7, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area/label/Small island", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11.3, + "text-letter-spacing": 0.05, + "text-max-width": 5, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Water area/label/Large island", + "type": "symbol", + "source": "esri", + "source-layer": "Water area/label", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 12.5, + "text-letter-spacing": 0.05, + "text-max-width": 7, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Water area large scale/label/River", + "type": "symbol", + "source": "esri", + "source-layer": "Water area large scale/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 7, + "maxzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-spacing": 1000, + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area large scale/label/Lake or lake intermittent", + "type": "symbol", + "source": "esri", + "source-layer": "Water area large scale/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 7, + "maxzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11, + "text-letter-spacing": 0.05, + "text-max-width": 5, + "text-field": "{_name}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area medium scale/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Water area medium scale/label", + "minzoom": 5, + "maxzoom": 7, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-max-width": 5, + "text-field": "{_name}", + "text-letter-spacing": 0.05, + "text-size": { + "base": 1.2, + "stops": [ + [ + 5, + 10 + ], + [ + 6, + 11 + ] + ] + } + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Water area small scale/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Water area small scale/label", + "minzoom": 3, + "maxzoom": 5, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-letter-spacing": 0.05, + "text-max-width": 5, + "text-field": "{_name}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 3, + 9 + ], + [ + 4, + 10 + ] + ] + } + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Marine area/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Marine area/label", + "minzoom": 11, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 10, + "text-max-width": 8, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Marine waterbody/label/small", + "type": "symbol", + "source": "esri", + "source-layer": "Marine waterbody/label", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11, + "text-letter-spacing": 0.1, + "text-max-width": 6, + "text-field": "{_name}", + "text-padding": 15 + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Marine waterbody/label/medium", + "type": "symbol", + "source": "esri", + "source-layer": "Marine waterbody/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11, + "text-letter-spacing": 0.1, + "text-max-width": 6, + "text-field": "{_name}", + "text-padding": 15 + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Marine waterbody/label/large", + "type": "symbol", + "source": "esri", + "source-layer": "Marine waterbody/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-letter-spacing": 0.1, + "text-max-width": 6, + "text-field": "{_name}", + "text-padding": 15, + "text-size": { + "base": 1.2, + "stops": [ + [ + 2, + 11 + ], + [ + 6, + 13 + ], + [ + 9, + 15 + ] + ] + } + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Marine waterbody/label/x large", + "type": "symbol", + "source": "esri", + "source-layer": "Marine waterbody/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-letter-spacing": 0.1, + "text-max-width": 6, + "text-field": "{_name}", + "text-padding": 15, + "text-size": { + "base": 1.2, + "stops": [ + [ + 2, + 11 + ], + [ + 4, + 13 + ], + [ + 9, + 15 + ] + ] + } + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Marine waterbody/label/2x large", + "type": "symbol", + "source": "esri", + "source-layer": "Marine waterbody/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Italic" + ], + "text-letter-spacing": 0.1, + "text-max-width": 6, + "text-field": "{_name}", + "text-padding": 15, + "text-size": { + "base": 1.2, + "stops": [ + [ + 0, + 10 + ], + [ + 1, + 12 + ], + [ + 2, + 13 + ], + [ + 9, + 15 + ] + ] + } + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-width": 1, + "text-halo-color": "#000000" + } + }, + { + "id": "Ferry/label/Rail ferry", + "type": "symbol", + "source": "esri", + "source-layer": "Ferry/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-offset": [ + 0, + -0.6 + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 15, + 11 + ], + [ + 17, + 11.3 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Railroad/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Railroad/label", + "minzoom": 15, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "symbol-spacing": 1000, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 10, + "text-offset": [ + 0, + -0.6 + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 15, + 11 + ], + [ + 17, + 11.3 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Place/POI Other/Color6", + "type": "symbol", + "source": "esri", + "source-layer": "POI Other place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "POI Other place/Color6_1" + ], + [ + 16.6, + "POI Other place/Color6" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/POI Other/Color5", + "type": "symbol", + "source": "esri", + "source-layer": "POI Other place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "POI Other place/Color5_1" + ], + [ + 16.6, + "POI Other place/Color5" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/POI Other/Color4", + "type": "symbol", + "source": "esri", + "source-layer": "POI Other place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "POI Other place/Color4_1" + ], + [ + 16.6, + "POI Other place/Color4" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/POI Other/Color3", + "type": "symbol", + "source": "esri", + "source-layer": "POI Other place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "POI Other place/Color3_1" + ], + [ + 16.6, + "POI Other place/Color3" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/POI Other/Color2", + "type": "symbol", + "source": "esri", + "source-layer": "POI Other place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "POI Other place/Color2_1" + ], + [ + 16.6, + "POI Other place/Color2" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/POI Other/Color1", + "type": "symbol", + "source": "esri", + "source-layer": "POI Other place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "POI Other place/Color1_1" + ], + [ + 16.6, + "POI Other place/Color1" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Unclassified", + "type": "symbol", + "source": "esri", + "source-layer": "Unclassified place", + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Unclassified place/Unclassified_1" + ], + [ + 16.6, + "Unclassified place/Unclassified" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Wharf", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Wharf_1" + ], + [ + 16.6, + "Water Feature place/Wharf" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Well", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Well_1" + ], + [ + 16.6, + "Water Feature place/Well" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Waterfall", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Waterfall_1" + ], + [ + 16.6, + "Water Feature place/Waterfall" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Stream", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Stream_1" + ], + [ + 16.6, + "Water Feature place/Stream" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Strait", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Strait_1" + ], + [ + 16.6, + "Water Feature place/Strait" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Spring", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Spring_1" + ], + [ + 16.6, + "Water Feature place/Spring" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Sound", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Sound_1" + ], + [ + 16.6, + "Water Feature place/Sound" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Sea", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Sea_1" + ], + [ + 16.6, + "Water Feature place/Sea" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Reservoir", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Reservoir_1" + ], + [ + 16.6, + "Water Feature place/Reservoir" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Reef", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Reef_1" + ], + [ + 16.6, + "Water Feature place/Reef" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Other Water Feature", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Other Water Feature_1" + ], + [ + 16.6, + "Water Feature place/Other Water Feature" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Ocean", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Ocean_1" + ], + [ + 16.6, + "Water Feature place/Ocean" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Lake", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Lake_1" + ], + [ + 16.6, + "Water Feature place/Lake" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Lagoon", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Lagoon_1" + ], + [ + 16.6, + "Water Feature place/Lagoon" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Jetty", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Jetty_1" + ], + [ + 16.6, + "Water Feature place/Jetty" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Irrigation", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Irrigation_1" + ], + [ + 16.6, + "Water Feature place/Irrigation" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Hot Spring", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Hot Spring_1" + ], + [ + 16.6, + "Water Feature place/Hot Spring" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Harbor", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Harbor_1" + ], + [ + 16.6, + "Water Feature place/Harbor" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Gulf", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Gulf_1" + ], + [ + 16.6, + "Water Feature place/Gulf" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Fjord", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Fjord_1" + ], + [ + 16.6, + "Water Feature place/Fjord" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Estuary", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Estuary_1" + ], + [ + 16.6, + "Water Feature place/Estuary" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Delta", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Delta_1" + ], + [ + 16.6, + "Water Feature place/Delta" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Dam", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Dam_1" + ], + [ + 16.6, + "Water Feature place/Dam" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Cove", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Cove_1" + ], + [ + 16.6, + "Water Feature place/Cove" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Channel", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Channel_1" + ], + [ + 16.6, + "Water Feature place/Channel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Canal", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Canal_1" + ], + [ + 16.6, + "Water Feature place/Canal" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Water Feature/Bay", + "type": "symbol", + "source": "esri", + "source-layer": "Water Feature place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Water Feature place/Bay_1" + ], + [ + 16.6, + "Water Feature place/Bay" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Wetland", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 35 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Wetland_1" + ], + [ + 16.6, + "Land Feature place/Wetland" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Volcano", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 34 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Volcano_1" + ], + [ + 16.6, + "Land Feature place/Volcano" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Valley", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 33 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Valley_1" + ], + [ + 16.6, + "Land Feature place/Valley" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Swamp", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 32 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Swamp_1" + ], + [ + 16.6, + "Land Feature place/Swamp" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Scrubland", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Scrubland_1" + ], + [ + 16.6, + "Land Feature place/Scrubland" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Rock", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Rock_1" + ], + [ + 16.6, + "Land Feature place/Rock" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Ridge", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 29 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Ridge_1" + ], + [ + 16.6, + "Land Feature place/Ridge" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Ravine", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 28 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Ravine_1" + ], + [ + 16.6, + "Land Feature place/Ravine" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Point", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Point_1" + ], + [ + 16.6, + "Land Feature place/Point" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Plateau", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Plateau_1" + ], + [ + 16.6, + "Land Feature place/Plateau" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Plain", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Plain_1" + ], + [ + 16.6, + "Land Feature place/Plain" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Peninsula", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Peninsula_1" + ], + [ + 16.6, + "Land Feature place/Peninsula" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Other Land Feature", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Other Land Feature_1" + ], + [ + 16.6, + "Land Feature place/Other Land Feature" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Oasis", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Oasis_1" + ], + [ + 16.6, + "Land Feature place/Oasis" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Mountain Range", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Mountain Range_1" + ], + [ + 16.6, + "Land Feature place/Mountain Range" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Mountain", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Mountain_1" + ], + [ + 16.6, + "Land Feature place/Mountain" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Mesa", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Mesa_1" + ], + [ + 16.6, + "Land Feature place/Mesa" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Meadow", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Meadow_1" + ], + [ + 16.6, + "Land Feature place/Meadow" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Marsh", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Marsh_1" + ], + [ + 16.6, + "Land Feature place/Marsh" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Lava", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Lava_1" + ], + [ + 16.6, + "Land Feature place/Lava" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Isthmus", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Isthmus_1" + ], + [ + 16.6, + "Land Feature place/Isthmus" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Island", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Island_1" + ], + [ + 16.6, + "Land Feature place/Island" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Hill", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Hill_1" + ], + [ + 16.6, + "Land Feature place/Hill" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Grassland", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Grassland_1" + ], + [ + 16.6, + "Land Feature place/Grassland" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Glacier", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Glacier_1" + ], + [ + 16.6, + "Land Feature place/Glacier" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Forest", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Forest_1" + ], + [ + 16.6, + "Land Feature place/Forest" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Flat", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Flat_1" + ], + [ + 16.6, + "Land Feature place/Flat" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Dune", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Dune_1" + ], + [ + 16.6, + "Land Feature place/Dune" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Desert", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Desert_1" + ], + [ + 16.6, + "Land Feature place/Desert" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Cliff", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Cliff_1" + ], + [ + 16.6, + "Land Feature place/Cliff" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Cave", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Cave_1" + ], + [ + 16.6, + "Land Feature place/Cave" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Cape", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Cape_1" + ], + [ + 16.6, + "Land Feature place/Cape" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Canyon", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Canyon_1" + ], + [ + 16.6, + "Land Feature place/Canyon" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Butte", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Butte_1" + ], + [ + 16.6, + "Land Feature place/Butte" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Basin", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Basin_1" + ], + [ + 16.6, + "Land Feature place/Basin" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Land Feature/Atoll", + "type": "symbol", + "source": "esri", + "source-layer": "Land Feature place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Land Feature place/Atoll_1" + ], + [ + 16.6, + "Land Feature place/Atoll" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Residence/Trailer Park", + "type": "symbol", + "source": "esri", + "source-layer": "Residence place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Residence place/Trailer Park_1" + ], + [ + 16.6, + "Residence place/Trailer Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Residence/Other Residence", + "type": "symbol", + "source": "esri", + "source-layer": "Residence place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Residence place/Other Residence_1" + ], + [ + 16.6, + "Residence place/Other Residence" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Residence/Nursing Home", + "type": "symbol", + "source": "esri", + "source-layer": "Residence place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Residence place/Nursing Home_1" + ], + [ + 16.6, + "Residence place/Nursing Home" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Residence/House", + "type": "symbol", + "source": "esri", + "source-layer": "Residence place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Residence place/House_1" + ], + [ + 16.6, + "Residence place/House" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Residence/Estate", + "type": "symbol", + "source": "esri", + "source-layer": "Residence place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Residence place/Estate_1" + ], + [ + 16.6, + "Residence place/Estate" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Residence/Construction", + "type": "symbol", + "source": "esri", + "source-layer": "Residence place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Residence place/Construction_1" + ], + [ + 16.6, + "Residence place/Construction" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Residence/Apartment Rental", + "type": "symbol", + "source": "esri", + "source-layer": "Residence place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Residence place/Apartment Rental_1" + ], + [ + 16.6, + "Residence place/Apartment Rental" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Other Religion", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Other Religion_1" + ], + [ + 16.6, + "Religion place/Other Religion" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Temple", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Temple_1" + ], + [ + 16.6, + "Religion place/Temple" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Synagogue", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Synagogue_1" + ], + [ + 16.6, + "Religion place/Synagogue" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Mosque", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Mosque_1" + ], + [ + 16.6, + "Religion place/Mosque" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Gurdwara", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Gurdwara_1" + ], + [ + 16.6, + "Religion place/Gurdwara" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Ashram", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Ashram_1" + ], + [ + 16.6, + "Religion place/Ashram" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Religious Center", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Religious Center_1" + ], + [ + 16.6, + "Religion place/Religious Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Place of Worship", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Place of Worship_1" + ], + [ + 16.6, + "Religion place/Place of Worship" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Other Infrastructure", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Other Infrastructure_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Other Infrastructure" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Other Industrial", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Other Industrial_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Other Industrial" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Other Communication", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Other Communication_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Other Communication" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Industrial Zone", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Industrial Zone_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Industrial Zone" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Construction", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Construction_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Construction" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Mine", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Mine_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Mine" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Distillery", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Distillery_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Distillery" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Water Treatment", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Water Treatment_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Water Treatment" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Water Tank", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Water Tank_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Water Tank" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Logging", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Logging_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Logging" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Ranch", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Ranch_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Ranch" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Farm", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Farm_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Farm" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Food Production", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Food Production_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Food Production" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Livestock", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Livestock_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Livestock" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Plantation", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Plantation_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Plantation" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Vineyard", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Vineyard_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Vineyard" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Orchard", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Orchard_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Orchard" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Distribution Center", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Distribution Center_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Distribution Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Warehouse", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 28 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Warehouse_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Warehouse" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Factory", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Factory_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Factory" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Waste Management", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 29 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Waste Management_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Waste Management" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Recycling Center", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Recycling Center_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Recycling Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Oil and Gas Facility", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Oil and Gas Facility_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Oil and Gas Facility" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Utilities", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Utilities_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Utilities" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Radio Station", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Radio Station_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Radio Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/TV Station", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/TV Station_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/TV Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Power Station", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Power Station_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Power Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Radio Tower", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Radio Tower_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Radio Tower" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Tower", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Tower_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Tower" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Telecom", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Telecom_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Telecom" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Communication", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Communication_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Communication" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Industrial or Infrastructure/Building", + "type": "symbol", + "source": "esri", + "source-layer": "Industrial or Infrastructure place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Industrial or Infrastructure place/Building_1" + ], + [ + 16.6, + "Industrial or Infrastructure place/Building" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Lodging/Other Lodging", + "type": "symbol", + "source": "esri", + "source-layer": "Lodging place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Lodging place/Other Lodging_1" + ], + [ + 16.6, + "Lodging place/Other Lodging" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Lodging/Guest House", + "type": "symbol", + "source": "esri", + "source-layer": "Lodging place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Lodging place/Guest House_1" + ], + [ + 16.6, + "Lodging place/Guest House" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Lodging/Bed and Breakfast", + "type": "symbol", + "source": "esri", + "source-layer": "Lodging place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Lodging place/Bed and Breakfast_1" + ], + [ + 16.6, + "Lodging place/Bed and Breakfast" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Lodging/Hostel", + "type": "symbol", + "source": "esri", + "source-layer": "Lodging place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Lodging place/Hostel_1" + ], + [ + 16.6, + "Lodging place/Hostel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Lodging/Motel", + "type": "symbol", + "source": "esri", + "source-layer": "Lodging place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Lodging place/Motel_1" + ], + [ + 16.6, + "Lodging place/Motel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Other Education", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Other Education_1" + ], + [ + 16.6, + "Education place/Other Education" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Exam Preparation and Testing", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Exam Preparation and Testing_1" + ], + [ + 16.6, + "Education place/Exam Preparation and Testing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Coaching Institute", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Coaching Institute_1" + ], + [ + 16.6, + "Education place/Coaching Institute" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Scientific Research", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Scientific Research_1" + ], + [ + 16.6, + "Education place/Scientific Research" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Research Station", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Research Station_1" + ], + [ + 16.6, + "Education place/Research Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Language Studies", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Language Studies_1" + ], + [ + 16.6, + "Education place/Language Studies" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Kindergarten", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Kindergarten_1" + ], + [ + 16.6, + "Education place/Kindergarten" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Fine Arts School", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Fine Arts School_1" + ], + [ + 16.6, + "Education place/Fine Arts School" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/Vocational School", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/Vocational School_1" + ], + [ + 16.6, + "Education place/Vocational School" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Other Medical", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Other Medical_1" + ], + [ + 16.6, + "Medical place/Other Medical" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Veterinarian", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Veterinarian_1" + ], + [ + 16.6, + "Medical place/Veterinarian" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Doctor", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Doctor_1" + ], + [ + 16.6, + "Medical place/Doctor" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Dentist", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 17, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Dentist_1" + ], + [ + 16.6, + "Medical place/Dentist" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Chiropractor", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Chiropractor_1" + ], + [ + 16.6, + "Medical place/Chiropractor" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Therapist", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Therapist_1" + ], + [ + 16.6, + "Medical place/Therapist" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Psychiatric Institute", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Psychiatric Institute_1" + ], + [ + 16.6, + "Medical place/Psychiatric Institute" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Blood Bank", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Blood Bank_1" + ], + [ + 16.6, + "Medical place/Blood Bank" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Other Sport", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Other Sport_1" + ], + [ + 16.6, + "Sport place/Other Sport" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Volleyball", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Volleyball_1" + ], + [ + 16.6, + "Sport place/Volleyball" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Tennis Court", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Tennis Court_1" + ], + [ + 16.6, + "Sport place/Tennis Court" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Swimming Pool", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Swimming Pool_1" + ], + [ + 16.6, + "Sport place/Swimming Pool" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Sports Field", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Sports Field_1" + ], + [ + 16.6, + "Sport place/Sports Field" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Soccer", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Soccer_1" + ], + [ + 16.6, + "Sport place/Soccer" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Rugby", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Rugby_1" + ], + [ + 16.6, + "Sport place/Rugby" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Roller Rink", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Roller Rink_1" + ], + [ + 16.6, + "Sport place/Roller Rink" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Racquetball Court", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Racquetball Court_1" + ], + [ + 16.6, + "Sport place/Racquetball Court" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Racetrack", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Racetrack_1" + ], + [ + 16.6, + "Sport place/Racetrack" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Racecourse", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Racecourse_1" + ], + [ + 16.6, + "Sport place/Racecourse" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Mini Golf", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Mini Golf_1" + ], + [ + 16.6, + "Sport place/Mini Golf" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Indoor Sports", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Indoor Sports_1" + ], + [ + 16.6, + "Sport place/Indoor Sports" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Ice Skating Rink", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Ice Skating Rink_1" + ], + [ + 16.6, + "Sport place/Ice Skating Rink" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Hockey", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Hockey_1" + ], + [ + 16.6, + "Sport place/Hockey" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Golf Driving Range", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Golf Driving Range_1" + ], + [ + 16.6, + "Sport place/Golf Driving Range" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Football", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Football_1" + ], + [ + 16.6, + "Sport place/Football" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Diving Center", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Diving Center_1" + ], + [ + 16.6, + "Sport place/Diving Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Disc Golf", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Disc Golf_1" + ], + [ + 16.6, + "Sport place/Disc Golf" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Curling", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Curling_1" + ], + [ + 16.6, + "Sport place/Curling" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Bowling Alley", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Bowling Alley_1" + ], + [ + 16.6, + "Sport place/Bowling Alley" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Billiards", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Billiards_1" + ], + [ + 16.6, + "Sport place/Billiards" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Baseball", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Baseball_1" + ], + [ + 16.6, + "Sport place/Baseball" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Badminton", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Badminton_1" + ], + [ + 16.6, + "Sport place/Badminton" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Other Outdoors", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Other Outdoors_1" + ], + [ + 16.6, + "Outdoors place/Other Outdoors" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Holiday Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Holiday Park_1" + ], + [ + 16.6, + "Outdoors place/Holiday Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Shooting Range", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Shooting Range_1" + ], + [ + 16.6, + "Outdoors place/Shooting Range" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Snowmobile", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Snowmobile_1" + ], + [ + 16.6, + "Outdoors place/Snowmobile" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Ski Lift", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Ski Lift_1" + ], + [ + 16.6, + "Outdoors place/Ski Lift" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Ski Resort", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Ski Resort_1" + ], + [ + 16.6, + "Outdoors place/Ski Resort" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Bike Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Bike Park_1" + ], + [ + 16.6, + "Outdoors place/Bike Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Skateboard Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Skateboard Park_1" + ], + [ + 16.6, + "Outdoors place/Skateboard Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Outdoor Service", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Outdoor Service_1" + ], + [ + 16.6, + "Outdoors place/Outdoor Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Fishing", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Fishing_1" + ], + [ + 16.6, + "Outdoors place/Fishing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/RV Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/RV Park_1" + ], + [ + 16.6, + "Outdoors place/RV Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Trail", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Trail_1" + ], + [ + 16.6, + "Outdoors place/Trail" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Trailhead", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Trailhead_1" + ], + [ + 16.6, + "Outdoors place/Trailhead" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Shelter", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Shelter_1" + ], + [ + 16.6, + "Outdoors place/Shelter" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Campground", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Campground_1" + ], + [ + 16.6, + "Outdoors place/Campground" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Ranger Station", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Ranger Station_1" + ], + [ + 16.6, + "Outdoors place/Ranger Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Playground", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Playground_1" + ], + [ + 16.6, + "Outdoors place/Playground" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Other Food and Drink", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 71 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Other Food and Drink_1" + ], + [ + 16.6, + "Food and Drink place/Other Food and Drink" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Winery", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 103 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Winery_1" + ], + [ + 16.6, + "Food and Drink place/Winery" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Vietnamese Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 102 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Vietnamese Food_1" + ], + [ + 16.6, + "Food and Drink place/Vietnamese Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Vegetarian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 101 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Vegetarian Food_1" + ], + [ + 16.6, + "Food and Drink place/Vegetarian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Vegan Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 100 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Vegan Food_1" + ], + [ + 16.6, + "Food and Drink place/Vegan Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Turkish Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 99 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Turkish Food_1" + ], + [ + 16.6, + "Food and Drink place/Turkish Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Truck or Cart", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 98 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Truck or Cart_1" + ], + [ + 16.6, + "Food and Drink place/Truck or Cart" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Thai Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 97 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Thai Food_1" + ], + [ + 16.6, + "Food and Drink place/Thai Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Tea House", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 96 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Tea House_1" + ], + [ + 16.6, + "Food and Drink place/Tea House" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Tapas", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 95 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Tapas_1" + ], + [ + 16.6, + "Food and Drink place/Tapas" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Take Out and Delivery Only", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 94 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Take Out and Delivery Only_1" + ], + [ + 16.6, + "Food and Drink place/Take Out and Delivery Only" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Swiss Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 93 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Swiss Food_1" + ], + [ + 16.6, + "Food and Drink place/Swiss Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Sweet Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 92 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Sweet Shop_1" + ], + [ + 16.6, + "Food and Drink place/Sweet Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Sushi", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 91 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Sushi_1" + ], + [ + 16.6, + "Food and Drink place/Sushi" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Steak House", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 90 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Steak House_1" + ], + [ + 16.6, + "Food and Drink place/Steak House" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Spanish Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 89 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Spanish Food_1" + ], + [ + 16.6, + "Food and Drink place/Spanish Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Southwestern Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 88 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Southwestern Food_1" + ], + [ + 16.6, + "Food and Drink place/Southwestern Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Southeast Asian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 87 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Southeast Asian Food_1" + ], + [ + 16.6, + "Food and Drink place/Southeast Asian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/South American Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 86 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/South American Food_1" + ], + [ + 16.6, + "Food and Drink place/South American Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Soup", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 85 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Soup_1" + ], + [ + 16.6, + "Food and Drink place/Soup" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Snacks", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 84 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Snacks_1" + ], + [ + 16.6, + "Food and Drink place/Snacks" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Singaporean Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 83 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Singaporean Food_1" + ], + [ + 16.6, + "Food and Drink place/Singaporean Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Seafood", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 82 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Seafood_1" + ], + [ + 16.6, + "Food and Drink place/Seafood" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Scandinavian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 81 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Scandinavian Food_1" + ], + [ + 16.6, + "Food and Drink place/Scandinavian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Sandwich Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 80 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Sandwich Shop_1" + ], + [ + 16.6, + "Food and Drink place/Sandwich Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Russian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 79 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Russian Food_1" + ], + [ + 16.6, + "Food and Drink place/Russian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Portuguese Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 77 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Portuguese Food_1" + ], + [ + 16.6, + "Food and Drink place/Portuguese Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Polish Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 76 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Polish Food_1" + ], + [ + 16.6, + "Food and Drink place/Polish Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Peruvian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 74 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Peruvian Food_1" + ], + [ + 16.6, + "Food and Drink place/Peruvian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Pastries", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 73 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Pastries_1" + ], + [ + 16.6, + "Food and Drink place/Pastries" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Pakistani Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 72 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Pakistani Food_1" + ], + [ + 16.6, + "Food and Drink place/Pakistani Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Noodles", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 70 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Noodles_1" + ], + [ + 16.6, + "Food and Drink place/Noodles" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Moroccan Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 69 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Moroccan Food_1" + ], + [ + 16.6, + "Food and Drink place/Moroccan Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Middle Eastern Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 68 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Middle Eastern Food_1" + ], + [ + 16.6, + "Food and Drink place/Middle Eastern Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Mexican Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 67 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Mexican Food_1" + ], + [ + 16.6, + "Food and Drink place/Mexican Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Mediterranean Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 66 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Mediterranean Food_1" + ], + [ + 16.6, + "Food and Drink place/Mediterranean Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Malaysian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 65 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Malaysian Food_1" + ], + [ + 16.6, + "Food and Drink place/Malaysian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Lebanese Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 64 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Lebanese Food_1" + ], + [ + 16.6, + "Food and Drink place/Lebanese Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Latin American Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 63 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Latin American Food_1" + ], + [ + 16.6, + "Food and Drink place/Latin American Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Kosher Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 62 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Kosher Food_1" + ], + [ + 16.6, + "Food and Drink place/Kosher Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Korean Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 61 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Korean Food_1" + ], + [ + 16.6, + "Food and Drink place/Korean Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Japanese Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 60 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Japanese Food_1" + ], + [ + 16.6, + "Food and Drink place/Japanese Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Italian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 59 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Italian Food_1" + ], + [ + 16.6, + "Food and Drink place/Italian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Irish Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 58 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Irish Food_1" + ], + [ + 16.6, + "Food and Drink place/Irish Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/International Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 57 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/International Food_1" + ], + [ + 16.6, + "Food and Drink place/International Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Indonesian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 56 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Indonesian Food_1" + ], + [ + 16.6, + "Food and Drink place/Indonesian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Indian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 55 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Indian Food_1" + ], + [ + 16.6, + "Food and Drink place/Indian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Ice Cream Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 54 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Ice Cream Shop_1" + ], + [ + 16.6, + "Food and Drink place/Ice Cream Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Hungarian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 53 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Hungarian Food_1" + ], + [ + 16.6, + "Food and Drink place/Hungarian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Hot Dogs", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 52 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Hot Dogs_1" + ], + [ + 16.6, + "Food and Drink place/Hot Dogs" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Healthy Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 51 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Healthy Food_1" + ], + [ + 16.6, + "Food and Drink place/Healthy Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Hawaiian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 50 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Hawaiian Food_1" + ], + [ + 16.6, + "Food and Drink place/Hawaiian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Grill", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 49 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Grill_1" + ], + [ + 16.6, + "Food and Drink place/Grill" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Greek Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 48 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Greek Food_1" + ], + [ + 16.6, + "Food and Drink place/Greek Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/German Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 47 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/German Food_1" + ], + [ + 16.6, + "Food and Drink place/German Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Fusion Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 46 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Fusion Food_1" + ], + [ + 16.6, + "Food and Drink place/Fusion Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/French Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 45 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/French Food_1" + ], + [ + 16.6, + "Food and Drink place/French Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Fondue", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 44 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Fondue_1" + ], + [ + 16.6, + "Food and Drink place/Fondue" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Fine Dining", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 43 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Fine Dining_1" + ], + [ + 16.6, + "Food and Drink place/Fine Dining" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Filipino Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 42 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Filipino Food_1" + ], + [ + 16.6, + "Food and Drink place/Filipino Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/European Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 40 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/European Food_1" + ], + [ + 16.6, + "Food and Drink place/European Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Ethiopian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 39 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Ethiopian Food_1" + ], + [ + 16.6, + "Food and Drink place/Ethiopian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/East European Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 38 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/East European Food_1" + ], + [ + 16.6, + "Food and Drink place/East European Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Deli", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 37 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Deli_1" + ], + [ + 16.6, + "Food and Drink place/Deli" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Dairy Goods", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 36 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Dairy Goods_1" + ], + [ + 16.6, + "Food and Drink place/Dairy Goods" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Cuban Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 35 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Cuban Food_1" + ], + [ + 16.6, + "Food and Drink place/Cuban Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Creperie", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 34 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Creperie_1" + ], + [ + 16.6, + "Food and Drink place/Creperie" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Continental Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 33 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Continental Food_1" + ], + [ + 16.6, + "Food and Drink place/Continental Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Cocktail Lounge", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Cocktail Lounge_1" + ], + [ + 16.6, + "Food and Drink place/Cocktail Lounge" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Chinese Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Chinese Food_1" + ], + [ + 16.6, + "Food and Drink place/Chinese Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Chilean Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 29 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Chilean Food_1" + ], + [ + 16.6, + "Food and Drink place/Chilean Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Chicken Restaurant", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 28 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Chicken Restaurant_1" + ], + [ + 16.6, + "Food and Drink place/Chicken Restaurant" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Caucasian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Caucasian Food_1" + ], + [ + 16.6, + "Food and Drink place/Caucasian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Catering", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Catering_1" + ], + [ + 16.6, + "Food and Drink place/Catering" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Casual Dining", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Casual Dining_1" + ], + [ + 16.6, + "Food and Drink place/Casual Dining" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Caribbean Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Caribbean Food_1" + ], + [ + 16.6, + "Food and Drink place/Caribbean Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Cambodian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Cambodian Food_1" + ], + [ + 16.6, + "Food and Drink place/Cambodian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Californian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Californian Food_1" + ], + [ + 16.6, + "Food and Drink place/Californian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Cajun and Creole Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Cajun and Creole Food_1" + ], + [ + 16.6, + "Food and Drink place/Cajun and Creole Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Cafeteria", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Cafeteria_1" + ], + [ + 16.6, + "Food and Drink place/Cafeteria" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Burmese Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Burmese Food_1" + ], + [ + 16.6, + "Food and Drink place/Burnmese Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Burgers", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Burgers_1" + ], + [ + 16.6, + "Food and Drink place/Burgers" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/British Isles Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/British Isles Food_1" + ], + [ + 16.6, + "Food and Drink place/British Isles Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Brewpub", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Brewpub_1" + ], + [ + 16.6, + "Food and Drink place/Brewpub" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Breakfast", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Breakfast_1" + ], + [ + 16.6, + "Food and Drink place/Breakfast" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Brazilian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Brazilian Food_1" + ], + [ + 16.6, + "Food and Drink place/Brazilian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Bohemian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Bohemian Food_1" + ], + [ + 16.6, + "Food and Drink place/Bohemian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Bistro", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Bistro_1" + ], + [ + 16.6, + "Food and Drink place/Bistro" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Belgian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Belgian Food_1" + ], + [ + 16.6, + "Food and Drink place/Belgian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/BBQ and Southern Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/BBQ and Southern Food_1" + ], + [ + 16.6, + "Food and Drink place/BBQ and Southern Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Baltic Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Baltic Food_1" + ], + [ + 16.6, + "Food and Drink place/Baltic Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Bakery", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Bakery_1" + ], + [ + 16.6, + "Food and Drink place/Bakery" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Austrian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Austrian Food_1" + ], + [ + 16.6, + "Food and Drink place/Austrian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Australian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Australian Food_1" + ], + [ + 16.6, + "Food and Drink place/Australian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Asian Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Asian Food_1" + ], + [ + 16.6, + "Food and Drink place/Asian Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Argentinean Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Argentinean Food_1" + ], + [ + 16.6, + "Food and Drink place/Argentinean Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/American Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/American Food_1" + ], + [ + 16.6, + "Food and Drink place/American Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/African Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/African Food_1" + ], + [ + 16.6, + "Food and Drink place/African Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Other Arts and Entertainment", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 33 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Other Arts and Entertainment_1" + ], + [ + 16.6, + "Arts and Entertainment place/Other Arts and Entertainment" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Entertainment (Adult)", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Entertainment (Adult)_1" + ], + [ + 16.6, + "Arts and Entertainment place/Entertainment (Adult)" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Named Place", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 29 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Named Place_1" + ], + [ + 16.6, + "Arts and Entertainment place/Named Place" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Video Arcade", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 44 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Video Arcade_1" + ], + [ + 16.6, + "Arts and Entertainment place/Video Arcade" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Arcade", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Arcade_1" + ], + [ + 16.6, + "Arts and Entertainment place/Arcade" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Laser Tag", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Laser Tag_1" + ], + [ + 16.6, + "Arts and Entertainment place/Laser Tag" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Go Kart Track", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Go Kart Track_1" + ], + [ + 16.6, + "Arts and Entertainment place/Go Kart Track" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Fair", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Fair_1" + ], + [ + 16.6, + "Arts and Entertainment place/Fair" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Circus", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Circus_1" + ], + [ + 16.6, + "Arts and Entertainment place/Circus" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Tour Provider", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 41 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Tour Provider_1" + ], + [ + 16.6, + "Arts and Entertainment place/Tour Provider" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Tourist Information", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 43 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Tourist Information_1" + ], + [ + 16.6, + "Arts and Entertainment place/Tourist Information" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Banquet Hall", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Banquet Hall_1" + ], + [ + 16.6, + "Arts and Entertainment place/Banquet Hall" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Pool Hall", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 35 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Pool Hall_1" + ], + [ + 16.6, + "Arts and Entertainment place/Pool Hall" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Ballroom", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Ballroom_1" + ], + [ + 16.6, + "Arts and Entertainment place/Ballroom" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Event Space", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Event Space_1" + ], + [ + 16.6, + "Arts and Entertainment place/Event Space" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Club House", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Club House_1" + ], + [ + 16.6, + "Arts and Entertainment place/Club House" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Social Club", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 40 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Social Club_1" + ], + [ + 16.6, + "Arts and Entertainment place/Social Club" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Comedy Club", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Comedy Club_1" + ], + [ + 16.6, + "Arts and Entertainment place/Comedy Club" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Country Dance Club", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Country Dance Club_1" + ], + [ + 16.6, + "Arts and Entertainment place/Country Dance Club" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Dancing", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Dancing_1" + ], + [ + 16.6, + "Arts and Entertainment place/Dancing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Karaoke", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Karaoke_1" + ], + [ + 16.6, + "Arts and Entertainment place/Karaoke" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Live Music", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Live Music_1" + ], + [ + 16.6, + "Arts and Entertainment place/Live Music" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Jazz Club", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Jazz Club_1" + ], + [ + 16.6, + "Arts and Entertainment place/Jazz Club" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Nightlife", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Nightlife_1" + ], + [ + 16.6, + "Arts and Entertainment place/Nightlife" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/General Entertainment", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/General Entertainment_1" + ], + [ + 16.6, + "Arts and Entertainment place/General Entertainment" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Recreation Facility", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 38 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Recreation Facility_1" + ], + [ + 16.6, + "Arts and Entertainment place/Recreation Facility" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Recreation Center", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 37 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Recreation Center_1" + ], + [ + 16.6, + "Arts and Entertainment place/Recreation Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Cultural Center", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Cultural Center_1" + ], + [ + 16.6, + "Arts and Entertainment place/Cultural Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Auditorium", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Auditorium_1" + ], + [ + 16.6, + "Arts and Entertainment place/Auditorium" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Public Art", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 36 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Public Art_1" + ], + [ + 16.6, + "Arts and Entertainment place/Public Art" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Exhibit", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Exhibit_1" + ], + [ + 16.6, + "Arts and Entertainment place/Exhibit" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Art Gallery", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Art Gallery_1" + ], + [ + 16.6, + "Arts and Entertainment place/Art Gallery" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Other Public Facility", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Other Public Facility_1" + ], + [ + 16.6, + "Government or Public Facility place/Other Public Facility" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Other Government", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Other Government_1" + ], + [ + 16.6, + "Government or Public Facility place/Other Government" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Voting Booth", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Voting Booth_1" + ], + [ + 16.6, + "Government or Public Facility place/Voting Booth" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Animal Shelter", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Animal Shelter_1" + ], + [ + 16.6, + "Government or Public Facility place/Animal Shelter" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Public Restroom", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Public Restroom_1" + ], + [ + 16.6, + "Government or Public Facility place/Public Restroom" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Organization", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Organization_1" + ], + [ + 16.6, + "Government or Public Facility place/Organization" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Civic Center", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Civic Center_1" + ], + [ + 16.6, + "Government or Public Facility place/Civic Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/County Council", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/County Council_1" + ], + [ + 16.6, + "Government or Public Facility place/County Council" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Public Administration", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Public Administration_1" + ], + [ + 16.6, + "Government or Public Facility place/Public Administration" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Library", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Library_1" + ], + [ + 16.6, + "Government or Public Facility place/Library" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Social Service", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Social Service_1" + ], + [ + 16.6, + "Government or Public Facility place/Social Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Prison", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Prison_1" + ], + [ + 16.6, + "Government or Public Facility place/Prison" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Embassy", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Embassy_1" + ], + [ + 16.6, + "Government or Public Facility place/Embassy" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Police Service", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Police Service_1" + ], + [ + 16.6, + "Government or Public Facility place/Police Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Other Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 101 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Other Service_1" + ], + [ + 16.6, + "Shop or Service place/Other Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Other Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 102 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Other Shop_1" + ], + [ + 16.6, + "Shop or Service place/Other Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Premium Default", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 111 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Premium Default_1" + ], + [ + 16.6, + "Shop or Service place/Premium Default" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Modeling Agencies", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 89 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Modeling Agencies_1" + ], + [ + 16.6, + "Shop or Service place/Modeling Agencies" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Marriage and Match Making Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 84 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Marriage and Match Making Services_1" + ], + [ + 16.6, + "Shop or Service place/Marriage and Match Making Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Wedding Services and Bridal Studio", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 151 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Wedding Services and Bridal Studio_1" + ], + [ + 16.6, + "Shop or Service place/Wedding Services and Bridal Studio" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Bridal Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Bridal Shop_1" + ], + [ + 16.6, + "Shop or Service place/Bridal Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Florist", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 55 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Florist_1" + ], + [ + 16.6, + "Shop or Service place/Florist" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Flowers and Jewelry", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 56 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Flowers and Jewelry_1" + ], + [ + 16.6, + "Shop or Service place/Flowers and Jewelry" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Jeweler", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 75 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Jeweler_1" + ], + [ + 16.6, + "Shop or Service place/Jeweler" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Ambulance Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Ambulance Services_1" + ], + [ + 16.6, + "Shop or Service place/Ambulance Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Medical Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 85 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Medical Service_1" + ], + [ + 16.6, + "Shop or Service place/Medical Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Healthcare", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 68 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Healthcare_1" + ], + [ + 16.6, + "Shop or Service place/Healthcare" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Crematorium", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 40 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Crematorium_1" + ], + [ + 16.6, + "Shop or Service place/Crematorium" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Funeral Homes and Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 60 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Funeral Homes and Services_1" + ], + [ + 16.6, + "Shop or Service place/Funeral Homes and Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Property Management", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 113 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Property Management_1" + ], + [ + 16.6, + "Shop or Service place/Property Management" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Realtor", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 114 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Realtor_1" + ], + [ + 16.6, + "Shop or Service place/Realtor" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Registration Office", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 116 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Registration Office_1" + ], + [ + 16.6, + "Shop or Service place/Registration Office" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Engineering and Scientific Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 46 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Engineering and Scientific Services_1" + ], + [ + 16.6, + "Shop or Service place/Engineering and Scientific Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Specialty Trade Contractors", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 129 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Specialty Trade Contractors_1" + ], + [ + 16.6, + "Shop or Service place/Specialty Trade Contractors" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/B2B Restaurant Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/B2B Restaurant Services_1" + ], + [ + 16.6, + "Shop or Service place/B2B Restaurant Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/B2B Sales and Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/B2B Sales and Services_1" + ], + [ + 16.6, + "Shop or Service place/B2B Sales and Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Business Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Business Service_1" + ], + [ + 16.6, + "Shop or Service place/Business Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Printing and Publishing", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 112 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Printing and Publishing_1" + ], + [ + 16.6, + "Shop or Service place/Printing and Publishing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Commercial Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 32 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Commercial Services_1" + ], + [ + 16.6, + "Shop or Service place/Commercial Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Consumer Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 36 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Consumer Services_1" + ], + [ + 16.6, + "Shop or Service place/Consumer Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Consumer Goods", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 35 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Consumer Goods_1" + ], + [ + 16.6, + "Shop or Service place/Consumer Goods" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Power Equipment Dealer", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 110 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Power Equipment Dealer_1" + ], + [ + 16.6, + "Shop or Service place/Power Equipment Dealer" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Major Appliance", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 82 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Major Appliance_1" + ], + [ + 16.6, + "Shop or Service place/Major Appliance" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Office Equipment Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 98 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Office Equipment Services_1" + ], + [ + 16.6, + "Shop or Service place/Office Equipment Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Non-Store Retailers", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 96 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Non-Store Retailers_1" + ], + [ + 16.6, + "Shop or Service place/Non-Store Retailers" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Travel Agent", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 142 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Travel Agent_1" + ], + [ + 16.6, + "Shop or Service place/Travel Agent" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Translation Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 140 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Translation Services_1" + ], + [ + 16.6, + "Shop or Service place/Translation Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Recruiting Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 115 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Recruiting Services_1" + ], + [ + 16.6, + "Shop or Service place/Recruiting Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Consulting Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 33 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Consulting Services_1" + ], + [ + 16.6, + "Shop or Service place/Consulting Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Investigation Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 73 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Investigation Services_1" + ], + [ + 16.6, + "Shop or Service place/Investigation Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Legal Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 77 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Legal Services_1" + ], + [ + 16.6, + "Shop or Service place/Legal Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Attorney", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Attorney_1" + ], + [ + 16.6, + "Shop or Service place/Attorney" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Notary", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 97 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Notary_1" + ], + [ + 16.6, + "Shop or Service place/Notary" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Second Hand Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 122 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Second Hand Store_1" + ], + [ + 16.6, + "Shop or Service place/Second Hand Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Pawnshop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 104 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Pawnshop_1" + ], + [ + 16.6, + "Shop or Service place/Pawnshop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Antique Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Antique Shop_1" + ], + [ + 16.6, + "Shop or Service place/Antique Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Flea Market", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 53 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Flea Market_1" + ], + [ + 16.6, + "Shop or Service place/Flea Market" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Hobby Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 69 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Hobby Shop_1" + ], + [ + 16.6, + "Shop or Service place/Hobby Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Musical Instrument and Supplies", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 94 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Musical Instrument and Supplies_1" + ], + [ + 16.6, + "Shop or Service place/Musical Instrument and Supplies" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Arts and Crafts Supplies", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Arts and Crafts Supplies_1" + ], + [ + 16.6, + "Shop or Service place/Arts and Crafts Supplies" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Child Care Service (Day care)", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 29 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Child Care Service (Day care)_1" + ], + [ + 16.6, + "Shop or Service place/Child Care Service (Day care)" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Childrens Apparel", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Childrens Apparel_1" + ], + [ + 16.6, + "Shop or Service place/Childrens Apparel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Baby Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Baby Store_1" + ], + [ + 16.6, + "Shop or Service place/Baby Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Candy Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Candy Store_1" + ], + [ + 16.6, + "Shop or Service place/Candy Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Toy Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 139 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Toy Store_1" + ], + [ + 16.6, + "Shop or Service place/Toy Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Transportation Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 141 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Transportation Service_1" + ], + [ + 16.6, + "Shop or Service place/Transportation Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Cargo Transportation", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Cargo Transportation_1" + ], + [ + 16.6, + "Shop or Service place/Cargo Transportation" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Aviation", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Aviation_1" + ], + [ + 16.6, + "Shop or Service place/Aviation" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Truck Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 145 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Truck Services_1" + ], + [ + 16.6, + "Shop or Service place/Truck Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Truck Dealership", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 143 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Truck Dealership_1" + ], + [ + 16.6, + "Shop or Service place/Truck Dealership" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Truck Wash", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 146 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Truck Wash_1" + ], + [ + 16.6, + "Shop or Service place/Truck Wash" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Truck Repair", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 144 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Truck Repair_1" + ], + [ + 16.6, + "Shop or Service place/Truck Repair" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Motorcycle Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 92 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Motorcycle Shop_1" + ], + [ + 16.6, + "Shop or Service place/Motorcycle Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Motorcycle Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 91 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Motorcycle Service_1" + ], + [ + 16.6, + "Shop or Service place/Motorcycle Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Bicycle Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Bicycle Shop_1" + ], + [ + 16.6, + "Shop or Service place/Bicycle Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Bicycle Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Bicycle Service_1" + ], + [ + 16.6, + "Shop or Service place/Bicycle Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Rental RV", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 119 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Rental RV_1" + ], + [ + 16.6, + "Shop or Service place/Rental RV" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Rental Cars", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 118 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Rental Cars_1" + ], + [ + 16.6, + "Shop or Service place/Rental Cars" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Automobile Club", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Automobile Club_1" + ], + [ + 16.6, + "Shop or Service place/Automobile Club" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Towing Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 138 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Towing Service_1" + ], + [ + 16.6, + "Shop or Service place/Towing Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Road Assistance", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 121 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Road Assistance_1" + ], + [ + 16.6, + "Shop or Service place/Road Assistance" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/EV Charging Station", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 48 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/EV Charging Station_1" + ], + [ + 16.6, + "Shop or Service place/EV Charging Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Emission Testing", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 45 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Emission Testing_1" + ], + [ + 16.6, + "Shop or Service place/Emission Testing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Auto Maintenance", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Auto Maintenance_1" + ], + [ + 16.6, + "Shop or Service place/Auto Maintenance" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Auto Parts", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Auto Parts_1" + ], + [ + 16.6, + "Shop or Service place/Auto Parts" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Tire Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 136 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Tire Store_1" + ], + [ + 16.6, + "Shop or Service place/Tire Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Car Wash", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Car Wash_1" + ], + [ + 16.6, + "Shop or Service place/Car Wash" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Used Car Dealership", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 147 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Used Car Dealership_1" + ], + [ + 16.6, + "Shop or Service place/Used Car Dealership" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Auto Dealership", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Auto Dealership_1" + ], + [ + 16.6, + "Shop or Service place/Auto Dealership" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Bill Payment Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Bill Payment Service_1" + ], + [ + 16.6, + "Shop or Service place/Bill Payment Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Check Cashing Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 28 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Check Cashing Service_1" + ], + [ + 16.6, + "Shop or Service place/Check Cashing Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Money Transferring Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 90 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Money Transferring Service_1" + ], + [ + 16.6, + "Shop or Service place/Money Transferring Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Finance and Insurance", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 50 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Finance and Insurance_1" + ], + [ + 16.6, + "Shop or Service place/Finance and Insurance" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Financial Investment Firm", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 51 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Financial Investment Firm_1" + ], + [ + 16.6, + "Shop or Service place/Financial Investment Firm" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Tax Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 134 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Tax Service_1" + ], + [ + 16.6, + "Shop or Service place/Tax Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Lumber", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 80 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Lumber_1" + ], + [ + 16.6, + "Shop or Service place/Lumber" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Exterminating and Pest Control", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 49 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Exterminating and Pest Control_1" + ], + [ + 16.6, + "Shop or Service place/Exterminating and Pest Control" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Dry Cleaning and Laundry", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 43 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Dry Cleaning and Laundry_1" + ], + [ + 16.6, + "Shop or Service place/Dry Cleaning and Laundry" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Floor and Carpet", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 54 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Floor and Carpet_1" + ], + [ + 16.6, + "Shop or Service place/Floor and Carpet" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Glass and Window", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 65 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Glass and Window_1" + ], + [ + 16.6, + "Shop or Service place/Glass and Window" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Interior and Exterior Design", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 71 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Interior and Exterior Design_1" + ], + [ + 16.6, + "Shop or Service place/Interior and Exterior Design" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Paint Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 103 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Paint Store_1" + ], + [ + 16.6, + "Shop or Service place/Paint Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Janitorial Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 74 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Janitorial Services_1" + ], + [ + 16.6, + "Shop or Service place/Janitorial Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Maid Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 81 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Maid Services_1" + ], + [ + 16.6, + "Shop or Service place/Maid Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Landscaping Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 76 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Landscaping Services_1" + ], + [ + 16.6, + "Shop or Service place/Landscaping Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Garden Center", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 62 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Garden Center_1" + ], + [ + 16.6, + "Shop or Service place/Garden Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Repair Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 120 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Repair Services_1" + ], + [ + 16.6, + "Shop or Service place/Repair Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Plumbing", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 109 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Plumbing_1" + ], + [ + 16.6, + "Shop or Service place/Plumbing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Electrical", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 44 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Electrical_1" + ], + [ + 16.6, + "Shop or Service place/Electrical" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Locksmiths and Security Systems Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 79 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Locksmiths and Security Systems Services_1" + ], + [ + 16.6, + "Shop or Service place/Locksmiths and Security Systems Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Wholesale Warehouse", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 153 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Wholesale Warehouse_1" + ], + [ + 16.6, + "Shop or Service place/Wholesale Warehouse" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Furniture Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 61 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Furniture Store_1" + ], + [ + 16.6, + "Shop or Service place/Furniture Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Home Improvement Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 70 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Home Improvement Store_1" + ], + [ + 16.6, + "Shop or Service place/Home Improvement Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Couriers", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 38 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Couriers_1" + ], + [ + 16.6, + "Shop or Service place/Couriers" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Big Box Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Big Box Store_1" + ], + [ + 16.6, + "Shop or Service place/Big Box Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Mover", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 93 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Mover_1" + ], + [ + 16.6, + "Shop or Service place/Mover" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Storage", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 131 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Storage_1" + ], + [ + 16.6, + "Shop or Service place/Storage" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Rental and Leasing", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 117 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Rental and Leasing_1" + ], + [ + 16.6, + "Shop or Service place/Rental and Leasing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Tailor", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 132 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Tailor_1" + ], + [ + 16.6, + "Shop or Service place/Tailor" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Sewing, Needlework and Piece Goods", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 123 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Sewing, Needlework and Piece Goods_1" + ], + [ + 16.6, + "Shop or Service place/Sewing, Needlework and Piece Goods" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Body Piercing and Tattoos", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Body Piercing and Tattoos_1" + ], + [ + 16.6, + "Shop or Service place/Body Piercing and Tattoos" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Tanning Salon", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 133 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Tanning Salon_1" + ], + [ + 16.6, + "Shop or Service place/Tanning Salon" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Barber", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Barber_1" + ], + [ + 16.6, + "Shop or Service place/Barber" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Hair and Beauty", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 67 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Hair and Beauty_1" + ], + [ + 16.6, + "Shop or Service place/Hair and Beauty" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Nail Salon", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 95 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Nail Salon_1" + ], + [ + 16.6, + "Shop or Service place/Nail Salon" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Spa", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 125 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Spa_1" + ], + [ + 16.6, + "Shop or Service place/Spa" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Wellness Center and Services", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 152 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Wellness Center and Services_1" + ], + [ + 16.6, + "Shop or Service place/Wellness Center and Services" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Optical", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 100 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Optical_1" + ], + [ + 16.6, + "Shop or Service place/Optical" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Butcher", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Butcher_1" + ], + [ + 16.6, + "Shop or Service place/Butcher" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Specialty Food Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 127 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Specialty Food Store_1" + ], + [ + 16.6, + "Shop or Service place/Specialty Food Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Food and Beverage Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 57 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Food and Beverage Shop_1" + ], + [ + 16.6, + "Shop or Service place/Food and Beverage Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Liquor Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 78 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Liquor Store_1" + ], + [ + 16.6, + "Shop or Service place/Liquor Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Office Supplies Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 99 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Office Supplies Store_1" + ], + [ + 16.6, + "Shop or Service place/Office Supplies Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Sporting Goods Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 130 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Sporting Goods Store_1" + ], + [ + 16.6, + "Shop or Service place/Sporting Goods Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Pet Care", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 105 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Pet Care_1" + ], + [ + 16.6, + "Shop or Service place/Pet Care" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Pet Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 106 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Pet Store_1" + ], + [ + 16.6, + "Shop or Service place/Pet Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Bookstore", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Bookstore_1" + ], + [ + 16.6, + "Shop or Service place/Bookstore" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Tobacco", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 137 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Tobacco_1" + ], + [ + 16.6, + "Shop or Service place/Tobacco" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Specialty Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 128 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Specialty Store_1" + ], + [ + 16.6, + "Shop or Service place/Specialty Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Consumer Electronics Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 34 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Consumer Electronics Store_1" + ], + [ + 16.6, + "Shop or Service place/Consumer Electronics Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Entertainment Electronics", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 47 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Entertainment Electronics_1" + ], + [ + 16.6, + "Shop or Service place/Entertainment Electronics" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Video and Game Rental", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 149 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Video and Game Rental_1" + ], + [ + 16.6, + "Shop or Service place/Video and Game Rental" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Video Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 150 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Video Shop_1" + ], + [ + 16.6, + "Shop or Service place/Video Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Photography", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 108 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Photography_1" + ], + [ + 16.6, + "Shop or Service place/Photography" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Telephone Service", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 135 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Telephone Service_1" + ], + [ + 16.6, + "Shop or Service place/Telephone Service" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Mobile Service Center", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 88 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Mobile Service Center_1" + ], + [ + 16.6, + "Shop or Service place/Mobile Service Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Mobile Phone Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 87 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Mobile Phone Shop_1" + ], + [ + 16.6, + "Shop or Service place/Mobile Phone Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Specialty Clothing Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 126 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Specialty Clothing Store_1" + ], + [ + 16.6, + "Shop or Service place/Specialty Clothing Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Footwear", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 59 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Footwear_1" + ], + [ + 16.6, + "Shop or Service place/Footwear" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Mens Apparel", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 86 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Mens Apparel_1" + ], + [ + 16.6, + "Shop or Service place/Mens Apparel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Womens Apparel", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 154 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Womens Apparel_1" + ], + [ + 16.6, + "Shop or Service place/Womens Apparel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Clothing Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Clothing Store_1" + ], + [ + 16.6, + "Shop or Service place/Clothing Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Internet Cafe", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 72 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Internet Cafe_1" + ], + [ + 16.6, + "Shop or Service place/Internet Cafe" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Fitness Center", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 52 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Fitness Center_1" + ], + [ + 16.6, + "Shop or Service place/Fitness Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Convenience Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 37 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Convenience Store_1" + ], + [ + 16.6, + "Shop or Service place/Convenience Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/ATM", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/ATM_1" + ], + [ + 16.6, + "Shop or Service place/ATM" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Credit Union", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 39 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Credit Union_1" + ], + [ + 16.6, + "Shop or Service place/Credit Union" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Bank", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Bank_1" + ], + [ + 16.6, + "Shop or Service place/Bank" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Food Market", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 58 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Food Market_1" + ], + [ + 16.6, + "Shop or Service place/Food Market" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Business Facility", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Business Facility_1" + ], + [ + 16.6, + "Shop or Service place/Business Facility" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Discount Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 42 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Discount Store_1" + ], + [ + 16.6, + "Shop or Service place/Discount Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Variety Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 148 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Variety Store_1" + ], + [ + 16.6, + "Shop or Service place/Variety Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Road tunnel/label/Local", + "type": "symbol", + "source": "esri", + "source-layer": "Road tunnel/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 5 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 13, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road tunnel/label/Minor", + "type": "symbol", + "source": "esri", + "source-layer": "Road tunnel/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 4 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 12, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road tunnel/label/Major, alt name", + "type": "symbol", + "source": "esri", + "source-layer": "Road tunnel/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 3 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 12, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road tunnel/label/Major", + "type": "symbol", + "source": "esri", + "source-layer": "Road tunnel/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 2 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 12, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road tunnel/label/Freeway Motorway, alt name", + "type": "symbol", + "source": "esri", + "source-layer": "Road tunnel/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 11, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road tunnel/label/Highway", + "type": "symbol", + "source": "esri", + "source-layer": "Road tunnel/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 7 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 11, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road tunnel/label/Freeway Motorway", + "type": "symbol", + "source": "esri", + "source-layer": "Road tunnel/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 11, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road/label/Rectangle hexagon brown white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 72 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon brown white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#81695E" + } + }, + { + "id": "Road/label/Rectangle hexagon green white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 70 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon green white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#718574" + } + }, + { + "id": "Road/label/Rectangle hexagon red white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 68 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon red white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#B66D58" + } + }, + { + "id": "Road/label/Rectangle hexagon blue white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 66 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon blue white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#5B708F" + } + }, + { + "id": "Road/label/Rectangle hexagon brown white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 71 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon brown white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#81695E" + } + }, + { + "id": "Road/label/Rectangle hexagon green white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 69 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon green white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#718574" + } + }, + { + "id": "Road/label/Rectangle hexagon red white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 67 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon red white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#B66D58" + } + }, + { + "id": "Road/label/Rectangle hexagon blue white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 65 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle hexagon blue white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#5B708F" + } + }, + { + "id": "Road/label/Octagon green white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 74 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Octagon green white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Hexagon orange black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 64 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon orange black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Hexagon green white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 62 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon green white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Hexagon red white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 60 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon red white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Hexagon white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 56 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Pentagon green yellow (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 54 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon green yellow (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFF73" + } + }, + { + "id": "Road/label/Pentagon green white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 52 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon green white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Pentagon yellow black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 50 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon yellow black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Pentagon blue white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 48 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon blue white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Pentagon white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 46 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.3 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Pentagon inverse white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 44 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon inverse white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.3 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Rectangle green yellow (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 42 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle green yellow (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFF73" + } + }, + { + "id": "Road/label/Rectangle green white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 40 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle green white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Rectangle yellow black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 38 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle yellow black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Hexagon blue white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 58 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon blue white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Rectangle red white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 36 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle red white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Rectangle blue white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 34 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle blue white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Rectangle white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 32 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/V-shaped white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 30 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/V-shaped white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#343434" + } + }, + { + "id": "Road/label/U-shaped blue white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 28 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped blue white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/U-shaped red white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 26 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped red white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/U-shaped yellow black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 24 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped yellow black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/U-shaped green leaf (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 22 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped green leaf (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#343434" + } + }, + { + "id": "Road/label/U-shaped white green (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 20 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped white green (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/U-shaped white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 18 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Secondary Hwy red white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 16 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Secondary Hwy red white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Secondary Hwy green white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 14 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Secondary Hwy green white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Secondary Hwy white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 12 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Secondary Hwy white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Shield white black (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 10 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Shield white black (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#343434" + } + }, + { + "id": "Road/label/Shield blue white (Alt)", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 8 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Shield blue white (Alt)/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Octagon green white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 73 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Octagon green white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Hexagon orange black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 63 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon orange black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Hexagon green white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 61 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon green white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Hexagon red white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 59 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon red white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Hexagon white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 55 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Pentagon green yellow", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 53 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon green yellow/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFF73" + } + }, + { + "id": "Road/label/Pentagon green white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 51 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon green white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Pentagon yellow black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 49 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon yellow black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Pentagon blue white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 47 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon blue white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Pentagon white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 45 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.3 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Pentagon inverse white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 43 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Pentagon inverse white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.3 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Rectangle green yellow", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 41 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle green yellow/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFF73" + } + }, + { + "id": "Road/label/Rectangle green white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 39 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle green white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Rectangle yellow black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 37 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle yellow black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Hexagon blue white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 57 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.03, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Hexagon blue white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Rectangle red white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 35 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle red white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Rectangle blue white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 33 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle blue white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Rectangle white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 31 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Rectangle white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.2 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/V-shaped white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 29 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/V-shaped white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#343434" + } + }, + { + "id": "Road/label/U-shaped blue white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 27 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped blue white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/U-shaped red white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 25 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped red white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/U-shaped yellow black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 23 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-letter-spacing": 0.02, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped yellow black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/U-shaped green leaf", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 21 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped green leaf/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#343434" + } + }, + { + "id": "Road/label/U-shaped white green", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 19 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped white green/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/U-shaped white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 17 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/U-shaped white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Secondary Hwy red white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 15 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Secondary Hwy red white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Secondary Hwy green white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 13 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Secondary Hwy green white/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#FFFFFF" + } + }, + { + "id": "Road/label/Secondary Hwy white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 11 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Secondary Hwy white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.1 + ], + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#000000" + } + }, + { + "id": "Road/label/Shield white black", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 9 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 9.33, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Shield white black/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport", + "text-padding": 30 + }, + "paint": { + "text-color": "#343434" + } + }, + { + "id": "Road/label/Shield blue white", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 7 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 10, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-size": 8.67, + "text-max-width": 8, + "text-field": "{_name}", + "icon-image": "Road/Shield blue white/{_len}", + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport", + "text-padding": { + "stops": [ + [ + 6, + 30 + ], + [ + 9, + 20 + ], + [ + 12, + 15 + ] + ] + } + }, + "paint": { + "text-color": "#FDFDFD" + } + }, + { + "id": "Road/label/Local", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 5 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 13, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 13, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road/label/Minor", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 4 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": { + "stops": [ + [ + 10, + 20 + ], + [ + 18, + 2 + ] + ] + }, + "text-size": { + "base": 1.2, + "stops": [ + [ + 12, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road/label/Major, alt name", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 3 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 12, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road/label/Major", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 2 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 12, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 12, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road/label/Freeway Motorway, alt name", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 1 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 11, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road/label/Highway", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 75 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 11, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Road/label/Freeway Motorway", + "type": "symbol", + "source": "esri", + "source-layer": "Road/label", + "filter": [ + "all", + [ + "==", + "_label_class", + 0 + ], + [ + "!in", + "Viz", + 3 + ] + ], + "minzoom": 11, + "layout": { + "symbol-placement": "line", + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 11, + 10 + ], + [ + 16, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Cemetery/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Cemetery/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Freight/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Freight/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Water and wastewater/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Water and wastewater/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Port/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Port/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Industry/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Industry/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Government/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Government/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Finance/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Finance/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Emergency/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Emergency/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Indigenous/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Indigenous/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Military/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Military/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 25, + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Transportation/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Beach/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Beach/label", + "minzoom": 13, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.08, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Golf course/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Golf course/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Zoo/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Zoo/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Retail/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Retail/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Landmark/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Openspace or forest/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Openspace or forest/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Park or farming/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Park or farming/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 25, + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Point of interest/Park", + "type": "symbol", + "source": "esri", + "source-layer": "Point of interest", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 9, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-anchor": "center", + "text-letter-spacing": 0.08, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Education/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Education/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#e8cc99", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Medical/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Medical/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#f2c2c2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Admin1 forest or park/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Admin1 forest or park/label", + "minzoom": 6, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 25, + "text-size": { + "base": 1.2, + "stops": [ + [ + 6, + 10 + ], + [ + 7, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Admin0 forest or park/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Admin0 forest or park/label", + "minzoom": 6, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 25, + "text-size": { + "base": 1.2, + "stops": [ + [ + 6, + 10 + ], + [ + 7, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#def2b6", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Airport/label/Airport property", + "type": "symbol", + "source": "esri", + "source-layer": "Airport/label", + "minzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 15, + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11 + ], + [ + 14, + 11.3 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Exit/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Exit", + "minzoom": 15, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-anchor": "center", + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "icon-image": "Exit/Default/{_len}", + "icon-rotation-alignment": "viewport", + "text-offset": [ + 0, + 0.3 + ], + "text-rotation-alignment": "viewport", + "text-size": { + "base": 1.2, + "stops": [ + [ + 15, + 9 + ], + [ + 17, + 10 + ] + ] + } + }, + "paint": { + "text-color": "#343434" + } + }, + { + "id": "Point of interest/General", + "type": "symbol", + "source": "esri", + "source-layer": "Point of interest", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 9, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-anchor": "center", + "text-letter-spacing": 0.08, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 20, + "text-size": { + "base": 1.2, + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-width": 1.33, + "text-halo-color": "#000000" + } + }, + { + "id": "Building/label/Default", + "type": "symbol", + "source": "esri", + "source-layer": "Building/label", + "minzoom": 15, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.08, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": 20, + "text-size": { + "base": 1.2, + "stops": [ + [ + 15, + 11 + ], + [ + 17, + 11.3 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-color": "#000000", + "text-halo-width": 1.33 + } + }, + { + "id": "Point of interest/Bus station", + "type": "symbol", + "source": "esri", + "source-layer": "Point of interest", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 9, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "Point of interest/Bus station", + "icon-padding": 1, + "icon-size": { + "stops": [ + [ + 11, + 0.6 + ], + [ + 18, + 1.3 + ] + ] + }, + "text-font": [ + "Arial Regular" + ], + "text-anchor": "bottom", + "text-letter-spacing": 0.04, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-offset": [ + 0, + -0.5 + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-width": 1.33, + "text-halo-color": "#000000" + } + }, + { + "id": "Point of interest/Rail station", + "type": "symbol", + "source": "esri", + "source-layer": "Point of interest", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 9, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "Point of interest/Rail station", + "icon-padding": 1, + "icon-size": { + "stops": [ + [ + 11, + 0.6 + ], + [ + 18, + 1.3 + ] + ] + }, + "text-font": [ + "Arial Regular" + ], + "text-anchor": "bottom", + "text-letter-spacing": 0.04, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-offset": [ + 0, + -0.5 + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + } + }, + "paint": { + "text-color": "#b2b2b2", + "text-halo-width": 1.33, + "text-halo-color": "#000000" + } + }, + { + "id": "Place/Shop or Service/Gas Station", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 63 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Gas Station_1" + ], + [ + 16.6, + "Shop or Service place/Gas Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Pharmacy and Retail", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 107 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Pharmacy and Retail_1" + ], + [ + 16.6, + "Shop or Service place/Pharmacy and Retail" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Market", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 83 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Market_1" + ], + [ + 16.6, + "Shop or Service place/Market" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Grocery", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 66 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Grocery_1" + ], + [ + 16.6, + "Shop or Service place/Grocery" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/General Merchandise", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 64 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/General Merchandise_1" + ], + [ + 16.6, + "Shop or Service place/General Merchandise" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Department Store", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 41 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Department Store_1" + ], + [ + 16.6, + "Shop or Service place/Department Store" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Shop or Service/Shopping Center", + "type": "symbol", + "source": "esri", + "source-layer": "Shop or Service place", + "filter": [ + "==", + "_symbol", + 124 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Shop or Service place/Shopping Center_1" + ], + [ + 16.6, + "Shop or Service place/Shopping Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "top", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + 0.9 + ] + ], + [ + 16.6, + [ + 0, + 1 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Restaurant", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 78 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Restaurant_1" + ], + [ + 16.6, + "Food and Drink place/Restaurant" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Fast Food", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 41 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Fast Food_1" + ], + [ + 16.6, + "Food and Drink place/Fast Food" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Pizza", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 75 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Pizza_1" + ], + [ + 16.6, + "Food and Drink place/Pizza" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Brewery", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Brewery_1" + ], + [ + 16.6, + "Food and Drink place/Brewery" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Bar or Pub", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Bar or Pub_1" + ], + [ + 16.6, + "Food and Drink place/Bar or Pub" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Food and Drink/Coffee Shop", + "type": "symbol", + "source": "esri", + "source-layer": "Food and Drink place", + "filter": [ + "==", + "_symbol", + 32 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Food and Drink place/Coffee Shop_1" + ], + [ + 16.6, + "Food and Drink place/Coffee Shop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Golf Course", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Golf Course_1" + ], + [ + 16.6, + "Sport place/Golf Course" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Sports Center", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Sports Center_1" + ], + [ + 16.6, + "Sport place/Sports Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Sport/Stadium", + "type": "symbol", + "source": "esri", + "source-layer": "Sport place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Sport place/Stadium_1" + ], + [ + 16.6, + "Sport place/Stadium" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Church", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Church_1" + ], + [ + 16.6, + "Religion place/Church" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Religion/Cemetery", + "type": "symbol", + "source": "esri", + "source-layer": "Religion place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Religion place/Cemetery_1" + ], + [ + 16.6, + "Religion place/Cemetery" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Lodging/Resort", + "type": "symbol", + "source": "esri", + "source-layer": "Lodging place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Lodging place/Resort_1" + ], + [ + 16.6, + "Lodging place/Resort" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Lodging/Hotel", + "type": "symbol", + "source": "esri", + "source-layer": "Lodging place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Lodging place/Hotel_1" + ], + [ + 16.6, + "Lodging place/Hotel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.9 + ] + ], + [ + 16.6, + [ + 0, + -0.8 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Water Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Water Park_1" + ], + [ + 16.6, + "Outdoors place/Water Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Wild Animal Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Wild Animal Park_1" + ], + [ + 16.6, + "Outdoors place/Wild Animal Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Dog Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Dog Park_1" + ], + [ + 16.6, + "Outdoors place/Dog Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Park_1" + ], + [ + 16.6, + "Outdoors place/Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Garden", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Garden_1" + ], + [ + 16.6, + "Outdoors place/Garden" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Beach", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Beach_1" + ], + [ + 16.6, + "Outdoors place/Beach" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Wildlife Reserve", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Wildlife Reserve_1" + ], + [ + 16.6, + "Outdoors place/Wildlife Reserve" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/Nature Reserve", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/Nature Reserve_1" + ], + [ + 16.6, + "Outdoors place/Nature Reserve" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/State Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/State Park_1" + ], + [ + 16.6, + "Outdoors place/State Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Outdoors/National Park", + "type": "symbol", + "source": "esri", + "source-layer": "Outdoors place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Outdoors place/National Park_1" + ], + [ + 16.6, + "Outdoors place/National Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/School", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/School_1" + ], + [ + 16.6, + "Education place/School" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Education/College or University", + "type": "symbol", + "source": "esri", + "source-layer": "Education place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Education place/College or University_1" + ], + [ + 16.6, + "Education place/College or University" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Laboratory", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Laboratory_1" + ], + [ + 16.6, + "Medical place/Laboratory" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Medical Clinic", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Medical Clinic_1" + ], + [ + 16.6, + "Medical place/Medical Clinic" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#ff8c90", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Pharmacy", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Pharmacy_1" + ], + [ + 16.6, + "Medical place/Pharmacy" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Medical/Hospital", + "type": "symbol", + "source": "esri", + "source-layer": "Medical place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Medical place/Hospital_1" + ], + [ + 16.6, + "Medical place/Hospital" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#ff8c90", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Night Club", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Night Club_1" + ], + [ + 16.6, + "Arts and Entertainment place/Night Club" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Casino", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Casino_1" + ], + [ + 16.6, + "Arts and Entertainment place/Casino" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Cinema", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Cinema_1" + ], + [ + 16.6, + "Arts and Entertainment place/Cinema" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Amphitheater", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Amphitheater_1" + ], + [ + 16.6, + "Arts and Entertainment place/Amphitheater" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Observatory", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 32 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Observatory_1" + ], + [ + 16.6, + "Arts and Entertainment place/Observatory" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Performing Arts", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 34 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Performing Arts_1" + ], + [ + 16.6, + "Arts and Entertainment place/Performing Arts" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Art Museum", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Art Museum_1" + ], + [ + 16.6, + "Arts and Entertainment place/Art Museum" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/History Museum", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/History Museum_1" + ], + [ + 16.6, + "Arts and Entertainment place/History Museum" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Science Museum", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 39 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Science Museum_1" + ], + [ + 16.6, + "Arts and Entertainment place/Science Museum" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Museum", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 28 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Museum_1" + ], + [ + 16.6, + "Arts and Entertainment place/Museum" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Aquarium", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Aquarium_1" + ], + [ + 16.6, + "Arts and Entertainment place/Aquarium" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Zoo", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 45 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Zoo_1" + ], + [ + 16.6, + "Arts and Entertainment place/Zoo" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#9cbe7c", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Amusement Park", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Amusement Park_1" + ], + [ + 16.6, + "Arts and Entertainment place/Amusement Park" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.9 + ] + ], + [ + 16.6, + [ + 0, + -0.8 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Arts and Entertainment/Tourist Attraction", + "type": "symbol", + "source": "esri", + "source-layer": "Arts and Entertainment place", + "filter": [ + "==", + "_symbol", + 42 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Arts and Entertainment place/Tourist Attraction_1" + ], + [ + 16.6, + "Arts and Entertainment place/Tourist Attraction" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Community Center", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Community Center_1" + ], + [ + 16.6, + "Government or Public Facility place/Community Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Post Office", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Post Office_1" + ], + [ + 16.6, + "Government or Public Facility place/Post Office" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Military Base", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Military Base_1" + ], + [ + 16.6, + "Government or Public Facility place/Military Base" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Court House", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Court House_1" + ], + [ + 16.6, + "Government or Public Facility place/Court House" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Fire Station", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Fire Station_1" + ], + [ + 16.6, + "Government or Public Facility place/Fire Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Police Station", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Police Station_1" + ], + [ + 16.6, + "Government or Public Facility place/Police Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Convention Center", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Convention Center_1" + ], + [ + 16.6, + "Government or Public Facility place/Convention Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/Government Office", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/Government Office_1" + ], + [ + 16.6, + "Government or Public Facility place/Government Office" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Government or Public Facility/City Hall", + "type": "symbol", + "source": "esri", + "source-layer": "Government or Public Facility place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Government or Public Facility place/City Hall_1" + ], + [ + 16.6, + "Government or Public Facility place/City Hall" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Windmill", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Windmill_1" + ], + [ + 16.6, + "Landmark place/Windmill" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Shrine", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Shrine_1" + ], + [ + 16.6, + "Landmark place/Shrine" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Scenic Overlook", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Scenic Overlook_1" + ], + [ + 16.6, + "Landmark place/Scenic Overlook" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Ruin", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Ruin_1" + ], + [ + 16.6, + "Landmark place/Ruin" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Other Landmark", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Other Landmark_1" + ], + [ + 16.6, + "Landmark place/Other Landmark" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Memorial Site", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Memorial Site_1" + ], + [ + 16.6, + "Landmark place/Memorial Site" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Lighthouse", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Lighthouse_1" + ], + [ + 16.6, + "Landmark place/Lighthouse" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Landmark/Historical Monument", + "type": "symbol", + "source": "esri", + "source-layer": "Landmark place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 11, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Landmark place/Historical Monument_1" + ], + [ + 16.6, + "Landmark place/Historical Monument" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Other Transportation", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 19 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Other Transportation_1" + ], + [ + 16.6, + "Transportation place/Other Transportation" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Cargo Center", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Cargo Center_1" + ], + [ + 16.6, + "Transportation place/Cargo Center" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Weigh Station", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 34 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Weigh Station_1" + ], + [ + 16.6, + "Transportation place/Weigh Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Truck Parking", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 30 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Truck Parking_1" + ], + [ + 16.6, + "Transportation place/Truck Parking" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Truck Stop", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 31 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Truck Stop_1" + ], + [ + 16.6, + "Transportation place/Truck Stop" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Off-Road Vehicle Area", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Off-Road Vehicle Area_1" + ], + [ + 16.6, + "Transportation place/Off-Road Vehicle Area" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Off Road Trailhead", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Off Road Trailhead_1" + ], + [ + 16.6, + "Transportation place/Off Road Trailhead" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Rail Ferry", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 23 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Rail Ferry_1" + ], + [ + 16.6, + "Transportation place/Rail Ferry" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Taxi", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 27 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Taxi_1" + ], + [ + 16.6, + "Transportation place/Taxi" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Railyard", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 25 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Railyard_1" + ], + [ + 16.6, + "Transportation place/Railyard" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Boating", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Boating_1" + ], + [ + 16.6, + "Transportation place/Boating" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Gondola", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Gondola_1" + ], + [ + 16.6, + "Transportation place/Gondola" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Dock", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Dock_1" + ], + [ + 16.6, + "Transportation place/Dock" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Pier", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 21 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Pier_1" + ], + [ + 16.6, + "Transportation place/Pier" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Port", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 22 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Port_1" + ], + [ + 16.6, + "Transportation place/Port" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Marina", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Marina_1" + ], + [ + 16.6, + "Transportation place/Marina" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Water Transit", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 33 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Water Transit_1" + ], + [ + 16.6, + "Transportation place/Water Transit" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Ferry Terminal", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Ferry Terminal_1" + ], + [ + 16.6, + "Transportation place/Ferry Terminal" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Border Crossing", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Border Crossing_1" + ], + [ + 16.6, + "Transportation place/Border Crossing" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Rest Area", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 26 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Rest Area_1" + ], + [ + 16.6, + "Transportation place/Rest Area" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Tollbooth", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 28 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Tollbooth_1" + ], + [ + 16.6, + "Transportation place/Tollbooth" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Highway Exit", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Highway Exit_1" + ], + [ + 16.6, + "Transportation place/Highway Exit" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Tunnel", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 32 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Tunnel_1" + ], + [ + 16.6, + "Transportation place/Tunnel" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Bridge", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Bridge_1" + ], + [ + 16.6, + "Transportation place/Bridge" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Bicycle Sharing Location", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Bicycle Sharing Location_1" + ], + [ + 16.6, + "Transportation place/Bicycle Sharing Location" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Parking", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 20 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Parking_1" + ], + [ + 16.6, + "Transportation place/Parking" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Heliport", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Heliport_1" + ], + [ + 16.6, + "Transportation place/Heliport" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Airport Terminal", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Airport Terminal_1" + ], + [ + 16.6, + "Transportation place/Airport Terminal" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Airport", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Airport_1" + ], + [ + 16.6, + "Transportation place/Airport" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#69beb9", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Local Transit", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Local Transit_1" + ], + [ + 16.6, + "Transportation place/Local Transit" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#69beb9", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Bus Station", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Bus Station_1" + ], + [ + 16.6, + "Transportation place/Bus Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#69beb9", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Metro Station", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 14, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Metro Station_1" + ], + [ + 16.6, + "Transportation place/Metro Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#69beb9", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Lightrail", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Lightrail_1" + ], + [ + 16.6, + "Transportation place/Lightrail" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#69beb9", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Rail Station", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 24 + ], + "minzoom": 1, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Rail Station_1" + ], + [ + 16.6, + "Transportation place/Rail Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#69beb9", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Transportation/Train Station", + "type": "symbol", + "source": "esri", + "source-layer": "Transportation place", + "filter": [ + "==", + "_symbol", + 29 + ], + "minzoom": 14, + "layout": { + "icon-image": { + "stops": [ + [ + 16.5, + "Transportation place/Train Station_1" + ], + [ + 16.6, + "Transportation place/Train Station" + ] + ] + }, + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#69beb9", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Indoors/Stairs", + "type": "symbol", + "source": "esri", + "source-layer": "Indoors place", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 17, + "layout": { + "icon-image": "Indoors place/Stairs", + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Indoors/Other Indoors", + "type": "symbol", + "source": "esri", + "source-layer": "Indoors place", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 17, + "layout": { + "icon-image": "Indoors place/Other Indoors", + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Indoors/Fire Extinguisher", + "type": "symbol", + "source": "esri", + "source-layer": "Indoors place", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 17, + "layout": { + "icon-image": "Indoors place/Fire Extinguisher", + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Indoors/Elevator", + "type": "symbol", + "source": "esri", + "source-layer": "Indoors place", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 17, + "layout": { + "icon-image": "Indoors place/Elevator", + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Place/Indoors/Delivery Entrance", + "type": "symbol", + "source": "esri", + "source-layer": "Indoors place", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 17, + "layout": { + "icon-image": "Indoors place/Delivery Entrance", + "text-font": [ + "Arial Bold", + "Arial Unicode MS Bold" + ], + "text-size": { + "stops": [ + [ + 16.5, + 10 + ], + [ + 16.6, + 12 + ] + ] + }, + "text-anchor": "bottom", + "text-offset": { + "stops": [ + [ + 16.5, + [ + 0, + -0.8 + ] + ], + [ + 16.6, + [ + 0, + -0.9 + ] + ] + ] + }, + "text-field": "{_name}", + "text-max-width": { + "stops": [ + [ + 16.5, + 7 + ], + [ + 16.6, + 9 + ] + ] + }, + "text-line-height": 1.1, + "visibility": "none" + }, + "paint": { + "text-color": "#CCCCCC", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Admin2 area/label/small", + "type": "symbol", + "source": "esri", + "source-layer": "Admin2 area/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 10, + "maxzoom": 12, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-size": 11, + "text-letter-spacing": 0.2, + "text-max-width": 8, + "text-field": "{_name}" + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin2 area/label/large", + "type": "symbol", + "source": "esri", + "source-layer": "Admin2 area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 10, + "maxzoom": 12, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-size": 12.5, + "text-letter-spacing": 0.2, + "text-max-width": 8, + "text-field": "{_name}" + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin1 area/label/x small", + "type": "symbol", + "source": "esri", + "source-layer": "Admin1 area/label", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 3, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 3, + 9 + ], + [ + 10, + 11 + ] + ] + } + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin1 area/label/small", + "type": "symbol", + "source": "esri", + "source-layer": "Admin1 area/label", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 3, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 3, + 9 + ], + [ + 10, + 11 + ] + ] + } + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin1 area/label/medium", + "type": "symbol", + "source": "esri", + "source-layer": "Admin1 area/label", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 3, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 3, + 9 + ], + [ + 10, + 13 + ] + ] + } + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin1 area/label/large", + "type": "symbol", + "source": "esri", + "source-layer": "Admin1 area/label", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 3, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 3, + 10 + ], + [ + 10, + 13 + ] + ] + }, + "text-letter-spacing": 0.1 + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin1 area/label/x large", + "type": "symbol", + "source": "esri", + "source-layer": "Admin1 area/label", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 3, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 3, + 10 + ], + [ + 10, + 16 + ] + ] + }, + "text-letter-spacing": 0.1 + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin1 area/label/2x large", + "type": "symbol", + "source": "esri", + "source-layer": "Admin1 area/label", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 3, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 3, + 11 + ], + [ + 10, + 16 + ] + ] + }, + "text-letter-spacing": 0.1 + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin0 point/x small", + "type": "symbol", + "source": "esri", + "source-layer": "Admin0 point", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 5, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-anchor": "center", + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 5, + 11 + ], + [ + 10, + 12.5 + ] + ] + }, + "text-transform": "uppercase" + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin0 point/small", + "type": "symbol", + "source": "esri", + "source-layer": "Admin0 point", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 4, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-anchor": "center", + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 4, + 10 + ], + [ + 10, + 12.5 + ] + ] + }, + "text-transform": "uppercase" + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin0 point/medium", + "type": "symbol", + "source": "esri", + "source-layer": "Admin0 point", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-anchor": "center", + "text-letter-spacing": 0.05, + "text-max-width": 6, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 2, + 9.5 + ], + [ + 10, + 15.5 + ] + ] + }, + "text-transform": "uppercase" + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin0 point/large", + "type": "symbol", + "source": "esri", + "source-layer": "Admin0 point", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-anchor": "center", + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 2, + 9.5 + ], + [ + 10, + 15.5 + ] + ] + }, + "text-transform": "uppercase" + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin0 point/x large", + "type": "symbol", + "source": "esri", + "source-layer": "Admin0 point", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-anchor": "center", + "text-letter-spacing": 0.12, + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 2, + 9.5 + ], + [ + 10, + 18 + ] + ] + }, + "text-transform": "uppercase" + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Admin0 point/2x large", + "type": "symbol", + "source": "esri", + "source-layer": "Admin0 point", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "text-font": [ + "Arial Bold" + ], + "text-anchor": "center", + "text-letter-spacing": 0.15, + "text-max-width": 8, + "text-field": "{_name}", + "text-size": { + "stops": [ + [ + 2, + 12 + ], + [ + 10, + 18 + ] + ] + }, + "text-transform": "uppercase" + }, + "paint": { + "text-color": { + "stops": [ + [ + 5, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Neighborhood", + "type": "symbol", + "source": "esri", + "source-layer": "Neighborhood", + "minzoom": 11, + "maxzoom": 18, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "Neighborhood", + "text-font": [ + "Arial Regular" + ], + "text-size": { + "stops": [ + [ + 11, + 8 + ], + [ + 18, + 18 + ] + ] + }, + "text-letter-spacing": 0.08, + "text-max-width": 8, + "text-padding": { + "stops": [ + [ + 11, + 20 + ], + [ + 18, + 2 + ] + ] + }, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "City large scale/town small", + "type": "symbol", + "source": "esri", + "source-layer": "City large scale", + "filter": [ + "==", + "_label_class", + 5 + ], + "minzoom": 10, + "maxzoom": 18, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City large scale", + "text-font": [ + "Arial Regular" + ], + "text-size": { + "stops": [ + [ + 10, + 11.3 + ], + [ + 15, + 16 + ], + [ + 17, + 20 + ] + ] + }, + "text-letter-spacing": 0.08, + "text-max-width": 8, + "text-padding": { + "stops": [ + [ + 10, + 20 + ], + [ + 18, + 2 + ] + ] + }, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "City large scale/town large", + "type": "symbol", + "source": "esri", + "source-layer": "City large scale", + "filter": [ + "==", + "_label_class", + 4 + ], + "minzoom": 10, + "maxzoom": 17, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City large scale", + "text-font": [ + "Arial Regular" + ], + "text-letter-spacing": 0.09, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-padding": { + "stops": [ + [ + 10, + 20 + ], + [ + 17, + 2 + ] + ] + }, + "text-size": { + "stops": [ + [ + 10, + 11.3 + ], + [ + 12, + 12 + ], + [ + 15, + 18 + ], + [ + 16, + 20 + ] + ] + } + }, + "paint": { + "text-halo-color": "#000000", + "text-halo-width": 2, + "text-color": { + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 13, + "#ffffd9" + ] + ] + } + } + }, + { + "id": "City large scale/small", + "type": "symbol", + "source": "esri", + "source-layer": "City large scale", + "filter": [ + "==", + "_label_class", + 3 + ], + "minzoom": 10, + "maxzoom": 17, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City large scale", + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 11.3 + ], + [ + 12, + 12 + ], + [ + 15, + 18 + ] + ] + } + }, + "paint": { + "text-halo-color": "#000000", + "text-halo-width": 2, + "text-color": { + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 13, + "#ffffd9" + ] + ] + } + } + }, + { + "id": "City large scale/medium", + "type": "symbol", + "source": "esri", + "source-layer": "City large scale", + "filter": [ + "==", + "_label_class", + 2 + ], + "minzoom": 10, + "maxzoom": 17, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City large scale", + "text-font": [ + "Arial Bold" + ], + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 12 + ], + [ + 12, + 12.67 + ], + [ + 14, + 20 + ] + ] + } + }, + "paint": { + "text-halo-color": "#000000", + "text-halo-width": 2, + "text-color": { + "stops": [ + [ + 10, + "#ffffff" + ], + [ + 11, + "#ffffd9" + ] + ] + } + } + }, + { + "id": "City large scale/large", + "type": "symbol", + "source": "esri", + "source-layer": "City large scale", + "filter": [ + "==", + "_label_class", + 1 + ], + "minzoom": 10, + "maxzoom": 17, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City large scale", + "text-font": [ + "Arial Bold" + ], + "text-size": { + "stops": [ + [ + 10, + 15 + ], + [ + 14, + 22 + ] + ] + }, + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#ffffcc", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "City large scale/x large", + "type": "symbol", + "source": "esri", + "source-layer": "City large scale", + "filter": [ + "==", + "_label_class", + 0 + ], + "minzoom": 10, + "maxzoom": 17, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City large scale", + "text-font": [ + "Arial Bold" + ], + "text-size": { + "stops": [ + [ + 10, + 15 + ], + [ + 14, + 25 + ] + ] + }, + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name_global}" + }, + "paint": { + "text-color": "#ffffcc", + "text-halo-color": "#000000", + "text-halo-width": 2 + } + }, + { + "id": "City small scale/town small non capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 17 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/town small non capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/town large non capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 15 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/town large non capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/small non capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 12 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/small non capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/medium non capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 9 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/medium non capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/other capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 18 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/other capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/town large other capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 14 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/town large other capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/small other capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 11 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/small other capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/medium other capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 8 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/medium other capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/town small admin0 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 16 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/town small admin0 capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/town large admin0 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 13 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/town large admin0 capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/small admin0 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 10 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/small admin0 capital", + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/medium admin0 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 7 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/medium admin0 capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/large other capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 5 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/large other capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/x large admin2 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 2 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/x large admin2 capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 12, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": { + "stops": [ + [ + 2, + "#ffffff" + ], + [ + 6, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/large non capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 6 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/large non capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffff", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/large admin0 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 4 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/large admin0 capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 10.67, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/x large non capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 3 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/x large non capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 12, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": { + "stops": [ + [ + 2, + "#ffffff" + ], + [ + 6, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/x large admin1 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 1 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/x large admin1 capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 12, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": { + "stops": [ + [ + 2, + "#ffffff" + ], + [ + 6, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "City small scale/x large admin0 capital", + "type": "symbol", + "source": "esri", + "source-layer": "City small scale", + "filter": [ + "==", + "_symbol", + 0 + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "City small scale/x large admin0 capital", + "icon-padding": 1, + "text-font": [ + "Arial Bold" + ], + "text-size": 12, + "text-anchor": "bottom-left", + "text-justify": "left", + "text-max-width": 8, + "text-field": "{_name}", + "text-offset": [ + 0.13, + -0.13 + ] + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Continent", + "type": "symbol", + "source": "esri", + "source-layer": "Continent", + "minzoom": 0, + "maxzoom": 2, + "layout": { + "symbol-avoid-edges": true, + "icon-image": "Continent", + "icon-allow-overlap": true, + "icon-padding": 1, + "text-font": [ + "Arial Regular" + ], + "text-size": { + "stops": [ + [ + 0, + 9 + ], + [ + 1, + 12 + ] + ] + }, + "text-anchor": "center", + "text-letter-spacing": 0.05, + "text-max-width": 8, + "text-field": "{_name_global}", + "text-transform": "uppercase" + }, + "paint": { + "text-color": "#ffffd9", + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + }, + { + "id": "Disputed label point/Island", + "type": "symbol", + "source": "esri", + "source-layer": "Disputed label point", + "filter": [ + "all", + [ + "==", + "_label_class", + 1 + ], + [ + "in", + "DisputeID", + 0 + ] + ], + "minzoom": 6, + "layout": { + "icon-image": "Disputed label point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11.5, + "text-anchor": "center", + "text-letter-spacing": 0.08, + "text-max-width": 7, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#d9d1ba", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Disputed label point/Waterbody", + "type": "symbol", + "source": "esri", + "source-layer": "Disputed label point", + "filter": [ + "all", + [ + "==", + "_label_class", + 0 + ], + [ + "in", + "DisputeID", + 1006 + ] + ], + "minzoom": 2, + "maxzoom": 10, + "layout": { + "icon-image": "Disputed label point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Italic" + ], + "text-size": 11, + "text-anchor": "center", + "text-letter-spacing": 0.1, + "text-max-width": 7, + "text-field": "{_name}", + "text-optional": true + }, + "paint": { + "text-color": "#9ecccc", + "text-halo-color": "#000000", + "text-halo-width": 1 + } + }, + { + "id": "Disputed label point/Admin0", + "type": "symbol", + "source": "esri", + "source-layer": "Disputed label point", + "filter": [ + "all", + [ + "==", + "_label_class", + 2 + ], + [ + "in", + "DisputeID", + 1021 + ] + ], + "minzoom": 2, + "layout": { + "icon-image": "Disputed label point", + "icon-allow-overlap": true, + "text-font": [ + "Arial Bold" + ], + "text-size": { + "stops": [ + [ + 2, + 9.5 + ], + [ + 10, + 15.5 + ] + ] + }, + "text-anchor": "center", + "text-letter-spacing": 0.1, + "text-max-width": 8, + "text-field": "{_name}", + "text-transform": "uppercase", + "text-optional": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 2, + "#cecdcd" + ], + [ + 7, + "#ffffd9" + ] + ] + }, + "text-halo-color": "#000000", + "text-halo-width": 1.2 + } + } + ] +} diff --git a/ui-ngx/src/assets/markers/iconContainer1.svg b/ui-ngx/src/assets/markers/iconContainer1.svg new file mode 100644 index 0000000000..d9e00d3934 --- /dev/null +++ b/ui-ngx/src/assets/markers/iconContainer1.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/iconContainer2.svg b/ui-ngx/src/assets/markers/iconContainer2.svg new file mode 100644 index 0000000000..fa716aa30e --- /dev/null +++ b/ui-ngx/src/assets/markers/iconContainer2.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/iconContainer3.svg b/ui-ngx/src/assets/markers/iconContainer3.svg new file mode 100644 index 0000000000..a64995d1ee --- /dev/null +++ b/ui-ngx/src/assets/markers/iconContainer3.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape1.svg b/ui-ngx/src/assets/markers/shape1.svg new file mode 100644 index 0000000000..7399bab288 --- /dev/null +++ b/ui-ngx/src/assets/markers/shape1.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape10.svg b/ui-ngx/src/assets/markers/shape10.svg new file mode 100644 index 0000000000..d13fca2d2b --- /dev/null +++ b/ui-ngx/src/assets/markers/shape10.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape2.svg b/ui-ngx/src/assets/markers/shape2.svg new file mode 100644 index 0000000000..c60d909881 --- /dev/null +++ b/ui-ngx/src/assets/markers/shape2.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape3.svg b/ui-ngx/src/assets/markers/shape3.svg new file mode 100644 index 0000000000..e1f0a8ac1b --- /dev/null +++ b/ui-ngx/src/assets/markers/shape3.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape4.svg b/ui-ngx/src/assets/markers/shape4.svg new file mode 100644 index 0000000000..4190ce6d06 --- /dev/null +++ b/ui-ngx/src/assets/markers/shape4.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape5.svg b/ui-ngx/src/assets/markers/shape5.svg new file mode 100644 index 0000000000..e1080d7e19 --- /dev/null +++ b/ui-ngx/src/assets/markers/shape5.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape6.svg b/ui-ngx/src/assets/markers/shape6.svg new file mode 100644 index 0000000000..b388f13bdf --- /dev/null +++ b/ui-ngx/src/assets/markers/shape6.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape7.svg b/ui-ngx/src/assets/markers/shape7.svg new file mode 100644 index 0000000000..99e2a5a623 --- /dev/null +++ b/ui-ngx/src/assets/markers/shape7.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape8.svg b/ui-ngx/src/assets/markers/shape8.svg new file mode 100644 index 0000000000..a303a36ba3 --- /dev/null +++ b/ui-ngx/src/assets/markers/shape8.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/shape9.svg b/ui-ngx/src/assets/markers/shape9.svg new file mode 100644 index 0000000000..52d1a03fa0 --- /dev/null +++ b/ui-ngx/src/assets/markers/shape9.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripIconContainer1.svg b/ui-ngx/src/assets/markers/tripIconContainer1.svg new file mode 100644 index 0000000000..c1186b2aa9 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripIconContainer1.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripIconContainer2.svg b/ui-ngx/src/assets/markers/tripIconContainer2.svg new file mode 100644 index 0000000000..229c98b31e --- /dev/null +++ b/ui-ngx/src/assets/markers/tripIconContainer2.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripIconContainer3.svg b/ui-ngx/src/assets/markers/tripIconContainer3.svg new file mode 100644 index 0000000000..8a8baf2b1b --- /dev/null +++ b/ui-ngx/src/assets/markers/tripIconContainer3.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape1.svg b/ui-ngx/src/assets/markers/tripShape1.svg new file mode 100644 index 0000000000..da98975b52 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui-ngx/src/assets/markers/tripShape10.svg b/ui-ngx/src/assets/markers/tripShape10.svg new file mode 100644 index 0000000000..5fb5d81af1 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape10.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape2.svg b/ui-ngx/src/assets/markers/tripShape2.svg new file mode 100644 index 0000000000..e53beaf187 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape2.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape3.svg b/ui-ngx/src/assets/markers/tripShape3.svg new file mode 100644 index 0000000000..ca81dc38e6 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape3.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape4.svg b/ui-ngx/src/assets/markers/tripShape4.svg new file mode 100644 index 0000000000..ff06ce5075 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape4.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape5.svg b/ui-ngx/src/assets/markers/tripShape5.svg new file mode 100644 index 0000000000..e774b4f92c --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape5.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape6.svg b/ui-ngx/src/assets/markers/tripShape6.svg new file mode 100644 index 0000000000..aeb7d7f3f5 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape6.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape7.svg b/ui-ngx/src/assets/markers/tripShape7.svg new file mode 100644 index 0000000000..38d35f6932 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape7.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape8.svg b/ui-ngx/src/assets/markers/tripShape8.svg new file mode 100644 index 0000000000..60fdc493c5 --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape8.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/ui-ngx/src/assets/markers/tripShape9.svg b/ui-ngx/src/assets/markers/tripShape9.svg new file mode 100644 index 0000000000..02521777af --- /dev/null +++ b/ui-ngx/src/assets/markers/tripShape9.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/ui-ngx/src/typings/leaflet-extend-tb.d.ts b/ui-ngx/src/typings/leaflet-extend-tb.d.ts index af03caaa04..c62d008878 100644 --- a/ui-ngx/src/typings/leaflet-extend-tb.d.ts +++ b/ui-ngx/src/typings/leaflet-extend-tb.d.ts @@ -15,10 +15,173 @@ /// import { FormattedData } from '@shared/models/widget.models'; +import L from 'leaflet'; +import { TbMapDatasource } from '@shared/models/widget/maps/map.models'; +import { MatIconRegistry } from '@angular/material/icon'; // redeclare module, maintains compatibility with @types/leaflet declare module 'leaflet' { interface MarkerOptions { - tbMarkerData?: FormattedData; + tbMarkerData?: FormattedData; + } + + interface TileLayer { + _url: string; + _getSubdomain(tilePoint: L.Coords): string; + _globalTileRange: L.Bounds; + } + + namespace TB { + + interface SidebarControlOptions extends ControlOptions { + container: JQuery; + paneWidth?: number; + } + + class SidebarControl extends Control { + constructor(options: SidebarControlOptions); + addPane(pane: JQuery, button: JQuery): this; + togglePane(pane: JQuery, button: JQuery): void; + } + + interface SidebarPaneControlOptions extends ControlOptions { + sidebar: SidebarControl; + uiClass: string; + buttonTitle?: string; + paneTitle: string; + } + + class SidebarPaneControl extends Control { + constructor(options: O); + onAddPane(map: Map, button: JQuery, $ui: JQuery, toggle: (e: JQuery.MouseEventBase) => void); + } + + interface LayerData { + title: string; + attributionPrefix?: string; + layer: Layer; + mini: Layer; + onAdd?: () => void; + } + + interface LayersControlOptions extends SidebarPaneControlOptions { + layers: LayerData[]; + } + + class LayersControl extends SidebarPaneControl { + constructor(options: LayersControlOptions); + } + + interface DataLayer { + toggleGroup(group: string): boolean; + } + + interface GroupData { + title: string; + group: string; + enabled: boolean; + dataLayers: DataLayer[]; + } + + interface GroupsControlOptions extends SidebarPaneControlOptions { + groups: GroupData[]; + } + + class GroupsControl extends SidebarPaneControl { + constructor(options: GroupsControlOptions); + } + + interface TopToolbarButtonOptions { + icon: string; + color?: string; + title: string; + } + + class TopToolbarButton { + constructor(options: TopToolbarButtonOptions, iconRegistry: MatIconRegistry); + onClick(onClick: (e: MouseEvent, button: TopToolbarButton) => void): void; + setActive(active: boolean): void; + isActive(): boolean; + setDisabled(disabled: boolean): void; + isDisabled(): boolean; + } + + interface TopToolbarControlOptions { + mapElement: JQuery; + iconRegistry: MatIconRegistry; + } + + class TopToolbarControl { + constructor(options: TopToolbarControlOptions); + toolbarButton(options: TopToolbarButtonOptions): TopToolbarButton; + setDisabled(disabled: boolean): void; + } + + interface ToolbarButtonOptions { + id: string; + title: string; + click: (e: MouseEvent, button: ToolbarButton) => void; + iconClass: string; + showText?: boolean; + } + + class ToolbarButton { + constructor(options: ToolbarButtonOptions); + setActive(active: boolean): void; + isActive(): boolean; + setDisabled(disabled: boolean): void; + isDisabled(): boolean; + } + + class ToolbarControl extends Control { + constructor(options: ControlOptions); + toolbarButton(options: ToolbarButtonOptions): ToolbarButton; + } + + interface BottomToolbarControlOptions { + mapElement: JQuery; + closeTitle: string; + onClose: () => boolean; + } + + class BottomToolbarControl { + constructor(options: BottomToolbarControlOptions); + getButton(id: string): ToolbarButton | undefined; + open(buttons: ToolbarButtonOptions[], showCloseButton?: boolean): void; + close(): void; + container: HTMLElement; + } + + function sidebar(options: SidebarControlOptions): SidebarControl; + + function sidebarPane(options: O): SidebarPaneControl; + + function layers(options: LayersControlOptions): LayersControl; + + function groups(options: GroupsControlOptions): GroupsControl; + + function topToolbar(options: TopToolbarControlOptions): TopToolbarControl; + + function toolbar(options: ControlOptions): ToolbarControl; + + function bottomToolbar(options: BottomToolbarControlOptions): BottomToolbarControl; + + namespace TileLayer { + + interface ChinaProvidersData { + [provider: string]: { + [type: string]: string; + Subdomains: string; + }; + } + + class ChinaProvider extends L.TileLayer { + constructor(type: string, options?: TileLayerOptions); + } + } + + namespace tileLayer { + function chinaProvider(type: string, options?: TileLayerOptions): TileLayer.ChinaProvider; + } } } diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 01a0062a39..1423d29ee1 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -307,13 +307,6 @@ dependencies: tslib "^2.3.0" -"@angular/flex-layout@^15.0.0-beta.42": - version "15.0.0-beta.42" - resolved "https://registry.yarnpkg.com/@angular/flex-layout/-/flex-layout-15.0.0-beta.42.tgz#ad5e1dda32ee6280ba73765be10fd916c222e38e" - integrity sha512-cTAPVMMxnyIFwpZwdq0PL5mdP9Qh+R8MB7ZBezVaN3Rz2fRrkagzKpLvPX3TFzepXrvHBdpKsU4b8u+NxEC/6g== - dependencies: - tslib "^2.3.0" - "@angular/forms@18.2.13": version "18.2.13" resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-18.2.13.tgz#119f9b32b0da5e2f1bc3c07f506a645e5635f583" @@ -1938,6 +1931,64 @@ resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz#93bcd6dc24afd1cc60dd88a65b9e4fab32dcf397" integrity sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA== +"@mapbox/geojson-rewind@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz#591a5d71a9cd1da1a0bf3420b3bea31b0fc7946a" + integrity sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA== + dependencies: + get-stream "^6.0.1" + minimist "^1.2.6" + +"@mapbox/jsonlint-lines-primitives@^2.0.2", "@mapbox/jsonlint-lines-primitives@~2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" + integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ== + +"@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" + integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ== + +"@mapbox/tiny-sdf@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz#9a1d33e5018093e88f6a4df2343e886056287282" + integrity sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA== + +"@mapbox/unitbezier@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01" + integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw== + +"@mapbox/vector-tile@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz#d3a74c90402d06e89ec66de49ec817ff53409666" + integrity sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw== + dependencies: + "@mapbox/point-geometry" "~0.1.0" + +"@mapbox/whoots-js@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe" + integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q== + +"@maplibre/maplibre-gl-leaflet@^0.0.22": + version "0.0.22" + resolved "https://registry.yarnpkg.com/@maplibre/maplibre-gl-leaflet/-/maplibre-gl-leaflet-0.0.22.tgz#04c4d5b65f44be0962330f8b5a7cafc8c3bd2676" + integrity sha512-9p4DSPLUE5t0StXH/IcZw37/UL7Ekea4jcVdkSsb2jWI+sM3K7mYHLOnti/2wn+HqxG4jMVUHah7Xun3bs00AQ== + +"@maplibre/maplibre-gl-style-spec@^20.3.1": + version "20.4.0" + resolved "https://registry.yarnpkg.com/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz#408339e051fb51e022b40af2235e0beb037937ea" + integrity sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw== + dependencies: + "@mapbox/jsonlint-lines-primitives" "~2.0.2" + "@mapbox/unitbezier" "^0.0.1" + json-stringify-pretty-compact "^4.0.0" + minimist "^1.2.8" + quickselect "^2.0.0" + rw "^1.3.3" + tinyqueue "^3.0.0" + "@mat-datetimepicker/core@~14.0.0": version "14.0.0" resolved "https://registry.yarnpkg.com/@mat-datetimepicker/core/-/core-14.0.0.tgz#1776ae74c3ff94b2bf4e1c6c01c9366c8ab876b5" @@ -2663,6 +2714,13 @@ resolved "https://registry.yarnpkg.com/@types/flowjs/-/flowjs-2.13.14.tgz#62cdd8d5d8e0222e505e4a140e73cd385c0a496c" integrity sha512-OlZFH9hbOq5B+GeBjcM3UvvSailseRT+sKoi9jyFFczvvS1n1LTiTKJysDSLVWnUvGD+tsn7kxb6eC2uaJD+Zw== +"@types/geojson-vt@3.2.5": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/geojson-vt/-/geojson-vt-3.2.5.tgz#b6c356874991d9ab4207533476dfbcdb21e38408" + integrity sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g== + dependencies: + "@types/geojson" "*" + "@types/geojson@*", "@types/geojson@^7946.0.10": version "7946.0.14" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.14.tgz#319b63ad6df705ee2a65a73ef042c8271e696613" @@ -2673,6 +2731,11 @@ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca" integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA== +"@types/geojson@^7946.0.14": + version "7946.0.16" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a" + integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg== + "@types/hammerjs@^2.0.45": version "2.0.45" resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.45.tgz#ffa764bb68a66c08db6efb9c816eb7be850577b1" @@ -2752,6 +2815,20 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb" integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg== +"@types/mapbox__point-geometry@*", "@types/mapbox__point-geometry@^0.1.4": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz#0ef017b75eedce02ff6243b4189210e2e6d5e56d" + integrity sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA== + +"@types/mapbox__vector-tile@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz#ad757441ef1d34628d9e098afd9c91423c1f8734" + integrity sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg== + dependencies: + "@types/geojson" "*" + "@types/mapbox__point-geometry" "*" + "@types/pbf" "*" + "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -2790,6 +2867,11 @@ dependencies: undici-types "~6.19.2" +"@types/pbf@*", "@types/pbf@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.5.tgz#a9495a58d8c75be4ffe9a0bd749a307715c07404" + integrity sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA== + "@types/qs@*": version "6.9.16" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.16.tgz#52bba125a07c0482d26747d5d4947a64daf8f794" @@ -2846,6 +2928,13 @@ dependencies: "@types/node" "*" +"@types/supercluster@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/supercluster/-/supercluster-7.1.3.tgz#1a1bc2401b09174d9c9e44124931ec7874a72b27" + integrity sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA== + dependencies: + "@types/geojson" "*" + "@types/systemjs@6.15.1": version "6.15.1" resolved "https://registry.yarnpkg.com/@types/systemjs/-/systemjs-6.15.1.tgz#dae1ec2fbe66af7c6ca1a110e2c9ca6b85135eec" @@ -4704,6 +4793,11 @@ domutils@^3.0.1: domelementtype "^2.3.0" domhandler "^5.0.3" +earcut@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/earcut/-/earcut-3.0.1.tgz#f60b3f671c5657cca9d3e131c5527c5dde00ef38" + integrity sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw== + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -5520,6 +5614,11 @@ geojson-rbush@3.x: "@types/geojson" "7946.0.8" rbush "^3.0.1" +geojson-vt@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/geojson-vt/-/geojson-vt-4.0.2.tgz#1162f6c7d61a0ba305b1030621e6e111f847828a" + integrity sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A== + get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -5541,7 +5640,7 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -5555,6 +5654,11 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +gl-matrix@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.3.tgz#fc1191e8320009fd4d20e9339595c6041ddc22c9" + integrity sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA== + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -5598,6 +5702,15 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +global-prefix@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-4.0.0.tgz#e9cc79aab9be1d03287e156a3f912dd0895463ed" + integrity sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA== + dependencies: + ini "^4.1.3" + kind-of "^6.0.3" + which "^4.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -5866,7 +5979,7 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.1.13: +ieee754@^1.1.12, ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -6395,6 +6508,11 @@ json-stable-stringify@^1.0.2: jsonify "^0.0.1" object-keys "^1.1.1" +json-stringify-pretty-compact@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz#cf4844770bddee3cb89a6170fe4b00eee5dbf1d4" + integrity sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q== + json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -6469,6 +6587,11 @@ katex@^0.16.0, katex@^0.16.9: dependencies: commander "^8.3.0" +kdbush@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-4.0.2.tgz#2f7b7246328b4657dd122b6c7f025fbc2c868e39" + integrity sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA== + keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -6481,7 +6604,7 @@ khroma@^2.1.0: resolved "https://registry.yarnpkg.com/khroma/-/khroma-2.1.0.tgz#45f2ce94ce231a437cf5b63c2e886e6eb42bbbb1" integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw== -kind-of@^6.0.2: +kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -6810,6 +6933,38 @@ make-plural@^7.0.0: resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.4.0.tgz#fa6990dd550dea4de6b20163f74e5ed83d8a8d6d" integrity sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg== +maplibre-gl@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-4.7.1.tgz#06a524438ee2aafbe8bcd91002a4e01468ea5486" + integrity sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA== + dependencies: + "@mapbox/geojson-rewind" "^0.5.2" + "@mapbox/jsonlint-lines-primitives" "^2.0.2" + "@mapbox/point-geometry" "^0.1.0" + "@mapbox/tiny-sdf" "^2.0.6" + "@mapbox/unitbezier" "^0.0.1" + "@mapbox/vector-tile" "^1.3.1" + "@mapbox/whoots-js" "^3.1.0" + "@maplibre/maplibre-gl-style-spec" "^20.3.1" + "@types/geojson" "^7946.0.14" + "@types/geojson-vt" "3.2.5" + "@types/mapbox__point-geometry" "^0.1.4" + "@types/mapbox__vector-tile" "^1.3.4" + "@types/pbf" "^3.0.5" + "@types/supercluster" "^7.1.3" + earcut "^3.0.0" + geojson-vt "^4.0.2" + gl-matrix "^3.4.3" + global-prefix "^4.0.0" + kdbush "^4.0.2" + murmurhash-js "^1.0.0" + pbf "^3.3.0" + potpack "^2.0.0" + quickselect "^3.0.0" + supercluster "^8.0.1" + tinyqueue "^3.0.0" + vt-pbf "^3.1.3" + marked@^13.0.2: version "13.0.3" resolved "https://registry.yarnpkg.com/marked/-/marked-13.0.3.tgz#5c5b4a5d0198060c7c9bc6ef9420a7fed30f822d" @@ -6954,7 +7109,7 @@ minimatch@^9.0.0, minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -minimist@1.2.8, minimist@^1.2.0, minimist@^1.2.6: +minimist@1.2.8, minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -7104,6 +7259,11 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" +murmurhash-js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51" + integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw== + mute-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" @@ -7785,6 +7945,14 @@ pathe@^1.1.2: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== +pbf@^3.2.1, pbf@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.3.0.tgz#1790f3d99118333cc7f498de816028a346ef367f" + integrity sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q== + dependencies: + ieee754 "^1.1.12" + resolve-protobuf-schema "^2.1.0" + picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -7978,6 +8146,11 @@ postinstall-prepare@^2.0.0: resolved "https://registry.yarnpkg.com/postinstall-prepare/-/postinstall-prepare-2.0.0.tgz#2a6867c1a13a05502aa115d0495efbbd778769cb" integrity sha512-lLFwEKdnGLAaRAm8OpXP6HwrXRW+b8Hh9vRhVHZKmCdobd+D21YM38BCDsi3zrePLSe8Tt0H/mbYkh7/ySQQMg== +potpack@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/potpack/-/potpack-2.0.0.tgz#61f4dd2dc4b3d5e996e3698c0ec9426d0e169104" + integrity sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8016,6 +8189,11 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== +protocol-buffers-schema@^3.3.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" + integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -8060,6 +8238,11 @@ quickselect@^2.0.0: resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== +quickselect@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-3.0.0.tgz#a37fc953867d56f095a20ac71c6d27063d2de603" + integrity sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8235,6 +8418,13 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-protobuf-schema@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz#9ca9a9e69cf192bbdaf1006ec1973948aa4a3758" + integrity sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ== + dependencies: + protocol-buffers-schema "^3.3.1" + resolve-url-loader@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz#ee3142fb1f1e0d9db9524d539cfa166e9314f795" @@ -8357,7 +8547,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rw@1: +rw@1, rw@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== @@ -8964,6 +9154,13 @@ sucrase@^3.35.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +supercluster@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-8.0.1.tgz#9946ba123538e9e9ab15de472531f604e7372df5" + integrity sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ== + dependencies: + kdbush "^4.0.2" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -9145,6 +9342,11 @@ tinymce@6.8.5, "tinymce@^7.0.0 || ^6.0.0 || ^5.5.0", tinymce@~6.8.5: resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-6.8.5.tgz#aa9a711c4e0b59d506dd281bade857d35a7b3c59" integrity sha512-qAL/FxL7cwZHj4BfaF818zeJJizK9jU5IQzTcSLL4Rj5MaJdiVblEj7aDr80VCV1w9h4Lak9hlnALhq/kVtN1g== +tinyqueue@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-3.0.0.tgz#101ea761ccc81f979e29200929e78f1556e3661e" + integrity sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -9537,6 +9739,15 @@ vscode-uri@~3.0.8: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== +vt-pbf@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac" + integrity sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA== + dependencies: + "@mapbox/point-geometry" "0.1.0" + "@mapbox/vector-tile" "^1.3.1" + pbf "^3.2.1" + watchpack@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff"