Skip to content

Commit

Permalink
Added #22 pickup predictor to frontend
Browse files Browse the repository at this point in the history
Also added the required API controller functions to access the backend.

In addition, handling correctly unresolvable addresses in the geocoding API,
as well as handling first-time setup correctly in LocationClustering by not
assuming k = 0 and using a default number of 1 if the renumbering algorithm has
not yet run.
  • Loading branch information
dmnisson committed Feb 19, 2019
1 parent 81d1905 commit 3650e35
Show file tree
Hide file tree
Showing 39 changed files with 43,988 additions and 31,484 deletions.
2 changes: 1 addition & 1 deletion DriverTracker.Tests/MockDriverRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public MockDriverRepository(Driver[] drivers)
public async Task AddAsync(Driver driver)
{
Console.WriteLine("AddAsync called");
Console.Write(JsonConvert.SerializeObject(driver));
Console.Write(await Task.Run(() => JsonConvert.SerializeObject(driver)));
Console.WriteLine();
}

Expand Down
98 changes: 96 additions & 2 deletions DriverTracker/Controllers/AnalysisApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

using DriverTracker.Models;
using DriverTracker.Domain;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace DriverTracker.Controllers
{
Expand All @@ -16,14 +18,27 @@ namespace DriverTracker.Controllers
public class AnalysisApiController : Controller
{
private readonly IDriverRepository _driverRepository;
private readonly ILocationClustering _locationClustering;
private readonly ILegRepository _legRepository;
private readonly IPickupPrediction _pickupPrediction;
private readonly DriverStatistics _driverStatistics;
private readonly UserManager<IdentityUser> _userManager;
private readonly MvcDriverContext _context;

public AnalysisApiController(IDriverRepository driverRepository,
ILegRepository legRepository) {
public AnalysisApiController(IDriverRepository driverRepository,
ILegRepository legRepository,
ILocationClustering locationClustering,
IPickupPrediction pickupPrediction,
UserManager<IdentityUser> userManager,
MvcDriverContext context
) {
_driverRepository = driverRepository;
_legRepository = legRepository;
_locationClustering = locationClustering;
_pickupPrediction = pickupPrediction;
_driverStatistics = new DriverStatistics(driverRepository, legRepository);
_userManager = userManager;
_context = context;
}

// GET: api/analysisapi
Expand Down Expand Up @@ -89,5 +104,84 @@ public async Task<double[]> GetMultiPickupProb(int id, double delay, double dura

return farePrediction.RidershipClassProbabilities(delay, duration, fare);
}

// GET api/analysisapi/fareclassprob/39.7/-121.8/38.5/-121.9/1.3/26/2/160
[HttpGet("fareclassprob/{startlat}/{startlon}/{endlat}/{endlon}/{delay}/{duration}/{pickups}/{interval}")]
public async Task<double[]> GetFareClassProb(double startlat, double startlon, double endlat, double endlon, double delay, double duration, int pickups, double interval)
{
await TrainPickupPrediction();

return _pickupPrediction.GetFareClassProbabilities(
new double[] { startlat, startlon },
new double[] { endlat, endlon },
delay, duration, pickups, interval
);
}

private async Task TrainPickupPrediction()
{

await SetFareClassIntervals();

// train
DateTime fromDateTime = DateTime.Now.AddMonths(-12);
DateTime toDateTime = DateTime.Now;
await _pickupPrediction.LearnFromDates(fromDateTime, toDateTime);
}

private async Task SetFareClassIntervals()
{
// set the fare class intervals from the database
string UserId = _userManager.GetUserId(User);
_pickupPrediction.FareClassIntervals = (await _context.Analysts
.FirstOrDefaultAsync(a => a.UserIDString == UserId)
).FareClassIntervals;
}

// GET api/analysisapi/pickupprob/39.7/-121.8/38.5/-121.9/1.3/26/13.10/160
[HttpGet("pickupprob/{startlat}/{startlon}/{endlat}/{endlon}/{delay}/{duration}/{fare}/{interval}")]
public async Task<double[]> GetPickupProb(double startlat, double startlon, double endlat, double endlon, double delay, double duration, double fare, double interval)
{
await TrainPickupPrediction();

return _pickupPrediction.GetPickupProbabilities(
new double[] { startlat, startlon },
new double[] { endlat, endlon },
delay, duration, fare, interval
);
}

// GET api/analysisapi/fareclassintervals
[HttpGet("fareclassintervals")]
public async Task<double[]> GetFareClassIntervals()
{
await SetFareClassIntervals();
return _pickupPrediction.FareClassIntervals.ToArray();
}

// PUT api/analysisapi/fareclassintervals
[HttpPut("fareclassintervals")]
public async Task<IActionResult> PutFareClassIntervals([FromBody] double[] intervalBounds)
{

// current analyst
Analyst analyst = await _context.Analysts.FirstOrDefaultAsync(
a => a.UserIDString == _userManager.GetUserId(User));
if (analyst == null) return Forbid();

analyst.FareClassIntervals = intervalBounds;
await _context.SaveChangesAsync();

return Ok();
}

// GET api/analysisapi/renumberclustring
[HttpGet("renumberclustering")]
public async Task<IActionResult> PutRenumberClustering()
{
await _locationClustering.RenumberAsync();

return Ok(_locationClustering.ClusterCollection.Count);
}
}
}
12 changes: 12 additions & 0 deletions DriverTracker/Controllers/GeocodingController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ public async Task<IActionResult> Get(int id)
return Ok(await _dbSync.GetLegCoordinatesAsync(id));
}

