WIP: add dynamic generation of marker if check-box in table is pressed

* fix: both use the same array -> delete if uncheck
This commit is contained in:
tim-herbst 2020-12-29 14:36:59 +01:00
parent a597343b0c
commit bba88ac1aa
14 changed files with 160 additions and 26 deletions

View File

@ -26,6 +26,7 @@ import {MatInputModule} from '@angular/material/input';
import {MatTableModule} from '@angular/material/table'; import {MatTableModule} from '@angular/material/table';
import {AutoRefreshComponent} from './map/auto-refresh/auto-refresh.component'; import {AutoRefreshComponent} from './map/auto-refresh/auto-refresh.component';
import {MatSlideToggleModule} from '@angular/material/slide-toggle'; import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatCheckboxModule} from '@angular/material/checkbox';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -57,7 +58,8 @@ import {MatSlideToggleModule} from '@angular/material/slide-toggle';
ReactiveFormsModule, ReactiveFormsModule,
MatInputModule, MatInputModule,
MatTableModule, MatTableModule,
MatSlideToggleModule MatSlideToggleModule,
MatCheckboxModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@ -75,13 +75,22 @@
</mat-card> </mat-card>
</div> </div>
<div class="container-table" fxFlex fxLayout="row" fxLayoutAlign="center"> <div class="container-table" fxFlex fxLayout="row" fxLayoutAlign="center">
<div class="dashboard-table-to" fxFlex> <div class="dashboard-table-to" fxFlex>
<table [dataSource]="stationToSource" class="mat-elevation-z8" fxFill mat-table> <table [dataSource]="stationToSource" class="mat-elevation-z8" fxFill mat-table>
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selectRowTo($event, row) : null"
[checked]="selectionTo.isSelected(row)"
[aria-label]="checkboxLabelTo(row)">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="endStationName"> <ng-container matColumnDef="endStationName">
<th *matHeaderCellDef mat-header-cell> station of lend destination</th> <th *matHeaderCellDef mat-header-cell> station of lend destination</th>
<td *matCellDef="let element" mat-cell><a <td *matCellDef="let element" mat-cell><a [style.color]="getColorTo(element)"
[routerLink]="['/dashboard/', element.stationId]">{{element.stationName}}</a></td> [routerLink]="['/dashboard/', element.stationId]">{{element.stationName}}</a></td>
</ng-container> </ng-container>
@ -101,9 +110,19 @@
</div> </div>
<div class="dashboard-table-from" fxFlex> <div class="dashboard-table-from" fxFlex>
<table [dataSource]="stationFromSource" class="mat-elevation-z9" fxFill mat-table> <table [dataSource]="stationFromSource" class="mat-elevation-z9" fxFill mat-table>
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let row">
<mat-checkbox (click)="$event.stopPropagation()"
(change)="$event ? selectRowFrom($event, row) : null"
[checked]="selectionFrom.isSelected(row)"
[aria-label]="checkboxLabelFrom(row)">
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="startStationName"> <ng-container matColumnDef="startStationName">
<th *matHeaderCellDef mat-header-cell> station of lend origin</th> <th *matHeaderCellDef mat-header-cell> station of lend origin</th>
<td *matCellDef="let element" mat-cell><a <td *matCellDef="let element" mat-cell><a [style.color]="getColorFrom(element)"
[routerLink]="['/dashboard/', element.stationId]"> {{element.stationName}}</a></td> [routerLink]="['/dashboard/', element.stationId]"> {{element.stationName}}</a></td>
</ng-container> </ng-container>

View File

