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 1/3] 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 From 54272e13396b19ac923a4ac578e7c9c5ef4bd509 Mon Sep 17 00:00:00 2001 From: Fedde Sijbrandij Date: Tue, 8 Sep 2020 16:23:10 +0200 Subject: [PATCH 2/3] added input data for planting API + update of the input json. --- .../Data/PlantingSampleDataLutum.zip | Bin 0 -> 5865 bytes FarmmapsPoten/PotenInput.json | 24 ++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 FarmmapsPoten/Data/PlantingSampleDataLutum.zip create mode 100644 FarmmapsPoten/PotenInput.json diff --git a/FarmmapsPoten/Data/PlantingSampleDataLutum.zip b/FarmmapsPoten/Data/PlantingSampleDataLutum.zip new file mode 100644 index 0000000000000000000000000000000000000000..2c83115cb64bab668290cae81db59ccfceafedec GIT binary patch literal 5865 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 L2ZfRr{x$m_igikE literal 0 HcmV?d00001 diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json new file mode 100644 index 0000000..e67acba --- /dev/null +++ b/FarmmapsPoten/PotenInput.json @@ -0,0 +1,24 @@ +[ + { + "File": "PlantingSampleDataLutum.zip", + //"File": "Lutum_SampleDataPlanting.zip", + "OutputFileName": "vraPoten_SampleData", + "FieldName": "lutum", + "PlantingYear": 2020, + "MeanDensity": "30", + "Variation": "20", + "UseShadow": false, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 5.66886041703652044, 52.52929999060298627 ], + [ 5.6716230923214912, 52.52946316399909676 ], + [ 5.67185376229668581, 52.5280565894154563 ], + [ 5.66903207841337231, 52.52790646510525363 ], + [ 5.66886041703652044, 52.52929999060298627 ] + ] + ] + } + } +] From 1691e09bf944838dc16adfd9fe99421b428d1a3a Mon Sep 17 00:00:00 2001 From: Wilco Krikke Date: Thu, 10 Sep 2020 22:38:31 +0200 Subject: [PATCH 3/3] Added BlightApiClient --- FarmMapsBlight/BlightApplication.cs | 111 +++++++++++++++++++++++++++ FarmMapsBlight/BlightService.cs | 67 ++++++++++++++++ FarmMapsBlight/FarmMapsBlight.csproj | 18 +++++ FarmMapsBlight/Models/Settings.cs | 7 ++ FarmMapsBlight/Program.cs | 23 ++++++ FarmMapsBlight/appsettings.json | 12 +++ FarmmapsApi/Constants.cs | 4 +- FarmmapsApiSamples.sln | 8 +- FarmmapsPoten/FarmmapsPoten.csproj | 2 +- FarmmapsPoten/appsettings.json | 10 +++ 10 files changed, 258 insertions(+), 4 deletions(-) create mode 100644 FarmMapsBlight/BlightApplication.cs create mode 100644 FarmMapsBlight/BlightService.cs create mode 100644 FarmMapsBlight/FarmMapsBlight.csproj create mode 100644 FarmMapsBlight/Models/Settings.cs create mode 100644 FarmMapsBlight/Program.cs create mode 100644 FarmMapsBlight/appsettings.json create mode 100644 FarmmapsPoten/appsettings.json diff --git a/FarmMapsBlight/BlightApplication.cs b/FarmMapsBlight/BlightApplication.cs new file mode 100644 index 0000000..f98f47f --- /dev/null +++ b/FarmMapsBlight/BlightApplication.cs @@ -0,0 +1,111 @@ +using FarmmapsApi; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmMapsBlight.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + + +namespace FarmMapsBlight +{ + public class BlightApplication : IApplication + { + private const string DownloadFolder = "Downloads"; + private const string SettingsFile = "settings.json"; + + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly BlightService _blightService; + private readonly GeneralService _generalService; + + private Settings _settings; + + public BlightApplication(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, BlightService haulmkillingService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _blightService = haulmkillingService; + } + + public async Task RunAsync() + { + if (!Directory.Exists(DownloadFolder)) + Directory.CreateDirectory(DownloadFolder); + + LoadSettings(); + + // !! 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(); + + 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; + } + + Item cropfieldItem; + if (string.IsNullOrEmpty(_settings.CropfieldItemCode)) + { + _logger.LogInformation("Creating cropfield"); + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield Blight", 2020, + @"{""type"":""Polygon"",""coordinates"":[[[4.617786844284247,52.22533706956424],[4.618642601314543,52.225938364585989],[4.6192153806397,52.22563988897754],[4.619192414656403,52.2256242822442],[4.620306732153958,52.225031745661528],[4.620542019225217,52.22519855319158],[4.621157509147853,52.22487436515405],[4.623387917230182,52.22367660757213],[4.624563444939009,52.22304740241544],[4.624562779355982,52.223046635247019],[4.624534908813479,52.22302596787506],[4.627873021330343,52.221240670658399],[4.627504935938338,52.220104419135129],[4.627324878706837,52.22020569669098],[4.627320696113512,52.22020660117888],[4.626707169518044,52.22053923770041],[4.624700376420229,52.221619047547488],[4.623471571183885,52.22227447969577],[4.623471511010673,52.22227500174403],[4.623468838689317,52.22228052566992],[4.617786844284247,52.22533706956424]]]}"); + _settings.CropfieldItemCode = cropfieldItem.Code; + SaveSettings(); + } + else + { + _logger.LogInformation("Cropfield already exists trying to get"); + cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); + } + + DateTime plantingDate = new DateTime(2020, 3, 20); + DateTime emergeDate = new DateTime(2020, 5, 20); + + var blightItem = await _blightService.CreateAdvice(cropfieldItem, plantingDate, emergeDate); + if (blightItem == null) + { + return; + } + + // advice as json + var data = blightItem.Data; + } + + private void LoadSettings() + { + if (File.Exists(SettingsFile)) + { + var jsonText = File.ReadAllText(SettingsFile); + _settings = JsonConvert.DeserializeObject(jsonText); + } + else + { + _settings = new Settings(); + } + } + + private void SaveSettings() + { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(SettingsFile, json); + } + } +} \ No newline at end of file diff --git a/FarmMapsBlight/BlightService.cs b/FarmMapsBlight/BlightService.cs new file mode 100644 index 0000000..7dc4c93 --- /dev/null +++ b/FarmMapsBlight/BlightService.cs @@ -0,0 +1,67 @@ +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; +using static FarmmapsApi.Extensions; +using static FarmmapsApiSamples.Constants; + +namespace FarmMapsBlight +{ + public class BlightService + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly GeneralService _generalService; + + public BlightService(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + } + + public async Task CreateAdvice(Item cropfieldItem, DateTime plantingDate, DateTime emergeDate) + { + var taskRequest = new TaskRequest() + { + TaskType = "vnd.farmmaps.task.blight" + }; + + taskRequest.attributes["plantingDate"] = plantingDate.ToUniversalTime().ToString("o"); + taskRequest.attributes["emergeDate"] = emergeDate.ToUniversalTime().ToString("o"); + + var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskRequest); + await PollTask(TimeSpan.FromSeconds(3), async (tokenSource) => + { + _logger.LogInformation("Checking blight task status"); + var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); + 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 = $"Blight"; + var blightAdviceItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, + BLIGHT_ITEMTYPE, itemName); + + // incorrect filter task is finished after updating + + // i => i.Updated >= itemTask.Finished && i.Name.ToLower().Contains(itemName.ToLower()) + if (blightAdviceItem == null) + { + _logger.LogError("Could not find the blight item under cropfield"); + return null; + } + + return blightAdviceItem; + } + } +} \ No newline at end of file diff --git a/FarmMapsBlight/FarmMapsBlight.csproj b/FarmMapsBlight/FarmMapsBlight.csproj new file mode 100644 index 0000000..e39b1b4 --- /dev/null +++ b/FarmMapsBlight/FarmMapsBlight.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + Always + + + + diff --git a/FarmMapsBlight/Models/Settings.cs b/FarmMapsBlight/Models/Settings.cs new file mode 100644 index 0000000..524211e --- /dev/null +++ b/FarmMapsBlight/Models/Settings.cs @@ -0,0 +1,7 @@ +namespace FarmMapsBlight.Models +{ + public class Settings + { + public string CropfieldItemCode { get; set; } + } +} \ No newline at end of file diff --git a/FarmMapsBlight/Program.cs b/FarmMapsBlight/Program.cs new file mode 100644 index 0000000..a12ff49 --- /dev/null +++ b/FarmMapsBlight/Program.cs @@ -0,0 +1,23 @@ +using FarmmapsApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace FarmMapsBlight +{ + public 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 diff --git a/FarmMapsBlight/appsettings.json b/FarmMapsBlight/appsettings.json new file mode 100644 index 0000000..c81c149 --- /dev/null +++ b/FarmMapsBlight/appsettings.json @@ -0,0 +1,12 @@ +{ + "Authority": "https://accounts.farmmaps.awtest.nl/", + //"Endpoint": "http://farmmaps.awtest.nl", + //"Endpoint": "http://localhost:8095", + "Endpoint": "http://localhost:8083", + "BasePath": "api/v1", + "DiscoveryEndpointUrl": "https://accounts.farmmaps.awtest.nl/.well-known/openid-configuration", + "RedirectUri": "http://example.nl/api", + "ClientId": "", + "ClientSecret": "", + "Scopes": [ "api" ] +} \ No newline at end of file diff --git a/FarmmapsApi/Constants.cs b/FarmmapsApi/Constants.cs index d8d1bdc..ddb8e9c 100644 --- a/FarmmapsApi/Constants.cs +++ b/FarmmapsApi/Constants.cs @@ -10,7 +10,8 @@ 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 BLIGHT_ITEMTYPE = "vnd.farmmaps.itemtype.blight"; + 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"; @@ -21,6 +22,5 @@ namespace FarmmapsApiSamples 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"; - } } diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index 3d96af1..f4c5fdb 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -17,7 +17,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsHerbicide", "Farmma EndProject 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}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsPoten", "FarmmapsPoten\FarmmapsPoten.csproj", "{AAFAB03A-6F5C-4D91-991F-867B7898F981}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmMapsBlight", "FarmMapsBlight\FarmMapsBlight.csproj", "{892E0932-5D11-4A37-979E-CEDB39C2E181}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -45,6 +47,10 @@ Global {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 + {892E0932-5D11-4A37-979E-CEDB39C2E181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {892E0932-5D11-4A37-979E-CEDB39C2E181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {892E0932-5D11-4A37-979E-CEDB39C2E181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {892E0932-5D11-4A37-979E-CEDB39C2E181}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FarmmapsPoten/FarmmapsPoten.csproj b/FarmmapsPoten/FarmmapsPoten.csproj index 01fd4da..f76d9da 100644 --- a/FarmmapsPoten/FarmmapsPoten.csproj +++ b/FarmmapsPoten/FarmmapsPoten.csproj @@ -1,4 +1,4 @@ - + Exe diff --git a/FarmmapsPoten/appsettings.json b/FarmmapsPoten/appsettings.json new file mode 100644 index 0000000..6d97d71 --- /dev/null +++ b/FarmmapsPoten/appsettings.json @@ -0,0 +1,10 @@ +{ + "Authority": "https://accounts.farmmaps.awtest.nl/", + "Endpoint": "http://localhost:8095/", + "BasePath": "api/v1", + "DiscoveryEndpointUrl": "https://accounts.farmmaps.awtest.nl/.well-known/openid-configuration", + "RedirectUri": "http://example.nl/api", + "ClientId": "", + "ClientSecret": "", + "Scopes": ["api"] +} \ No newline at end of file