diff --git a/mobi.chouette.exchange/src/main/java/mobi/chouette/exchange/CompassBearingGenerator.java b/mobi.chouette.exchange/src/main/java/mobi/chouette/exchange/CompassBearingGenerator.java new file mode 100644 index 0000000000..2e69e99724 --- /dev/null +++ b/mobi.chouette.exchange/src/main/java/mobi/chouette/exchange/CompassBearingGenerator.java @@ -0,0 +1,160 @@ +package mobi.chouette.exchange; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +import lombok.extern.log4j.Log4j; +import mobi.chouette.model.JourneyPattern; +import mobi.chouette.model.Route; +import mobi.chouette.model.StopArea; +import mobi.chouette.model.StopPoint; +import mobi.chouette.model.type.ChouetteAreaEnum; +import mobi.chouette.model.util.Referential; + +@Log4j +public class CompassBearingGenerator { + private static final int MAX_DIFF_BEARING_DEGREES = 60; + + public void cacluateCompassBearings(Referential referential) { + + for (StopArea boardingPosition : referential.getSharedStopAreas().values()) { + + if (boardingPosition.getAreaType() == ChouetteAreaEnum.BoardingPosition + && boardingPosition.getCompassBearing() == null) { + Set compassBearings = findCompassBearingForBoardingPosition(boardingPosition); + + if (compassBearings.size() > 1) { + // See if we can merge some nearly identical + Integer min = Collections.min(compassBearings); + Integer max = Collections.max(compassBearings); + int angle = getAngle(min, max); + if (Math.abs(angle) < MAX_DIFF_BEARING_DEGREES) { + compassBearings.clear(); + compassBearings.add((max + (angle / 2)) % 360); + log.info("Average compass bearing is " + compassBearings.iterator().next() + + " for BoardingPosition " + boardingPosition.getObjectId() + " and name " + + boardingPosition.getName()); + + } + } + + if (compassBearings.size() == 1) { + boardingPosition.setCompassBearing(compassBearings.iterator().next()); + } else if (compassBearings.size() > 1) { + log.warn( + "Found at least 2 conflicting compass bearings " + + ToStringBuilder.reflectionToString(compassBearings.toArray(), + ToStringStyle.SIMPLE_STYLE) + + " for BoardingPosition " + boardingPosition.getObjectId() + " and name " + + boardingPosition.getName()); + } else { + } + + } + + } + + } + + protected Set findCompassBearingForBoardingPosition(StopArea sa) { + Set compassBearings = new TreeSet(); + List stopPoints = sa.getContainedStopPoints(); + for (StopPoint stop : stopPoints) { + Route route = stop.getRoute(); + List journeyPatterns = route.getJourneyPatterns(); + for (JourneyPattern jp : journeyPatterns) { + StopPoint previous = null; + StopPoint next = null; + + List stopPointsInJourneyPattern = jp.getStopPoints(); + + if (jp.getDepartureStopPoint().getContainedInStopArea().getObjectId() + .equals(stop.getContainedInStopArea().getObjectId())) { + next = stopPointsInJourneyPattern.get(1); + } else if (jp.getArrivalStopPoint().getContainedInStopArea().getObjectId() + .equals(stop.getContainedInStopArea().getObjectId())) { + previous = stopPointsInJourneyPattern.get(stopPointsInJourneyPattern.size() - 2); + } else { + // In the middle somewhere + for (int i = 0; i < stopPointsInJourneyPattern.size(); i++) { + if (stop.getContainedInStopArea().getObjectId() + .equals(stopPointsInJourneyPattern.get(i).getContainedInStopArea().getObjectId())) { + previous = stopPointsInJourneyPattern.get(i - 1); + next = stopPointsInJourneyPattern.get(i + 1); + break; + } + } + } + + Integer bearing = null; + // Calculate general direction previous -> stop -> next + if (previous != null && next != null) { + // Use previous and next + bearing = bearing(previous, next); + } else if (previous != null) { + bearing = bearing(previous, stop); + } else { + bearing = bearing(stop, next); + } + + if(bearing != null) { + compassBearings.add(bearing); + } + + } + + } + + return compassBearings; + + } + + private Integer bearing(StopPoint from, StopPoint to) { + + StopArea fromArea = from.getContainedInStopArea(); + StopArea toArea = to.getContainedInStopArea(); + + if(fromArea != null && toArea != null && hasCoordinates(fromArea) && hasCoordinates(toArea)) { + + double longitude1 = fromArea.getLongitude().doubleValue(); + double longitude2 = toArea.getLongitude().doubleValue(); + double latitude1 = Math.toRadians(fromArea.getLatitude().doubleValue()); + double latitude2 = Math.toRadians(toArea.getLatitude().doubleValue()); + double longDiff = Math.toRadians(longitude2 - longitude1); + double y = Math.sin(longDiff) * Math.cos(latitude2); + double x = Math.cos(latitude1) * Math.sin(latitude2) + - Math.sin(latitude1) * Math.cos(latitude2) * Math.cos(longDiff); + + double bearing = (Math.toDegrees(Math.atan2(y, x)) + 360) % 360; + + // 1 to 360 degrees, not 0 to 359 + + return new Integer((int) bearing + 1); + } else { + if(fromArea == null) { + log.warn("StopPoint "+from.getObjectId()+" in route "+from.getRoute().getObjectId()+" and line "+from.getRoute().getLine().getObjectId()+"/" +from.getRoute().getLine().getName()+" has no StopArea"); + } + if(toArea == null) { + log.warn("StopPoint "+to.getObjectId()+" in route "+to.getRoute().getObjectId()+" and line "+to.getRoute().getLine().getObjectId()+"/" +to.getRoute().getLine().getName()+" has no StopArea"); + } + + return null; + } + + } + + private boolean hasCoordinates(StopArea stopArea) { + return stopArea.getLatitude() != null && stopArea.getLongitude() != null; + } + + private int getAngle(Integer bearing, Integer heading) { + return ((((bearing - heading) % 360) + 540) % 360) - 180; + + } + +} diff --git a/mobi.chouette.exchange/src/main/java/mobi/chouette/exchange/importer/updater/StopAreaUpdater.java b/mobi.chouette.exchange/src/main/java/mobi/chouette/exchange/importer/updater/StopAreaUpdater.java index af908a5060..b4ec41e881 100644 --- a/mobi.chouette.exchange/src/main/java/mobi/chouette/exchange/importer/updater/StopAreaUpdater.java +++ b/mobi.chouette.exchange/src/main/java/mobi/chouette/exchange/importer/updater/StopAreaUpdater.java @@ -105,6 +105,7 @@ public void update(Context context, StopArea oldValue, StopArea newValue) throws oldValue.setZipCode(newValue.getZipCode()); oldValue.setCityName(newValue.getCityName()); oldValue.setStreetName(newValue.getStreetName()); + oldValue.setCompassBearing(newValue.getCompassBearing()); oldValue.setDetached(false); } else { twoDatabaseStopAreaTwoTest(validationReporter, context, oldValue, newValue, data); @@ -183,6 +184,9 @@ public void update(Context context, StopArea oldValue, StopArea newValue) throws if (newValue.getStreetName() != null && !newValue.getStreetName().equals(oldValue.getStreetName())) { oldValue.setStreetName(newValue.getStreetName()); } + if (newValue.getCompassBearing() != null && !newValue.getCompassBearing().equals(oldValue.getCompassBearing())) { + oldValue.setCompassBearing(newValue.getCompassBearing()); + } } // StopArea Parent diff --git a/mobi.chouette.model/src/main/java/mobi/chouette/model/StopArea.java b/mobi.chouette.model/src/main/java/mobi/chouette/model/StopArea.java index 5473a4dbfa..782733c387 100644 --- a/mobi.chouette.model/src/main/java/mobi/chouette/model/StopArea.java +++ b/mobi.chouette.model/src/main/java/mobi/chouette/model/StopArea.java @@ -278,6 +278,20 @@ public void setTimeZone(String value) { @Column(name = "stairs_availability") private Boolean stairsAvailable = false; + /** + * general compass bearing of traffic to stoparea + * + * + * + * @param compassBearing in degrees from 1 to 360 + * + + */ + @Getter + @Setter + @Column(name = "compass_bearing") + private Integer compassBearing; + /** * coded user needs as binary map
* diff --git a/src/main/sql/chouette.sql b/src/main/sql/chouette.sql index 38321e4824..1907955a13 100644 --- a/src/main/sql/chouette.sql +++ b/src/main/sql/chouette.sql @@ -885,7 +885,8 @@ CREATE TABLE stop_areas ( zip_code character varying(255), city_name character varying(255), url character varying(255), - time_zone character varying(255) + time_zone character varying(255), + compass_bearing integer ); diff --git a/src/test/sql/chouette_test.sql b/src/test/sql/chouette_test.sql index bd79b4f3be..d555e1927f 100644 --- a/src/test/sql/chouette_test.sql +++ b/src/test/sql/chouette_test.sql @@ -885,7 +885,8 @@ CREATE TABLE stop_areas ( zip_code character varying(255), city_name character varying(255), url character varying(255), - time_zone character varying(255) + time_zone character varying(255), + compass_bearing integer );