@ -6,12 +6,9 @@ mat-sidenav-content {
flex: 1 1 auto; flex: 1 1 auto;
} }
a {
color: #017bfe;
}
.button-back:hover, .button-wiki:hover { .button-back:hover, .button-wiki:hover {
background: #086ed2; background: #086ed2;
} }
.submit-date { .submit-date {
@ -31,7 +28,7 @@ a {
margin-top: 1em; margin-top: 1em;
margin-left: 1em; margin-left: 1em;
margin-bottom: 1em; margin-bottom: 1em;
background-image: url('../../assets/bike-point.png'); background-image: url('../../assets/bike-point-blue.png');
background-size: cover; background-size: cover;
} }

View File

@ -24,6 +24,8 @@ import {
ChartComponent ChartComponent
} from 'ng-apexcharts'; } from 'ng-apexcharts';
import {IMapBikePoint} from '../service/domain/map-bike-point'; import {IMapBikePoint} from '../service/domain/map-bike-point';
import {SelectionModel} from '@angular/cdk/collections';
import {MatCheckboxChange} from '@angular/material/checkbox';
export type ChartOptions = { export type ChartOptions = {
title: ApexTitleSubtitle title: ApexTitleSubtitle
@ -54,10 +56,12 @@ export class DashboardComponent implements OnInit {
public durationChartOptions: Partial<ChartOptions>; public durationChartOptions: Partial<ChartOptions>;
public timeChartOptions: Partial<ChartOptions>; public timeChartOptions: Partial<ChartOptions>;
public bikePointChartOptions: Partial<ChartOptions>; public bikePointChartOptions: Partial<ChartOptions>;
displayedColumnsTo: string[] = ['endStationName', 'number', 'avgDuration']; displayedColumnsTo: string[] = ['select', 'endStationName', 'number', 'avgDuration'];
displayedColumnsFrom: string[] = ['startStationName', 'number', 'avgDuration']; displayedColumnsFrom: string[] = ['select', 'startStationName', 'number', 'avgDuration'];
stationToSource = new MatTableDataSource<any>(); stationToSource = new MatTableDataSource<IDashboardCommonBikePoint>();
selectionTo = new SelectionModel<any>(true, []);
stationFromSource = new MatTableDataSource<any>(); stationFromSource = new MatTableDataSource<any>();
selectionFrom = new SelectionModel<any>(true, []);
station: IDashboardCommonBikePoint; station: IDashboardCommonBikePoint;
maxStartDate: Date; maxStartDate: Date;
@ -67,6 +71,7 @@ export class DashboardComponent implements OnInit {
form: FormGroup; form: FormGroup;
bikePoint: IMapBikePoint; bikePoint: IMapBikePoint;
bikePointWithColor = [];
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
@ -205,15 +210,15 @@ export class DashboardComponent implements OnInit {
} }
initDashboard(): void { async initDashboard(): Promise<any> {
const initDate = this.maxEndDate.toISOString().substring(0, 10); const initDate = this.maxEndDate.toISOString().substring(0, 10);
this.form.get('daterange').get('start').setValue(initDate); this.form.get('daterange').get('start').setValue(initDate);
this.form.get('daterange').get('end').setValue(initDate); this.form.get('daterange').get('end').setValue(initDate);
this.service.fetchDashboardStationTo(this.station.id, initDate, initDate).then((source) => { await this.service.fetchDashboardStationTo(this.station.id, initDate, initDate).then((source) => {
this.stationToSource = source; this.stationToSource = source;
this.changeDetectorRefs.detectChanges(); this.changeDetectorRefs.detectChanges();
}); });
this.service.fetchDashboardStationFrom(this.station.id, initDate, initDate).then((source) => { await this.service.fetchDashboardStationFrom(this.station.id, initDate, initDate).then((source) => {
this.stationFromSource = source; this.stationFromSource = source;
this.changeDetectorRefs.detectChanges(); this.changeDetectorRefs.detectChanges();
}); });
@ -495,4 +500,98 @@ export class DashboardComponent implements OnInit {
humanizeAvgDuration(avgDuration: number): string { humanizeAvgDuration(avgDuration: number): string {
return stht(avgDuration); return stht(avgDuration);
} }
isToAllSelected(): boolean {
const numSelected = this.selectionTo.selected.length;
const numRows = this.stationToSource.data.length;
return numSelected === numRows;
}
isFromAllSelected(): boolean {
const numSelected = this.selectionFrom.selected.length;
const numRows = this.stationFromSource.data.length;
return numSelected === numRows;
}
checkboxLabelTo(row?: any): string {
if (!row) {
return `${this.isToAllSelected() ? 'select' : 'deselect'} all`;
}
return `${this.selectionTo.isSelected(row) ? 'deselect' : 'select'} row ${row.position + 1}`;
}
checkboxLabelFrom(row?: any): string {
if (!row) {
return `${this.isFromAllSelected() ? 'select' : 'deselect'} all`;
}
return `${this.selectionFrom.isSelected(row) ? 'deselect' : 'select'} row ${row.position + 1}`;
}
selectRowTo(selection: MatCheckboxChange, row): void {
this.selectionTo.toggle(row);
this.selectionTo.selected.forEach(point => {
if (point.stationId === row.stationId) {
point.color = this.getColorTo(row);
}
this.bikePointWithColor.push(point);
});
this.map.drawTableStationMarker(this.bikePointWithColor);
}
selectRowFrom(selection: MatCheckboxChange, row): void {
this.selectionFrom.toggle(row);
this.selectionFrom.selected.forEach(point => {
if (point.stationId === row.stationId) {
point.color = this.getColorFrom(row);
}
this.bikePointWithColor.push(point);
});
this.map.drawTableStationMarker(this.bikePointWithColor);
}
getColorTo(value): string {
switch (value.stationName) {
case this.stationToSource[0].stationName:
if (this.stationToSource[0].stationName === this.station.commonName) {
return 'blue';
} else {
return 'black';
}
case this.stationToSource[1].stationName:
if (this.stationToSource[1].stationName === this.station.commonName) {
return 'blue';
} else {
return 'red';
}
case this.stationToSource[2].stationName:
if (this.stationToSource[2].stationName === this.station.commonName) {
return 'blue';
} else {
return 'green';
}
}
}
getColorFrom(value): string {
switch (value.stationName) {
case this.stationFromSource[0].stationName:
if (this.stationFromSource[0].stationName === this.station.commonName) {
return 'blue';
} else {
return 'orange';
}
case this.stationFromSource[1].stationName:
if (this.stationFromSource[1].stationName === this.station.commonName) {
return 'blue';
} else {
return 'purple';
}
case this.stationFromSource[2].stationName:
if (this.stationFromSource[2].stationName === this.station.commonName) {
return 'blue';
} else {
return 'gray';
}
}
}
} }

View File

@ -1,6 +1,5 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {MapService} from '../service/map.service'; import {MapService} from '../service/map.service';
import {BehaviorSubject, Observable, of, Subject} from 'rxjs';
@Component({ @Component({

View File

@ -1,5 +1,6 @@
export interface IDashboardCommonBikePoint { export interface IDashboardCommonBikePoint {
id?: string; id?: string;
color?: string;
commonName?: string; commonName?: string;
lat?: number; lat?: number;
lon?: number; lon?: number;
@ -10,6 +11,7 @@ export interface IDashboardCommonBikePoint {
export class DashboardCommonBikePoint implements IDashboardCommonBikePoint { export class DashboardCommonBikePoint implements IDashboardCommonBikePoint {
constructor( constructor(
public id?: string, public id?: string,
public color?: string,
public commonName?: string, public commonName?: string,
public lat?: number, public lat?: number,
public lon?: number, public lon?: number,

View File

@ -9,8 +9,8 @@ import {IMapBikePoint} from './domain/map-bike-point';
import {Observable, Subject} from 'rxjs'; import {Observable, Subject} from 'rxjs';
const createIcon = L.icon({ const createIcon = color => L.icon({
iconUrl: '../../assets/bike-point.png', iconUrl: `../../assets/bike-point-${color}.png`,
iconSize: [45, 45], iconSize: [45, 45],
iconAnchor: [21, 40], iconAnchor: [21, 40],
popupAnchor: [1, -35] popupAnchor: [1, -35]
@ -27,6 +27,9 @@ export class MapService {
public miniMap; public miniMap;
bikePoints: Array<IMapBikePoint> = []; bikePoints: Array<IMapBikePoint> = [];
mapOverlays: any = {}; mapOverlays: any = {};
miniMapMarker: L.layerGroup;
markerLayer = [];
dashBoardMarker = L.marker;
constructor( constructor(
private client: HttpClient, private client: HttpClient,
@ -78,7 +81,7 @@ export class MapService {
this.mapOverlays.Bikepoints = markerClusters; this.mapOverlays.Bikepoints = markerClusters;
this.map.addLayer(markerClusters); this.map.addLayer(markerClusters);
for (const station of data) { for (const station of data) {
const marker = L.marker([station.lat, station.lon], {icon: createIcon}); const marker = L.marker([station.lat, station.lon], {icon: createIcon('blue')});
markerClusters.addLayer(marker); markerClusters.addLayer(marker);
marker.on('click', e => { marker.on('click', e => {
e.target.bindPopup(this.popUpService.makeAvailabilityPopUp(station), {maxWidth: 'auto'}) e.target.bindPopup(this.popUpService.makeAvailabilityPopUp(station), {maxWidth: 'auto'})
@ -137,7 +140,20 @@ export class MapService {
} }
public drawDashboardStationMarker(lat: number, lon: number): void { public drawDashboardStationMarker(lat: number, lon: number): void {
L.marker([lat, lon], {icon: createIcon}).addTo(this.miniMap); this.dashBoardMarker = L.marker([lat, lon], {icon: createIcon('blue')}).addTo(this.miniMap);
}
public drawTableStationMarker(bikePoints: any[]): void {
if (this.markerLayer) {
this.markerLayer.forEach(marker => {
this.miniMap.removeLayer(marker);
});
}
for (const point of bikePoints) {
const marker = L.marker([point.stationLat, point.stationLon], {icon: createIcon(point.color)}).addTo(this.miniMap);
this.markerLayer.push(marker);
this.miniMap.fitBounds(L.featureGroup([...this.markerLayer, this.dashBoardMarker]).getBounds());
}
} }
private drawMapControl(): void { private drawMapControl(): void {

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB