Skip to content

Commit

Permalink
Fixed timeline and added images to person snapshots.
Browse files Browse the repository at this point in the history
Next step: Integrate ionic-app for notifications
  • Loading branch information
IMS94 committed Oct 27, 2018
1 parent a8bfd10 commit 1b8004b
Show file tree
Hide file tree
Showing 16 changed files with 349 additions and 327 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,27 +190,30 @@ public Response getRealTimeMap(@PathParam("cameraGroupId") int cameraGroupId) {
}

@GET
@Path("/timeBoundMap/{from}/{to}")
public Response getTimeboundMap(@PathParam("from") long from, @PathParam("to") long to) {
@Path("/timeBoundMap/{cameraGroupId}/{from}/{to}")
public Response getTimeBoundMap(@PathParam("cameraGroupId") int cameraGroupId,
@PathParam("from") long from,
@PathParam("to") long to) {
try {
Date fromD = new Date(from);
Date toD = new Date(to);

return Response.ok(analyticsService.getTimeBoundMovements(fromD, toD, false)).build();
return Response.ok(analyticsService.getTimeBoundMovements(cameraGroupId, fromD, toD, false)).build();
} catch (Exception e) {
logger.error("Error occurred when obtaining real time map. {}", e);
return Response.status(500).build();
}
}

@GET
@Path("/timeBoundMap/{from}/{to}/trackSegmented")
public Response getTimeboundMapWithSegments(@PathParam("from") long from, @PathParam("to") long to) {
@Path("/timeBoundMap/{cameraGroupId}/{from}/{to}/trackSegmented")
public Response getTimeBoundMapWithSegments(@PathParam("cameraGroupId") int cameraGroupId,
@PathParam("from") long from,
@PathParam("to") long to) {
try {
Date fromD = new Date(from);
Date toD = new Date(to);

return Response.ok(analyticsService.getTimeBoundMovements(fromD, toD, true)).build();
return Response.ok(analyticsService.getTimeBoundMovements(cameraGroupId, fromD, toD, true)).build();
} catch (Exception e) {
logger.error("Error occurred when obtaining real time map. {}", e);
return Response.status(500).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,31 +60,20 @@ public class AnalyticsService implements ProcessedMapListener {

public AnalyticsService() { }

public List<List<Person>> getTimeBoundMovements(Date start, Date end, boolean useSegmentIndex) {
List<Person> candidates = personDAO.list(start, end);
public List<List<Person>> getTimeBoundMovements(int cameraGroupId, Date start, Date end, boolean useSegmentIndex) {
CameraGroup cameraGroup = cameraConfigDAO.findCameraGroupById(cameraGroupId);
List<Zone> zones = cameraGroup.getZones();
List<Person> people = personDAO.listByInstantZone(zones, start, end);

Map<String, List<Person>> trackedCandidates = new HashMap<>();

for (Person p : candidates) {
// List<Person> target = null;
// if(!trackedCandidates.containsKey(p.getPreviousUuid())){
// target = new ArrayList<>();
// }
// else{
// target = trackedCandidates.remove(p.getPreviousUuid());
// }
// target.add(p);
//
// trackedCandidates.put(p.getUuid(),target);
int _id = p.getId();
String id = String.valueOf(_id);
// if (useSegmentIndex)
// id += "_" + p.getTrackSegmentIndex();
if (!trackedCandidates.containsKey(id)) {
trackedCandidates.put(id, new ArrayList<>());
}
trackedCandidates.get(id).add(p);
}
return new ArrayList<List<Person>>(trackedCandidates.values());
people.forEach(person -> {
String id = String.valueOf(person.getPersonId());
List<Person> tracks = trackedCandidates.computeIfAbsent(id, key -> new ArrayList<>());
tracks.add(person);
});

return new ArrayList<>(trackedCandidates.values());
}

public List<PersonCoordinate> getRealtimePhotosAll() {
Expand Down
2 changes: 2 additions & 0 deletions ngapp/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {DashboardComponent} from './dashboard/dashboard.component';
import {ZonesComponent} from './zones/zones.component';
import {ConfigComponent} from './settings/config/config.component';
import {HeatmapsComponent} from './heatmaps/heatmaps.component';
import {TimeBoundMapComponent} from './time-bound-map/time-bound-map.component';

const routes: Routes = [
{path: 'dashboard', component: DashboardComponent},
{path: 'time-line', component: TimeBoundMapComponent},
{path: 'zones', component: ZonesComponent},
{path: 'heat-map', component: HeatmapsComponent},
{path: 'settings', component: ConfigComponent},
Expand Down
5 changes: 5 additions & 0 deletions ngapp/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export class AppComponent {
name: 'Dashboard',
icon: 'tachometer'
},
{
route: '/time-line',
name: 'Time Line',
icon: 'tachometer'
},
{
route: '/zones',
name: 'Zones',
Expand Down
5 changes: 4 additions & 1 deletion ngapp/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {SuccessMessageComponent} from './helpers/success-message/success-message
import {ZoneUiComponent} from './settings/zones-config/zone-ui/zone-ui.component';
import {ZoneComponent} from "./zones/zone/zone.component";
import {HeatmapComponent} from "./heatmaps/heatmap/heatmap.component";
import {TimeLineComponent} from "./time-bound-map/time-line/time-line.component";


@NgModule({
Expand All @@ -61,10 +62,12 @@ import {HeatmapComponent} from "./heatmaps/heatmap/heatmap.component";
ZonesComponent,
ZoneComponent,
ZoneInfoComponent,
/* Timelines */
RealtimeMapComponent,
RealtimeInfoComponent,
PersonStopPointsComponent,
TimeBoundMapComponent,
TimeLineComponent,
PersonStopPointsComponent,
ReIdComponent,
TimelineComponent,
TimeVelocityDistributionComponent,
Expand Down
5 changes: 1 addition & 4 deletions ngapp/src/app/realtime-info/realtime-info.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {PersonImage} from '../resources/person-image';
import {AnalyticsService} from '../services/analytics.service';
import {ConfigService} from '../services/config.service';

@Component({
selector: 'app-realtime-info',
Expand All @@ -25,12 +24,10 @@ export class RealtimeInfoComponent implements OnInit {
@Input()
reIDOnClick = false;


@Output()
personClicked: EventEmitter<PersonImage> = new EventEmitter<PersonImage>();


constructor(private analyticsService: AnalyticsService, private configService: ConfigService) {
constructor(private analyticsService: AnalyticsService) {
}

ngOnInit() {
Expand Down
4 changes: 2 additions & 2 deletions ngapp/src/app/resources/global-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export class GlobalMap {
img.src = image;
}

public static fromJSON(image: string) {
return new GlobalMap('data:image/png;base64,' + image);
public static fromJSON(image: string, type: string = 'png') {
return new GlobalMap(`data:image/${type};base64,${image}`);
}

public toJSON() {
Expand Down
33 changes: 30 additions & 3 deletions ngapp/src/app/resources/person-snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,47 @@
* IN THE SOFTWARE.
*/

import {Zone} from "./zone";
import {GlobalMap} from "./global-map";

export class PersonSnapshot {
id: number;
timestamp: number;
x: number;
y: number;
instanceZone: number;
persistantZone: number;
pastPersistantZone: number;
instanceZone: Zone;
persistantZone: Zone;
pastPersistantZone: Zone;
standProbability: number;
sitProbability: number;
headDirectionX: number;
headDirectionY: number;
colour: string;
standSitColour: string;
image: GlobalMap;

constructor(id: number, timestamp: number, x: number, y: number, instanceZone: Zone, persistantZone: Zone,
pastPersistantZone: Zone, standProbability: number, sitProbability: number, headDirectionX: number,
headDirectionY: number, image: GlobalMap) {
this.id = id;
this.timestamp = timestamp;
this.x = x;
this.y = y;
this.instanceZone = instanceZone;
this.persistantZone = persistantZone;
this.pastPersistantZone = pastPersistantZone;
this.standProbability = standProbability;
this.sitProbability = sitProbability;
this.headDirectionX = headDirectionX;
this.headDirectionY = headDirectionY;
this.image = image;
}

public static fromJSON(obj: any): PersonSnapshot {
return new PersonSnapshot(obj.id, obj.timestamp, obj.x, obj.y, Zone.fromJSON(obj.instanceZone),
Zone.fromJSON(obj.persistantZone), Zone.fromJSON(obj.pastPersistantZone), obj.standProbability, obj.sitProbability,
obj.headDirectionX, obj.headDirectionY, GlobalMap.fromJSON(obj.image, "jpg"));
}
}


4 changes: 4 additions & 0 deletions ngapp/src/app/resources/zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export class Zone {
}

public static fromJSON(obj: any): Zone {
if (!obj) {
return null;
}

return new Zone(obj.id, obj.zoneName, obj.xCoordinates, obj.yCoordinates, obj.zoneLimit,
CameraGroup.fromJSON(obj.cameraGroup));
}
Expand Down
26 changes: 15 additions & 11 deletions ngapp/src/app/services/analytics.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,24 +111,28 @@ export class AnalyticsService {
};
img.src = base64;
obj['image'] = base64;

});

}
return status;
})
.catch(AnalyticsService.handleError);
}

getTimeboundMap(from: number, to: number, useTrackSegments: boolean): Promise<PersonSnapshot[][]> {
let url = this.baseUrl + 'timeBoundMap/' + from + '/' + to;
if (useTrackSegments) {
url += '/trackSegmented';
}
getTimeboundMap(cameraGroupId: number, from: number, to: number): Promise<PersonSnapshot[][]> {
const url = `${this.baseUrl}timeBoundMap/${cameraGroupId}/${from}/${to}`;
return this.http.get(url)
.toPromise()
.then(response => {
return response as PersonSnapshot[][];
const people = response as PersonSnapshot[][];
const results: PersonSnapshot[][] = [];
for (const person of people) {
const snaps = [];
for (const snap of person) {
snaps.push(PersonSnapshot.fromJSON(snap));
}
results.push(snaps);
}
return results;
})
.catch(AnalyticsService.handleError);
}
Expand All @@ -137,15 +141,15 @@ export class AnalyticsService {
return this.http.get(`${this.baseUrl}zoneStatistics/${cameraGroupId}/${from}/${to}`)
.toPromise()
.then(response => {
console.debug(response);
console.log(response);
return response as ZoneStatistic[];
})
.catch(AnalyticsService.handleError);
}


private static getZoneColour(index: number): string {
//return "rgb(" + Math.round(((index / 256 / 256) * 80) % 256).toString() + "," + Math.round(((index / 256) * 80) % 256).toString() + "," + Math.round((index * 80) % 256).toString() + ")";
// return "rgb(" + Math.round(((index / 256 / 256) * 80) % 256).toString() + "," + Math.round(((index / 256) * 80) % 256).toString() + "," + Math.round((index * 80) % 256).toString() + ")";
const colours = ['#c0392b', '#f1c40f', '#16a085', '#2980b9', '#34495e', '#9b59b5', '#2cee91', '#171796', '#fec3fc', '#8e44ad'];
return colours[index % colours.length];
}
Expand All @@ -170,7 +174,7 @@ export class AnalyticsService {
result.timelineZones = results;
if (results.length > 0) {
result.person = results[0].person;
result.label = results[0].person.ids[0].toString();
result.label = results[0].person.id.toString();
}


Expand Down
Empty file.
93 changes: 11 additions & 82 deletions ngapp/src/app/time-bound-map/time-bound-map.component.html
Original file line number Diff line number Diff line change
@@ -1,89 +1,18 @@
<div class="row">
<div class="col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2 col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">
Timeline
</h3>
<div class="box-tools pull-right">
<span class="badge">{{personSnapshots.length}}</span> people
</div>
</div>
<div class="box-body">
<div class="form-horizontal container-fluid">
<div class="form-group">
<label class="col-md-1 col-sm-3 col-xs-12">From</label>
<p-calendar [(ngModel)]="startTime" [showTime]="true" class="col-md-11 col-sm-9 col-xs-12"></p-calendar>
</div>
<div class="form-group">
<label class="col-md-1 col-sm-3 col-xs-12">To</label>
<p-calendar [(ngModel)]="endTime" [showTime]="true" class="col-md-11 col-sm-9 xol-xs-12"></p-calendar>
</div>
<div class="form-group">
<p-checkbox [(ngModel)]="useTrackSegments" binary="true" value="UseTrackSegments"
label="Use Track Segments"
class="col-md-offset-1 col-sm-offset-3 col-md-11 col-sm-9"></p-checkbox>
</div>
</div>

<div class="row container-fluid">
<p-slider [(ngModel)]="secondRange" [range]="true" [min]="0" [max]="300" [step]="1"></p-slider>
<p>From {{ from | date:'medium' }} to {{ to | date:'medium' }}</p>
</div>

<div class="row" *ngIf="globalMap">
<div class="col-md-12 col-sm-12 col-xs-12">
<svg x="0" y="0" [attr.width]="globalMap.width" [attr.height]="globalMap.height" class="center-block">
<g height="100%" width="100%">
<image [attr.xlink:href]="globalMap.image" x="0" y="0" (click)="backgroundClicked();"></image>
</g>
<div class="nav-tabs-custom">

<g *ngFor="let personTrack of personSnapshots" (click)="trackClicked(personTrack);">
<g *ngIf="personTrack[0] != null">
<g *ngIf="!isSelected(personTrack[0])">
<circle [attr.fill]="personTrack[0].colour" r="5" [attr.cx]="personTrack[0].x"
[attr.cy]="personTrack[0].y"></circle>
</g>
<g *ngIf="isSelected(personTrack[0])">
<circle fill="gold" r="5" [attr.cx]="personTrack[0].x" [attr.cy]="personTrack[0].y"></circle>
</g>
<ul class="nav nav-tabs">
<li [ngClass]="{'active': active === group}" *ngFor="let group of cameraGroups">
<a [href]="'#'+group.id" data-toggle="tab">{{group.name}}</a>
</li>
<li class="pull-right"><a href="#" class="text-muted"><i class="fa fa-gear"></i></a></li>
</ul>

<g *ngIf="!isSelected(personTrack[0])">
<line [attr.stroke]="personTrack[0].colour" stroke-width="2"
*ngFor="let person of personTrack | slice : 1; let i = index" [attr.x1]="personTrack[i].x"
[attr.y1]="personTrack[i].y" [attr.x2]="personTrack[i+1].x"
[attr.y2]="personTrack[i+1].y"></line>
</g>
<g *ngIf="isSelected(personTrack[0])">
<line stroke="gold" stroke-width="2" *ngFor="let person of personTrack | slice : 1; let i = index"
[attr.x1]="personTrack[i].x" [attr.y1]="personTrack[i].y" [attr.x2]="personTrack[i+1].x"
[attr.y2]="personTrack[i+1].y"></line>
</g>

<line [attr.x1]="personTrack[0].x" [attr.y1]="personTrack[0].y"
[attr.x2]="personTrack[0].x + personTrack[0].headDirectionX * 10"
[attr.y2]="personTrack[0].y + personTrack[0].headDirectionY * 10"
[attr.stroke]="personTrack[0].standSitColour" stroke-width="2"
*ngIf="personTrack[0]['headDirectionX'] != null && personTrack[0]['headDirectionY'] != null"></line>

</g>
</g>
</svg>
</div>
</div>
<div class="tab-content">
<div class="tab-pane" [ngClass]="{'active': active === group}" [id]="group.id" *ngFor="let group of cameraGroups">
<div class="container-fluid">
<app-time-line [cameraGroup]="group"></app-time-line>
</div>
</div>
</div>

<div class="col-md-8 col-md-offset-2 col-sm-12">
<mat-card>
<mat-card-title>Track Details</mat-card-title>
<mat-card-content>
<app-realtime-info [useRealtimeEndpoint]="false" [showAll]="selectedTrackIndex==-1" [id]="selectedTrackIndex"
[reIDOnClick]="selectedTrackIndex!=-1" [from]="from" [to]="to"
[useTrackSegment]="useTrackSegments" [segmentIndex]="selectedSegmentIndex"
(personClicked)="personClicked($event)"></app-realtime-info>
</mat-card-content>
</mat-card>
</div>
</div>
Loading

0 comments on commit 1b8004b

Please sign in to comment.