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

org.opentripplanner.ext.flex.AreaStopsToVerticesMapper Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.ext.flex;

import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMultimap;
import jakarta.inject.Inject;
import java.util.stream.Stream;
import org.locationtech.jts.geom.Point;
import org.opentripplanner.framework.geometry.GeometryUtils;
import org.opentripplanner.graph_builder.model.GraphBuilderModule;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.index.StreetIndex;
import org.opentripplanner.street.model.vertex.StreetVertex;
import org.opentripplanner.transit.model.site.AreaStop;
import org.opentripplanner.transit.service.TimetableRepository;
import org.opentripplanner.utils.logging.ProgressTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Iterates over all area stops in the stop  and adds them to vertices that are suitable for
 * boarding flex trips.
 */
public class AreaStopsToVerticesMapper implements GraphBuilderModule {

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

  private final Graph graph;
  private final TimetableRepository timetableRepository;

  @Inject
  public AreaStopsToVerticesMapper(Graph graph, TimetableRepository timetableRepository) {
    this.graph = graph;
    this.timetableRepository = timetableRepository;
  }

  @Override
  @SuppressWarnings("Convert2MethodRef")
  public void buildGraph() {
    if (!timetableRepository.getSiteRepository().hasAreaStops()) {
      return;
    }

    StreetIndex streetIndex = graph.getStreetIndexSafe(timetableRepository.getSiteRepository());

    ProgressTracker progress = ProgressTracker.track(
      "Add flex locations to street vertices",
      1,
      timetableRepository.getSiteRepository().listAreaStops().size()
    );

    LOG.info(progress.startMessage());
    var results = timetableRepository
      .getSiteRepository()
      .listAreaStops()
      .parallelStream()
      .flatMap(areaStop -> {
        var matchedVertices = matchingVerticesForStop(streetIndex, areaStop);
        // Keep lambda! A method-ref would cause incorrect class and line number to be logged
        progress.step(m -> LOG.info(m));
        return matchedVertices;
      });

    ImmutableMultimap mappedResults = results.collect(
      ImmutableListMultimap.flatteningToImmutableListMultimap(
        MatchResult::vertex,
        mr -> Stream.of(mr.stop())
      )
    );

    mappedResults
      .keySet()
      .forEach(vertex -> {
        vertex.addAreaStops(mappedResults.get(vertex));
      });

    LOG.info(progress.completeMessage());
  }

  private static Stream matchingVerticesForStop(
    StreetIndex streetIndex,
    AreaStop areaStop
  ) {
    return streetIndex
      .getVerticesForEnvelope(areaStop.getGeometry().getEnvelopeInternal())
      .stream()
      .filter(StreetVertex.class::isInstance)
      .map(StreetVertex.class::cast)
      .filter(StreetVertex::isEligibleForCarPickupDropoff)
      .filter(vertx -> {
        // The street index overselects, so need to check for exact geometry inclusion
        Point p = GeometryUtils.getGeometryFactory().createPoint(vertx.getCoordinate());
        return areaStop.getGeometry().intersects(p);
      })
      .map(vertx -> new MatchResult(vertx, areaStop));
  }

  /**
   * The result of an area stop being matched with a vertex.
   */
  private record MatchResult(StreetVertex vertex, AreaStop stop) {}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy