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

org.opentripplanner.ext.vehicleparking.liipi.LiipiParkUpdater Maven / Gradle / Ivy

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

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.service.vehicleparking.model.VehicleParking;
import org.opentripplanner.service.vehicleparking.model.VehicleParkingGroup;
import org.opentripplanner.service.vehicleparking.model.VehicleParkingSpaces;
import org.opentripplanner.service.vehicleparking.model.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 Liipi format APIs. The format is documented in
 * https://parking.fintraffic.fi/docs/index.html).
 */
public class LiipiParkUpdater implements DataSource {

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

  private static final String JSON_PARSE_PATH = "results";

  private final LiipiFacilitiesDownloader facilitiesDownloader;
  private final int facilitiesFrequencySec;
  private final LiipiHubsDownloader hubsDownloader;
  private final JsonDataListDownloader utilizationsDownloader;
  private final LiipiParkToVehicleParkingMapper vehicleParkingMapper;
  private final LiipiHubToVehicleParkingGroupMapper vehicleParkingGroupMapper;
  private final LiipiParkUtilizationToPatchMapper parkPatchMapper;

  private long lastFacilitiesFetchTime;

  private List parks;
  private Map hubForPark;

  public LiipiParkUpdater(
    LiipiParkUpdaterParameters parameters,
    OpeningHoursCalendarService openingHoursCalendarService
  ) {
    String feedId = parameters.feedId();
    vehicleParkingMapper = new LiipiParkToVehicleParkingMapper(
      feedId,
      openingHoursCalendarService,
      parameters.timeZone()
    );
    vehicleParkingGroupMapper = new LiipiHubToVehicleParkingGroupMapper(feedId);
    parkPatchMapper = new LiipiParkUtilizationToPatchMapper(feedId);
    var otpHttpClientFactory = new OtpHttpClientFactory();
    facilitiesDownloader = new LiipiFacilitiesDownloader(
      parameters.facilitiesUrl(),
      JSON_PARSE_PATH,
      vehicleParkingMapper::parsePark,
      otpHttpClientFactory
    );
    hubsDownloader = new LiipiHubsDownloader(
      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 (LiipiParkPatch 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