From 7005269701963f0863aa9f4f859765eb02833aaf Mon Sep 17 00:00:00 2001 From: Mark van der Wal Date: Wed, 19 Aug 2020 13:40:42 +0200 Subject: [PATCH] added vrapoten and changed some nbs stuff --- FarmmapsApi/Constants.cs | 11 +- FarmmapsApi/Services/GeneralService.cs | 161 ++++++++++++++++++-- FarmmapsApiSamples.sln | 26 +++- FarmmapsNbs/FarmmapsNbs.csproj.user | 10 ++ FarmmapsNbs/NbsApplication.cs | 31 ++++ FarmmapsNbs/NitrogenService.cs | 26 ++-- FarmmapsPoten/FarmmapsPoten.csproj | 21 +++ FarmmapsPoten/Models/PotenInput.cs | 18 +++ FarmmapsPoten/PotenApplication.cs | 196 +++++++++++++++++++++++++ FarmmapsPoten/PotenService.cs | 91 ++++++++++++ FarmmapsPoten/Program.cs | 23 +++ 11 files changed, 586 insertions(+), 28 deletions(-) create mode 100644 FarmmapsNbs/FarmmapsNbs.csproj.user create mode 100644 FarmmapsPoten/FarmmapsPoten.csproj create mode 100644 FarmmapsPoten/Models/PotenInput.cs create mode 100644 FarmmapsPoten/PotenApplication.cs create mode 100644 FarmmapsPoten/PotenService.cs create mode 100644 FarmmapsPoten/Program.cs diff --git a/FarmmapsApi/Constants.cs b/FarmmapsApi/Constants.cs index 191fc88..d8d1bdc 100644 --- a/FarmmapsApi/Constants.cs +++ b/FarmmapsApi/Constants.cs @@ -10,10 +10,17 @@ namespace FarmmapsApiSamples public const string SHAPE_PROCESSED_ITEMTYPE = "vnd.farmmaps.itemtype.shape.processed"; public const string SHAPE_ITEMTYPE = "vnd.farmmaps.itemtype.shape"; public const string GEOJSON_ITEMTYPE = "vnd.farmmaps.itemtype.geojson"; - + public const string VRANBS_TASK = "vnd.farmmaps.task.vranbs"; public const string VRAHERBICIDE_TASK = "vnd.farmmaps.task.vraherbicide"; public const string VRAHAULMKILLING_TASK = "vnd.farmmaps.task.vrahaulmkilling"; + public const string VRAPLANTING_TASK = "vnd.farmmaps.task.vrapoten"; public const string SATELLITE_TASK = "vnd.farmmaps.task.satellite"; + public const string TASKMAP_TASK = "vnd.farmmaps.task.taskmap"; + public const string WORKFLOW_TASK = "vnd.farmmaps.task.workflow"; + public const string BOFEK_TASK = "vnd.farmmaps.task.bofek"; + public const string SHADOW_TASK = "vnd.farmmaps.task.shadow"; + public const string AHN_TASK = "vnd.farmmaps.task.ahn"; + } -} \ No newline at end of file +} diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 8296e7a..c3d0c08 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -22,8 +22,9 @@ namespace FarmmapsApi.Services _logger = logger; _farmmapsApiService = farmmapsApiService; } - - public async Task CreateCropfieldItemAsync(string parentItemCode, string name, int year, string fieldGeomJson) + + public async Task CreateCropfieldItemAsync(string parentItemCode, string name, int year, + string fieldGeomJson) { var currentYear = new DateTime(year, 1, 1); var cropfieldItemRequest = new ItemRequest() @@ -39,7 +40,7 @@ namespace FarmmapsApi.Services return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest); } - + public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName) { var startUpload = DateTime.UtcNow; @@ -49,8 +50,8 @@ namespace FarmmapsApi.Services if (result.Progress.Status == UploadStatus.Failed) return null; - return await FindChildItemAsync(root.Code, itemType, itemName, - i => i.Created >= startUpload && + return await FindChildItemAsync(root.Code, itemType, itemName, + i => i.Created >= startUpload && i.Name.ToLower().Contains(itemName.ToLower())); } @@ -77,11 +78,11 @@ namespace FarmmapsApi.Services SHAPE_PROCESSED_ITEMTYPE); items = items.Where(i => i.Created >= startUpload).OrderByDescending(i => i.Created).ToList(); - + if (items.Any()) { shapeItem = items.First(i => i.Name.Contains(itemName)); - if(shapeItem != null) + if (shapeItem != null) { source.Cancel(); break; @@ -102,7 +103,43 @@ namespace FarmmapsApi.Services return await _farmmapsApiService.GetItemAsync(shapeItem.ParentCode); } - public async Task RunAndWaitForTask(Item subjectItem, string taskIdentifier, + + public async Task GeotiffToShape(Item tiffItem) + { + var taskmapRequest = new TaskRequest {TaskType = TASKMAP_TASK}; + + 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 + var itemName = "Taskmap"; + var taskMapItem = await FindChildItemAsync(tiffItem.Code, + 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; + } + + + public async Task RunAndWaitForTask(Item subjectItem, string taskIdentifier, Action configureCallback = null, int retrySeconds = 3) { var taskRequest = new TaskRequest() @@ -110,7 +147,7 @@ namespace FarmmapsApi.Services TaskType = taskIdentifier }; configureCallback?.Invoke(taskRequest); - + var taskCode = await _farmmapsApiService.QueueTaskAsync(subjectItem.Code, taskRequest); await PollTask(TimeSpan.FromSeconds(retrySeconds), async (tokenSource) => @@ -120,9 +157,9 @@ namespace FarmmapsApi.Services if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); - + _logger.LogInformation($"{taskIdentifier} finished"); - + return await _farmmapsApiService.GetTaskStatusAsync(subjectItem.Code, taskCode); } @@ -158,5 +195,107 @@ namespace FarmmapsApi.Services _logger.LogInformation($"Found {containsName} item"); return dataItem; } + + 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 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; + } } } \ No newline at end of file diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index 256b705..3d96af1 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -1,19 +1,23 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsNbs", "FarmmapsNbs\FarmmapsNbs.csproj", "{E08EF7E9-F09E-42D8-825C-164E458C78F4}" +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30320.27 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsNbs", "FarmmapsNbs\FarmmapsNbs.csproj", "{E08EF7E9-F09E-42D8-825C-164E458C78F4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsApi", "FarmmapsApi\FarmmapsApi.csproj", "{1FA9E50B-F45E-4534-953A-37C783D03C74}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsApi", "FarmmapsApi\FarmmapsApi.csproj", "{1FA9E50B-F45E-4534-953A-37C783D03C74}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "General", "General", "{6FA66E07-A59E-480E-B5D1-DBEFC4E4583D}" ProjectSection(SolutionItems) = preProject - README.MD = README.MD .gitignore = .gitignore + README.MD = README.MD EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsHerbicide", "FarmmapsHerbicide\FarmmapsHerbicide.csproj", "{731A88CD-9DC4-4969-86F2-2315830A6998}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsHerbicide", "FarmmapsHerbicide\FarmmapsHerbicide.csproj", "{731A88CD-9DC4-4969-86F2-2315830A6998}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsHaulmkilling", "FarmmapsHaulmkilling\FarmmapsHaulmkilling.csproj", "{DFA89D0B-5400-4374-B824-8367B76B4B6E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsHaulmkilling", "FarmmapsHaulmkilling\FarmmapsHaulmkilling.csproj", "{DFA89D0B-5400-4374-B824-8367B76B4B6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsPoten", "FarmmapsPoten\FarmmapsPoten.csproj", "{AAFAB03A-6F5C-4D91-991F-867B7898F981}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -37,5 +41,15 @@ Global {DFA89D0B-5400-4374-B824-8367B76B4B6E}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFA89D0B-5400-4374-B824-8367B76B4B6E}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFA89D0B-5400-4374-B824-8367B76B4B6E}.Release|Any CPU.Build.0 = Release|Any CPU + {AAFAB03A-6F5C-4D91-991F-867B7898F981}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AAFAB03A-6F5C-4D91-991F-867B7898F981}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AAFAB03A-6F5C-4D91-991F-867B7898F981}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AAFAB03A-6F5C-4D91-991F-867B7898F981}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {88AA58C4-40E6-4FC6-B501-6557C7E9DA81} EndGlobalSection EndGlobal diff --git a/FarmmapsNbs/FarmmapsNbs.csproj.user b/FarmmapsNbs/FarmmapsNbs.csproj.user new file mode 100644 index 0000000..c41d825 --- /dev/null +++ b/FarmmapsNbs/FarmmapsNbs.csproj.user @@ -0,0 +1,10 @@ + + + + false + FarmmapsNbs + + + ProjectDebugger + + \ No newline at end of file diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index 80e6b73..ee6eda3 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -34,6 +34,7 @@ namespace FarmmapsNbs public async Task RunAsync() { var nitrogenInputJson = File.ReadAllText("NitrogenInput.json"); + //var nitrogenInputJson = File.ReadAllText("fivefieldsinput.json"); List nitrogenInputs = JsonConvert.DeserializeObject>(nitrogenInputJson); if (!Directory.Exists(DownloadFolder)) @@ -75,6 +76,7 @@ namespace FarmmapsNbs return; } + var cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, $"VRA NBS cropfield {input.OutputFileName}", plantingDate.Year, input.GeometryJson.ToString(Formatting.None)); @@ -98,6 +100,11 @@ namespace FarmmapsNbs return; } + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(geotiffItem.Code, + Path.Combine(DownloadFolder, $"{input.OutputFileName}.input_geotiff.zip")); + + _logger.LogInformation($"Calculating targetN with targetYield: {input.TargetYield}"); var targetNItem = await _nitrogenService.CreateTargetNItem(cropfieldItem); var targetNData = await _nitrogenService.CalculateTargetN(cropfieldItem, targetNItem, plantingDate, @@ -127,6 +134,7 @@ namespace FarmmapsNbs _logger.LogInformation("Downloading uptake map"); await _farmmapsApiService.DownloadItemAsync(uptakeMapItem.Code, Path.Combine(DownloadFolder, $"{input.OutputFileName}.uptake.zip")); + _logger.LogInformation("UptakeMap downloaded to {0}", Path.Combine(DownloadFolder, $"{input.OutputFileName}.uptake.zip")); _logger.LogInformation("Calculating application map"); var applicationMapItem = @@ -142,6 +150,29 @@ namespace FarmmapsNbs _logger.LogInformation("Downloading application map"); await _farmmapsApiService.DownloadItemAsync(applicationMapItem.Code, Path.Combine(DownloadFolder, $"{input.OutputFileName}.application.zip")); + _logger.LogInformation("Application map can be found in {0}", Path.Combine(DownloadFolder, $"{input.OutputFileName}.application.zip")); + + //transforming tiff to shape + var tiffItem = applicationMapItem; + + if (tiffItem == null) + { + _logger.LogError("Could not find item for uploaded data"); + return; + } + _logger.LogInformation($"Converting geotiff to shape"); + var taskmap = await _generalService.GeotiffToShape(tiffItem); + if (taskmap == null) + { + _logger.LogError("Something went wrong with geotiff to shape transformation"); + return; + } + + _logger.LogInformation("Downloading taskmap"); + await _farmmapsApiService.DownloadItemAsync(taskmap.Code, + Path.Combine(DownloadFolder, $"{input.OutputFileName}.taskmap.zip")); + + } } } \ No newline at end of file diff --git a/FarmmapsNbs/NitrogenService.cs b/FarmmapsNbs/NitrogenService.cs index 4256147..012325c 100644 --- a/FarmmapsNbs/NitrogenService.cs +++ b/FarmmapsNbs/NitrogenService.cs @@ -51,8 +51,8 @@ namespace FarmmapsNbs var nbsTargetNRequest = new TaskRequest {TaskType = VRANBS_TASK}; nbsTargetNRequest.attributes["operation"] = "targetn"; nbsTargetNRequest.attributes["inputCode"] = targetNItem.Code; - nbsTargetNRequest.attributes["plantingDate"] = plantingDate.ToString(); - nbsTargetNRequest.attributes["measurementDate"] = measurementDate.ToString(); + nbsTargetNRequest.attributes["plantingDate"] = plantingDate.ToString("o"); + nbsTargetNRequest.attributes["measurementDate"] = measurementDate.ToString("o"); nbsTargetNRequest.attributes["purposeType"] = purposeType.ToLower(); nbsTargetNRequest.attributes["targetYield"] = targetYield.ToString(); string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsTargetNRequest); @@ -89,9 +89,14 @@ namespace FarmmapsNbs var nbsUptakeMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; nbsUptakeMapRequest.attributes["operation"] = "uptake"; nbsUptakeMapRequest.attributes["inputCode"] = inputItem.Code; - nbsUptakeMapRequest.attributes["plantingDate"] = plantingDate.ToString(); - nbsUptakeMapRequest.attributes["measurementDate"] = measurementDate.ToString(); + nbsUptakeMapRequest.attributes["plantingDate"] = plantingDate.ToString("o"); + nbsUptakeMapRequest.attributes["measurementDate"] = measurementDate.ToString("o"); nbsUptakeMapRequest.attributes["inputType"] = inputType.ToLower(); + nbsUptakeMapRequest.attributes["inputLayerName"] = "IRMI"; //toevoeging FS. Kolom IRMI hernoemd als IMI. Deze wordt niet automatisch herkend. En moet dus gespecificeerd worden. + + var layers = inputItem.Data["layers"]; //toevoeging FS, check welke data lagen worden omgezet + _logger.LogInformation($"DataLayers: {layers}"); //toevoeging FS check welke data lagen worden omgezet + string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsUptakeMapRequest); @@ -140,21 +145,24 @@ namespace FarmmapsNbs var nbsApplicationMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; nbsApplicationMapRequest.attributes["operation"] = "application"; nbsApplicationMapRequest.attributes["inputCode"] = inputItem.Code; - nbsApplicationMapRequest.attributes["plantingDate"] = plantingDate.ToString(); - nbsApplicationMapRequest.attributes["measurementDate"] = measurementDate.ToString(); + nbsApplicationMapRequest.attributes["plantingDate"] = plantingDate.ToString("o"); + nbsApplicationMapRequest.attributes["measurementDate"] = measurementDate.ToString("o"); nbsApplicationMapRequest.attributes["inputCode"] = inputItem.Code; nbsApplicationMapRequest.attributes["inputType"] = inputType.ToLower(); nbsApplicationMapRequest.attributes["targetN"] = targetN.ToString(CultureInfo.InvariantCulture); - + string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsApplicationMapRequest); - + await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); + if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); - + + + var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if(itemTask.State == ItemTaskState.Error) { diff --git a/FarmmapsPoten/FarmmapsPoten.csproj b/FarmmapsPoten/FarmmapsPoten.csproj new file mode 100644 index 0000000..01fd4da --- /dev/null +++ b/FarmmapsPoten/FarmmapsPoten.csproj @@ -0,0 +1,21 @@ + + + + Exe + netcoreapp3.1 + + + + + Always + + + Always + + + + + + + + diff --git a/FarmmapsPoten/Models/PotenInput.cs b/FarmmapsPoten/Models/PotenInput.cs new file mode 100644 index 0000000..3bb03be --- /dev/null +++ b/FarmmapsPoten/Models/PotenInput.cs @@ -0,0 +1,18 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsPoten.Models +{ + public class PotenInput + { + public bool UseShadow { get; set; } + + public string File { get; set; } + public string OutputFileName { get; set; } + public string FieldName { get; set; } + public int PlantingYear { get; set; } + public string MeanDensity { get; set; } + public string Variation { get; set; } + public JObject GeometryJson { get; set; } + } +} \ No newline at end of file diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs new file mode 100644 index 0000000..ea24416 --- /dev/null +++ b/FarmmapsPoten/PotenApplication.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using FarmmapsApi; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmmapsPoten.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static FarmmapsApiSamples.Constants; + + +namespace FarmmapsVRApoten +{ + public class PotenApplication : IApplication + + { + private const string DownloadFolder = "Downloads"; + + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly PotenService _potenService; + private readonly GeneralService _generalService; + + + public PotenApplication(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, PotenService potenService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _potenService = potenService; + } + + public async Task RunAsync() + { + // read field data from separate json file + var VRAPotenInputJson = File.ReadAllText("PotenInput.json"); + List potenInputs = JsonConvert.DeserializeObject>(VRAPotenInputJson); + + + if (!Directory.Exists(DownloadFolder)) + Directory.CreateDirectory(DownloadFolder); + + // !! this call is needed the first time an api is called with a fresh clientid and secret !! + await _farmmapsApiService.GetCurrentUserCodeAsync(); + var roots = await _farmmapsApiService.GetCurrentUserRootsAsync(); + + foreach (var input in potenInputs) + { + try + { + await Process(roots, input); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + + private async Task Process(List roots, PotenInput input) + { + var meanDensity = input.MeanDensity; + var variation = input.Variation; + var fieldName = input.FieldName; + bool useShadow = input.UseShadow; + + var myDrive = roots.SingleOrDefault(r => r.Name == "My drive"); + if (myDrive == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); + if (uploadedRoot == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + _logger.LogInformation("Creating cropfield"); + + var cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, + $"VRA Poten cropfield {input.OutputFileName}", input.PlantingYear, + input.GeometryJson.ToString(Formatting.None)); + + //Calculating shadow map + if (useShadow) + { + _logger.LogInformation("Calculate shadow map for field"); + var shadowItem = await _generalService.RunShadowTask(cropfieldItem); + if (shadowItem == null) + { + _logger.LogError("Something went wrong while obtaining the shadow map"); + return; + } + + _logger.LogInformation("Downloading shadow map"); + await _farmmapsApiService.DownloadItemAsync(shadowItem.Code, + Path.Combine(DownloadFolder, $"{input.OutputFileName}.shadow.zip")); + } + + + _logger.LogInformation("Looking for local data to use"); + var localDataAvailable = input.File; + var geotiffItem = (Item) null; + + + if (String.IsNullOrEmpty(localDataAvailable)) + { + _logger.LogInformation("Could not find item for uploaded data, using BOFEK"); + + //Retreiving BOFEK + _logger.LogInformation("Get BOFEK for field"); + var bofekItem = await _generalService.RunBofekTask(cropfieldItem); + if (bofekItem == null) + { + _logger.LogError("Something went wrong while obtaining the BOFEK data"); + return; + } + + _logger.LogInformation("Downloading Bofek map"); + await _farmmapsApiService.DownloadItemAsync(bofekItem.Code, + Path.Combine(DownloadFolder, $"{input.OutputFileName}.BOFEK.zip")); + } + + else + { + var isGeoJson = input.File.Contains("json"); + var dataPath = Path.Combine("Data", input.File); + var shapeItem = isGeoJson + ? await _generalService.UploadDataAsync(uploadedRoot, SHAPE_PROCESSED_ITEMTYPE, dataPath, + Path.GetFileNameWithoutExtension(input.File)) + : await _generalService.UploadZipWithShapeAsync(uploadedRoot, dataPath, + Path.GetFileNameWithoutExtension(input.File)); + + if (shapeItem == null) + { + _logger.LogError("Something went wrong while searching for the shape file"); + return; + } + + // transform shape to geotiff as VRA poten only supports tiff input item + _logger.LogInformation($"Converting shape to geotiff"); + + geotiffItem = await _generalService.ShapeToGeotiff(shapeItem); + if (geotiffItem == null) + { + _logger.LogError("Something went wrong with shape to geotiff transformation"); + return; + } + + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(geotiffItem.Code, + Path.Combine(DownloadFolder, $"VRApoten_inputGeotiff_{input.OutputFileName}.zip")); + } + + // create appliance map + _logger.LogInformation("Calculating application map"); + + // INPUT IS NEEDED as GEOTIFF + var applianceMapItem = + await _potenService.CalculateApplicationMapAsync(cropfieldItem, geotiffItem, meanDensity, variation); + + if (applianceMapItem == null) + { + return; + } + + _logger.LogInformation("Downloading application map"); + await _farmmapsApiService.DownloadItemAsync(applianceMapItem.Code, + Path.Combine(DownloadFolder, $"VRApoten_appliancemap_{input.OutputFileName}.zip")); + + string finalOutput = Path.Combine(DownloadFolder, $"VRApoten_appliancemap_{input.OutputFileName}.zip"); + _logger.LogInformation(File.Exists(finalOutput) + ? "Download application map completed." + : "Something went wrong while downloading."); + + _logger.LogInformation($"Converting geotiff to shape"); + var taskmap = await _generalService.GeotiffToShape(applianceMapItem); + if (taskmap == null) + { + _logger.LogError("Something went wrong with geotiff to shape transformation"); + return; + } + + _logger.LogInformation("Downloading taskmap"); + await _farmmapsApiService.DownloadItemAsync(taskmap.Code, + Path.Combine(DownloadFolder, $"VRApoten_taskmap_{input.OutputFileName}.zip")); + } + } +} \ No newline at end of file diff --git a/FarmmapsPoten/PotenService.cs b/FarmmapsPoten/PotenService.cs new file mode 100644 index 0000000..7a839c2 --- /dev/null +++ b/FarmmapsPoten/PotenService.cs @@ -0,0 +1,91 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static FarmmapsApi.Extensions; +using static FarmmapsApiSamples.Constants; + + +namespace FarmmapsVRApoten +{ + public class PotenService + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly GeneralService _generalService; + + public PotenService(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + } + + public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem,string meanDensity, string variation) + { + var potenApplicationMapRequest = new TaskRequest() { TaskType = VRAPLANTING_TASK }; + if (inputItem != null) {potenApplicationMapRequest.attributes["inputCode"] = inputItem.Code; } + potenApplicationMapRequest.attributes["meanDensity"] = meanDensity; + potenApplicationMapRequest.attributes["variation"] = variation; + + //var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, potenApplicationMapRequest); + //await PollTask(TimeSpan.FromSeconds(3), async (tokenSource) => + //{ + // _logger.LogInformation("Checking VRAPoten task status"); + // var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); + // // Code + // if (itemTaskStatus.IsFinished) + // tokenSource.Cancel(); + //}); + + + + var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, potenApplicationMapRequest); + _logger.LogInformation($"itemTaskCode: {taskCode}"); + _logger.LogInformation($"potenTaskmapRequest: {potenApplicationMapRequest}"); + _logger.LogInformation($"potenTaskmapRequest type: {potenApplicationMapRequest.TaskType}"); + _logger.LogInformation($"cropfieldItemCode: {cropfieldItem.Code}"); + + + + await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { + var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); + _logger.LogInformation($"Waiting on calculation of application map; Status: {itemTaskStatus.State}"); + if (itemTaskStatus.IsFinished) + tokenSource.Cancel(); + }); + + + + var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); + if (itemTask.State == ItemTaskState.Error) + { + _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); + return null; + } + + var itemName = $"VRAPoten"; + var applianceMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, + GEOTIFF_PROCESSED_ITEMTYPE, itemName, + i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && + i.Name.ToLower().Contains(itemName.ToLower())); + + if (applianceMapItem == null) + { + _logger.LogError("Could not find the VRAPoten geotiff child item under cropfield"); + return null; + } + + return applianceMapItem; + + } + + + } +} + + diff --git a/FarmmapsPoten/Program.cs b/FarmmapsPoten/Program.cs new file mode 100644 index 0000000..a3fa657 --- /dev/null +++ b/FarmmapsPoten/Program.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using FarmmapsApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace FarmmapsVRApoten +{ + class Program : FarmmapsProgram + { + private static async Task Main(string[] args) + { + await new Program().Start(args); + } + + protected override void Configure(IServiceCollection serviceCollection) + { + serviceCollection.AddLogging(opts => opts + .AddConsole() + .AddFilter("System.Net.Http", LogLevel.Warning)) + .AddTransient(); + } + } +} \ No newline at end of file