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

org.opentripplanner.ext.restapi.resources.PlannerResource Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.ext.restapi.resources;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import org.glassfish.grizzly.http.server.Request;
import org.opentripplanner.api.common.Message;
import org.opentripplanner.api.error.PlannerError;
import org.opentripplanner.apis.support.mapping.PlannerErrorMapper;
import org.opentripplanner.apis.transmodel.ResponseTooLargeException;
import org.opentripplanner.ext.restapi.mapping.TripPlanMapper;
import org.opentripplanner.ext.restapi.mapping.TripSearchMetadataMapper;
import org.opentripplanner.ext.restapi.model.ElevationMetadata;
import org.opentripplanner.ext.restapi.model.TripPlannerResponse;
import org.opentripplanner.framework.application.OTPRequestTimeoutException;
import org.opentripplanner.routing.error.RoutingValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is the primary entry point for the trip planning web service. All parameters are passed in
 * the query string. These parameters are defined as fields in the abstract RoutingResource
 * superclass, which also has methods for building routing requests from query parameters. This
 * allows multiple web services to have the same set of query parameters. In order for inheritance
 * to work, the REST resources are request-scoped (constructed at each request) rather than
 * singleton-scoped (a single instance existing for the lifetime of the OTP server).
 */
@Path("routers/{ignoreRouterId}/plan")
// final element needed here rather than on method to distinguish from routers API
public class PlannerResource extends RoutingResource {

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

  /**
   * @deprecated The support for multiple routers are removed from OTP2. See
   * https://github.com/opentripplanner/OpenTripPlanner/issues/2760
   */
  @Deprecated
  @PathParam("ignoreRouterId")
  private String ignoreRouterId;

  // We inject info about the incoming request so we can include the incoming query
  // parameters in the outgoing response. This is a TriMet requirement.
  // Jersey uses @Context to inject internal types and @InjectParam or @Resource for DI objects.
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Response plan(@Context UriInfo uriInfo, @Context Request grizzlyRequest) {
    /*
     * TODO: add Lang / Locale parameter, and thus get localized content (Messages & more...)
     * TODO: from/to inputs should be converted / geocoded / etc... here, and maybe send coords
     *       or vertex ids to planner (or error back to user)
     * TODO: org.opentripplanner.routing.module.PathServiceImpl has COOORD parsing. Abstract that
     *       out so it's used here too...
     */

    // Create response object, containing a copy of all request parameters. Maybe they should be in the debug section of the response.
    TripPlannerResponse response = new TripPlannerResponse(uriInfo);
    try {
      /* Fill in request fields from query parameters via shared superclass method, catching any errors. */
      var request = super.buildRequest(uriInfo.getQueryParameters());

      // Route
      var res = serverContext.routingService().route(request);

      // Map to API
      // TODO VIA (Leonard) - we should store the default showIntermediateStops somewhere
      TripPlanMapper tripPlanMapper = new TripPlanMapper(request.locale(), showIntermediateStops);
      response.setPlan(tripPlanMapper.mapTripPlan(res.getTripPlan()));
      if (res.getPreviousPageCursor() != null) {
        response.setPreviousPageCursor(res.getPreviousPageCursor().encode());
      }
      if (res.getNextPageCursor() != null) {
        response.setNextPageCursor(res.getNextPageCursor().encode());
      }
      response.setMetadata(TripSearchMetadataMapper.mapTripSearchMetadata(res.getMetadata()));
      if (!res.getRoutingErrors().isEmpty()) {
        // The api can only return one error message, so the first one is mapped
        response.setError(PlannerErrorMapper.mapMessage(res.getRoutingErrors().get(0)));
      }

      /* Populate up the elevation metadata */
      response.elevationMetadata = new ElevationMetadata();
      response.elevationMetadata.ellipsoidToGeoidDifference = serverContext.graph()
        .ellipsoidToGeoidDifference;
      response.elevationMetadata.geoidElevation = request.preferences().system().geoidElevation();

      response.debugOutput = res.getDebugTimingAggregator().finishedRendering();
    } catch (RoutingValidationException e) {
      if (e.isFromToLocationNotFound()) {
        response.setError(new PlannerError(Message.GEOCODE_FROM_TO_NOT_FOUND));
      } else if (e.isFromLocationNotFound()) {
        response.setError(new PlannerError(Message.GEOCODE_FROM_NOT_FOUND));
      } else if (e.isToLocationNotFound()) {
        response.setError(new PlannerError(Message.GEOCODE_TO_NOT_FOUND));
      } else {
        LOG.error("System error - unhandled error case?", e);
        response.setError(new PlannerError(Message.SYSTEM_ERROR));
      }
    } catch (OTPRequestTimeoutException | ResponseTooLargeException e) {
      response.setError(new PlannerError(Message.UNPROCESSABLE_REQUEST));
    } catch (Exception e) {
      LOG.error("System error", e);
      response.setError(new PlannerError(Message.SYSTEM_ERROR));
    }
    return Response.ok().entity(response).build();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy