WIP: add dynamic generation of marker if check-box in table is pressed
* fix: both use the same array -> delete if uncheck
@ -26,6 +26,7 @@ import {MatInputModule} from '@angular/material/input';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {AutoRefreshComponent} from './map/auto-refresh/auto-refresh.component';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -57,7 +58,8 @@ import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
ReactiveFormsModule,
|
||||
MatInputModule,
|
||||
MatTableModule,
|
||||
MatSlideToggleModule
|
||||
MatSlideToggleModule,
|
||||
MatCheckboxModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
@ -75,13 +75,22 @@
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container-table" fxFlex fxLayout="row" fxLayoutAlign="center">
|
||||
<div class="dashboard-table-to" fxFlex>
|
||||
<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">
|
||||
<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>
|
||||
</ng-container>
|
||||
|
||||
@ -101,9 +110,19 @@
|
||||
</div>
|
||||
<div class="dashboard-table-from" fxFlex>
|
||||
<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">
|
||||
<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>
|
||||
</ng-container>
|
||||
|
||||
|
@ -6,12 +6,9 @@ mat-sidenav-content {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #017bfe;
|
||||
}
|
||||
|
||||
.button-back:hover, .button-wiki:hover {
|
||||
background: #086ed2;
|
||||
|
||||
}
|
||||
|
||||
.submit-date {
|
||||
@ -31,7 +28,7 @@ a {
|
||||
margin-top: 1em;
|
||||
margin-left: 1em;
|
||||
margin-bottom: 1em;
|
||||
background-image: url('../../assets/bike-point.png');
|
||||
background-image: url('../../assets/bike-point-blue.png');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ import {
|
||||
ChartComponent
|
||||
} from 'ng-apexcharts';
|
||||
import {IMapBikePoint} from '../service/domain/map-bike-point';
|
||||
import {SelectionModel} from '@angular/cdk/collections';
|
||||
import {MatCheckboxChange} from '@angular/material/checkbox';
|
||||
|
||||
export type ChartOptions = {
|
||||
title: ApexTitleSubtitle
|
||||
@ -54,10 +56,12 @@ export class DashboardComponent implements OnInit {
|
||||
public durationChartOptions: Partial<ChartOptions>;
|
||||
public timeChartOptions: Partial<ChartOptions>;
|
||||
public bikePointChartOptions: Partial<ChartOptions>;
|
||||
displayedColumnsTo: string[] = ['endStationName', 'number', 'avgDuration'];
|
||||
displayedColumnsFrom: string[] = ['startStationName', 'number', 'avgDuration'];
|
||||
stationToSource = new MatTableDataSource<any>();
|
||||
displayedColumnsTo: string[] = ['select', 'endStationName', 'number', 'avgDuration'];
|
||||
displayedColumnsFrom: string[] = ['select', 'startStationName', 'number', 'avgDuration'];
|
||||
stationToSource = new MatTableDataSource<IDashboardCommonBikePoint>();
|
||||
selectionTo = new SelectionModel<any>(true, []);
|
||||
stationFromSource = new MatTableDataSource<any>();
|
||||
selectionFrom = new SelectionModel<any>(true, []);
|
||||
|
||||
station: IDashboardCommonBikePoint;
|
||||
maxStartDate: Date;
|
||||
@ -67,6 +71,7 @@ export class DashboardComponent implements OnInit {
|
||||
form: FormGroup;
|
||||
|
||||
bikePoint: IMapBikePoint;
|
||||
bikePointWithColor = [];
|
||||
|
||||
constructor(
|
||||
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);
|
||||
this.form.get('daterange').get('start').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.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.changeDetectorRefs.detectChanges();
|
||||
});
|
||||
@ -323,16 +328,16 @@ export class DashboardComponent implements OnInit {
|
||||
text: 'amount of drives',
|
||||
},
|
||||
}, {
|
||||
opposite: true,
|
||||
title: {
|
||||
text: 'average Duration'
|
||||
},
|
||||
opposite: true,
|
||||
title: {
|
||||
text: 'average Duration'
|
||||
},
|
||||
labels: {
|
||||
formatter: (val: number): string => {
|
||||
return val + ' min';
|
||||
}
|
||||
}
|
||||
}],
|
||||
}],
|
||||
legend: {
|
||||
horizontalAlign: 'left'
|
||||
},
|
||||
@ -495,4 +500,98 @@ export class DashboardComponent implements OnInit {
|
||||
humanizeAvgDuration(avgDuration: number): string {
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {MapService} from '../service/map.service';
|
||||
import {BehaviorSubject, Observable, of, Subject} from 'rxjs';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -25,7 +24,7 @@ export class MapComponent implements OnInit {
|
||||
console.log(this.isRefreshActive);
|
||||
}
|
||||
|
||||
async initMapView(): Promise<any> {
|
||||
async initMapView(): Promise<any> {
|
||||
this.service.initMap(51.509865, -0.118092, 14);
|
||||
await this.service.drawStationMarkers();
|
||||
this.service.drawHeatmap();
|
||||
|
@ -1,5 +1,6 @@
|
||||
export interface IDashboardCommonBikePoint {
|
||||
id?: string;
|
||||
color?: string;
|
||||
commonName?: string;
|
||||
lat?: number;
|
||||
lon?: number;
|
||||
@ -10,6 +11,7 @@ export interface IDashboardCommonBikePoint {
|
||||
export class DashboardCommonBikePoint implements IDashboardCommonBikePoint {
|
||||
constructor(
|
||||
public id?: string,
|
||||
public color?: string,
|
||||
public commonName?: string,
|
||||
public lat?: number,
|
||||
public lon?: number,
|
||||
|
@ -9,8 +9,8 @@ import {IMapBikePoint} from './domain/map-bike-point';
|
||||
import {Observable, Subject} from 'rxjs';
|
||||
|
||||
|
||||
const createIcon = L.icon({
|
||||
iconUrl: '../../assets/bike-point.png',
|
||||
const createIcon = color => L.icon({
|
||||
iconUrl: `../../assets/bike-point-${color}.png`,
|
||||
iconSize: [45, 45],
|
||||
iconAnchor: [21, 40],
|
||||
popupAnchor: [1, -35]
|
||||
@ -27,6 +27,9 @@ export class MapService {
|
||||
public miniMap;
|
||||
bikePoints: Array<IMapBikePoint> = [];
|
||||
mapOverlays: any = {};
|
||||
miniMapMarker: L.layerGroup;
|
||||
markerLayer = [];
|
||||
dashBoardMarker = L.marker;
|
||||
|
||||
constructor(
|
||||
private client: HttpClient,
|
||||
@ -78,7 +81,7 @@ export class MapService {
|
||||
this.mapOverlays.Bikepoints = markerClusters;
|
||||
this.map.addLayer(markerClusters);
|
||||
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);
|
||||
marker.on('click', e => {
|
||||
e.target.bindPopup(this.popUpService.makeAvailabilityPopUp(station), {maxWidth: 'auto'})
|
||||
@ -137,7 +140,20 @@ export class MapService {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
BIN
projects/project-3/frontend/src/assets/bike-point-black.png
Normal file
After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
BIN
projects/project-3/frontend/src/assets/bike-point-gray.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
projects/project-3/frontend/src/assets/bike-point-green.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
projects/project-3/frontend/src/assets/bike-point-orange.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
projects/project-3/frontend/src/assets/bike-point-purple.png
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
projects/project-3/frontend/src/assets/bike-point-red.png
Normal file
After Width: | Height: | Size: 76 KiB |