Skip to content

Commit

Permalink
Add journey planner and real time endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
daveajlee committed Apr 15, 2023
1 parent 4ba18d1 commit b0cf1cd
Show file tree
Hide file tree
Showing 15 changed files with 1,031 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package de.davelee.trams.server.controller;

import de.davelee.trams.server.request.JourneyRequest;
import de.davelee.trams.server.response.JourneyResponse;
import de.davelee.trams.server.service.CalculateJourneyService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
* This class provides REST endpoints which provide operations associated with journey planning in the TraMS Server API.
* @author Dave Lee
*/
@RestController
@Tag(name="/api/journeyPlanner")
@RequestMapping(value="/api/journeyPlanner")
public class JourneyPlannerController {

@Autowired
private CalculateJourneyService calculateJourneyService;

/**
* Calculate the journey based on the supplied journey request.
* @param journeyRequest a <code>JourneyRequest</code> object containing the journey that should be calculated.
* @return a <code>JourneyResponse</code> object containing either the journey instructions or an error message if
* no journey could be calculated.
*/
@PostMapping("/determineJourney")
@CrossOrigin
@ResponseBody
@Operation(summary = "Suggest a journey based on the request journey parameters", description="Return proposed journey")
public JourneyResponse determineJourney (@RequestBody final JourneyRequest journeyRequest) {
//Create a journeyResponse object.
JourneyResponse journeyResponse = new JourneyResponse();
//If the start equals the destination then return an error message.
if ( journeyRequest.getFrom().contentEquals(journeyRequest.getTo()) ) {
journeyResponse.setErrorMessage("Cannot suggest route as start and end points are identical!");
} else {
//Otherwise, calculate journey and generate a list of instructions.
journeyResponse.setJourneyInstructionList(calculateJourneyService.calculateJourney(journeyRequest));
//If the instructions is empty then produce an error message.
if ( journeyResponse.getJourneyInstructionList().size() == 0 ) {
journeyResponse.setErrorMessage("No journeys found!");
}
}
//Generate journey response.
return journeyResponse;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import de.davelee.trams.server.response.RouteResponse;
import de.davelee.trams.server.response.RoutesResponse;
import de.davelee.trams.server.service.RouteService;
import de.davelee.trams.server.service.StopTimeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
Expand All @@ -14,6 +15,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

/**
Expand All @@ -28,6 +30,9 @@ public class RoutesController {
@Autowired
private RouteService routeService;

@Autowired
private StopTimeService stopTimeService;

/**
* Return all routes for the specified company that are currently stored in the database.
* @param company a <code>String</code> containing the name of the company to search for.
Expand Down Expand Up @@ -62,6 +67,42 @@ public ResponseEntity<RoutesResponse> getRoutesByCompany ( final String company
.routeResponses(routeResponses).build());
}


/**
* Return all routes for a particular stop and company.
* @param stop a <code>String</code> containing the stop to return routes for.
* @param company a <code>String</code> containing the name of the company to return routes for.
* @return a <code>List</code> of <code>Route</code> objects which may be null if there are no routes in the database.
*/
@GetMapping("/allRoutesForStop")
@CrossOrigin
@ResponseBody
@Operation(summary = "View all routes for a company for a particular stop", description="Return all matching routes")
public ResponseEntity<RoutesResponse> allRoutesForStop ( final String stop, final String company) {
// Retrieve the route numbers for this company and stop.
List<String> routeNumbers = stopTimeService.getAllRouteNumbersByStop(company, stop);
//Convert the list of route numbers to a list of routes.
List<Route> routes = new ArrayList<>();
for ( String routeNumber : routeNumbers ) {
routes.addAll(routeService.getRoutesByCompanyAndRouteNumber(company, routeNumber));
}
//If routes is null or empty then return 204.
if ( routes == null || routes.size() == 0 ) {
return ResponseEntity.noContent().build();
}
//Otherwise convert to routes response and return.
RouteResponse[] routeResponses = new RouteResponse[routes.size()];
for ( int i = 0; i < routeResponses.length; i++ ) {
routeResponses[i] = RouteResponse.builder()
.company(routes.get(i).getCompany())
.routeNumber(routes.get(i).getRouteNumber())
.build();
}
return ResponseEntity.ok(RoutesResponse.builder()
.count((long) routeResponses.length)
.routeResponses(routeResponses).build());
}

/**
* Delete all routes for the specified company that are currently stored in the database.
* @param company a <code>String</code> containing the name of the company to delete routes for.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import de.davelee.trams.server.model.Stop;
import de.davelee.trams.server.request.AddStopRequest;
import de.davelee.trams.server.service.StopService;
import de.davelee.trams.server.service.SuggestStopService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
Expand All @@ -13,6 +14,9 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

/**
* This class provides REST endpoints which provide operations associated with a single stop in the TraMS Server API.
* @author Dave Lee
Expand All @@ -25,6 +29,9 @@ public class StopController {
@Autowired
private StopService stopService;

@Autowired
private SuggestStopService suggestStopService;

/**
* Add a stop to the database for a particular company.
* @param stopRequest a <code>StopRequest</code> object containing the information about this stop.
Expand Down Expand Up @@ -52,4 +59,32 @@ public ResponseEntity<Void> addStop ( @RequestBody final AddStopRequest stopRequ
return stopService.addStop(stop) ? ResponseEntity.status(201).build() : ResponseEntity.status(500).build();
}

/**
* Return a suggestion for the nearest stop for a particular address.
* @param operator a <code>String</code> containing the operator to return the suggestion for.
* @param address a <code>String</code> based on which the nearest stop should be found.
* @return a <code>String</code> containing the name of the nearest stop.
*/
@GetMapping("/suggest")
@CrossOrigin
@Operation(summary = "Suggest Stop", description="Suggest a nearby stop for a particular address")
@ApiResponses(value = {@ApiResponse(responseCode="200",description="Successfully suggested stop")})
public @ResponseBody String suggestStop ( final String operator, final String address) {
return suggestStopService.suggestNearestStop(operator, address);
}

/**
* Return a set of information including picture, address, attractions and name.
* @param operator a <code>String</code> containing the operator to return the stop information for.
* @param stopName a <code>String</code> containing the name of the stop to retrieve information for.
* @return a <code>StopModel</code> object containing the stop information.
*/
@GetMapping("/information")
@CrossOrigin
@Operation(summary = "Get the stop information for a particular stop", description="Return stop information")
@ApiResponses(value = {@ApiResponse(responseCode="200",description="Successfully retrieved stop information")})
public @ResponseBody Stop getStop ( final String operator, final String stopName) {
return stopService.getStop(operator, stopName);
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package de.davelee.trams.server.controller;

import de.davelee.trams.server.model.RealTimeEntryModel;
import de.davelee.trams.server.model.RealTimeModel;
import de.davelee.trams.server.model.StopTime;
import de.davelee.trams.server.request.GenerateStopTimesRequest;
import de.davelee.trams.server.response.StopTimeResponse;
import de.davelee.trams.server.response.StopTimesResponse;
import de.davelee.trams.server.service.RouteService;
import de.davelee.trams.server.service.StopService;
import de.davelee.trams.server.service.StopTimeService;
import de.davelee.trams.server.utils.DateUtils;
Expand All @@ -19,9 +22,12 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.*;

/**
Expand All @@ -39,6 +45,9 @@ public class StopTimesController {
@Autowired
private StopService stopService;

@Autowired
private RouteService routeService;

/**
* Return the next departures and/or arrivals based on the supplied user parameters.
* @param stopName a <code>String</code> containing the name of the stop to retrieve departures/arrivals from.
Expand Down Expand Up @@ -110,6 +119,60 @@ else if (departures) {
.stopTimeResponses(stopTimeResponses).build());
}

/**
* Return a model of the next departures for a particular stop and operator. The number of next departures to be
* retrieved must also be supplied. Optionally a route can be supplied to only display departures for a particular
* route. Further the language can be supplied for formatting purposes.
* @param stop a <code>String</code> with the name of the stop to retrieve departures for.
* @param numDepartures a <code>int</code> with the number of next departures to return.
* @param operator a <code>String</code> with the name of the operator to retrieve departures for.
* @param language a <code>String</code> with the language code e.g. DE or UK to use for formatting purposes.
* @param route a <code>String</code> with the name of the route to retrieve departures for (optional)
* @return a <code>RealTimeModel</code> with the next departures matching the specified criteria.
*/
@GetMapping("/nextDepartures")
@CrossOrigin
@ResponseBody
@Operation(summary = "View the next departures for a particular stop", description="Return the stop times.")
@ApiResponses(value = {@ApiResponse(responseCode="200",description="Successfully returned stop times")})
public RealTimeModel nextDepartures(
final String stop,
final int numDepartures,
final String operator,
final String language,
final String route) {
//Get the current date and time.
RealTimeModel realTimeModel = new RealTimeModel();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM).withLocale(Locale.UK);
LocalDateTime localDateTime = LocalDateTime.now();
realTimeModel.setTimestamp(localDateTime.format(dateTimeFormatter));
List<RealTimeEntryModel> realTimeEntryModels = new ArrayList<>();
//Get the departures from this stop - optionally only for a particular route.
List<StopTime> stopTimes = stopTimeService.getDeparturesByDate(stop, operator, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
stopTimes.stream().
filter(stopTime -> stopTime.getDepartureTime().isAfter(LocalTime.of(localDateTime.getHour(), localDateTime.getMinute()))).
forEach(stopTime -> {
RealTimeEntryModel realTimeEntryModel = new RealTimeEntryModel();
realTimeEntryModel.setMins((int) Duration.between(LocalTime.now(), stopTime.getDepartureTime()).getSeconds() / 60);
realTimeEntryModel.setRoute(routeService.getRoutesByCompanyAndRouteNumber(operator, stopTime.getRouteNumber()).get(0));
if ( stopTime.getDestination().contentEquals(stop)) {
realTimeEntryModel.setDestination("Journey Terminates Here");
} else {
realTimeEntryModel.setDestination(stopTime.getDestination());
}
realTimeEntryModels.add(realTimeEntryModel);
});
//Sort the list now by minutes - this is necessary as Spring Data JPA only sorts by hours and not by minutes.
Comparator<RealTimeEntryModel> byMinutes = Comparator.comparingInt(RealTimeEntryModel::getMins);
Collections.sort(realTimeEntryModels, byMinutes);
//Trim list to the maximum number of real time elements.
realTimeModel.setRealTimeEntryModelList( (realTimeEntryModels.size() > numDepartures) ?
realTimeEntryModels.subList(0, numDepartures) : realTimeEntryModels );
return realTimeModel;
}



/**
* Generate stop time entries within a specified time and frequency and save them to the database.
* @param generateStopTimesRequest a <code>GenerateStopTimesRequest</code> object containing the information to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import de.davelee.trams.server.response.StopsResponse;
import de.davelee.trams.server.service.StopService;
import de.davelee.trams.server.service.StopTimeService;
import de.davelee.trams.server.service.SuggestStopService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
Expand Down Expand Up @@ -34,6 +35,9 @@ public class StopsController {
@Autowired
private StopTimeService stopTimeService;

@Autowired
private SuggestStopService suggestStopService;

/**
* Return all stops currently stored in the database for a particular company or if a route number
* is supplied all stops for that company and route number.
Expand Down Expand Up @@ -75,6 +79,20 @@ public ResponseEntity<StopsResponse> getStops (final String company, final Optio
.stopResponses(stopResponses).build());
}

/**
* Return all address and/or landmarks for a particular operator.
* @param operator a <code>String</code> containing the operator to return the addresses/landmarks for.
* @return a <code>List</code> of <code>String</code> containing the addresses/landmarks.
*/
@GetMapping("/addressesForOperator")
@CrossOrigin
@ResponseBody
@Operation(summary = "Get addresses/landmarks", description="Get all addresses/landmarks for a particular operator")
@ApiResponses(value = {@ApiResponse(responseCode="200",description="Successfully returned stops")})
public List<String> getAddressesForOperator ( final String operator) {
return suggestStopService.getAddressesForOperator(operator);
}

/**
* Delete all stops currently stored in the database for a particular company.
* @param company a <code>String</code> containing the name of the company to search for.
Expand Down
46 changes: 46 additions & 0 deletions server/src/main/java/de/davelee/trams/server/model/Address.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package de.davelee.trams.server.model;

import lombok.*;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

/**
* This class represents an address which is mapped to a particular stop. This stop is the closest stop to this address.
* @author Dave Lee
*/
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
@Document
public class Address {

/**
* A unique id for this address.
*/
@Id
private ObjectId id;

/**
* The operator which serves this address.
*/
private String addressOperator;

/**
* The address that can be searched for.
*/
private String address;

/**
* The stop that serves this address.
*/
private Stop stop;

/**
* The distance between stop and address in minutes.
*/
private int durationInMins;

}
Loading

0 comments on commit b0cf1cd

Please sign in to comment.