Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.transitclock.db.structs.AvlReport.AssignmentType;

import com.amazonaws.services.sqs.model.Message;
import org.transitclock.db.structs.OccupancyStatus;

/**
* Implementation of SqsMessageUnmarshaller for WMATA data.
Expand Down Expand Up @@ -95,6 +96,13 @@ private AvlReportWrapper toAvlReport(JSONObject jsonObj) {
speed = (float) msgObj.getDouble("averageSpeed") * 0.3048f; // convert to m/s
}

OccupancyStatus occupancyStatus = null;
if (msgObj.has("crowdingStatus")) {
String occupancyString = null;
occupancyString = msgObj.getString("crowdingStatus");
occupancyStatus = OccupancyStatus.lenientParse(occupancyString);
}

Long forwarderTimeReceived = null;
if (msgObj.has("received")) {
forwarderTimeReceived = msgObj.getLong("received");
Expand Down Expand Up @@ -126,6 +134,9 @@ private AvlReportWrapper toAvlReport(JSONObject jsonObj) {
String source = "sqs";
if (vehicleId != null && lat != null && lon != null && time != null) {
AvlReport ar = new AvlReport(vehicleId, time, lat, lon, speed, heading, source);
if (occupancyStatus != null) {
ar.setOccupancyStatus(occupancyStatus);
}
if (msgObj.has("blockAlpha")) {
String blockAlpha = msgObj.getString("blockAlpha");
if (blockAlpha != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public class AvlReport implements Serializable {
// Can be block, trip, or route ID
@Column(length=HibernateUtils.DEFAULT_ID_SIZE)
private String assignmentId; // optional

// The type of the assignment received in the AVL feed
public enum AssignmentType {
UNSET,
Expand Down Expand Up @@ -160,7 +160,13 @@ public enum AssignmentType {
// This parameter is optional. Set to null if data not available.
@Column(length=HibernateUtils.DEFAULT_ID_SIZE)
private final Float passengerFullness;


// Optional. Mirrors GTFS-RT occupancy status so it can be passed through
// we choose ordinal sacrificing readability for size!
@Column
@Enumerated(EnumType.ORDINAL)
private OccupancyStatus occupancyStatus;

// Optional. For containing additional info for a particular feed.
// Not declared final because setField1() is used to set values.
@Column(length=HibernateUtils.DEFAULT_ID_SIZE)
Expand All @@ -179,7 +185,7 @@ public enum AssignmentType {
LoggerFactory.getLogger(AvlReport.class);

// Needed because serializable so can transmit using JMS or RMI
private static final long serialVersionUID = 92384928349823L;
private static final long serialVersionUID = 92384928349824L;

/********************** Member Functions **************************/

