/// <summary> /// Execute HTTP request and read the response. /// </summary> /// <param name="request"></param> /// <returns>Formatted response from service.</returns> private RootRouteObject ExecuteAndRead(DriveReportTransportType transportType, HttpWebRequest request) { var responseString = ""; Stream responseStream; try { var distanceResponse = request.GetResponse(); responseStream = distanceResponse.GetResponseStream(); } catch (WebException e) { var up = new RouteInformationException("Server error", e); //Logger.Error("Server error", up); throw up; } if (responseStream == null) { return(null); } var streamReader = new StreamReader(responseStream); responseString = streamReader.ReadToEnd(); streamReader.Close(); return(ParseJson(transportType, responseString)); }
public double GetNDKWorkRouteCalculation(int employmentId, DriveReportTransportType transportType, bool startsHome, bool endsHome, Address[] addresses) { //Manipulate the list of adresses, so that we get the route for work instead of home. Employment employment = _employmentRepository.AsQueryable().Where(x => x.Id == employmentId).FirstOrDefault(); if (employment == null) { return(-1); } Address workAddress; if (employment.AlternativeWorkAddress != null) { workAddress = employment.AlternativeWorkAddress; } else { workAddress = employment.OrgUnit.Address; } if (startsHome) { addresses[0] = workAddress; } if (endsHome) { addresses[addresses.Length - 1] = workAddress; } return(_route.GetRoute(transportType, addresses).Length); }
/// <summary> /// Formats and structures the response. /// </summary> /// <param name="response"></param> /// <returns>Response reprensented in custom class.</returns> private RootRouteObject ParseJson(DriveReportTransportType transportType, string response) { JToken jRouteObject = JToken.Parse(response); if (jRouteObject == null) { return(null); } RootRouteObject route = new RootRouteObject(jRouteObject) { route_summary = new RouteSummary(jRouteObject["route_summary"]) }; if (jRouteObject["alternative_geometries"] != null) { if (jRouteObject["alternative_summaries"] != null) { var altSums = jRouteObject["alternative_summaries"].ToList(); var altGeos = jRouteObject["alternative_geometries"].ToList(); for (int i = 0; i < altGeos.Count; i++) { route.alternative_geometries.Add((string)altGeos[i]); route.alternative_summaries.Add(new AlternativeSummary(altSums[i])); } } } if (jRouteObject["route_instructions"] != null) { var ferryDistance = 0; var list = jRouteObject["route_instructions"].ToList(); // Iterate all route instructions except for the last one. // The last one is different from the others somehow. // It does not have a mode. It only has 7 properties where the others have 8. // It never seems to have any distance though, so it shouldnt matter. for (var i = 0; i < list.Count - 1; i++) { var currentInstruction = list.ElementAt(i).ToList(); var mode = currentInstruction.ElementAt(8).ToString(); // If the transportType is car mode 2 means travelling on a ferry. // If the transportType is bicycle mode 3 means travelling on a ferry. Annoying that it's not mode 2 for both of them. if ((transportType.Equals(DriveReportTransportType.Car) && mode == CarFerryMode) || (transportType.Equals(DriveReportTransportType.Bike) && mode == BicycleFerryMode)) { // Replace "m" with nothing in the string, because the distance is given as "123m" ferryDistance += int.Parse(currentInstruction.ElementAt(5).ToString().Replace("m", "")); } } route.route_summary.distance_not_including_ferry = route.route_summary.total_distance - ferryDistance; } return(route); }
public RouteInformation GetRoute(DriveReportTransportType transportType, IEnumerable <Address> addresses) { return(new RouteInformation() { Duration = 1337, EndStreet = "Katrinebjergvej 95, 8200 Aarhus", StartStreet = "Katrinebjergvej 40, 8200 Aarhus", GeoPoints = "", Length = 42 }); }
/// <summary> /// Build query URL and create HTTP request following the service API url specifications. /// </summary> /// <param name="transportType">Type of transport. Car or Bike</param> /// <param name="routeCoordinates"></param> /// <returns></returns> private HttpWebRequest CreateRequest(DriveReportTransportType transportType, IEnumerable <Coordinates> routeCoordinates) { string query = ""; var coordinateses = routeCoordinates as IList <Coordinates> ?? routeCoordinates.ToList(); Coordinates origin, destination; try { origin = coordinateses.Single(p => p.Type == Coordinates.CoordinatesType.Origin); destination = coordinateses.Single(p => p.Type == Coordinates.CoordinatesType.Destination); } catch (InvalidOperationException e) { var up = new RouteInformationException("Mutltiple coordinates with type Origin and/or Destination.", e); //Logger.Error("Multiple coordinates with type origin and/or destination", up); throw up; } if (origin == null || destination == null) { var up = new RouteInformationException("Coordinate of type Origin and/or Destination missing."); //Logger.Error("Coordinate of type Origin and/or destination missing.", up); throw up; } //if (!((origin.Latitude.Length - origin.Latitude.IndexOf(".") + 1) <= 5)) //{ // origin.Latitude = origin.Latitude.Substring(0, origin.Latitude.IndexOf('.') + 1 + AddressCoordinates.CoordDecimals); // origin.Longitude = origin.Longitude.Substring(0, origin.Longitude.IndexOf('.') + 1 + AddressCoordinates.CoordDecimals); //} query = "loc=" + origin.Latitude + "," + origin.Longitude; query = coordinateses.Where(p => p.Type == Coordinates.CoordinatesType.Via).Aggregate(query, (current, point) => current + ("&loc=" + point.Latitude + "," + point.Longitude)); query += "loc=" + destination.Latitude + "," + destination.Longitude + "&instructions=true"; switch (transportType) { case DriveReportTransportType.Car: return((HttpWebRequest)WebRequest.Create(UrlDefinitions.RoutingUrl + query)); case DriveReportTransportType.Bike: return((HttpWebRequest)WebRequest.Create(UrlDefinitions.BikeRoutingUrl + query)); } // Should never be reached return(null); }
/// <summary> /// Get routes and alternative routes for the given set of coordinates. /// </summary> /// <param name="transportType">Type of transport. Car or Bike.</param> /// <param name="routeCoordinates"></param> /// <exception cref="RouteInformationException">Thrown if no route was returned or no rute was found.</exception> /// <returns></returns> public IEnumerable <RouteInformation> GetRoute(DriveReportTransportType transportType, IEnumerable <Coordinates> routeCoordinates) { List <RouteInformation> routes = new List <RouteInformation>(); HttpWebRequest request = CreateRequest(transportType, routeCoordinates); RootRouteObject result = ExecuteAndRead(transportType, request); if (result == null) { var e = new RouteInformationException("No route returned."); //Logger.Error("Exception when getting route information", e); throw e; } //From septima 01/09 2016: //Routingservices kan forventes senere at returnere status = 200, //idet OSRM er gået fra at bruge "C style" exit-koder, til "HTTP Style" status-koder. if (result.status != 0 && result.status / 100 == 2) { var e = new RouteInformationException("No route found.");; //Logger.Error("Exception when getting route information", e); throw e; } var route = new RouteInformation(); route.Length = result.route_summary.distance_not_including_ferry; route.Duration = result.route_summary.total_time; route.EndStreet = result.route_summary.end_point; route.StartStreet = result.route_summary.start_point; route.GeoPoints = result.route_geometry; routes.Add(route); for (int i = 0; i < result.alternative_summaries.Count; i++) { route = new RouteInformation { Length = result.alternative_summaries[i].total_distance, Duration = result.alternative_summaries[i].total_time, EndStreet = result.alternative_summaries[i].end_point, StartStreet = result.alternative_summaries[i].start_point, GeoPoints = result.alternative_geometries[i] }; routes.Add(route); } return(routes); }
/// <summary> /// Get routes and alternative routes for the given set of coordinates. /// </summary> /// <param name="transportType">Type of transport. Car or Bike.</param> /// <param name="routeCoordinates"></param> /// <exception cref="RouteInformationException">Thrown if no route was returned or no rute was found.</exception> /// <returns></returns> public IEnumerable <RouteInformation> GetRoute(DriveReportTransportType transportType, IEnumerable <Coordinates> routeCoordinates) { List <RouteInformation> routes = new List <RouteInformation>(); HttpWebRequest request = CreateRequest(transportType, routeCoordinates); RootRouteObject result = ExecuteAndRead(transportType, request); if (result == null) { var e = new RouteInformationException("No route returned."); //Logger.Error("Exception when getting route information", e); throw e; } if (result.status != 0) { var e = new RouteInformationException("No route found.");; //Logger.Error("Exception when getting route information", e); throw e; } var route = new RouteInformation(); route.Length = result.route_summary.distance_not_including_ferry; route.Duration = result.route_summary.total_time; route.EndStreet = result.route_summary.end_point; route.StartStreet = result.route_summary.start_point; route.GeoPoints = result.route_geometry; routes.Add(route); for (int i = 0; i < result.alternative_summaries.Count; i++) { route = new RouteInformation { Length = result.alternative_summaries[i].total_distance, Duration = result.alternative_summaries[i].total_time, EndStreet = result.alternative_summaries[i].end_point, StartStreet = result.alternative_summaries[i].start_point, GeoPoints = result.alternative_geometries[i] }; routes.Add(route); } return(routes); }
public IHttpActionResult NDKWorkRouteCalculation(int employmentId, DriveReportTransportType transportType, bool startsHome, bool endsHome, [FromBody] Address[] addresses) { double result = 0.0; try { result = _driveReportService.GetNDKWorkRouteCalculation(employmentId, transportType, startsHome, endsHome, addresses); } catch (Exception e) { return(InternalServerError(e)); } if (result < 0) { return(BadRequest()); } return(Ok(result)); }
/// <summary> /// Returns the shortest route within the time limit. (Duration <= 300s , Length difference > 3000m) /// </summary> /// <param name="transportType">Type of transport. Car or bike.</param> /// <param name="addresses"></param> /// <exception cref="AddressLaunderingException"></exception> /// <exception cref="AddressCoordinatesException"></exception> /// <exception cref="RouteInformationException"></exception> /// <returns></returns> public RouteInformation GetRoute(DriveReportTransportType transportType, IEnumerable <Address> addresses) { if (addresses == null || !addresses.Any()) { return(null); } var addressesList = addresses.ToList(); List <Coordinates> routeCoordinates = new List <Coordinates>(); AddressCoordinates coordService = new AddressCoordinates(); SeptimaRouter septimaService = new SeptimaRouter(); var origin = addressesList[0]; var destination = addressesList[addressesList.Count - 1]; addressesList.Remove(origin); addressesList.Remove((destination)); if (String.IsNullOrEmpty(origin.Longitude)) { routeCoordinates.Add(coordService.GetCoordinates(origin, Coordinates.CoordinatesType.Origin)); } else { routeCoordinates.Add(new Coordinates() { Longitude = origin.Longitude, Latitude = origin.Latitude, Type = Coordinates.CoordinatesType.Origin }); } foreach (var address in addressesList) { if (String.IsNullOrEmpty(address.Longitude)) { routeCoordinates.Add(coordService.GetCoordinates(address, Coordinates.CoordinatesType.Via)); } else { routeCoordinates.Add(new Coordinates() { Longitude = address.Longitude, Latitude = address.Latitude, Type = Coordinates.CoordinatesType.Via }); } } if (String.IsNullOrEmpty(destination.Longitude)) { routeCoordinates.Add(coordService.GetCoordinates(destination, Coordinates.CoordinatesType.Destination)); } else { routeCoordinates.Add(new Coordinates() { Longitude = destination.Longitude, Latitude = destination.Latitude, Type = Coordinates.CoordinatesType.Destination }); } try { List <RouteInformation> routes = septimaService.GetRoute(transportType, routeCoordinates).OrderBy(x => x.Duration).ToList(); // Sort routes by duration and pick the one with the shortest duration. // OS2RouteMap.js in the frontend picks the route with the shortest duration // Therefore the backend should pick a route based on the same criteria. routes = routes.OrderBy(x => x.Duration).ToList(); var bestRoute = routes[0]; // Divide by 1000 to get it in kilometers. bestRoute.Length /= 1000; return(bestRoute); } catch (AddressCoordinatesException e) { //Logger.Error("Exception when getting route information", e); } return(null); }
/// <summary> /// Takes a DriveReport as input and returns it with data. /// /// FourKmRule: If a user has set the FourKmRule to be used, the distance between /// the users home and municipality is used in the correction of the driven distance. /// If the rule is not used, the distance between the users home and work address are /// calculated and used, provided that the user has not set a override for this value. /// /// Calculated: The driven route is calculated, and based on whether the user starts /// and/or ends at home, the driven distance is corrected by subtracting the distance /// between the users home address and work address. /// Again, this is dependend on wheter the user has overridden this value. /// /// Calculated without extra distance: If this method is used, the driven distance is /// still calculated, but the distance is not corrected with the distance between the /// users home address and work address. The distance calculated from the service is /// therefore used directly in the calculation of the amount to reimburse /// /// </summary> public DriveReport Calculate(RouteInformation drivenRoute, DriveReport report) { //Check if user has manually provided a distance between home address and work address var homeWorkDistance = 0.0; var person = _personRepo.AsQueryable().First(x => x.Id == report.PersonId); var homeAddress = _personService.GetHomeAddress(person); // Get Work and Homeaddress of employment at time of DriveDateTimestamp for report. AddressHistory addressHistory = null; try { addressHistory = _addressHistoryRepo.AsQueryable().SingleOrDefault(x => x.EmploymentId == report.EmploymentId && x.StartTimestamp <report.DriveDateTimestamp && x.EndTimestamp> report.DriveDateTimestamp); } catch (InvalidOperationException) { _logger.LogForAdmin(report.FullName + " har et overlap i sin adressehistorik"); throw; } if (homeAddress.Type != PersonalAddressType.AlternativeHome) { if (addressHistory != null && addressHistory.HomeAddress != null) { // If user doesn't have an alternative address set up then use the homeaddress at the time of DriveDateTimestamp // If the user does have an alternative address then always use that. homeAddress = addressHistory.HomeAddress; } } var employment = _emplrepo.AsQueryable().FirstOrDefault(x => x.Id.Equals(report.EmploymentId)); Address workAddress = employment.OrgUnit.Address; if (addressHistory != null && addressHistory.WorkAddress != null) { // If an AddressHistory.WorkAddress exists, then use that. workAddress = addressHistory.WorkAddress; } if (employment.AlternativeWorkAddress != null) { // Overwrite workaddress if an alternative work address exists. workAddress = employment.AlternativeWorkAddress; } if (report.KilometerAllowance != KilometerAllowance.Read && !report.IsFromApp) { //Check if drivereport starts at users home address. report.StartsAtHome = areAddressesCloseToEachOther(homeAddress, report.DriveReportPoints.First()); //Check if drivereport ends at users home address. report.EndsAtHome = areAddressesCloseToEachOther(homeAddress, report.DriveReportPoints.Last()); } homeWorkDistance = employment.WorkDistanceOverride; if (homeWorkDistance <= 0) { homeWorkDistance = _route.GetRoute(DriveReportTransportType.Car, new List <Address>() { homeAddress, workAddress }).Length; } //Calculate distance to subtract //double toSubtract = 0; double toSubtractFourKmRule = 0; double toSubtractHomeRule = 0; double toSubtractAltRule = 0; //If user indicated to use the Four Km Rule if (report.FourKmRule) { //Take users provided distance from home to border of municipality. If report is from app, use distance provided in report, else use distance saved on person. var borderDistance = report.IsFromApp ? report.HomeToBorderDistance : person.DistanceFromHomeToBorder; //Adjust distance based on if user starts or ends at home if (report.StartsAtHome) { //toSubtract += borderDistance; toSubtractFourKmRule += borderDistance; } if (report.EndsAtHome) { //toSubtract += borderDistance; toSubtractFourKmRule += borderDistance; } } else { //Same logic as above, but uses calculated distance between home and work if (report.StartsAtHome) { //toSubtract += homeWorkDistance; toSubtractHomeRule += homeWorkDistance; } if (report.EndsAtHome) { //toSubtract += homeWorkDistance; toSubtractHomeRule += homeWorkDistance; } } switch (report.KilometerAllowance) { case KilometerAllowance.Calculated: { string alternativeCalculationMethod = ConfigurationManager.AppSettings["AlternativeCalculationMethod"]; if (alternativeCalculationMethod == null) { alternativeCalculationMethod = string.Empty; } // Use Norddjurs alternative reimbursemnt calculation method if configured so. if (alternativeCalculationMethod.ToLower() == "ndk") { // Newer subtract the "daily" distance between HOME and WORK. toSubtractHomeRule = 0.0; // Get the drive report points. List <DriveReportPoint> altDriveReportPoints = new List <DriveReportPoint>(); foreach (DriveReportPoint altDriveReportPoint in report.DriveReportPoints) { altDriveReportPoints.Add(altDriveReportPoint); } // Get if a bike is used for transportation. Boolean altIsBike = _rateTypeRepo.AsQueryable().First(x => x.TFCode.Equals(report.TFCode)).IsBike; DriveReportTransportType altTransportType = (altIsBike == true) ? DriveReportTransportType.Bike : DriveReportTransportType.Car; Double altToSubtract1 = 0.0; Double altToSubtract2 = 0.0; // 1a. When starting from home if ( //(report.IsFromApp == false) && (report.StartsAtHome == true) && (altDriveReportPoints.Count >= 2) && ((altDriveReportPoints[1].Latitude != workAddress.Latitude) && (altDriveReportPoints[1].Longitude != workAddress.Longitude))) { // A1) Get the distance between HOME and the first LOCATION (the route in the report). // This is used to select the reported route, or the alternative route. List <Address> altAddressesToHome = new List <Address>(); altAddressesToHome.Add(homeAddress); altAddressesToHome.Add(altDriveReportPoints[1]); Double altDistanceToHome = _route.GetRoute(altTransportType, altAddressesToHome).Length; // A2) Get the distance for the entire route (the route in the report). // This is used to calculate the distance to subtract. altAddressesToHome = new List <Address>(altDriveReportPoints); Double altDistanceA = _route.GetRoute(altTransportType, altAddressesToHome).Length; // B1) Get the distance between WORK and the first LOCATION. // This is used to select the reported route, or the alternative route. List <Address> altAddressesToWork = new List <Address>(); altAddressesToWork.Add(workAddress); altAddressesToWork.Add(altDriveReportPoints[1]); Double altDistanceToWork = _route.GetRoute(altTransportType, altAddressesToWork).Length; // B2) Get the distance for the entire alternative route. // This is used to calculate the distance to subtract. altAddressesToWork = new List <Address>(altDriveReportPoints); altAddressesToWork[0] = workAddress; Double altDistanceB = _route.GetRoute(altTransportType, altAddressesToWork).Length; // The current report distance is including the route between HOME and LOCATION. // Substract the difference, if the distance between WORK and LOCATION is smaller. if (altDistanceToWork < altDistanceToHome) { altToSubtract1 = (altDistanceA - altDistanceB); } } // 1b. When starting from home if ( //(report.IsFromApp == false) && (report.StartsAtHome == true) && (altDriveReportPoints.Count == 2) && ((altDriveReportPoints[1].Latitude == workAddress.Latitude) && (altDriveReportPoints[1].Longitude == workAddress.Longitude))) { toSubtractHomeRule += homeWorkDistance; } // 2a. When finishing at home if ( //(report.IsFromApp == false) && (report.EndsAtHome == true) && (altDriveReportPoints.Count >= 2) && ((altDriveReportPoints[altDriveReportPoints.Count - 2].Latitude != workAddress.Latitude) && (altDriveReportPoints[altDriveReportPoints.Count - 2].Longitude != workAddress.Longitude))) { // A1) Get the distance between the second last LOCATION and HOME (the route in the report). // This is used to select the reported route, or the alternative route. List <Address> altAddressesToHome = new List <Address>(); altAddressesToHome.Add(altDriveReportPoints[altDriveReportPoints.Count - 2]); altAddressesToHome.Add(homeAddress); Double altDistanceToHome = _route.GetRoute(altTransportType, altAddressesToHome).Length; // A2) Get the distance for the entire route (the route in the report). // This is used to calculate the distance to subtract. altAddressesToHome = new List <Address>(altDriveReportPoints); Double altDistanceA = _route.GetRoute(altTransportType, altAddressesToHome).Length; // B1) Get the distance between the second last LOCATION and WORK. // This is used to select the reported route, or the alternative route. List <Address> altAddressesToWork = new List <Address>(); altAddressesToWork.Add(altDriveReportPoints[altDriveReportPoints.Count - 2]); altAddressesToWork.Add(workAddress); Double altDistanceToWork = _route.GetRoute(altTransportType, altAddressesToWork).Length; // B2) Get the distance for the entire alternative route. // This is used to calculate the distance to subtract. altAddressesToWork = new List <Address>(altDriveReportPoints); altAddressesToWork[altAddressesToWork.Count - 1] = workAddress; Double altDistanceB = _route.GetRoute(altTransportType, altAddressesToWork).Length; // The current report distance is including the route between HOME and LOCATION. // Substract the difference, if the distance between WORK and LOCATION is smaller. if (altDistanceToWork < altDistanceToHome) { altToSubtract2 = (altDistanceA - altDistanceB); } } // 2b. When finishing at home and 2 points and 1 is work if ( //(report.IsFromApp == false) && (report.EndsAtHome == true) && (altDriveReportPoints.Count == 2) && ((altDriveReportPoints[altDriveReportPoints.Count - 2].Latitude == workAddress.Latitude) && (altDriveReportPoints[altDriveReportPoints.Count - 2].Longitude == workAddress.Longitude))) { toSubtractHomeRule += homeWorkDistance; } // Subtract. toSubtractAltRule = altToSubtract1 + altToSubtract2; } //End NDK if ((report.StartsAtHome || report.EndsAtHome) && !report.FourKmRule) { report.IsExtraDistance = true; } double drivenDistance = report.Distance; if (!report.IsFromApp) { // In case the report is not from app then get distance from the supplied route. drivenDistance = drivenRoute.Length; } //Adjust distance based on FourKmRule and if user start and/or ends at home //var correctDistance = drivenDistance - toSubtract; var correctDistance = drivenDistance - (toSubtractFourKmRule + toSubtractHomeRule + toSubtractAltRule); //Set distance to corrected report.Distance = correctDistance; if (!report.IsFromApp) { //Get RouteGeometry from driven route if the report is not from app. If it is from App then RouteGeometry is already set. report.RouteGeometry = drivenRoute.GeoPoints; } break; } case KilometerAllowance.CalculatedWithoutExtraDistance: { report.Distance = drivenRoute.Length; //Save RouteGeometry report.RouteGeometry = drivenRoute.GeoPoints; break; } case KilometerAllowance.Read: { if ((report.StartsAtHome || report.EndsAtHome) && !report.FourKmRule) { report.IsExtraDistance = true; } //Take distance from report var manuallyProvidedDrivenDistance = report.Distance; //report.Distance = manuallyProvidedDrivenDistance - toSubtract; report.Distance = manuallyProvidedDrivenDistance - toSubtractFourKmRule - toSubtractHomeRule; break; } default: { throw new Exception("No calculation method provided"); } } //Calculate the actual amount to reimburse if (report.Distance < 0) { report.Distance = 0; } // Multiply the distance by two if the report is a return trip if (report.IsRoundTrip == true) { report.Distance *= 2; } CalculateFourKmRuleForReport(report); SetAmountToReimburse(report); return(report); }