using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using FarmmapsApi.Models; using Google.Apis.Upload; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using static FarmmapsApi.Extensions; using static FarmmapsApiSamples.Constants; namespace FarmmapsApi.Services { public class GeneralService { private readonly ILogger _logger; private readonly FarmmapsApiService _farmmapsApiService; public GeneralService(ILogger logger, FarmmapsApiService farmmapsApiService) { _logger = logger; _farmmapsApiService = farmmapsApiService; } public async Task CreateCropfieldItemAsync(string parentItemCode, string name, int year, string fieldGeomJson, string data = "{}") { var currentYear = new DateTime(year, 1, 1); var cropfieldItemRequest = new ItemRequest() { ParentCode = parentItemCode, ItemType = CROPFIELD_ITEMTYPE, Name = name, DataDate = currentYear, DataEndDate = currentYear.AddYears(1).AddDays(-1), Data = JObject.Parse(data), Geometry = JObject.Parse(fieldGeomJson) }; return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest); } public async Task CreateOperationItemAsync(string cropRecordingItemCode, int year, string fieldGeomJson, string data = "{}") { // not sure if here we also need to specify DataDate, DataEndDate & Geometry. Do it just in case var currentYear = new DateTime(year, 1, 1); JObject jdata = JObject.Parse(data); string name = string.Format($"CrpRec Operation, {jdata.GetValue("name")}"); int code022; string type022; double quantity; double applied_kgNha; double ncontent = 0.0; // for now just any value //Is it a fertilizer application? //If the operation contains an element "n" then nothing, use that value (kg N/ha administred) //Else: look up the N content for the code022, calculate "n" based on fertilizer amount (data) & content (cl022) and add applied_kgNha to the jdata if (jdata.ContainsKey("n") == false) { quantity = jdata.GetValue("quantity").ToObject(); code022 = jdata.GetValue("product").ToObject(); //TODO: Now here look up this code022 in the cl022 and get the ncontent from that list. //And check the unit in which the ncontent is expressed, e.g. % or kg/ton and check if it is not null applied_kgNha = quantity * ncontent; jdata.Add("n", applied_kgNha.ToString()); //all Data elements in Farmmaps code ar strings }; ItemRequest operationItemRequest = new ItemRequest() { ParentCode = cropRecordingItemCode, ItemType = CROPOP_ITEMTYPE, Name = name, DataDate = currentYear, DataEndDate = currentYear.AddYears(1).AddDays(-1), Data = jdata, Geometry = JObject.Parse(fieldGeomJson) }; return await _farmmapsApiService.CreateItemAsync(operationItemRequest); } public async Task CreateCropfieldCharacteristicItemAsync(string cropfieldItemCode, int year, string fieldGeomJson, string data = "{}") { // not sure if here we also need to specify DataDate, DataEndDate & Geometry. Do it just in case string name = "Cropfield characteristic"; var currentYear = new DateTime(year, 1, 1); ItemRequest cropfieldCharactericsticItemRequest = new ItemRequest() { ParentCode = cropfieldItemCode, ItemType = CROPCHAR_ITEMTYPE, Name = name, DataDate = currentYear, DataEndDate = currentYear.AddYears(1).AddDays(-1), Data = JObject.Parse(data), Geometry = JObject.Parse(fieldGeomJson) }; return await _farmmapsApiService.CreateItemAsync(cropfieldCharactericsticItemRequest); } public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName, string geoJsonString = null) { var startUpload = DateTime.UtcNow.AddSeconds(-3); var result = await _farmmapsApiService.UploadFile(filePath, root.Code, geoJsonString, progress => { _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}"); if(progress.Status == UploadStatus.Failed && progress.Exception != null) _logger.LogError(progress.Exception.Message ?? "No further error"); }); if (result.Progress.Status == UploadStatus.Failed) return null; return await FindChildItemAsync(root.Code, itemType, itemName, i => i.Created >= startUpload && i.Name.ToLower().Contains(itemName.ToLower())); } public async Task UploadZipWithShapeAsync(UserRoot root, string filePath, string itemName, string geoJsonString = null) { var startUpload = DateTime.UtcNow.AddSeconds(-3); var result = await _farmmapsApiService.UploadFile(filePath, root.Code, geoJsonString, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); if (result.Progress.Status == UploadStatus.Failed) return null; return await FindChildItemAsync(root.Code, SHAPE_PROCESSED_ITEMTYPE, itemName, i => i.Created >= startUpload && i.Name.ToLower().Contains(itemName.ToLower())); ; } public async Task ShapeToGeotiff(Item shapeItem, int resolution = 1, params string[] inputLayerNames) { var startUpload = DateTime.UtcNow.AddSeconds(-3); var taskStatus = await RunAndWaitForTask(shapeItem, "vnd.farmmaps.task.shapetogeotiff", request => { request.attributes["resolution"] = resolution.ToString(); if(inputLayerNames.Length > 0) request.attributes["inputLayers"] = $"[{string.Join(",", inputLayerNames.Select(v => $"\"{v}\""))}]"; }); if (taskStatus.State == ItemTaskState.Error) return null; return await FindChildItemAsync(shapeItem.ParentCode, GEOTIFF_PROCESSED_ITEMTYPE, shapeItem.Name, i => i.Created >= startUpload && i.Name.ToLower().Contains(shapeItem.Name.ToLower())); } public async Task GeotiffToShape(Item tiffItem) { var taskmapRequest = new TaskRequest {TaskType = TASKMAP_TASK}; taskmapRequest.attributes["cellWidth"] = "3"; taskmapRequest.attributes["cellHeight"] = "1"; taskmapRequest.attributes["angle"] = "0"; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(tiffItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(tiffItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on converting geotiff to shape; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(tiffItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the taskmap is a child of the input tiff *** Update feb 2021: it is a child of the cropfield. var itemName = "Taskmap"; var taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, SHAPE_PROCESSED_ITEMTYPE, itemName); if (taskMapItem == null) { _logger.LogError("Could not find the shape taskmap as a child item under the input"); return null; } return taskMapItem; } // Create taskmap based on width, height and direction public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string outputType, string cellWidth, string cellHeight, string startPoint, string inputLayerName = null, string ddiCode = "0001", string centered = "false", string endPoint = null, string angle = null, string precision = null, string cropTypeName = null, string costumerName = null, string ProductGroupName = null, string productName = null, string resolution = "3", string unitScale = null, string maximumClasses = null) { var taskmapRequest = new TaskRequest { TaskType = TASKMAP_TASK }; taskmapRequest.attributes["inputLayerName"] = inputLayerName; taskmapRequest.attributes["inputCode"] = tiffItem.Code; taskmapRequest.attributes["operation"] = outputType; // Currently onlye "shape" supported, if ISOXML is supported this should be an input taskmapRequest.attributes["cellWidth"] = cellWidth; //metres taskmapRequest.attributes["cellHeight"] = cellHeight; //metres taskmapRequest.attributes["startPoint"] = startPoint; // Coordinates WGS84 taskmapRequest.attributes["centered"] = centered; if (outputType == "isoxml") taskmapRequest.attributes["ddiCode"] = ddiCode; // ddi is obligatory for isoxml, if not given set to 0001 if (angle == null) taskmapRequest.attributes["endPoint"] = endPoint; // Coordinates WGS84 if (endPoint == null) taskmapRequest.attributes["angle"] = angle; // degrees between 0.0 and 360.0 // Optional attributes if (precision != null) taskmapRequest.attributes["precision"] = precision; if (cropTypeName != null) taskmapRequest.attributes["cropTypeName"] = cropTypeName; if (costumerName != null) taskmapRequest.attributes["costumerName"] = costumerName; if (ProductGroupName != null) taskmapRequest.attributes["ProductGroupName"] = ProductGroupName; if (productName != null) taskmapRequest.attributes["productName"] = productName; if (resolution != null) taskmapRequest.attributes["resolution"] = resolution; if (unitScale != null) taskmapRequest.attributes["unitScale"] = unitScale; if (maximumClasses != null) taskmapRequest.attributes["maximumClasses"] = maximumClasses; // Can be used for shapefile too string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on conversion to Taskmap; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the taskmap is a child of the input tiff var itemName = "Taskmap"; Item taskMapItem = null; if (outputType == "isoxml") {taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, ISOXML_PROCESSED_ITEMTYPE, itemName); } else if (outputType== "shape") { taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, SHAPE_PROCESSED_ITEMTYPE, outputType); } else { _logger.LogError("OutputType not specified, could not determine if output should be shape or ISOXML"); taskMapItem = null; } if (taskMapItem == null) { _logger.LogError("Could not find the shape/isoxml taskmap as a child item under the cropfield"); return null; } return taskMapItem; } public async Task RunAndWaitForTask(Item subjectItem, string taskIdentifier, Action configureCallback = null, int retrySeconds = 3) { var taskRequest = new TaskRequest() { TaskType = taskIdentifier }; configureCallback?.Invoke(taskRequest); var taskCode = await _farmmapsApiService.QueueTaskAsync(subjectItem.Code, taskRequest); await PollTask(TimeSpan.FromSeconds(retrySeconds), async (tokenSource) => { _logger.LogInformation($"Checking {taskIdentifier} task status"); var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(subjectItem.Code, taskCode); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); _logger.LogInformation($"{taskIdentifier} finished"); return await _farmmapsApiService.GetTaskStatusAsync(subjectItem.Code, taskCode); } public async Task FindChildItemAsync(string parentCode, string itemType, string containsName, Func filter = null, int maxTries = 10) { Item dataItem = null; int tries = 0; await PollTask(TimeSpan.FromSeconds(3), async source => { _logger.LogInformation($"Trying to get {containsName} data"); var uploadedFilesChildren = await _farmmapsApiService.GetItemChildrenAsync(parentCode, itemType); Func func = filter ?? (i => i.Name.ToLower().Contains(containsName.ToLower())); dataItem = uploadedFilesChildren.FirstOrDefault(func); if (dataItem != null || tries == maxTries) { source.Cancel(); } tries++; }); if (dataItem == null) { _logger.LogError("dataItem not found"); return null; } _logger.LogInformation($"Found {containsName} item"); return dataItem; } public async Task RunCropRecordingTask(Item cropfieldItem) { var cropRecordingRequest = new TaskRequest { TaskType = CROPREC_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, cropRecordingRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on RunCropRecordingTask; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the CropRecording data is a child of the cropfield var itemName = "Crprec"; var cropRecordingItem = await FindChildItemAsync(cropfieldItem.Code, CROPREC_ITEMTYPE, itemName); if (cropRecordingItem == null) { _logger.LogError("Could not find the CropRecording data as a child item under the cropfield"); return null; } return cropRecordingItem; } public async Task RunBofekTask(Item cropfieldItem) { var taskmapRequest = new TaskRequest { TaskType = BOFEK_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on retreiving BOFEK data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the BOFEK data is a child of the cropfield var itemName = "bofek"; var bofekItem = await FindChildItemAsync(cropfieldItem.Code, SHAPE_PROCESSED_ITEMTYPE, itemName); if (bofekItem == null) { _logger.LogError("Could not find the BOFEK data as a child item under the cropfield"); return null; } return bofekItem; } public async Task> GetKpiItemsForCropField(Item cropfieldItem) { TaskRequest kpiRequest = new TaskRequest { TaskType = KPI_TASK }; kpiRequest.attributes["processAggregateKpi"] = "false"; int year = cropfieldItem.DataDate.Value.Year; kpiRequest.attributes["year"] = year.ToString(); string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, kpiRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on retreiving KPI data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } await Task.Delay(6000); // wait 6000 secs for task to be completed //After the task is completed we have 1 kpiContainerItem with a code and with no data //Because the data are in the children of this kpiContainerItem. The container will have a list of children each with an "id" aand data, //The ids' are "a1", "b1", "b2", "c1","d1","d3","d5" //with following meanings: //| A1 | Opbrengst | Yield | //| B1 | Stikstofoverschot | Nitrogen surplus | //| B2 | Fosfaatoverschot | Phosphate surplus | //| C1 | Effectieve Organischestof aanvoer| Effective Organic Matter supply | //| D1 | Gebruik bestrijdingsmiddelen | Use of pesticides| //| D3 | Gewasdiversiteit(randdichtheid) | Crop diversity(edge density) | //| D5 | Percentage rustgewassen | Percentage of rest crops | List kpiContainerItem = await _farmmapsApiService.GetItemChildrenAsync(cropfieldItem.Code, KPICONTAINER_ITEM); string kpiContainerItemCode = kpiContainerItem[0].Code; List kpiItems = await _farmmapsApiService.GetItemChildrenAsync(kpiContainerItemCode); return kpiItems; } public async Task RunAhnTask(Item cropfieldItem) { var taskmapRequest = new TaskRequest { TaskType = AHN_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on retreiving AHN data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the AHN data is a child of the cropfield var itemName = "ahn"; var ahnItem = await FindChildItemAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE, itemName); if (ahnItem == null) { _logger.LogError("Could not find the AHN data as a child item under the cropfield"); return null; } return ahnItem; } public async Task RunShadowTask(Item cropfieldItem) { var taskmapRequest = new TaskRequest { TaskType = SHADOW_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on calculation shadow data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the shadow data is a child of the cropfield var itemName = "shadow"; var shadowItem = await FindChildItemAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE, itemName); if (shadowItem == null) { _logger.LogError("Could not find the shadow data as a child item under the cropfield"); return null; } return shadowItem; } public async Task RunSatelliteTask(Item cropfieldItem) { _logger.LogInformation("Gathering satellite information for cropfield, this might take a while!"); var taskmapRequest = new TaskRequest { TaskType = SATELLITE_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on satellite data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong when trying to process satellite data; {itemTask.Message}"); } return itemTask.Code; } public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Cropfield Satellite items", item => item.SourceTask == SATELLITE_TASK && taskStatus.Finished >= item.Created && taskStatus.Finished <= item.Created.Value.AddHours(1)); if (temporalItem == null) { _logger.LogError("Temporal item not found"); } var satelliteTiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); _logger.LogInformation("Available satellite images:"); var count = 0; TimeSpan.FromSeconds(0.5); foreach (var item in satelliteTiffs) { Console.WriteLine($"Satellite image #{count}: {item.DataDate}"); count++; } _logger.LogInformation("Enter satellite image number for NBS application"); int element = Int32.Parse(Console.ReadLine()); var selectedSatelliteItem = satelliteTiffs[element]; if (selectedSatelliteItem == null) { _logger.LogError("Satellite item not found"); } return selectedSatelliteItem; } public async Task> FindSatelliteItems(Item cropfieldItem, string satelliteTaskCode) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); if (taskStatus.State == ItemTaskState.Error) { _logger.LogWarning(taskStatus.Message); return null; } else { // find satellite data geotiffs var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Cropfield Satellite items", item => item.SourceTask == SATELLITE_TASK && taskStatus.Finished >= item.Created && taskStatus.Finished <= item.Created.Value.AddHours(1)); if (temporalItem == null) { _logger.LogWarning("Temporal item not found"); return null; } else { var satelliteTiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); return satelliteTiffs; } } } public async Task DownloadSatelliteStats(List satelliteTiffs, string fieldName = null, List satelliteBands = null, string downloadFolder = null) { string satelliteDataStatisticsFile = Path.Combine(downloadFolder, $"satelliteStats_{fieldName}.csv"); File.Delete(satelliteDataStatisticsFile); // Delete the SatelliteFile file if exists string headerLineStats = $"FieldName,satelliteDate,satelliteBand,max,min,mean,mode,median,stddev,minPlus,curtosis,maxMinus,skewness,variance,populationCount,variationCoefficient,confidenceIntervalLow, confidenceIntervalHigh,confidenceIntervalErrorMargin" + Environment.NewLine; File.AppendAllText(satelliteDataStatisticsFile, headerLineStats); foreach (var satelliteTiff in satelliteTiffs) { List layers = satelliteTiff.Data["layers"].Children().ToList(); foreach (JToken layer in layers) { DateTime satelliteImageDate = (DateTime)satelliteTiff.DataDate; string satelliteBand = layer["name"].ToString(); if (satelliteBands.Contains(satelliteBand)) { JToken satelliteStatisticsJtoken = layer["renderer"]["band"]["statistics"]; if (satelliteStatisticsJtoken == null) { _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found for satelliteBand '{satelliteBand}'"); } else { SatelliteStatistics satelliteStatistics = satelliteStatisticsJtoken.ToObject(); satelliteStatistics.fieldName = fieldName; satelliteStatistics.satelliteDate = satelliteImageDate; satelliteStatistics.satelliteBand = satelliteBand; File.AppendAllText(satelliteDataStatisticsFile, $"" + $"{satelliteStatistics.fieldName}," + $"{satelliteStatistics.satelliteDate.ToString("yyyy-MM-dd")}," + $"{satelliteStatistics.satelliteBand}," + $"{satelliteStatistics.max}," + $"{satelliteStatistics.min}," + $"{satelliteStatistics.mean}," + $"{satelliteStatistics.mode}," + $"{satelliteStatistics.median}," + $"{satelliteStatistics.stddev}," + $"{satelliteStatistics.minPlus}," + $"{satelliteStatistics.curtosis}," + $"{satelliteStatistics.maxMinus}," + $"{satelliteStatistics.skewness}," + $"{satelliteStatistics.variance}," + $"{satelliteStatistics.populationCount}," + $"{satelliteStatistics.variationCoefficient}," + $"{satelliteStatistics.confidenceIntervalLow}," + $"{satelliteStatistics.confidenceIntervalHigh}," + $"{satelliteStatistics.confidenceIntervalErrorMargin}" + Environment.NewLine); } } } } return satelliteDataStatisticsFile; } public async Task> ListSatelliteStatistics(Item satelliteTiff, List satelliteBands = null, string fieldName = null) { SatelliteStatistics satelliteStatistics; List listSatelliteStatistics = new List(); List layers = satelliteTiff.Data["layers"].Children().ToList(); foreach (JToken layer in layers) { DateTime satelliteImageDate = (DateTime)satelliteTiff.DataDate; string satelliteBand = layer["name"].ToString(); //_logger.LogInformation($"Date '{satelliteImageDate.ToString("yyyy-MM-dd")}': satelliteBand: {satelliteBand}"); if (satelliteBands.Contains(satelliteBand)) { JToken satelliteStatisticsJtoken = layer["renderer"]["band"]["statistics"]; if (satelliteStatisticsJtoken == null) { _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found for satelliteBand '{satelliteBand}'"); //Console.WriteLine($"Available data: {item.Data}"); } else { //_logger.LogInformation($"Adding satelliteStatistics to listSatelliteStatistics"); satelliteStatistics = satelliteStatisticsJtoken.ToObject(); satelliteStatistics.fieldName = fieldName; satelliteStatistics.satelliteDate = satelliteImageDate; satelliteStatistics.satelliteBand = satelliteBand; listSatelliteStatistics.Add(satelliteStatistics); } } //else //{ // _logger.LogInformation($"this satelliteBand is not in your list satelliteBands"); //} } return listSatelliteStatistics; } //VanDerSat public async Task RunVanDerSatTask(Item cropfieldItem) { _logger.LogInformation("Gathering VanDerSat information for cropfield, this might take a while!"); var taskmapRequest = new TaskRequest { TaskType = VANDERSAT_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on VanDerSat data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong when trying to process VanDerSat data; {itemTask.Message}"); } return itemTask.Code; } public async Task FindVanDerSatItem(Item cropfieldItem, string VanDerSatTaskCode, string FieldName, bool StoreStatistics) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, VanDerSatTaskCode); // find VanDerSat data temporal var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Van der Sat"); if (temporalItem == null) { _logger.LogError("Temporal item not found"); } var VanDerSatiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); _logger.LogInformation("Available VanDerSat images:"); var count = 0; TimeSpan.FromSeconds(0.5); foreach (var item in VanDerSatiffs) { //Console.WriteLine($"Van der Sat image #{count}: {item.DataDate}"); //if (count == 0 ) { // Console.WriteLine($"vandersat image #{count}: {item.Data}"); //} if (StoreStatistics == true) { var VanDerSatBand = item.Data["layers"][0]["name"]; var VanderSatFile = $"C:\\Akkerweb\\{FieldName}_{VanDerSatBand}.csv"; var NewLineField = $"Field,Date,Mean,Min,Max,Standard deviation, ConfidenceInterval low, ConfidenceInterval high" + Environment.NewLine; if (count == 0) { File.AppendAllText(VanderSatFile, NewLineField); var numbervandersat = VanDerSatiffs.Count; Console.WriteLine($"{numbervandersat} Van der Sat images found"); } var VanderSatStatistics = item.Data["layers"][0]["renderer"]["band"]["statistics"]; var VanDerSatImageDate = (DateTime)item.DataDate; var VanderSatDate = VanDerSatImageDate.ToString("yyyy-MM-dd"); var NewLineDate = $"\"date\":{VanderSatDate}" + Environment.NewLine; if (VanderSatStatistics == null) { Console.WriteLine($"{VanderSatDate} no statistics found"); //Console.WriteLine($"Available data: {item.Data}"); } else { File.AppendAllText(VanderSatFile, $"{FieldName},{VanderSatDate},{VanderSatStatistics["mean"]},{VanderSatStatistics["min"]},{VanderSatStatistics["max"]},{VanderSatStatistics["stddev"]},{VanderSatStatistics["confidenceIntervalLow"]},{VanderSatStatistics["confidenceIntervalHigh"]}" + Environment.NewLine); } } count++; } //_logger.LogInformation("Enter VanDerSat image number"); //int element = Int32.Parse(Console.ReadLine()); int element = 0; var selectedVanDerSatItem = VanDerSatiffs[element]; if (selectedVanDerSatItem == null) { _logger.LogError("VanDerSat item not found"); } return selectedVanDerSatItem; } public async Task RunWatBalTask(Item cropfieldItem) { _logger.LogInformation("Gathering WatBal information for cropfield, this might take a while!"); var taskmapRequest = new TaskRequest { TaskType = WATBAL_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on VanDerSat data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong when trying to process WatBal data; {itemTask.Message}"); } return itemTask.Code; } public async Task FindWatBalItem(Item cropfieldItem, string WatBalTaskCode, string FieldName, bool StoreStatistics) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, WatBalTaskCode); // find WatBal data temporal var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Watbal", item => item.SourceTask == WATBAL_TASK && taskStatus.Finished >= item.Created && taskStatus.Finished <= item.Created.Value.AddHours(1)); if (temporalItem == null) { _logger.LogError("Temporal item not found"); } var WatBalData = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); _logger.LogInformation("Available WatBal Data:"); var count = 0; TimeSpan.FromSeconds(0.5); foreach (var item in WatBalData) { Console.WriteLine($"WatBal data #{count}: {item.DataDate}"); if (count == 0) { Console.WriteLine($"WatBalData #{count}: {item.Data}"); } count++; } int element = 0; var selectedWatBalItem = WatBalData[element]; if (selectedWatBalItem == null) { _logger.LogError("WatBal item not found"); } return selectedWatBalItem; } } }