UI: Map - add show marker option for trips.

This commit is contained in:
Igor Kulikov 2025-03-06 19:08:39 +02:00
parent 14de7726c2
commit ac03bf27a3
5 changed files with 217 additions and 157 deletions

View File

@ -149,6 +149,7 @@ class TbTripDataItem extends TbDataLayerItem<TripsDataLayerSettings, TbTripsData
}
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, {
@ -170,8 +171,10 @@ class TbTripDataItem extends TbDataLayerItem<TripsDataLayerSettings, TbTripsData
});
}
}
}
private updateMarker() {
if (this.settings.showMarker) {
const dsData = this.dataLayer.getMap().getData();
this.marker.options.tbMarkerData = this.pointData;
this.updateMarkerLocation(this.pointData, dsData);
@ -181,6 +184,7 @@ class TbTripDataItem extends TbDataLayerItem<TripsDataLayerSettings, TbTripsData
}
this.updateMarkerIcon(this.pointData, dsData);
}
}
private createPath() {
if (this.settings.showPath) {
@ -262,6 +266,7 @@ class TbTripDataItem extends TbDataLayerItem<TripsDataLayerSettings, TbTripsData
}
private updateMarkerIcon(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
if (this.settings.showMarker) {
this.dataLayer.dataProcessor.createMarkerIcon(data, dsData, data.rotationAngle).subscribe(
(iconInfo) => {
const options = deepClone(iconInfo.icon.options);
@ -276,6 +281,7 @@ class TbTripDataItem extends TbDataLayerItem<TripsDataLayerSettings, TbTripsData
}
);
}
}
private updateMarkerLabel(data: FormattedData<TbMapDatasource>, dsData: FormattedData<TbMapDatasource>[]) {
if (this.settings.label.show) {
@ -384,6 +390,10 @@ export class TbTripsDataLayer extends TbMapDataLayer<TripsDataLayerSettings, TbT
return 'trips';
}
public showMarker(): boolean {
return this.settings.showMarker;
}
public prepareTripsData(tripsData: FormattedData<TbMapDatasource>[][], tripsLatestData: FormattedData<TbMapDatasource>[]): {minTime: number; maxTime: number} {
let minTime = Infinity;
let maxTime = -Infinity;

View File

@ -148,23 +148,6 @@ export abstract class TbMap<S extends BaseMapSettings> {
this.mapLayoutElement = mapLayoutElement[0];
$(containerElement).append(mapLayoutElement);
if (this.settings.tripTimeline?.showTimelineControl) {
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';
containerElement.append(content);
}
const mapElement = $('<div class="tb-map"></div>');
mapLayoutElement.append(mapElement);
@ -380,6 +363,27 @@ export abstract class TbMap<S extends BaseMapSettings> {
}
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'),

View File

@ -176,15 +176,28 @@
<div class="tb-form-panel">
<div class="tb-form-panel-title">{{ 'widget-config.appearance' | translate }}</div>
<ng-container *ngIf="['trips', 'markers'].includes(dataLayerType)">
<div class="tb-form-panel stroked">
<div class="flex flex-row items-center justify-between">
<div class="tb-form-panel tb-slide-toggle stroked">
<mat-expansion-panel #markerExpansionPanel class="tb-settings" [expanded]="dataLayerType === 'markers' || dataLayerFormGroup.get('showMarker').value"
[disabled]="dataLayerType === 'trips' && !dataLayerFormGroup.get('showMarker').value">
<mat-expansion-panel-header class="flex flex-row flex-wrap">
<mat-panel-title>
<div class="flex flex-1 flex-row items-center justify-between">
@if (dataLayerType === 'markers') {
<div class="tb-form-panel-title">{{ 'widgets.maps.data-layer.marker.marker' | translate }}</div>
<tb-toggle-select formControlName="markerType">
} @else {
<mat-slide-toggle class="mat-slide flex items-stretch justify-center" formControlName="showMarker" (click)="$event.stopPropagation()">
<div class="tb-form-panel-title">{{ 'widgets.maps.data-layer.marker.marker' | translate }}</div>
</mat-slide-toggle>
}
<tb-toggle-select [class.!hidden]="!markerExpansionPanel.expanded" formControlName="markerType" (click)="$event.stopPropagation()">
<tb-toggle-option [value]="MarkerType.shape">{{ 'widgets.maps.data-layer.marker.marker-type-shape' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="MarkerType.icon">{{ 'widgets.maps.data-layer.marker.marker-type-icon' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="MarkerType.image">{{ 'widgets.maps.data-layer.marker.marker-type-image' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div *ngIf="dataLayerFormGroup.get('markerType').value === MarkerType.shape" class="tb-form-row space-between">
<div translate>widgets.maps.data-layer.marker.shape</div>
<tb-marker-shape-settings formControlName="markerShape" [trip]="dataLayerType === 'trips'" [markerType]="MarkerType.shape"></tb-marker-shape-settings>
@ -228,6 +241,8 @@
<ng-container *ngTemplateOutlet="dataLayerLabelAndTooltip"></ng-container>
<ng-container *ngTemplateOutlet="behavior; context: {stroked: true}"></ng-container>
}
</ng-template>
</mat-expansion-panel>
</div>
<div *ngIf="mapType === MapType.image" class="tb-form-panel stroked">
<div class="tb-form-panel-title" translate>widgets.maps.data-layer.marker.position-conversion</div>

View File

@ -168,6 +168,7 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
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));
@ -193,7 +194,8 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
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('markerType').valueChanges,
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,
@ -323,31 +325,42 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
this.dataLayerFormGroup.get('dsDeviceId').disable({emitEvent: false});
this.dataLayerFormGroup.get('dsEntityAliasId').enable({emitEvent: false});
}
if (['trips', 'markers'].includes(this.dataLayerType)) {
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});
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;
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 (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});
@ -389,6 +402,22 @@ export class MapDataLayerDialogComponent extends DialogComponent<MapDataLayerDia
}
}
}
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') {

View File

@ -400,6 +400,7 @@ export const pathDecoratorSymbolTranslationMap = new Map<PathDecoratorSymbol, st
);
export interface TripsDataLayerSettings extends MarkersDataLayerSettings {
showMarker: boolean;
rotateMarker: boolean;
offsetAngle: number;
showPath: boolean;
@ -425,6 +426,7 @@ export const defaultTripsDataLayerSettings = (mapType: MapType, functionsOnly =
export const defaultBaseTripsDataLayerSettings = (mapType: MapType): Partial<TripsDataLayerSettings> => mergeDeep(
defaultBaseMarkersDataLayerSettings(mapType),
{
showMarker: true,
tooltip: {
offsetY: -0.5,
pattern: mapType === MapType.geoMap ?