From acc5764565241b28e81d51a42760136804f732e9 Mon Sep 17 00:00:00 2001 From: trikimiki <41070061+trikimiki@users.noreply.github.com> Date: Wed, 26 Feb 2025 18:31:18 +0200 Subject: [PATCH 01/11] rewrite volume check/create scripts add option to skip ownership change (fix for Docker Desktop "Synchronized file shares " feature) --- docker/compose-utils.sh | 72 ++++++++++++++++++++++++----- docker/docker-check-log-folders.sh | 9 +++- docker/docker-create-log-folders.sh | 2 +- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index d5e60bee7f..dd2d2890fc 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -182,29 +182,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..22e91ea3f3 100755 --- a/docker/docker-check-log-folders.sh +++ b/docker/docker-check-log-folders.sh @@ -17,5 +17,10 @@ set -e source compose-utils.sh -checkFolders || exit $? -echo "OK" +if checkFolders "$@" ; then + echo "------" + echo "All checks have passed" +else + echo "------" + echo "Some checks did not pass - check the output" +fi \ No newline at end of file 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 "$@" From 801a1baab488b9d47352499e856da1d9506a5eda Mon Sep 17 00:00:00 2001 From: trikimiki <41070061+trikimiki@users.noreply.github.com> Date: Wed, 26 Feb 2025 18:37:43 +0200 Subject: [PATCH 02/11] remove duplicate from volume checklist --- docker/compose-utils.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index dd2d2890fc..55615800b4 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -131,7 +131,6 @@ function additionalStartupServices() { 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 From 07256796897cbeef008c4b2b258c78d8fee8f633 Mon Sep 17 00:00:00 2001 From: trikimiki <41070061+trikimiki@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:59:40 +0200 Subject: [PATCH 03/11] remove checkFolders from other scripts reason - it breaks running TB for MacOS users; will update site documentation accordingly --- docker/docker-install-tb.sh | 2 -- docker/docker-start-services.sh | 2 -- docker/docker-upgrade-tb.sh | 2 -- 3 files changed, 6 deletions(-) diff --git a/docker/docker-install-tb.sh b/docker/docker-install-tb.sh index 1956e50eac..25f089afb4 100755 --- a/docker/docker-install-tb.sh +++ b/docker/docker-install-tb.sh @@ -51,8 +51,6 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || 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 3cdf10d00f..39dc57c1de 100755 --- a/docker/docker-start-services.sh +++ b/docker/docker-start-services.sh @@ -29,8 +29,6 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || exit $? ADDITIONAL_COMPOSE_MONITORING_ARGS=$(additionalComposeMonitoringArgs) || exit $? -checkFolders --create || exit $? - COMPOSE_ARGS="\ -f docker-compose.yml ${ADDITIONAL_CACHE_ARGS} ${ADDITIONAL_COMPOSE_ARGS} ${ADDITIONAL_COMPOSE_QUEUE_ARGS} ${ADDITIONAL_COMPOSE_MONITORING_ARGS} \ up -d" diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index 3be5fbc14b..5a34e29aa9 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -44,8 +44,6 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || 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} \ pull \ From f772bab21843b6935ff44ac99e7d96a74760d4c1 Mon Sep 17 00:00:00 2001 From: trikimiki <41070061+trikimiki@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:16:05 +0200 Subject: [PATCH 04/11] bump zookeeper version --- docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From 705a5742d0142be67b63a355726dc1ad9fccbbb9 Mon Sep 17 00:00:00 2001 From: trikimiki <41070061+trikimiki@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:27:12 +0200 Subject: [PATCH 05/11] add exitcode for check folder script --- docker/docker-check-log-folders.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/docker-check-log-folders.sh b/docker/docker-check-log-folders.sh index 22e91ea3f3..6122f3d2c1 100755 --- a/docker/docker-check-log-folders.sh +++ b/docker/docker-check-log-folders.sh @@ -21,6 +21,8 @@ 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 From b4898568d1ec38c5c6419fdc72adc56f175c3f93 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 11 Mar 2025 15:12:01 +0200 Subject: [PATCH 06/11] removed edqs stats logs, fixed edqs apiUsage name --- application/src/main/resources/thingsboard.yml | 2 -- .../server/common/data/edqs/fields/FieldsUtil.java | 2 +- .../thingsboard/server/edqs/repo/TenantRepo.java | 3 +++ .../server/edqs/stats/EdqsStatsService.java | 13 ------------- edqs/src/main/resources/edqs.yml | 2 -- 5 files changed, 4 insertions(+), 18 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index badd779350..0483526831 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1757,8 +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 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..59255b11cc 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 @@ -430,6 +430,9 @@ public class TenantRepo { private String getEntityName(EntityId entityId) { EntityType entityType = entityId.getEntityType(); + if (entityType == EntityType.TENANT && entityId.getId().equals(TenantId.NULL_UUID)) { + return ""; + } return switch (entityType) { case CUSTOMER, TENANT -> getEntityMap(entityType).get(entityId.getId()).getFields().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/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 From 12d07f1cc8220a09a67941e4ebee5ab8c3a6033d Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 11 Mar 2025 19:23:56 +0200 Subject: [PATCH 07/11] UI: Map - introduce reference vector layers. --- ui-ngx/package.json | 2 + .../widget/lib/maps/leaflet/leaflet-tb.ts | 21 +- .../components/widget/lib/maps/map-layer.ts | 116 +- .../home/components/widget/lib/maps/map.scss | 8 + .../common/map/map-layer-row.component.ts | 3 +- .../map-layer-settings-panel.component.html | 11 + .../map/map-layer-settings-panel.component.ts | 9 +- .../shared/models/widget/maps/map.models.ts | 22 +- .../assets/locale/locale.constant-en_US.json | 6 + .../openstreetmap_hybrid_reference_style.json | 25800 +++++++ .../world_edition_hybrid_reference_style.json | 57837 ++++++++++++++++ ui-ngx/src/typings/leaflet-extend-tb.d.ts | 1 + ui-ngx/yarn.lock | 228 +- 13 files changed, 84033 insertions(+), 31 deletions(-) create mode 100644 ui-ngx/src/assets/map/openstreetmap_hybrid_reference_style.json create mode 100644 ui-ngx/src/assets/map/world_edition_hybrid_reference_style.json diff --git a/ui-ngx/package.json b/ui-ngx/package.json index b2e2dc18c3..1536858de8 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -28,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", @@ -64,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/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 index 06d49d0343..df02c46482 100644 --- 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 @@ -17,6 +17,7 @@ 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'; @@ -211,15 +212,19 @@ class LayersControl extends SidebarPaneControl { input.on('click', (e: JQuery.MouseEventBase) => { e.stopPropagation(); - layers.forEach((other) => { - if (other.layer === layerData.layer) { - map.addLayer(other.layer); - map.attributionControl.setPrefix(other.attributionPrefix); - } else { - map.removeLayer(other.layer); + if (!map.hasLayer(layerData.layer)) { + map.addLayer(layerData.layer); + map.attributionControl.setPrefix(layerData.attributionPrefix); + if (layerData.onAdd) { + layerData.onAdd(); } - }); - map.fire('baselayerchange', { layer: layerData.layer }); + layers.forEach((other) => { + if (other.layer !== layerData.layer) { + map.removeLayer(other.layer); + } + }); + map.fire('baselayerchange', { layer: layerData.layer }); + } }); item.on('dblclick', (e) => { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-layer.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-layer.ts index 304100828b..87ca69034f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-layer.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-layer.ts @@ -26,17 +26,34 @@ import { HereMapLayerSettings, MapLayerSettings, MapProvider, - OpenStreetMapLayerSettings, + OpenStreetMapLayerSettings, ReferenceLayerType, TencentMapLayerSettings } from '@shared/models/widget/maps/map.models'; import { WidgetContext } from '@home/models/widget-component.models'; import { DeepPartial } from '@shared/models/common'; import { mergeDeep } from '@core/utils'; -import { Observable, of, switchMap } from 'rxjs'; +import { Observable, of, shareReplay, switchMap } from 'rxjs'; import { CustomTranslatePipe } from '@shared/pipe/custom-translate.pipe'; import L from 'leaflet'; import { catchError, map } from 'rxjs/operators'; import { ResourcesService } from '@core/services/resources.service'; +import { StyleSpecification, VectorSourceSpecification } from '@maplibre/maplibre-gl-style-spec'; +import { ResourceType } from 'maplibre-gl'; + +const referenceLayerStyleUrlMap = new Map( + [ + [ReferenceLayerType.openstreetmap_hybrid, '/assets/map/openstreetmap_hybrid_reference_style.json'], + [ReferenceLayerType.world_edition_hybrid, '/assets/map/world_edition_hybrid_reference_style.json'] + ] +); + +const referenceLayerCache = new Map>(); + +interface TbMapLayerData { + layer: L.Layer; + attribution: boolean; + onAdd?: () => void; +} export abstract class TbMapLayer { @@ -65,19 +82,23 @@ export abstract class TbMapLayer { } public loadLayer(theMap: L.Map): Observable { - return this.createLayer().pipe( - switchMap((layer) => { - if (layer) { - return this.createLayer().pipe( - map((mini) => { - if (mini) { - const attribution = layer.getAttribution(); - const attributionPrefix = attribution ? theMap.attributionControl.options.prefix as string : null; + return this.generateLayer().pipe( + switchMap((layerData) => { + if (layerData) { + return this.generateLayer().pipe( + map((miniLayerData) => { + if (miniLayerData) { + const attributionPrefix = layerData.attribution ? theMap.attributionControl.options.prefix as string : null; return { title: this.title(), attributionPrefix: attributionPrefix, - layer, - mini + layer: layerData.layer, + mini: miniLayerData.layer, + onAdd: () => { + if (layerData.onAdd) { + layerData.onAdd(); + } + } }; } else { return null; @@ -91,6 +112,77 @@ export abstract class TbMapLayer { ); } + private generateLayer(): Observable { + return this.createLayer().pipe( + switchMap((baseLayer) => { + if (baseLayer) { + if (this.settings.referenceLayer) { + return this.loadReferenceLayer(this.settings.referenceLayer).pipe( + map((referenceLayer) => { + if (referenceLayer) { + const layer = L.featureGroup(); + baseLayer.addTo(layer); + referenceLayer.addTo(layer); + return { + layer, + attribution: !!baseLayer.getAttribution() || !!referenceLayer.getAttribution(), + onAdd: () => { + (referenceLayer as any)._update(); + } + }; + } else { + return { + layer: baseLayer, + attribution: !!baseLayer.getAttribution() + }; + } + })); + } else { + return of({ + layer: baseLayer, + attribution: !!baseLayer.getAttribution() + }); + } + } else { + return of(null); + } + } + )); + } + + private loadReferenceLayer(referenceLayer: ReferenceLayerType): Observable { + let spec$ = referenceLayerCache.get(referenceLayer); + if (!spec$) { + const styleUrl = referenceLayerStyleUrlMap.get(referenceLayer); + spec$ = this.ctx.http.get(styleUrl).pipe( + shareReplay({ + bufferSize: 1, + refCount: true + }) + ); + referenceLayerCache.set(referenceLayer, spec$); + } + return spec$.pipe( + map(spec => { + const sourceSpec = (spec.sources['esri'] as VectorSourceSpecification); + const tileUrl = sourceSpec.url; + const attribution = sourceSpec.attribution; + const transformRequest = (url: string, resourceType: ResourceType) => { + if (resourceType === 'Tile') { + url = tileUrl + '/' + url; + } + return {url} + } + const gl = L.maplibreGL({ + style: spec, + transformRequest + }); + gl.options.attribution = attribution; + return gl; + }) + ); + } + private title(): string { const customTranslate = this.ctx.$injector.get(CustomTranslatePipe); if (this.settings.label) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss index 592610f8b2..985bdc17f5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.scss @@ -35,6 +35,10 @@ div.tb-widget .tb-widget-content.tb-no-interaction { .tb-map-container { flex-direction: column; + .leaflet-gl-layer.maplibregl-map { + position: relative; + z-index: 1; + } } .tb-map-layout { @@ -47,6 +51,10 @@ div.tb-widget .tb-widget-content.tb-no-interaction { .tb-map { position: relative; flex: 1; + .leaflet-control-attribution { + font-size: 0.6rem; + background: rgba(255,255,255,0.5); + } &.leaflet-touch { .leaflet-bar { border: 1px solid rgba(0,0,0,0.38); 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 index 7112a0c3c2..392c9f9565 100644 --- 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 @@ -123,7 +123,8 @@ export class MapLayerRowComponent implements ControlValueAccessor, OnInit { provider: [null, [Validators.required]], layerType: [null, [Validators.required]], tileUrl: [null, [Validators.required]], - apiKey: [null, [Validators.required]] + apiKey: [null, [Validators.required]], + referenceLayer: [null, []] }); this.layerFormGroup.valueChanges.pipe( takeUntilDestroyed(this.destroyRef) 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 index c30033f7e1..cf4c4b90c4 100644 --- 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 @@ -82,6 +82,17 @@ +
+
widgets.maps.layer.reference.reference-layer
+ + + {{ 'widgets.maps.layer.reference.no-layer' | translate }} + + {{ referenceLayerTypeTranslationMap.get(layer) | translate }} + + + +