All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.ext.vehicleparking.hslpark.HslParkUpdater Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.ext.vehicleparking.hslpark;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.opentripplanner.framework.io.JsonDataListDownloader;
import org.opentripplanner.framework.io.OtpHttpClientFactory;
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.VehicleParkingSpaces.VehicleParkingSpacesBuilder;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.updater.spi.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Vehicle parking updater class for https://github.com/HSLdevcom/parkandrideAPI format APIs. There
 * has been further development in a private repository (the current state is documented in
 * https://p.hsl.fi/docs/index.html) but this updater supports both formats.
 */
public class HslParkUpdater implements DataSource {

  private static final Logger LOG = LoggerFactory.getLogger(HslParkUpdater.class);

  private static final String JSON_PARSE_PATH = "results";

  private final HslFacilitiesDownloader facilitiesDownloader;
  private final int facilitiesFrequencySec;
  private final HslHubsDownloader hubsDownloader;
  private final JsonDataListDownloader utilizationsDownloader;
  private final HslParkToVehicleParkingMapper vehicleParkingMapper;
  private final HslHubToVehicleParkingGroupMapper vehicleParkingGroupMapper;
  private final HslParkUtilizationToPatchMapper parkPatchMapper;

  private long lastFacilitiesFetchTime;

  private List parks;
  private Map hubForPark;

  public HslParkUpdater(
    HslParkUpdaterParameters parameters,
    OpeningHoursCalendarService openingHoursCalendarService
  ) {
    String feedId = parameters.feedId();
    vehicleParkingMapper =
      new HslParkToVehicleParkingMapper(feedId, openingHoursCalendarService, parameters.timeZone());
    vehicleParkingGroupMapper = new HslHubToVehicleParkingGroupMapper(feedId);
    parkPatchMapper = new HslParkUtilizationToPatchMapper(feedId);
    var otpHttpClientFactory = new OtpHttpClientFactory();
    facilitiesDownloader =
      new HslFacilitiesDownloader(
        parameters.facilitiesUrl(),
        JSON_PARSE_PATH,
        vehicleParkingMapper::parsePark,
        otpHttpClientFactory
      );
    hubsDownloader =
      new HslHubsDownloader(
        parameters.hubsUrl(),
        JSON_PARSE_PATH,
        vehicleParkingGroupMapper::parseHub,
        otpHttpClientFactory
      );
    utilizationsDownloader =
      new JsonDataListDownloader<>(
        parameters.utilizationsUrl(),
        "",
        parkPatchMapper::parseUtilization,
        Map.of(),
        otpHttpClientFactory.create(LOG)
      );
    this.facilitiesFrequencySec = parameters.facilitiesFrequencySec();
  }

  /**
   * Update the data from the sources. It first fetches parks from the facilities URL and park
   * groups from hubs URL and then real-time updates from utilizations URL. If facilitiesFrequencySec
   * is configured to be over 0, it also occasionally retches the parks as new parks might have been
   * added or the state of the old parks might have changed.
   *
   * @return true if there might have been changes
   */
  @Override
  public boolean update() {
    List parks = null;
    Map hubForPark;
    if (fetchFacilitiesAndHubsNow()) {
      hubForPark = hubsDownloader.downloadHubs();
      if (hubForPark != null) {
        parks = facilitiesDownloader.downloadFacilities(hubForPark);
        if (parks != null) {
          lastFacilitiesFetchTime = System.currentTimeMillis();
        }
      }
    } else {
      parks = this.parks;
      hubForPark = this.hubForPark;
    }
    if (parks != null) {
      List utilizations = utilizationsDownloader.download();
      if (utilizations != null) {
        Map> patches = utilizations
          .stream()
          .collect(Collectors.groupingBy(utilization -> utilization.getId()));
        parks.forEach(park -> {
          List patchesForPark = patches.get(park.getId());
          if (patchesForPark != null) {
            park.updateAvailability(createVehicleAvailability(patchesForPark));
          }
        });
      } else if (this.parks != null) {
        return false;
      }
      synchronized (this) {
        // Update atomically
        this.parks = parks;
        this.hubForPark = hubForPark;
      }
      return true;
    }
    return false;
  }

  @Override
  public synchronized List getUpdates() {
    return parks;
  }

  private static VehicleParkingSpaces createVehicleAvailability(List patches) {
    VehicleParkingSpacesBuilder availabilityBuilder = VehicleParkingSpaces.builder();
    boolean hasHandledSpaces = false;

    for (HslParkPatch patch : patches) {
      String type = patch.getCapacityType();

      if (type != null) {
        Integer spaces = patch.getSpacesAvailable();

        switch (type) {
          case "CAR":
            availabilityBuilder.carSpaces(spaces);
            hasHandledSpaces = true;
            break;
          case "BICYCLE":
            availabilityBuilder.bicycleSpaces(spaces);
            hasHandledSpaces = true;
            break;
          case "DISABLED":
            availabilityBuilder.wheelchairAccessibleCarSpaces(spaces);
            hasHandledSpaces = true;
            break;
        }
      }
    }

    return hasHandledSpaces ? availabilityBuilder.build() : null;
  }

  /**
   * @return true if facilities and hubs have not been successfully downloaded before, or
   * facilitiesFrequencySec > 0 and over facilitiesFrequencySec has passed since last successful
   * fetch
   */
  private boolean fetchFacilitiesAndHubsNow() {
    if (parks == null) {
      return true;
    }
    if (facilitiesFrequencySec <= 0) {
      return false;
    }
    return System.currentTimeMillis() > lastFacilitiesFetchTime + facilitiesFrequencySec * 1000;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy