/*
* Copyright 2011
*
* This file is part of Mobile Shuttle Tracker.
*
* Mobile Shuttle Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Mobile Shuttle Tracker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Mobile Shuttle Tracker. If not, see <http://www.gnu.org/licenses/>.
*/
package com.abstractedsheep.world;
import java.util.ArrayList;
import java.util.HashMap;
/**
* The purpose of this class is to hold information about a shuttle from
* the current.js file being read in by JSONExtractor. Each shuttle contains
* information about its current location, when that shuttle was last updated,
* its name, current heading, the route it is on and the stops it will visit.
* This information is then used to determine how long it will take the shuttle
* to reach each stop on its list, while accommodating enough time for when the shuttle
* might wait at each stop.
*
* @author saiumesh
* @author wagnea
*/
public class Shuttle implements IRouteFinder {
private int shuttleId;
private HashMap<String, Stop> stops;
private ArrayList<Integer> speedList;
private String cardinalPoint;
private String shuttleName;
private int speed;
private Coordinate currentLocation;
private long lastUpdateTime;
private Coordinate SnappedCoordinate;
private int NextRouteCoordinate;
private Route currentRoute;
// Jackson requires a constructor with no parameters to be available
// Also notice 'this.' preceding the variables, this makes it clear that the
// variable
// is a global variable and although it is not necessary to use 'this.' if
// you do not
// have a local variable by the same name, it is still a good idea to
// include it
public Shuttle(ArrayList<Route> rt) {
this.shuttleId = -1;
this.stops = new HashMap<String, Stop>();
new HashMap<String, ArrayList<Integer>>();
this.shuttleName = "Bus 42";
this.cardinalPoint = "North";
this.speed = 0;
this.currentLocation = new Coordinate();
this.speedList = new ArrayList<Integer>();
this.lastUpdateTime = System.currentTimeMillis();
this.currentRoute = new Route();
}
// This constructor is not required by Jackson, but it makes manually
// creating a new point a
// one line operation.
public Shuttle(int shuttleId, ArrayList<Route> rt) {
// Here, 'this.' is necessary because we have a local variable named the
// same as the global
this.shuttleId = shuttleId;
this.stops = new HashMap<String, Stop>();
new HashMap<String, ArrayList<Integer>>();
this.speedList = new ArrayList<Integer>();
this.shuttleName = "Bus 42";
this.cardinalPoint = "North";
this.speed = 0;
this.currentLocation = new Coordinate();
this.lastUpdateTime = System.currentTimeMillis();
this.currentRoute = new Route();
}
/**
* updates the current state of the shuttle object.
*
* @param newShuttle
*/
public void updateShuttle(Shuttle newShuttle) {
this.setCurrentLocation(newShuttle.getCurrentLocation(), System.currentTimeMillis());
if (newShuttle.getDistanceToClosestPoint() < this.getDistanceToClosestPoint()) {
this.setCurrentRoute(newShuttle.getCurrentRoute());
this.snapToRoute(newShuttle.getCurrentRoute());
}
}
// Jackson will not work unless all of the variables have accessors and
// mutators
// Since these usually only have one line of code in them, put the entire
// method
// on a single line to increase readability
public int getShuttleId() {
return this.shuttleId;
}
public void setShuttleId(int shuttleId) {
this.shuttleId = shuttleId;
}
public int getRouteId() {
return this.currentRoute.getIdNum();
}
public HashMap<String, Stop> getStops() {
return stops;
}
/**
* @return average speed of shuttle
*/
public int getSpeed() {
return speed;
}
/**
* given a new speed value, calculate the average speed by this shuttle
* and set that value as the speed of the shuttle.
*
* @param newSpd - new instantaneous speed value.
*/
public void setSpeed(int newSpd) {
if (speedList.size() > 10)
speedList.remove(0);
speedList.add((newSpd < 10) ? 10 : newSpd);
int count = 0;
for (int s : speedList) {
count += s;
}
this.speed = count / speedList.size();
}
public Coordinate getCurrentLocation() {
return currentLocation;
}
/**
* Change the current state of the shuttle's location
* and change value of the most recent update time to time.
*
* @param newLocation - new shuttle location
* @param time - most recent update time
*/
public void setCurrentLocation(Coordinate newLocation, long time) {
if (!this.currentLocation.equals(newLocation))
this.lastUpdateTime = time;
this.currentLocation = newLocation;
}
public void setCurrentLocation(Coordinate newLoc) {
setCurrentLocation(newLoc, System.currentTimeMillis());
}
public String getCardinalPoint() {
return cardinalPoint;
}
public void setCardinalPoint(String cardinalPoint) {
this.cardinalPoint = cardinalPoint;
}
public String getName() {
return shuttleName;
}
public void setName(String newName) {
this.shuttleName = newName;
}
public Coordinate getClosestPoint() {
return this.SnappedCoordinate;
}
public double getDistanceToClosestPoint() {
return this.SnappedCoordinate.distanceFromCoordiante(currentLocation);
}
/**
* @return the nextRouteCoordinate
*/
public int getNextRouteCoordinate() {
return NextRouteCoordinate;
}
public void setCurrentRoute(Route rt) {
this.currentRoute = rt;
this.snapToRoute(rt);
}
public Route getCurrentRoute() {
return this.currentRoute;
}
/**
* @return - the age of this shuttle.
*/
public long getAge() {
return System.currentTimeMillis() - this.lastUpdateTime;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Shuttle))
return false;
Shuttle s = (Shuttle) obj;
return this.shuttleId == s.getShuttleId();
}
@Override
public void snapToRoute(Route r) {
if (this.currentRoute != null && this.currentLocation != null) {
Coordinate c1, c2;
Coordinate closestPoint = null, tempClosestPoint;
int nextPointId = -1;
double shortestDistance = 10000, tempShortestDistance = 10000;
int size = this.currentRoute.getCoordinateList().size();
for (int i = 0; i < size; i++) {
if (i == 0)
c1 = this.currentRoute.getCoordinateList().get(size - 1);
else
c1 = this.currentRoute.getCoordinateList().get(i - 1);
c2 = this.currentRoute.getCoordinateList().get(i);
tempClosestPoint = this.currentLocation.closestPoint(c1, c2);
tempShortestDistance = tempClosestPoint.distanceFromCoordiante(this.currentLocation);
if (tempShortestDistance < shortestDistance) {
shortestDistance = tempShortestDistance;
closestPoint = tempClosestPoint;
nextPointId = (size >= (i + 1)) ? 1 : i + 1;
}
}
this.SnappedCoordinate = closestPoint;
this.NextRouteCoordinate = nextPointId;
}
}
public long getLastUpdateTime() {
return this.lastUpdateTime;
}
}