// GET api/geocoding/direct/670%20Sycamore
[HttpGet("direct/{address}")]
[Authorize]
public async Task<IActionResult> Get(string address)
{
IEnumerable<Geocoding.Address> geoAddress = await _dbSync.Geocoder.GeocodeAsync(address);
return Ok(new double[] {
geoAddress.Average(a => a.Coordinates.Latitude),
geoAddress.Average(a => a.Coordinates.Longitude)
});
}

// POST api/geocoding/update
[HttpPost("update")]
[Authorize]
Expand Down
19 changes: 19 additions & 0 deletions DriverTracker/Controllers/PickupPredictorController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace DriverTracker.Controllers
{
public class PickupPredictorController : Controller
{
// GET: /<controller>/
[Authorize(Roles = "Admin,Analyst")]
public IActionResult Index()
{
return View();
}
}
}
24 changes: 17 additions & 7 deletions DriverTracker/Domain/GeocodingDbSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ namespace DriverTracker.Domain
public class GeocodingDbSync : IGeocodingDbSync
{
private MvcDriverContext _context;
private IGeocoder _geocoder;

public IGeocoder Geocoder { get; }

public GeocodingDbSync(IConfiguration configuration, MvcDriverContext context)
{
_context = context;
_geocoder = GeocoderFactory.GetGeocoder(configuration);
Geocoder = GeocoderFactory.GetGeocoder(configuration);
}

public async Task<bool> UpdateAllAsync()
Expand Down Expand Up @@ -96,8 +97,14 @@ public async Task<IEnumerable<LegCoordinates>> ListLegCoordinatesAsync(Expressio
}

private async Task<LegCoordinates> GeocodeLeg(Leg leg) {
IEnumerable<Address> startAddressList = await _geocoder.GeocodeAsync(leg.StartAddress);
IEnumerable<Address> endAddressList = await _geocoder.GeocodeAsync(leg.DestinationAddress);
IEnumerable<Address> startAddressList = await Geocoder.GeocodeAsync(leg.StartAddress);
IEnumerable<Address> endAddressList = await Geocoder.GeocodeAsync(leg.DestinationAddress);

if (!startAddressList.Any() || !endAddressList.Any())
{
return null;
}

return new LegCoordinates
{
LegID = leg.LegID,
Expand All @@ -119,14 +126,17 @@ private async Task CreateLegCoordinatesAsync(Leg leg)
{
LegCoordinates legCoordinates = await GeocodeLeg(leg);

_context.LegCoordinates.Add(legCoordinates);
if (legCoordinates != null)
_context.LegCoordinates.Add(legCoordinates);
}

private async Task UpdateLegCoordinatesAsync(Leg leg)
{
LegCoordinates legCoordinates = await GeocodeLeg(leg);

_context.LegCoordinates.Update(legCoordinates);
if (legCoordinates != null)
{
_context.LegCoordinates.Update(legCoordinates);
}
}
}
}
5 changes: 5 additions & 0 deletions DriverTracker/Domain/IGeocodingDbSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading.Tasks;

using DriverTracker.Models;
using Geocoding;

namespace DriverTracker.Domain
{
Expand Down Expand Up @@ -53,5 +54,9 @@ public interface IGeocodingDbSync
/// <param name="predicate">Predicate.</param>
Task<IEnumerable<LegCoordinates>> ListLegCoordinatesAsync(Expression<Func<Leg, bool>> predicate);

/// <summary>
/// The geocoder used by this object
/// </summary>
IGeocoder Geocoder { get; }
}
}
13 changes: 7 additions & 6 deletions DriverTracker/Domain/LocationClustering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public LocationClustering(
_geocodingDbSync = geocodingDbSync;
}