Expand Down Expand Up @@ -795,6 +801,24 @@ public String getSource() {
public void setSource(String source) {
this.source=sized(source);
}

/**
* GTFS-RT inspired levels of crowding information.
* @return enumeration
*/
public OccupancyStatus getOccupancyStatus() {
return occupancyStatus;
}

/**
* GTFS-RT inspired levels of crowding information.
*/
public void setOccupancyStatus(OccupancyStatus occupancyStatus) {
this.occupancyStatus = occupancyStatus;
}



/**
* Returns how many msec elapsed between the GPS fix was generated
* to the time it was finally processed. Returns 0 if timeProcessed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package org.transitclock.db.structs;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;

/**
* this mirrors GTFS-RT's occupancyStatus (2016 version):
* https://developers.google.com/transit/gtfs-realtime/reference/OccupancyStatus-vp
*
* Constants proposed here:
* https://github.com/OneBusAway/onebusaway-application-modules/issues/121
*
* Borrowed from https://github.com/OneBusAway/onebusaway-application-modules/onebusaway-realtime-api
*/
public enum OccupancyStatus implements Serializable {

/**
* proposed addition
*/
UNKNOWN(-1),
/**
* The vehicle is considered empty by most measures, has few or no passengers
* onboard, and is accepting passengers.
*/
EMPTY(0),
/**
* The vehicle has a large percentage of seats available. What percentage of
* free seats out of the total seats available is large enough to fall into
* this category is determined by the producer.
*/
MANY_SEATS_AVAILABLE(1),
/**
* The vehicle has a small percentage of seats available. What percentage
* of free seats out of the total seats available is small enough to fall
* into this category is determined by the producer.
*/
FEW_SEATS_AVAILABLE(2),
/**
* The vehicle can accommodate only standing passengers.
*/
STANDING_ROOM_ONLY(3),
/**
* The vehicle can accommodate only standing passengers but has limited
* space for them.
*/
CRUSHED_STANDING_ROOM_ONLY(4),
/**
* The vehicle is considered full by most measures but may still be allowing
* passengers to board.
*/
FULL(5),
/**
* The vehicle is not accepting passengers.
*/
NOT_ACCEPTING_PASSENGERS(6);

private static Logger _log = LoggerFactory.getLogger(OccupancyStatus.class);
private int _status;

OccupancyStatus() {
_status = -1;
}

OccupancyStatus(int status) {
_status = status;
}

public static OccupancyStatus lenientParse(String occupancyString) {
OccupancyStatus status = null;
try {
status = OccupancyStatus.valueOf(occupancyString);
} catch (IllegalArgumentException iae) {
return mapFromExternalSystems(occupancyString);
}
return status;
}

private static OccupancyStatus mapFromExternalSystems(String occupancyString) {
if ("MANY SEATS".equals(occupancyString))
return OccupancyStatus.MANY_SEATS_AVAILABLE;
if ("FEW SEATS".equals(occupancyString))
return OccupancyStatus.FEW_SEATS_AVAILABLE;
throw new IllegalArgumentException(occupancyString + "not expected");
}

public int valueOf() {
return _status;
}

public static boolean contains(String status) {
for (OccupancyStatus OccupancyStatus : values()) {
if (OccupancyStatus.name().equalsIgnoreCase(status)) {
return true;
}
}
return false;
}

public String toString() {
return toCamelCase(this.name());
}

public static OccupancyStatus toEnum(int status) {
if (status == UNKNOWN.valueOf() || status < 0)
return UNKNOWN;
if (status == EMPTY.valueOf())
return EMPTY;
if (status == MANY_SEATS_AVAILABLE.valueOf())
return MANY_SEATS_AVAILABLE;
if (status == FEW_SEATS_AVAILABLE.valueOf())
return FEW_SEATS_AVAILABLE;
if (status == STANDING_ROOM_ONLY.valueOf())
return STANDING_ROOM_ONLY;
if (status == CRUSHED_STANDING_ROOM_ONLY.valueOf())
return CRUSHED_STANDING_ROOM_ONLY;
if (status == FULL.valueOf())
return FULL;
if (status == NOT_ACCEPTING_PASSENGERS.valueOf()) {
_log.warn("Occupancy Status set to NotAcceptingPassengers");
return NOT_ACCEPTING_PASSENGERS;
}
throw new IllegalArgumentException("unexpected value " + status);
}

public static OccupancyStatus toEnum(double rid) {
int status;
if (rid < 0.0) {
status = -1;
} else if (rid == 0.0) {
status = 0;
} else if (rid <= 25.0) {
status = 1;
} else if (rid <= 50.0) {
status = 2;
} else if (rid <= 75.0) {
status = 3;
} else if (rid <= 90.0) {
status = 4;
} else if (rid <= 100.0) {
status = 5;
} else {
status = 6;
}
return OccupancyStatus.toEnum(status);
}

private String toCamelCase(String upperCase) {
if (upperCase == null || upperCase.length() == 0) return upperCase;
String[] parts = upperCase.split("_");
StringBuffer camelCase = new StringBuffer();
for (String part : parts) {
camelCase.append(part.substring(0, 1).toUpperCase());
camelCase.append(part.substring(1).toLowerCase());
}
String result = camelCase.substring(0, 1).toLowerCase() + camelCase.substring(1);
return result;
}
}
29 changes: 23 additions & 6 deletions transitclock/src/main/java/org/transitclock/ipc/data/IpcAvl.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import org.transitclock.db.structs.AvlReport;
import org.transitclock.db.structs.AvlReport.AssignmentType;
import org.transitclock.db.structs.OccupancyStatus;
import org.transitclock.utils.Geo;
import org.transitclock.utils.Time;

Expand All @@ -47,8 +48,10 @@ public class IpcAvl implements Serializable {
private final String driverId;
private final String licensePlate;
private final int passengerCount;
private final IpcOccupancyStatus occupancyStatus;

private static final long serialVersionUID = 2506303490106709587L;

private static final long serialVersionUID = 2506303490106709586L;

/********************** Member Functions **************************/

Expand All @@ -69,7 +72,7 @@ public class IpcAvl implements Serializable {
public IpcAvl(String vehicleId, long time, float latitude, float longitude,
float speed, float heading, String source, String assignmentId,
AssignmentType assignmentType, String driverId,
String licensePlate, int passengerCount) {
String licensePlate, int passengerCount, IpcOccupancyStatus occupancyStatus) {
this.vehicleId = vehicleId;
this.time = time;
this.latitude = latitude;
Expand All @@ -82,10 +85,11 @@ public IpcAvl(String vehicleId, long time, float latitude, float longitude,
this.driverId = driverId;
this.licensePlate = licensePlate;
this.passengerCount = passengerCount;
this.occupancyStatus = occupancyStatus;
}

/**
* @param lastAvlReport
* copy from AvlReport
*/
public IpcAvl(AvlReport a) {
this.vehicleId = a.getVehicleId();
Expand All @@ -100,6 +104,12 @@ public IpcAvl(AvlReport a) {
this.driverId = a.getDriverId();
this.licensePlate = a.getLicensePlate();
this.passengerCount = a.getPassengerCount();
this.occupancyStatus = toIpcOccupancyStatus(a.getOccupancyStatus());
}

private IpcOccupancyStatus toIpcOccupancyStatus(OccupancyStatus occupancyStatus) {
if (occupancyStatus == null) return null;
return IpcOccupancyStatus.toEnum(occupancyStatus.valueOf());
}

/*
Expand All @@ -120,7 +130,8 @@ private static class SerializationProxy implements Serializable {
private String driverId;
private String licensePlate;
private int passengerCount;

private IpcOccupancyStatus occupancyStatus;

private static final long serialVersionUID = 6220698347690060245L;
private static final short serializationVersion = 0;

Expand All @@ -140,6 +151,7 @@ private SerializationProxy(IpcAvl avl) {
this.driverId = avl.driverId;
this.licensePlate = avl.licensePlate;
this.passengerCount = avl.passengerCount;
this.occupancyStatus = avl.occupancyStatus;
}

/*
Expand All @@ -163,6 +175,7 @@ private void writeObject(java.io.ObjectOutputStream stream)
stream.writeObject(driverId);
stream.writeObject(licensePlate);
stream.writeInt(passengerCount);
stream.writeObject(occupancyStatus);
}

/*
Expand All @@ -175,7 +188,7 @@ private void writeObject(java.io.ObjectOutputStream stream)
private Object readResolve() {
return new IpcAvl(vehicleId, time, latitude, longitude, speed,
heading, source, assignmentId, assignmentType, driverId,
licensePlate, passengerCount);
licensePlate, passengerCount, occupancyStatus);
}

/*
Expand Down Expand Up @@ -203,6 +216,7 @@ private void readObject(java.io.ObjectInputStream stream)
driverId = (String) stream.readObject();
licensePlate = (String) stream.readObject();
passengerCount = stream.readInt();
occupancyStatus = (IpcOccupancyStatus) stream.readObject();
}
}

Expand Down Expand Up @@ -277,6 +291,8 @@ public int getPassengerCount() {
return passengerCount;
}

public IpcOccupancyStatus getOccupancyStatus() { return occupancyStatus; }

@Override
public String toString() {
return "IpcAvl [vehicleId=" + vehicleId
Expand All @@ -290,7 +306,8 @@ public String toString() {
+ ", assignmentType=" + assignmentType
+ ", driverId=" + driverId
+ ", licensePlate=" + licensePlate
+ ", passengerCount=" + passengerCount
+ ", passengerCount=" + passengerCount
+ ", occupancyStatus=" + occupancyStatus
+ "]";
}

Expand Down
Loading