org.opentripplanner.ext.vehicleparking.hslpark.HslParkToVehicleParkingMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.ext.vehicleparking.hslpark;
import com.bedatadriven.jackson.datatype.jts.parsers.GenericGeometryParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.locationtech.jts.geom.Geometry;
import org.opentripplanner.model.calendar.openinghours.OHCalendar;
import org.opentripplanner.model.calendar.openinghours.OpeningHoursCalendarService;
import org.opentripplanner.routing.vehicle_parking.VehicleParking;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingGroup;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingSpaces;
import org.opentripplanner.routing.vehicle_parking.VehicleParkingState;
import org.opentripplanner.transit.model.basic.I18NString;
import org.opentripplanner.transit.model.basic.NonLocalizedString;
import org.opentripplanner.transit.model.basic.TranslatedString;
import org.opentripplanner.transit.model.basic.WgsCoordinate;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.util.geometry.GeometryUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Maps a HSL Park facility into a {@link VehicleParking}.
*/
public class HslParkToVehicleParkingMapper {
private static final Logger log = LoggerFactory.getLogger(HslParkToVehicleParkingMapper.class);
private static final GenericGeometryParser GEOMETRY_PARSER = new GenericGeometryParser(
GeometryUtils.getGeometryFactory()
);
private final String feedId;
private final OpeningHoursCalendarService openingHoursCalendarService;
private final ZoneId zoneId;
public HslParkToVehicleParkingMapper(
String feedId,
OpeningHoursCalendarService openingHoursCalendarService,
ZoneId zoneId
) {
this.feedId = feedId;
this.openingHoursCalendarService = openingHoursCalendarService;
this.zoneId = zoneId;
}
public static FeedScopedId createIdForNode(JsonNode jsonNode, String idName, String feedId) {
String id = jsonNode.path(idName).asText();
return new FeedScopedId(feedId, id);
}
public static Integer parseIntegerValue(JsonNode jsonNode, String fieldName) {
if (!jsonNode.has(fieldName)) {
return null;
}
return jsonNode.get(fieldName).asInt();
}
public VehicleParking parsePark(
JsonNode jsonNode,
Map hubForPark
) {
var vehicleParkId = createIdForNode(jsonNode, "id", feedId);
try {
var capacity = parseVehicleSpaces(
jsonNode.path("builtCapacity"),
"BICYCLE",
"CAR",
"DISABLED"
);
Map translations = new HashMap<>();
JsonNode nameNode = jsonNode.path("name");
nameNode
.fieldNames()
.forEachRemaining(lang -> {
String name = nameNode.path(lang).asText();
if (!name.equals("")) {
translations.put(lang, nameNode.path(lang).asText());
}
});
I18NString name = translations.isEmpty()
? new NonLocalizedString(vehicleParkId.getId())
: TranslatedString.getI18NString(translations, true, false);
Geometry geometry = GEOMETRY_PARSER.geometryFromJson(jsonNode.path("location"));
var stateText = jsonNode.path("status").asText();
var state = stateMapper(stateText);
var tags = parseTags(jsonNode);
var maybeCapacity = Optional.ofNullable(capacity);
var bicyclePlaces = maybeCapacity
.map(c -> hasPlaces(capacity.getBicycleSpaces()))
.orElse(false);
var carPlaces = maybeCapacity.map(c -> hasPlaces(capacity.getCarSpaces())).orElse(false);
var wheelChairAccessiblePlaces = maybeCapacity
.map(c -> hasPlaces(capacity.getWheelchairAccessibleCarSpaces()))
.orElse(false);
var openingHoursByDayType = jsonNode.path("openingHours").path("byDayType");
var openingHoursCalendar = parseOpeningHours(openingHoursByDayType);
VehicleParkingGroup vehicleParkingGroup = hubForPark.get(vehicleParkId);
return VehicleParking
.builder()
.id(vehicleParkId)
.name(name)
.state(state)
.coordinate(new WgsCoordinate(geometry.getCentroid()))
.capacity(capacity)
.bicyclePlaces(bicyclePlaces)
.carPlaces(carPlaces)
.wheelchairAccessibleCarPlaces(wheelChairAccessiblePlaces)
.tags(tags)
.openingHoursCalendar(openingHoursCalendar)
.entrance(builder ->
builder
.entranceId(new FeedScopedId(feedId, vehicleParkId.getId() + "/entrance"))
.name(name)
.coordinate(new WgsCoordinate(geometry.getCentroid()))
.walkAccessible(true)
.carAccessible(carPlaces || wheelChairAccessiblePlaces)
)
.vehicleParkingGroup(vehicleParkingGroup)
.build();
} catch (Exception e) {
log.warn("Error parsing park {}", vehicleParkId, e);
return null;
}
}
private VehicleParkingSpaces parseVehicleSpaces(
JsonNode node,
String bicycleTag,
String carTag,
String wheelchairAccessibleCarTag
) {
var bicycleSpaces = parseIntegerValue(node, bicycleTag);
var carSpaces = parseIntegerValue(node, carTag);
var wheelchairAccessibleCarSpaces = parseIntegerValue(node, wheelchairAccessibleCarTag);
if (bicycleSpaces == null && carSpaces == null && wheelchairAccessibleCarSpaces == null) {
return null;
}
return createVehiclePlaces(carSpaces, wheelchairAccessibleCarSpaces, bicycleSpaces);
}
private VehicleParkingSpaces createVehiclePlaces(
Integer carSpaces,
Integer wheelchairAccessibleCarSpaces,
Integer bicycleSpaces
) {
return VehicleParkingSpaces
.builder()
.bicycleSpaces(bicycleSpaces)
.carSpaces(carSpaces)
.wheelchairAccessibleCarSpaces(wheelchairAccessibleCarSpaces)
.build();
}
private VehicleParkingState stateMapper(String stateText) {
if (stateText == null) {
return VehicleParkingState.OPERATIONAL;
}
switch (stateText) {
case "INACTIVE":
return VehicleParkingState.CLOSED;
case "TEMPORARILY_CLOSED":
return VehicleParkingState.TEMPORARILY_CLOSED;
case "IN_OPERATION":
case "EXCEPTIONAL_SITUATION":
default:
return VehicleParkingState.OPERATIONAL;
}
}
private boolean hasPlaces(Integer spaces) {
return spaces != null && spaces > 0;
}
private List parseTags(JsonNode node) {
var tagList = new ArrayList();
ArrayNode servicesArray = (ArrayNode) node.get("services");
if (servicesArray != null && servicesArray.isArray()) {
for (JsonNode jsonNode : servicesArray) {
tagList.add(feedId + ":SERVICE_" + jsonNode.asText());
}
}
ArrayNode authenticationMethods = (ArrayNode) node.get("authenticationMethods");
if (authenticationMethods != null && authenticationMethods.isArray()) {
for (JsonNode jsonNode : authenticationMethods) {
tagList.add(feedId + ":AUTHENTICATION_METHOD_" + jsonNode.asText());
}
}
if (node.has("pricingMethod")) {
tagList.add(feedId + ":PRICING_METHOD_" + node.path("pricingMethod").asText());
}
return tagList;
}
private record DayTypeAndDays(String typeKey, String name, List days) {}
private static final List DAYS_FOR_DAY_TYPES = List.of(
new DayTypeAndDays(
"BUSINESS_DAY",
"Business days",
List.of(
DayOfWeek.MONDAY,
DayOfWeek.TUESDAY,
DayOfWeek.WEDNESDAY,
DayOfWeek.THURSDAY,
DayOfWeek.FRIDAY
)
),
new DayTypeAndDays("SATURDAY", "Saturday", List.of(DayOfWeek.SATURDAY)),
new DayTypeAndDays("SUNDAY", "Sunday", List.of(DayOfWeek.SUNDAY))
);
private OHCalendar parseOpeningHours(JsonNode openingHoursByDayType) {
if (zoneId == null) {
return null;
}
var calendarBuilder = openingHoursCalendarService.newBuilder(zoneId);
for (DayTypeAndDays dayTypeAndDays : DAYS_FOR_DAY_TYPES) {
String key = dayTypeAndDays.typeKey();
if (openingHoursByDayType.has(key) && openingHoursByDayType.path(key).has("from")) {
LocalTime fromTime = convertTimeStringLocalTime(
openingHoursByDayType.path(key).path("from").asText()
);
LocalTime toTime = convertTimeStringLocalTime(
openingHoursByDayType.path(key).path("until").asText()
);
var openingHoursBuilder = calendarBuilder.openingHours(
dayTypeAndDays.name(),
fromTime,
toTime
);
for (DayOfWeek day : dayTypeAndDays.days()) {
openingHoursBuilder.on(day);
}
openingHoursBuilder.add();
}
}
return calendarBuilder.build();
}
/**
* Parses a string with format "05" or "05:30" to a {@link LocalTime}.
* If a park is open until 24h, the end time will be 24 but it should be
* adjusted to be 23:59 for opening hours.
*/
private LocalTime convertTimeStringLocalTime(String timeString) {
int hours = Integer.parseInt(timeString.substring(0, 2));
int minutes = timeString.length() > 2 ? Integer.parseInt(timeString.substring(3, 5)) : 0;
return hours == 24 ? LocalTime.MAX : LocalTime.of(hours, minutes);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy