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 index 1bc1e8a395..72400b0f38 100644 --- 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 @@ -44,6 +44,11 @@ import { MarkerDataProcessor } from '@home/components/widget/lib/maps/data-layer type TripRouteData = {[time: number]: FormattedData}; +interface PointItem { + point: L.CircleMarker; + tooltip?: L.Popup; +} + class TbTripDataItem extends TbDataLayerItem { private tripRouteData: TripRouteData; @@ -54,7 +59,8 @@ class TbTripDataItem extends TbDataLayerItem(); private currentTime: number; private currentPositionData: FormattedData; @@ -94,7 +100,7 @@ class TbTripDataItem extends TbDataLayerItem { + pointItem.point.off(); + }); this.dataLayer.getDataLayerContainer().removeLayer(this.layer); this.layer.off(); } @@ -216,34 +228,61 @@ class TbTripDataItem extends TbDataLayerItem !!this.dataLayer.dataProcessor.extractLocationData(data)); - for (const pData of pointsList) { - let pointData = pData; + 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 point = L.circleMarker(this.dataLayer.dataProcessor.extractLocation(pointData, dsData), { + const pointStyle = { stroke: false, fillOpacity: 1, fillColor: pointColor, radius: this.settings.pointSize - }); - if (this.settings.pointTooltip?.show) { - const pointTooltip = createTooltip(this.dataLayer.getMap(), - point, this.settings.pointTooltip, pointData, () => true); - updateTooltip(this.dataLayer.getMap(), pointTooltip, - this.settings.pointTooltip, this.dataLayer.pointTooltipProcessor, pointData, dsData); + }; + 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); } - this.points.addLayer(point); + 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); + } + }); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts index 7fc4cf5e54..c88d8072e3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map.ts @@ -175,6 +175,25 @@ export abstract class TbMap { this.map.zoomControl.setPosition(this.settings.controlsPosition); } this.dragMode = !this.settings.dragModeButton; + const tripsWithMarkers = this.settings.trips?.length ? this.settings.trips.filter(trip => trip.showMarker) : []; + const showTimeline = this.settings.tripTimeline?.showTimelineControl && tripsWithMarkers.length; + + if (showTimeline) { + this.timeline = true; + this.timeStep = this.settings.tripTimeline.timeStep; + this.timeLineComponentRef = this.ctx.widgetContentContainer.createComponent(MapTimelinePanelComponent); + this.timeLineComponent = this.timeLineComponentRef.instance; + this.timeLineComponent.settings = this.settings.tripTimeline; + this.timeLineComponent.timeChanged.subscribe((time) => { + this.currentTime = time; + this.updateTripsTime(); + }); + const parentElement = this.timeLineComponentRef.instance.element.nativeElement; + const content = parentElement.firstChild; + parentElement.removeChild(content); + parentElement.style.display = 'none'; + this.containerElement.append(content); + } const setup = [this.doSetupControls()]; if (this.timeline && this.settings.tripTimeline.snapToRealLocation) { setup.push(parseTbFunction(this.getCtx().http, this.settings.tripTimeline.locationSnapFilter, ['data', 'dsData']).pipe( @@ -269,7 +288,7 @@ export abstract class TbMap { buttonTitle: this.ctx.translate.instant('widgets.maps.data-layer.groups'), }).addTo(this.map); this.map.on('layergroupchange', () => { - this.updateBounds(); + this.updateBounds(true); }); } const setup = this.dataLayers.map(dl => dl.setup()); @@ -322,6 +341,7 @@ export abstract class TbMap { const tripDataLayersSubscriptionOptions: WidgetSubscriptionOptions = { datasources: tripDatasources, hasDataPageLink: true, + ignoreDataUpdateOnIntervalTick: true, useDashboardTimewindow: isDefined(this.ctx.widgetConfig.useDashboardTimewindow) ? this.ctx.widgetConfig.useDashboardTimewindow : true, type: widgetType.timeseries, @@ -364,26 +384,6 @@ export abstract class TbMap { private setupEditMode() { - const tripsWithMarkers = this.tripDataLayers.filter(dl => dl.showMarker()); - const showTimeline = this.settings.tripTimeline?.showTimelineControl && tripsWithMarkers.length; - - if (showTimeline) { - this.timeline = true; - this.timeStep = this.settings.tripTimeline.timeStep; - this.timeLineComponentRef = this.ctx.widgetContentContainer.createComponent(MapTimelinePanelComponent); - this.timeLineComponent = this.timeLineComponentRef.instance; - this.timeLineComponent.settings = this.settings.tripTimeline; - this.timeLineComponent.timeChanged.subscribe((time) => { - this.currentTime = time; - this.updateTripsTime(); - }); - const parentElement = this.timeLineComponentRef.instance.element.nativeElement; - const content = parentElement.firstChild; - parentElement.removeChild(content); - parentElement.style.display = 'none'; - this.containerElement.append(content); - } - this.editToolbar = L.TB.bottomToolbar({ mapElement: $(this.mapElement), closeTitle: this.ctx.translate.instant('action.cancel'), @@ -919,7 +919,7 @@ export abstract class TbMap { this.currentPopover?.updatePosition(); } - private updateBounds() { + private updateBounds(force = false) { const enabledDataLayers = this.dataLayers.filter(dl => dl.isEnabled()); const dataLayersBounds = enabledDataLayers.map(dl => dl.getBounds()).filter(b => b.isValid()); let bounds: L.LatLngBounds; @@ -927,7 +927,7 @@ export abstract class TbMap { bounds = new L.LatLngBounds(null, null); dataLayersBounds.forEach(b => bounds.extend(b)); const mapBounds = this.map.getBounds(); - if (bounds.isValid() && (!this.bounds || !this.bounds.isValid() || !this.bounds.equals(bounds) && this.settings.fitMapBounds && !mapBounds.contains(bounds))) { + if (bounds.isValid() && (!this.bounds || !this.bounds.isValid() || (!this.bounds.equals(bounds) || force) && this.settings.fitMapBounds && !mapBounds.contains(bounds))) { this.bounds = bounds; if (!this.ignoreUpdateBounds && !this.isPlacingItem) { this.fitBounds(bounds); 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 index 22fc96bcbc..681b93da4a 100644 --- 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 @@ -81,6 +81,10 @@ export class DataLayerPatternSettingsComponent implements OnInit, ControlValueAc @coerceBoolean() hasTooltipOffset = false; + @Input() + @coerceBoolean() + expand = true; + @Input() context: MapSettingsContext; @@ -149,7 +153,7 @@ export class DataLayerPatternSettingsComponent implements OnInit, ControlValueAc value, {emitEvent: false} ); this.updateValidators(); - this.settingsExpanded = this.patternSettingsFormGroup.get('show').value; + this.settingsExpanded = this.patternSettingsFormGroup.get('show').value && this.expand; this.patternSettingsFormGroup.get('show').valueChanges.pipe( takeUntilDestroyed(this.destroyRef) ).subscribe((show) => { 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 index 8e634e178b..012fbf6625 100644 --- 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 @@ -238,7 +238,7 @@ @if (dataLayerType === 'trips') { - + } @@ -360,6 +360,7 @@ patternType="tooltip" patternTitle="{{ 'widgets.maps.data-layer.points.point-tooltip' | translate }}" [context]="context" + [expand]="false" helpId="widget/lib/map/path_point_tooltip_fn" formControlName="pointTooltip"> @@ -384,7 +385,7 @@ @if (dataLayerType !== 'trips') { - + } @if (dataLayerType !== 'trips') { @@ -436,18 +437,20 @@ - + 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 index b52e7b9013..64d75eb78f 100644 --- 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 @@ -428,6 +428,7 @@ export class MapDataLayerDialogComponent extends DialogComponent { if (updatedDataKey) { this.dataLayerFormGroup.get(keyType).patchValue(updatedDataKey); + this.dataLayerFormGroup.markAsDirty(); } } );