public ActionResult <IEnumerable <TrackResult> > GetTracks([FromQuery] TracksByBoundingBox request) { // Due to a bug in the CosmosDb client, we need to specify the invariant-culture here. // The cosmos-db query will be created using the current culture settings. // When querying on a double, the decimal point could otherwise be a comma instead of a point // which would lead to syntax-errors. // (see https://github.com/Azure/azure-cosmos-dotnet-v2/issues/651 ) System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; var searchKeys = CalculateSearchKeys(request); // First coordinate in the boundingbox-polygon should be the upper left corner. // From there, add the points counter-clockwise and finish by repeating the first location again. // Points must appear counter-clockwise so that st_within will know that we're interested in // the area inside the polygon. var bbox = new Polygon( new[] { new Microsoft.Azure.Documents.Spatial.Position(request.MinLongitude, request.MaxLatitude), new Microsoft.Azure.Documents.Spatial.Position(request.MinLongitude, request.MinLatitude), new Microsoft.Azure.Documents.Spatial.Position(request.MaxLongitude, request.MinLatitude), new Microsoft.Azure.Documents.Spatial.Position(request.MaxLongitude, request.MaxLatitude), new Microsoft.Azure.Documents.Spatial.Position(request.MinLongitude, request.MaxLatitude), }); var query = DbClient.CreateDocumentQuery <VesselGeoPosition>(TracksGeoCollectionUri, new FeedOptions() { EnableCrossPartitionQuery = true }) .Where(p => searchKeys.Contains(p.GeoHash_Date) && p.Position.Within(bbox) && p.Timestamp >= request.StartDate && p.Timestamp <= request.EndDate); var searchResults = query.ToList(); var groupedByVesselId = new Dictionary <long, List <VesselGeoPosition> >(); foreach (var kvp in searchResults) { if (groupedByVesselId.ContainsKey(kvp.ObjectId) == false) { groupedByVesselId.Add(kvp.ObjectId, new List <VesselGeoPosition>()); } groupedByVesselId[kvp.ObjectId].Add(kvp); } return(Ok(groupedByVesselId.Select(kvp => new TrackResult { ObjectId = kvp.Key, Positions = kvp.Value.Select(p => new Position() { Location = p.Position, Timestamp = p.Timestamp }) }).ToArray())); }
private static IEnumerable <string> CalculateSearchKeys(TracksByBoundingBox request) { var geohashes = NGeoHash.GeoHash.Bboxes(request.MinLatitude, request.MinLongitude, request.MaxLatitude, request.MaxLongitude, 3); var numberOfDays = request.EndDate - request.StartDate; for (int i = 0; i <= numberOfDays.Days; i++) { foreach (var geohash in geohashes) { yield return($"{geohash}_{request.StartDate.AddDays(i):yyyyMMdd}"); } } }