From 54bf6f95ceffef4c6800d238ee6d7960564f4c5a Mon Sep 17 00:00:00 2001 From: Ratnadeep Rajendra Kharade <92khra1mst@hft-stuttgart.de> Date: Fri, 29 Nov 2019 03:23:20 +0100 Subject: [PATCH 1/4] Added new page for My reservation functionality --- .../myreservation-routing.module.ts | 17 ++ src/app/myreservation/myreservation.module.ts | 20 ++ src/app/myreservation/myreservation.page.html | 57 ++++ src/app/myreservation/myreservation.page.scss | 0 .../myreservation/myreservation.page.spec.ts | 24 ++ src/app/myreservation/myreservation.page.ts | 264 ++++++++++++++++++ 6 files changed, 382 insertions(+) create mode 100644 src/app/myreservation/myreservation-routing.module.ts create mode 100644 src/app/myreservation/myreservation.module.ts create mode 100644 src/app/myreservation/myreservation.page.html create mode 100644 src/app/myreservation/myreservation.page.scss create mode 100644 src/app/myreservation/myreservation.page.spec.ts create mode 100644 src/app/myreservation/myreservation.page.ts diff --git a/src/app/myreservation/myreservation-routing.module.ts b/src/app/myreservation/myreservation-routing.module.ts new file mode 100644 index 0000000..9b5fb9f --- /dev/null +++ b/src/app/myreservation/myreservation-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { MyreservationPage } from './myreservation.page'; + +const routes: Routes = [ + { + path: '', + component: MyreservationPage + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class MyreservationPageRoutingModule {} diff --git a/src/app/myreservation/myreservation.module.ts b/src/app/myreservation/myreservation.module.ts new file mode 100644 index 0000000..aaf6b5a --- /dev/null +++ b/src/app/myreservation/myreservation.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { IonicModule } from '@ionic/angular'; + +import { MyreservationPageRoutingModule } from './myreservation-routing.module'; + +import { MyreservationPage } from './myreservation.page'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + MyreservationPageRoutingModule + ], + declarations: [MyreservationPage] +}) +export class MyreservationPageModule {} diff --git a/src/app/myreservation/myreservation.page.html b/src/app/myreservation/myreservation.page.html new file mode 100644 index 0000000..76a54fc --- /dev/null +++ b/src/app/myreservation/myreservation.page.html @@ -0,0 +1,57 @@ + + + + + + + My Reservation + + + + + + + + No reservation found + + +
+
+ + +
+
+
+ + + Bike Name + {{bikeDetails.name}} + + + Battery Level + + + {{bikeDetails.batteryPercentage +" %"}} + + + + Bike Location + {{bikeDetails.address}} + + + Bike Distance + {{ bikeDetails.distance +" m"}} + + + + Hire + + + Cancel Reservation + + + +
+
+
+
\ No newline at end of file diff --git a/src/app/myreservation/myreservation.page.scss b/src/app/myreservation/myreservation.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/myreservation/myreservation.page.spec.ts b/src/app/myreservation/myreservation.page.spec.ts new file mode 100644 index 0000000..13da464 --- /dev/null +++ b/src/app/myreservation/myreservation.page.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { MyreservationPage } from './myreservation.page'; + +describe('MyreservationPage', () => { + let component: MyreservationPage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MyreservationPage ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(MyreservationPage); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/myreservation/myreservation.page.ts b/src/app/myreservation/myreservation.page.ts new file mode 100644 index 0000000..0991f09 --- /dev/null +++ b/src/app/myreservation/myreservation.page.ts @@ -0,0 +1,264 @@ +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; + +import { Geolocation } from '@ionic-native/geolocation/ngx'; +import { RestService } from '../rest.service'; +import { Observable } from 'rxjs'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Storage } from '@ionic/storage'; +import { ToastService } from '../services/toast.service'; +import { Router } from '@angular/router'; + +declare var H: any; + +@Component({ + selector: 'app-myreservation', + templateUrl: './myreservation.page.html', + styleUrls: ['./myreservation.page.scss'], +}) +export class MyreservationPage implements OnInit { + private platform: any; + private map: any; + // Get an instance of the routing service: + private mapRouter: any; + + reservedBike: any = {}; + bikeDetails: any = {}; + + noReservation = true; + + private currentLocation = { lat: 0, lng: 0 }; + + // Create the parameters for the routing request: + private routingParameters = { + // The routing mode: + mode: 'shortest;pedestrian', + // The start point of the route: + waypoint0: 'geo!50.1120423728813,8.68340740740811', + // The end point of the route: + waypoint1: 'geo!52.5309916298853,13.3846220493377', + // To retrieve the shape of the route we choose the route + // representation mode 'display' + representation: 'display' + }; + + @ViewChild("mapElement", { static: false }) + public mapElement: ElementRef; + + constructor(private geolocation: Geolocation, + public restService: RestService, + public httpClient: HttpClient, + private storage: Storage, + private toastService: ToastService, + private router: Router) { + this.platform = new H.service.Platform({ + 'apikey': 'tiVTgBnPbgV1spie5U2MSy-obhD9r2sGiOCbBzFY2_k' + }); + this.mapRouter = this.platform.getRoutingService(); + } + + ngOnInit() { + this.getReservedBike(); + } + + ngAfterViewInit() { + + } + + getReservedBike() { + this.storage.get('token').then((token) => { + const headers = new HttpHeaders().set("Authorization", "Bearer " + token); + //call reserved bike api + let reserveUrl = 'http://193.196.52.237:8081/active-rent'; + let bikeReservationStatusApi = this.httpClient.get(reserveUrl, { headers }); + bikeReservationStatusApi.subscribe((resp: any) => { + console.log('Reserved Bike', resp); + if (resp.data) { + this.reservedBike = resp.data; + //Call Bike Details api + let bikeDetailsUrl = 'http://193.196.52.237:8081/bikes/' + this.reservedBike.bikeId; + let bikeDetailsApi = this.httpClient.get(bikeDetailsUrl, { headers }); + bikeDetailsApi.subscribe((resp: any) => { + console.log('Bike Details', resp); + this.bikeDetails = resp.data; + this.noReservation = false; + + // display map + setTimeout(() => { + this.loadmap(); + }, 1000); + window.addEventListener('resize', () => this.map.getViewPort().resize()); + }, (reservedBikeError) => console.log(reservedBikeError)); + } + }, (bikeDetailsError) => console.log(bikeDetailsError)); + }); + } + + cancelReservation() { + this.storage.get('token').then((token) => { + let url = 'http://193.196.52.237:8081/reservation' + '?bikeId=' + this.bikeDetails.id; + const headers = new HttpHeaders().set("Authorization", "Bearer " + token); + let bikeApi = this.httpClient.delete(url, { headers }); + bikeApi.subscribe((resp) => { + console.log('Reservation Cancelled: ', resp); + this.toastService.showToast("Bike Reservation successfully cancelled."); + this.router.navigateByUrl('/home'); + }, (error) => console.log(error)); + }); + } + + loadmap() { + var defaultLayers = this.platform.createDefaultLayers(); + this.map = new H.Map( + this.mapElement.nativeElement, + defaultLayers.raster.normal.map, + { + zoom: 17, + pixelRatio: window.devicePixelRatio || 1 + } + ); + + var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(this.map)); + var ui = H.ui.UI.createDefault(this.map, defaultLayers); + ui.removeControl("mapsettings"); + // create custom one + var ms = new H.ui.MapSettingsControl({ + baseLayers: [{ + label: "3D", layer: defaultLayers.vector.normal.map + }, { + label: "Normal", layer: defaultLayers.raster.normal.map + }, { + label: "Satellite", layer: defaultLayers.raster.satellite.map + }, { + label: "Terrain", layer: defaultLayers.raster.terrain.map + } + ], + layers: [{ + label: "layer.traffic", layer: defaultLayers.vector.normal.traffic + }, + { + label: "layer.incidents", layer: defaultLayers.vector.normal.trafficincidents + } + ] + }); + ui.addControl("customized", ms); + var mapSettings = ui.getControl('customized'); + var zoom = ui.getControl('zoom'); + mapSettings.setAlignment('top-right'); + zoom.setAlignment('left-top'); + + //get user location + this.getLocation(this.map); + + var img = ['../../../assets/images/100_percent.png', '../../../assets/images/75_percent.png', '../../../assets/images/50_percent.png', '../../../assets/images/25_percent.png', '../../../assets/images/0_percent.png']; + if (this.bikeDetails.batteryPercentage < 100 && this.bikeDetails.batteryPercentage >= 75) { + this.addMarker(Number(this.bikeDetails.lat), Number(this.bikeDetails.lon), img[0]); + } + else if (this.bikeDetails.batteryPercentage < 75 && this.bikeDetails.batteryPercentage >= 50) { + this.addMarker(Number(this.bikeDetails.lat), Number(this.bikeDetails.lon), img[1]); + } + else if (this.bikeDetails.batteryPercentage < 50 && this.bikeDetails.batteryPercentage >= 25) { + this.addMarker(Number(this.bikeDetails.lat), Number(this.bikeDetails.lon), img[2]); + } else if (this.bikeDetails.batteryPercentage < 25 && this.bikeDetails.batteryPercentage >= 0) { + this.addMarker(Number(this.bikeDetails.lat), Number(this.bikeDetails.lon), img[3]); + } + } + + getLocation(map) { + this.geolocation.getCurrentPosition( + { + maximumAge: 1000, timeout: 5000, + enableHighAccuracy: true + } + ).then((resp) => { + let lat = resp.coords.latitude + let lng = resp.coords.longitude + this.currentLocation.lat = resp.coords.latitude; + this.currentLocation.lng = resp.coords.longitude; + this.moveMapToGiven(map, lat, lng); + // set routing params + this.routingParameters.waypoint1 = 'geo!' + this.bikeDetails.lat + ',' + this.bikeDetails.lon; + this.routingParameters.waypoint0 = 'geo!' + this.currentLocation.lat + ',' + this.currentLocation.lng; + + // show route on map + this.mapRouter.calculateRoute(this.routingParameters, this.onResult.bind(this), + (error) => { + alert(error.message); + }); + }, er => { + alert('Can not retrieve Location') + }).catch((error) => { + alert('Error getting location - ' + JSON.stringify(error)) + }); + } + + moveMapToGiven(map, lat, lng) { + var icon = new H.map.Icon('../../../assets/images/current_location.png'); + // Create a marker using the previously instantiated icon: + var marker = new H.map.Marker({ lat: lat, lng: lng }, { icon: icon }); + // Add the marker to the map: + map.addObject(marker); + map.setCenter({ lat: lat, lng: lng }); + } + + addMarker(lat, lng, img) { + var icon = new H.map.Icon(img); + // Create a marker using the previously instantiated icon: + var marker = new H.map.Marker({ lat: lat, lng: lng }, { icon: icon }); + // Add the marker to the map: + this.map.addObject(marker); + } + + + // Define a callback function to process the routing response: + onResult(result) { + var route, + routeShape, + startPoint, + endPoint, + linestring; + if (result.response.route) { + // Pick the first route from the response: + route = result.response.route[0]; + // Pick the route's shape: + routeShape = route.shape; + + // Create a linestring to use as a point source for the route line + linestring = new H.geo.LineString(); + + // Push all the points in the shape into the linestring: + routeShape.forEach(function (point) { + var parts = point.split(','); + linestring.pushLatLngAlt(parts[0], parts[1]); + }); + + // Retrieve the mapped positions of the requested waypoints: + startPoint = route.waypoint[0].mappedPosition; + endPoint = route.waypoint[1].mappedPosition; + + // Create a polyline to display the route: + var routeLine = new H.map.Polyline(linestring, { + /* style: { + lineWidth: 10, + fillColor: 'white', + strokeColor: 'rgba(255, 255, 255, 1)', + lineDash: [0, 2], + lineTailCap: 'arrow-tail', + lineHeadCap: 'arrow-head' + } */ + style: { + lineWidth: 5, + strokeColor: 'rgba(0, 128, 255, 0.7)', + lineDash: [0, 2] + } + }); + + // Add the route polyline and the two markers to the map: + this.map.addObjects([routeLine]); + + // Set the map's viewport to make the whole route visible: + this.map.getViewModel().setLookAtData({ bounds: routeLine.getBoundingBox() }); + //this.map.setZoom(this.map.getZoom() - 4.3, true); + } + }; + +} -- GitLab From 46365fc258d39ea34f8d1b7216f247a74154dfa5 Mon Sep 17 00:00:00 2001 From: Ratnadeep Rajendra Kharade <92khra1mst@hft-stuttgart.de> Date: Fri, 29 Nov 2019 03:23:52 +0100 Subject: [PATCH 2/4] Added My Reservation tab in navigation manu. --- src/app/app.component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 6993443..90896ac 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -23,6 +23,11 @@ export class AppComponent { url: '/home', icon: 'home' }, + { + title: 'My Reservation', + url: '/myreservation', + icon: 'clipboard' + }, { title: 'Logout', url: '/login', -- GitLab From 40f45d2ccac95b35e553fa4b9e6a433019b58a71 Mon Sep 17 00:00:00 2001 From: Ratnadeep Rajendra Kharade <92khra1mst@hft-stuttgart.de> Date: Fri, 29 Nov 2019 03:24:53 +0100 Subject: [PATCH 3/4] updated routing module with my reservation route. --- src/app/app-routing.module.ts | 72 ++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d394142..a074565 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,36 +1,40 @@ -import { NgModule } from '@angular/core'; -import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; - -const routes: Routes = [ - { - path: '', - redirectTo: 'login', - pathMatch: 'full' - }, - { - path: 'home', - loadChildren: () => import('./home/home.module').then(m => m.HomePageModule) - }, - { - path: 'list', - loadChildren: () => import('./list/list.module').then(m => m.ListPageModule) - }, - { - path: 'login', - loadChildren: () => import('./auth/login/login.module').then( m => m.LoginPageModule) - }, - { - path: 'register', - loadChildren: () => import('./auth/register/register.module').then( m => m.RegisterPageModule) +import { NgModule } from '@angular/core'; +import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; + +const routes: Routes = [ + { + path: '', + redirectTo: 'login', + pathMatch: 'full' + }, + { + path: 'home', + loadChildren: () => import('./home/home.module').then(m => m.HomePageModule) + }, + { + path: 'list', + loadChildren: () => import('./list/list.module').then(m => m.ListPageModule) + }, + { + path: 'login', + loadChildren: () => import('./auth/login/login.module').then( m => m.LoginPageModule) + }, + { + path: 'register', + loadChildren: () => import('./auth/register/register.module').then( m => m.RegisterPageModule) + }, { + path: 'myreservation', + loadChildren: () => import('./myreservation/myreservation.module').then( m => m.MyreservationPageModule) } - - -]; -@NgModule({ - imports: [ - RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) - ], - exports: [RouterModule] -}) -export class AppRoutingModule {} + + +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }) + ], + exports: [RouterModule] +}) +export class AppRoutingModule {} -- GitLab From 3d588c7d7838c124006e87edf9d12e18c15e9b1a Mon Sep 17 00:00:00 2001 From: Ratnadeep Rajendra Kharade <92khra1mst@hft-stuttgart.de> Date: Fri, 29 Nov 2019 03:25:55 +0100 Subject: [PATCH 4/4] Removed extra spaces from files and formatted code. --- src/app/home/home.page.html | 2 +- src/app/home/home.page.ts | 5 ----- src/assets/images/bike2gologo.png | Bin 10285 -> 8449 bytes 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html index 59baf69..7a3a36c 100644 --- a/src/app/home/home.page.html +++ b/src/app/home/home.page.html @@ -12,8 +12,8 @@ - +
diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index 1df890b..62d3c51 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -91,10 +91,6 @@ export class HomePage { }); } - - - - loadmap(style) { // Obtain the default map types from the platform object var mapStyle = "raster"; @@ -263,7 +259,6 @@ export class HomePage { this.isBikeReserved=false; }, (error) => console.log(error)); }); - } } diff --git a/src/assets/images/bike2gologo.png b/src/assets/images/bike2gologo.png index 03ef687e2a677670be246f79533c0bd0b1e80b50..91d91c1610162aa0a6f5664783ef8ba061510da4 100644 GIT binary patch literal 8449 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 P10+GJ>#3EfScU%|npGl0 -- GitLab