/// <summary> /// Checks the data endpoints for the Sub-Surface Flow component. /// </summary> /// <returns></returns> public static async Task <Dictionary <string, Dictionary <string, string> > > CheckSubsurfaceEndpoints() { Dictionary <string, Dictionary <string, string> > endpoints = new Dictionary <string, Dictionary <string, string> >(); List <SubSurfaceFlow.SubSurfaceFlow> subsurfaces = new List <SubSurfaceFlow.SubSurfaceFlow>(); List <string> sources = new List <string>() { "nldas", "gldas" }; ITimeSeriesInput testInput = new TimeSeriesInput() { Source = "nldas", DateTimeSpan = new DateTimeSpan() { StartDate = new DateTime(2005, 01, 01), EndDate = new DateTime(2005, 01, 05) }, Geometry = new TimeSeriesGeometry() { Point = new PointCoordinate() { Latitude = 33.925673, Longitude = -83.355723 }, GeometryMetadata = new Dictionary <string, string>() } }; ITimeSeriesInputFactory iFactory = new TimeSeriesInputFactory(); foreach (string source in sources) { SubSurfaceFlow.SubSurfaceFlow subsurface = new SubSurfaceFlow.SubSurfaceFlow(); testInput.Source = source; subsurface.Input = iFactory.SetTimeSeriesInput(testInput, new List <string>() { "subsurfaceflow" }, out string errorMsg); subsurfaces.Add(subsurface); } Parallel.ForEach(subsurfaces, (SubSurfaceFlow.SubSurfaceFlow subsurface) => { endpoints.Add(subsurface.Input.Source, subsurface.CheckEndpointStatus()); }); return(endpoints); }
/// <summary> /// Gets subsurfaceflow data using the given TimeSeriesInput parameters. /// </summary> /// <param name="input"></param> /// <returns></returns> public ITimeSeriesOutput GetSubSurfaceFlow(ITimeSeriesInput input) { string errorMsg = ""; // Constructs default error output object containing error message. Utilities.ErrorOutput err = new Utilities.ErrorOutput(); // Validate subsurfaceflow sources. errorMsg = (!Enum.TryParse(input.Source, true, out SSFlowSources pSource)) ? "ERROR: 'Source' was not found or is invalid." : ""; if (errorMsg.Contains("ERROR")) { return(err.ReturnError(errorMsg)); } // SubSurfaceFlow object SubSurfaceFlow.SubSurfaceFlow evapo = new SubSurfaceFlow.SubSurfaceFlow(); // ITimeSeriesInputFactory object used to validate and initialize all variables of the input object. ITimeSeriesInputFactory iFactory = new TimeSeriesInputFactory(); evapo.Input = iFactory.SetTimeSeriesInput(input, new List <string>() { "BASEFLOW" }, out errorMsg); // If error occurs in input validation and setup, errorMsg is added to metadata of an empty object. if (errorMsg.Contains("ERROR")) { return(err.ReturnError(errorMsg)); } // Gets the SubSurfaceFlow data. ITimeSeriesOutput result = evapo.GetData(out errorMsg); if (errorMsg.Contains("ERROR")) { return(err.ReturnError(errorMsg)); } return(result); }
/// <summary> /// Default function for retrieving Total Flow data /// </summary> /// <param name="input"></param> /// <returns></returns> public async Task <ITimeSeriesOutput> GetTotalFlowData(TotalFlowInput input) { //TODO: Extract the geometry percentage from following code and place in Utility class, for possible use by all controllers. // Steps: // 1 - determine geometry // type case ID: is equal to "huc", "commid", or "catchmentid" // type case ID action: send request to HMS-GIS /rest/catchment?source=SOURCE%type=TYPE%id=ID for geometry // type case geojson: is equal to "catchment", or "flowline" // type case geojson action: send request to HMS-GIS /rest/catchment/geojson/ (geojson in body) OR // : send request to HMS-GIS /rest/catchment/flowlines/ (flowline geojson in body) // type case points: is equal to "points" // type case points action: if request is one point - send request to EPA waters for watershed geometry, if request is two points // 2 - retrieve catchments-points table // 3 - collect all points catchment table (this resolves issue of possible duplicate calls on catchment edge) // 4 - retrieve data for all points in catchment table [Parallel] // 5 - iterate through catchments and assign data to appropriate catchment // 6 - aggregate the data for each catchment based upon how much area that cell has in the catchment [Parallel] // 7 - return aggregated data as a timeseries in the form of { datetime : [ catchment1, catchment2, catchment3, ... ]} // 8 - set metadata (request inputs, catchment data, aggregation data, output structure) // 9 - return output string error = ""; Utilities.ErrorOutput err = new Utilities.ErrorOutput(); GeometryResponse geo = new GeometryResponse(); string requestUrl = this.baseUrl + "/hms/rest/api/v2/hms/gis/percentage/"; if (input.GeometryInputs != null) { if (input.GeometryInputs.ContainsKey("huc8") && input.GeometryInputs.ContainsKey("commid")) { Dictionary <string, string> taskID; string queryUrl = requestUrl + "?huc_8_num=" + input.GeometryInputs["huc8"] + "&com_id_num=" + input.GeometryInputs["commid"]; using (var httpClientHandler = new HttpClientHandler()) { httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return(true); }; using (var client = new HttpClient(httpClientHandler)) { taskID = JsonConvert.DeserializeObject <Dictionary <string, string> >(client.GetStringAsync(queryUrl).Result); } geo = this.RequestData(taskID["job_id"], out error); } } else { return(err.ReturnError("Input error - GeometryInputs provided is invalid.")); } } else { switch (input.GeometryType.ToLower()) { case "huc": // use case 1 // use case 2 string hucID = input.GeometryInput; using (var client = new HttpClient()) { string queryUrl = baseUrl + "?huc_8_id=" + hucID; client.Timeout = TimeSpan.FromMinutes(10); geo = JsonConvert.DeserializeObject <GeometryResponse>(client.GetStringAsync(queryUrl).Result); } //goto default; break; case "commid": // use case 3 //string commid = input.GeometryInput; //using (var client = new HttpClient()) //{ // string queryUrl = baseUrl + "?com_id=" + commid; // client.Timeout = TimeSpan.FromMinutes(10); // geo = JsonConvert.DeserializeObject<GeometryResponse>(client.GetStringAsync(queryUrl).Result); //} goto default; break; case "catchmentid": // use case 3 // string catchmentID = input.GeometryInput; //using (var client = new HttpClient()) //{ // geo = JsonConvert.DeserializeObject<GeometryResponse>(client.GetStringAsync(baseUrl + "/api/GridCell/catchmentid/" + catchmentID).Result); //} goto default; break; case "catchment": // use case 4 // Use POST call with geometry goto default; break; case "flowline": // use case 5 // Use POST call with geometry goto default; break; case "points": // use case 6 // use case 7 // GET call with points, hms-gis will get geometries goto default; break; case "test": string testGeometry = "{\"geometry\":{\"9311911\": { \"points\": [ { \"cellArea\": 0.015624999999992895, \"containedArea\": 4.178630503273804e-05, \"longitude\": -71.43749999999996, \"latitude\": 44.18749999999999, \"percentArea\": 0.26743235220964506 }, { \"cellArea\": 0.015624999999996447, \"containedArea\": 0.005083393397351494, \"longitude\": -71.31249999999997, \"latitude\": 44.18750000000001, \"percentArea\": 32.53371774305696 }, { \"cellArea\": 0.015624999999996447, \"containedArea\": 0.0002419268603100419, \"longitude\": -71.31249999999997, \"latitude\": 44.31249999999997, \"percentArea\": 1.5483319059846201 } ] } }, \"metadata\": { \"execution time\": 86.99717831611633, \"nldas source\": \"https://ldas.gsfc.nasa.gov/nldas/gis/NLDAS_Grid_Reference.zip\", \"number of points\": 3, \"request date\": \"Thu, 22 Mar 2018 11:46:44 GMT\", \"shapefile source\": \"ftp://newftp.epa.gov/exposure/BasinsData/NHDPlus21/NHDPlus01060002.zip\" } }"; //string testGeometry = "{\"Geometry\": {\"02080107\": [{\"Latitude\": 37.437499999999972,\"Longitude\": -76.687499999999972,\"CellArea\": 0.0156250000000036,\"ContainedArea\": 0.00559514073505796,\"PercentArea\": 35.8089007043628},{\"Latitude\": 37.437499999999972,\"Longitude\": -76.5625,\"CellArea\": 0.0156250000000053,\"ContainedArea\": 0.0100796700330301,\"PercentArea\": 64.5098882113707},{\"Latitude\": 37.437499999999972,\"Longitude\": -76.437499999999972,\"CellArea\": 0.0156250000000142,\"ContainedArea\": 0.00780989236262298,\"PercentArea\": 49.9833111207416},{\"Latitude\": 37.437499999999972,\"Longitude\": -76.312499999999957,\"CellArea\": 0.0156250000000036,\"ContainedArea\": 0.0012605882348706,\"PercentArea\": 8.06776470316997}]},\"Metadata\": {}}"; geo = JsonConvert.DeserializeObject <GeometryResponse>(testGeometry); break; default: return(err.ReturnError("Input error - GeometryType provided is invalid. Provided value = " + input.GeometryType)); } } // Check for any errors if (!String.IsNullOrWhiteSpace(error) || !geo.status.Equals("SUCCESS")) { return(err.ReturnError(error)); } // Collect all unique points in Catchment List <GeometryPoint> points = new List <GeometryPoint>(); List <string> pointsSTR = new List <string>(); foreach (KeyValuePair <string, Catchment> k in geo.data.geometry) { foreach (Point cp in k.Value.points) { GeometryPoint p = new GeometryPoint(); p.Latitude = cp.latitude; p.Longitude = cp.longitude; if (!points.Contains(p)) { points.Add(p); pointsSTR.Add("[" + p.Latitude.ToString() + ", " + p.Longitude.ToString() + "]"); } } } ITimeSeriesInputFactory inputFactory = new TimeSeriesInputFactory(); List <string> errorMessages = new List <string>(); string errorMsg = ""; // Initialize all surface and subsurface points Dictionary <string, SurfaceRunoff.SurfaceRunoff> surfaceFlow = new Dictionary <string, SurfaceRunoff.SurfaceRunoff>(); Dictionary <string, SubSurfaceFlow.SubSurfaceFlow> subsurfaceFlow = new Dictionary <string, SubSurfaceFlow.SubSurfaceFlow>(); foreach (GeometryPoint point in points) { string key = point.Latitude.ToString() + "," + point.Longitude.ToString(); TimeSeriesGeometry tsGeometry = new TimeSeriesGeometry(); tsGeometry.Point = new PointCoordinate() { Latitude = point.Latitude, Longitude = point.Longitude }; // Initialize surfaceFlow catchment point object errorMsg = ""; TimeSeriesInput surfaceTempInput = new TimeSeriesInput(); surfaceTempInput = input; surfaceTempInput.Geometry = tsGeometry; SurfaceRunoff.SurfaceRunoff sFlow = new SurfaceRunoff.SurfaceRunoff(); sFlow.Input = inputFactory.SetTimeSeriesInput(surfaceTempInput, new List <string>() { "surfacerunoff" }, out errorMsg); surfaceFlow.Add(key, sFlow); errorMessages.Add(errorMsg); // Initialize subsurfaceFlow catchment point object errorMsg = ""; TimeSeriesInput subSurfaceTempInput = new TimeSeriesInput(); subSurfaceTempInput = input; subSurfaceTempInput.Geometry = tsGeometry; SubSurfaceFlow.SubSurfaceFlow subFlow = new SubSurfaceFlow.SubSurfaceFlow(); subFlow.Input = inputFactory.SetTimeSeriesInput(subSurfaceTempInput, new List <string>() { "subsurfaceflow" }, out errorMsg); subsurfaceFlow.Add(key, subFlow); errorMessages.Add(errorMsg); } // TODO: merge parallelized calls to surfaceRunoff and subsurfaceFlow // Parallelized surfaceRunoff object outputListLock = new object(); List <string> surfaceError = new List <string>(); var options = new ParallelOptions { MaxDegreeOfParallelism = -1 }; Parallel.ForEach(surfaceFlow, options, (KeyValuePair <string, SurfaceRunoff.SurfaceRunoff> surF) => { string errorM = ""; surF.Value.GetData(out errorM); lock (outputListLock) { surfaceError.Add(errorM); } }); // Parallelized subsurfaceFlow List <string> subsurfaceError = new List <string>(); Parallel.ForEach(subsurfaceFlow, options, (KeyValuePair <string, SubSurfaceFlow.SubSurfaceFlow> subF) => { string errorM = ""; subF.Value.GetData(out errorM); lock (outputListLock) { subsurfaceError.Add(errorM); } }); ITimeSeriesOutputFactory outputFactory = new TimeSeriesOutputFactory(); ITimeSeriesOutput output = outputFactory.Initialize(); // Aggregate catchments List <ITimeSeriesOutput> catchmentFlow = new List <ITimeSeriesOutput>(); ITimeSeriesOutput iOutput = outputFactory.Initialize(); var options2 = new ParallelOptions { MaxDegreeOfParallelism = 1 }; Dictionary <string, string> catchmentMeta = new Dictionary <string, string>(); Parallel.ForEach(geo.data.geometry, options2, (KeyValuePair <string, Catchment> catchments) => { string catchmentID = catchments.Key; List <ITimeSeriesOutput> catchmentPoints = new List <ITimeSeriesOutput>(); foreach (Point cp in catchments.Value.points) { IPointCoordinate point = new PointCoordinate() { Latitude = cp.latitude, Longitude = cp.longitude }; string key = point.Latitude.ToString() + "," + point.Longitude.ToString(); ITimeSeriesOutput output1 = Utilities.Merger.ModifyTimeSeries(surfaceFlow[key].Output, cp.percentArea / 100); ITimeSeriesOutput output2 = Utilities.Merger.ModifyTimeSeries(subsurfaceFlow[key].Output, cp.percentArea / 100); if (iOutput.Data.Count == 0) { iOutput = output1; iOutput = Utilities.Merger.MergeTimeSeries(iOutput, output2); } else { iOutput = Utilities.Merger.MergeTimeSeries(iOutput, output1); iOutput = Utilities.Merger.MergeTimeSeries(iOutput, output2); } catchmentPoints.Add(Utilities.Merger.AddTimeSeries(output1, output2, "TotalFlow")); } output.Data = (Utilities.Merger.MergeTimeSeries(catchmentPoints).Data); int currentCatchment = catchmentMeta.Count + 1; catchmentMeta.Add("catchment_column_" + currentCatchment.ToString() + "_ID", catchments.Key); }); // Testing // output.Data = iOutput.Data; output.DataSource = input.Source; output.Dataset = "Total Flow"; output.Metadata = surfaceFlow.First().Value.Output.Metadata; output.Metadata.Remove(input.Source + "_lat"); output.Metadata.Remove(input.Source + "_lon"); output.Metadata.Remove(input.Source + "_prod_name"); output.Metadata.Remove(input.Source + "_param_short_name"); output.Metadata.Remove(input.Source + "_param_name"); output.Metadata[input.Source + "_points"] = string.Join(", ", pointsSTR); // Adding geometry metadata to output metadata foreach (KeyValuePair <string, string> kv in geo.data.metadata) { if (!output.Metadata.ContainsKey(kv.Key)) { output.Metadata.Add(kv.Key, kv.Value); } } // Cleaning up output metadata Dictionary <string, string> cleanedMetaData = new Dictionary <string, string>(); foreach (KeyValuePair <string, string> kv in output.Metadata) { if (!kv.Key.Contains("column")) { cleanedMetaData.Add(kv.Key, kv.Value); } } output.Metadata = cleanedMetaData; // Add catchments metadata to output metadata foreach (KeyValuePair <string, string> kv in catchmentMeta) { output.Metadata.Add(kv.Key, kv.Value); } return(output); }