/// <summary> /// Calculate the intersection point of two line segments /// adapted to C# from http://rbrundritt.wordpress.com/2008/10/20/approximate-points-of-intersection-of-two-line-segments/ /// </summary> /// <returns>Null if there is no intersection. Otherwise the point of intersection.</returns> public static IGeoLocation SimplePolylineIntersection(IGeoLocation latlong1, IGeoLocation latlong2, IGeoLocation latlong3, IGeoLocation latlong4) { //Line segment 1 (p1, p2) var a1 = latlong2.Latitude - latlong1.Latitude; var b1 = latlong1.Longitude - latlong2.Longitude; var c1 = a1 * latlong1.Longitude + b1 * latlong1.Latitude; //Line segment 2 (p3, p4) var a2 = latlong4.Latitude - latlong3.Latitude; var b2 = latlong3.Longitude - latlong4.Longitude; var c2 = a2 * latlong3.Longitude + b2 * latlong3.Latitude; var determinate = a1 * b2 - a2 * b1; IGeoLocation intersection; if (determinate != 0) { var x = (b2 * c1 - b1 * c2) / determinate; var y = (a1 * c2 - a2 * c1) / determinate; var intersect = new GeoLocation(y, x); if (InBoundedBox(latlong1, latlong2, intersect) && InBoundedBox(latlong3, latlong4, intersect)) intersection = intersect; else intersection = null; } else //lines are parallel intersection = null; return intersection; }
/// <summary> /// Stores employee trackpoints. /// Used by the mobile phone application. /// NOTE: TrackPoints CollectedTimeStamp should be in UTC. /// </summary> /// <param name="roleId">Current roleId </param> /// <param name="trackPoints">The list of TrackPoints being passed from an Employee's device</param> /// <returns>An Http response to the device signaling that the TrackPoint was successfully created</returns> public void Post(Guid roleId, TrackPoint[] trackPoints) { if (!trackPoints.Any() || !trackPoints.First().RouteId.HasValue) throw Request.BadRequest(); var routeId = trackPoints.First().RouteId.Value; var currentUserAccount = CoreEntitiesContainer.CurrentUserAccount(); var currentBusinessAccount = CoreEntitiesContainer.Owner(roleId, new[] { RoleType.Administrator, RoleType.Regular, RoleType.Mobile }).FirstOrDefault(); //Return an Unauthorized Status Code if //a) there is no UserAccount //b) the user does not have access to the route (currentBusinessAccount == null) if (currentUserAccount == null || currentBusinessAccount == null) throw Request.NotAuthorized(); var employee = CoreEntitiesContainer.Employees.FirstOrDefault(e => e.LinkedUserAccount.Id == currentUserAccount.Id && e.EmployerId == currentBusinessAccount.Id); if (employee == null) throw Request.NotFound("Employee"); //order the new track points by their TimeStamps //remove inaccurate trackpoints var orderedModelTrackPoints = trackPoints.Where(tp => tp.Accuracy < 50).OrderBy(tp => tp.CollectedTimeStamp).ToArray(); var latestTrackPoint = orderedModelTrackPoints.LastOrDefault(); //if there was a previous point today, check the latest point is not erroneous if (employee.LastTimeStamp.HasValue && DateTime.UtcNow.Subtract(employee.LastTimeStamp.Value) < TimeSpan.FromDays(1)) { var previous = new GeoLocation(employee.LastLatitude.Value, employee.LastLongitude.Value); latestTrackPoint = orderedModelTrackPoints.LastOrDefault(t => !Erroneous(t, previous, t.CollectedTimeStamp.Subtract(employee.LastTimeStamp.Value))); //set the heading based on the previous point if (latestTrackPoint != null) latestTrackPoint.Heading = (int)GeoLocationTools.Bearing(previous, latestTrackPoint); } if (latestTrackPoint != null) { //Save the latest (most current) TrackPoint to the database employee.LastCompassDirection = latestTrackPoint.Heading; employee.LastLatitude = (double?)latestTrackPoint.Latitude; employee.LastLongitude = (double?)latestTrackPoint.Longitude; employee.LastSource = latestTrackPoint.Source; employee.LastSpeed = (double?)latestTrackPoint.Speed; employee.LastTimeStamp = latestTrackPoint.CollectedTimeStamp; employee.LastAccuracy = latestTrackPoint.Accuracy; } //Push TrackPoints to Azure as long as either //the LastPushToAzureTimeStamp is null //the LastPushToAzureTimeStamp difference from the TrackPoint is > TimeBetweenPushesToAzure var lastPushToAzureTimeStamp = employee.LastPushToAzureTimeStamp; //send the TrackPoints to azure, that are seperated by TimeBetweenPushesToAzure (in seconds) foreach (var trackPoint in orderedModelTrackPoints) { if (lastPushToAzureTimeStamp.HasValue && (trackPoint.CollectedTimeStamp - lastPushToAzureTimeStamp) < TimeSpan.FromSeconds(UpdateConstants.SecondsBetweenHistoricalTrackPoints)) continue; if (trackPoint.Id == Guid.Empty) trackPoint.Id = Guid.NewGuid(); PushTrackPointToAzure(currentBusinessAccount, trackPoint, employee, routeId); lastPushToAzureTimeStamp = trackPoint.CollectedTimeStamp; } employee.LastPushToAzureTimeStamp = lastPushToAzureTimeStamp; //Save all the changes that have been made in the Database SaveWithRetry(); }