public int NumberOfClusters => ClusterCollection == null ? 0 : ClusterCollection.Count;
public int NumberOfClusters => ClusterCollection == null ? 1 : ClusterCollection.Count;

/// <summary>
/// Train on number of clusters using gap statistic
Expand Down Expand Up @@ -126,10 +126,11 @@ private async Task ComputeK(int maxK = 100, int B = 10, int driverID = 0, DateTi

private double[][] GetDataset(IEnumerable<Leg> legs)
{
return legs.Select(leg =>
{
LegCoordinates coordinates = _geocodingDbSync.GetLegCoordinatesAsync(leg.LegID).Result;
return new double[] {
return legs.Select(leg => _geocodingDbSync.GetLegCoordinatesAsync(leg.LegID).Result)
.Where(coordinates => coordinates != null)
.Select(coordinates =>
{
return new double[] {
Convert.ToDouble(coordinates.StartLatitude),
Convert.ToDouble(coordinates.StartLongitude),
Convert.ToDouble(coordinates.DestLatitude),
Expand Down Expand Up @@ -263,7 +264,7 @@ public async Task RetrainAsync()
private async Task RetrainAsync(Expression<Func<Leg, bool>> predicate)
{
await _geocodingDbSync.UpdateAllAsync();
KMeans kMeans = new KMeans(ClusterCollection.Count)
KMeans kMeans = new KMeans(NumberOfClusters)
{
Distance = new GeographicDistance()
};
Expand Down
5 changes: 5 additions & 0 deletions DriverTracker/DriverTracker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,14 @@
<Content Remove="drivertracker\src\tsconfig.app.json" />
<Content Remove="drivertracker\src\tsconfig.spec.json" />
<Content Remove="drivertracker\src\tslint.json" />
<Content Remove="wwwroot\libs\polyfills.js" />
</ItemGroup>
<ItemGroup>
<None Remove="NPM_Task_Runner_v1.4.90 %281%29.vsix" />
<None Remove="DriverTracker.db" />
<None Remove="=2.3.1" />
<None Remove="=3.3" />
<None Remove="=4.1.11" />
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\app\" />
Expand All @@ -122,6 +126,7 @@
<Folder Include="Views\Shared\Components\" />
<Folder Include="Views\Shared\Components\CookieConsent\" />
<Folder Include="Authorization\" />
<Folder Include="Views\PickupPredictor\" />
</ItemGroup>
<ItemGroup>
<None Include="DriverTracker.csproj" />
Expand Down
Binary file modified DriverTracker/DriverTracker.db
Binary file not shown.
Loading

0 comments on commit 3650e35

Please sign in to comment.