1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
using System.Net;
using Enmarcha.Sources.OpenTripPlannerGql;
using Enmarcha.Sources.OpenTripPlannerGql.Queries;
using Enmarcha.Backend.Configuration;
using Enmarcha.Backend.Services;
using Enmarcha.Backend.Types.Planner;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace Enmarcha.Backend.Controllers;
[ApiController]
[Route("api/planner")]
public partial class RoutePlannerController : ControllerBase
{
private readonly ILogger<RoutePlannerController> _logger;
private readonly OtpService _otpService;
private readonly IGeocodingService _geocodingService;
private readonly AppConfiguration _config;
private readonly HttpClient _httpClient;
public RoutePlannerController(
ILogger<RoutePlannerController> logger,
OtpService otpService,
IGeocodingService geocodingService,
IOptions<AppConfiguration> config,
HttpClient httpClient
)
{
_logger = logger;
_otpService = otpService;
_geocodingService = geocodingService;
_config = config.Value;
_httpClient = httpClient;
}
[HttpGet("autocomplete")]
public async Task<ActionResult<List<PlannerSearchResult>>> Autocomplete([FromQuery] string query)
{
if (string.IsNullOrWhiteSpace(query))
{
return BadRequest("Query cannot be empty");
}
var results = await _geocodingService.GetAutocompleteAsync(query);
return Ok(results);
}
[HttpGet("reverse")]
public async Task<ActionResult<PlannerSearchResult>> Reverse([FromQuery] double lat, [FromQuery] double lon)
{
var result = await _geocodingService.GetReverseGeocodeAsync(lat, lon);
if (result == null)
{
return NotFound();
}
return Ok(result);
}
[HttpGet("plan")]
public async Task<ActionResult<RoutePlan>> Plan(
[FromQuery] double fromLat,
[FromQuery] double fromLon,
[FromQuery] double toLat,
[FromQuery] double toLon,
[FromQuery] DateTimeOffset? time,
[FromQuery] bool arriveBy = false)
{
try
{
var requestContent = PlanConnectionContent.Query(
new PlanConnectionContent.Args(fromLat, fromLon, toLat, toLon, time ?? DateTimeOffset.Now, arriveBy)
);
var request = new HttpRequestMessage(HttpMethod.Post, $"{_config.OpenTripPlannerBaseUrl}/gtfs/v1");
request.Content = JsonContent.Create(new GraphClientRequest
{
Query = requestContent
});
var response = await _httpClient.SendAsync(request);
var responseBody = await response.Content.ReadFromJsonAsync<GraphClientResponse<PlanConnectionResponse>>();
if (responseBody is not { IsSuccess: true })
{
LogErrorFetchingRoutes(response.StatusCode, await response.Content.ReadAsStringAsync());
return StatusCode(500, "An error occurred while planning the route.");
}
var plan = _otpService.MapPlanResponse(responseBody.Data!);
return Ok(plan);
}
catch (Exception e)
{
_logger.LogError("Exception planning route: {e}", e);
return StatusCode(500, "An error occurred while planning the route.");
}
}
[LoggerMessage(LogLevel.Error, "Error fetching route planning, received {statusCode} {responseBody}")]
partial void LogErrorFetchingRoutes(HttpStatusCode? statusCode, string responseBody);
}
|