From 02708ba01defb9b4095a874009aa67779638126b Mon Sep 17 00:00:00 2001 From: Riepma Date: Fri, 11 Dec 2020 14:02:01 +0100 Subject: [PATCH 01/35] Eerdere aanpassingen NBS en zonering verwerkt --- FarmmapsApi/Services/GeneralService.cs | 85 ++++++ FarmmapsNbs/Models/NitrogenInput.cs | 3 + FarmmapsNbs/NbsApplication.cs | 127 +++++++-- FarmmapsNbs/NitrogenInput.json | 315 +++++++++++++--------- FarmmapsNbs/NitrogenService.cs | 23 +- FarmmapsZonering/Models/InputParameter.cs | 2 +- FarmmapsZonering/ZoneringApplication.cs | 89 ++---- 7 files changed, 429 insertions(+), 215 deletions(-) diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index e553174..3a04ca2 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -147,6 +147,8 @@ namespace FarmmapsApi.Services { _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) @@ -268,5 +270,88 @@ namespace FarmmapsApi.Services 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); + + + // find ndvi or wdvi 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.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 elment = Int32.Parse(Console.ReadLine()); + + var selectedSatelliteItem = satelliteTiffs[elment]; + + if (selectedSatelliteItem == null) + { + _logger.LogError("Satellite item not found"); + + } + + return selectedSatelliteItem; + + + + + + } + + + + } } \ No newline at end of file diff --git a/FarmmapsNbs/Models/NitrogenInput.cs b/FarmmapsNbs/Models/NitrogenInput.cs index 3a42b37..87fb186 100644 --- a/FarmmapsNbs/Models/NitrogenInput.cs +++ b/FarmmapsNbs/Models/NitrogenInput.cs @@ -13,5 +13,8 @@ namespace FarmmapsNbs.Models public string PotatoPurposeType { get; set; } public int TargetYield { get; set; } public JObject GeometryJson { get; set; } + public string InputLayerName { get; set; } + + } } \ No newline at end of file diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index 28e8d65..23c4ce0 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -9,6 +9,7 @@ using FarmmapsApi.Services; using FarmmapsNbs.Models; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using static FarmmapsApiSamples.Constants; namespace FarmmapsNbs @@ -16,12 +17,15 @@ namespace FarmmapsNbs public class NbsApplication : IApplication { private const string DownloadFolder = "Downloads"; + private const string SettingsFile = "settings.json"; private readonly ILogger _logger; private readonly FarmmapsApiService _farmmapsApiService; private readonly NitrogenService _nitrogenService; private readonly GeneralService _generalService; + private Settings _settings; + public NbsApplication(ILogger logger, FarmmapsApiService farmmapsApiService, GeneralService generalService, NitrogenService nitrogenService) { @@ -40,6 +44,10 @@ namespace FarmmapsNbs 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(); @@ -59,6 +67,8 @@ namespace FarmmapsNbs private async Task Process(List roots, NitrogenInput input) { + // !!specify if you are using an already created cropfield: + bool useCreatedCropfield = false; var plantingDate = input.PlantingDate; var measurementDate = input.MeasurementDate; @@ -75,24 +85,83 @@ namespace FarmmapsNbs _logger.LogError("Could not find a needed root item"); return; } + - var cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, - $"VRA NBS cropfield {input.OutputFileName}", plantingDate.Year, input.GeometryJson.ToString(Formatting.None)); + // Use already created cropfield or create new one + Item cropfieldItem; + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) + { + _logger.LogInformation("Creating cropfield"); + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, + $"VRA NBS cropfield {input.OutputFileName}", plantingDate.Year, input.GeometryJson.ToString(Formatting.None)); + _settings.CropfieldItemCode = cropfieldItem.Code; + SaveSettings(); + } + else + { + _logger.LogInformation("Cropfield already exists, trying to get it"); + cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); + } + + var geotiffItem = (Item)null; - if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { - _logger.LogInformation("input = tiff data"); - var dataPath = Path.Combine("Data", input.File); - geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, - Path.GetFileNameWithoutExtension(input.File)); + // No file input, use most recent satellite image + if (string.IsNullOrEmpty(input.File)) + { + _logger.LogInformation("No specific data given, retrieving most recent satellite image"); - if (geotiffItem == null) { - _logger.LogError("Could not find item for uploaded data"); - return; - } + // check if satellite task not yet done, do here and save taskcode + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) + { + var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); + _settings.SatelliteTaskCode = satelliteTaskCode; + SaveSettings(); } - + + + // Select a particular satellite item from satelliteTask + Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode); + + + // must be wdvi[1] + var inputType = (satalliteItem.Data["layers"] as JArray)?[1]["name"].ToString(); + if (string.IsNullOrEmpty(inputType)) + { + _logger.LogError("Could not get the input type name from the satellite item"); + return; + } + + // download the geotiff + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, + Path.Combine(DownloadFolder, $"nbs_inputSatelliteGeotiff_{input.OutputFileName}.zip")); + + // overwrite measurement date by date of satellite item + measurementDate = satalliteItem.DataDate.Value; + + geotiffItem = satalliteItem; + + + } + + + + // (geo)tiff input: + else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { + _logger.LogInformation("input = tiff data"); + var dataPath = Path.Combine("Data", input.File); + geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, + Path.GetFileNameWithoutExtension(input.File)); + + if (geotiffItem == null) { + _logger.LogError("Could not find item for uploaded data"); + return; + } + } + + // json/shape input else { var isGeoJson = input.File.Contains("json"); var dataPath = Path.Combine("Data", input.File); @@ -117,6 +186,12 @@ namespace FarmmapsNbs 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, @@ -139,7 +214,7 @@ namespace FarmmapsNbs _logger.LogInformation("Calculating uptake map"); var uptakeMapItem = await _nitrogenService.CalculateUptakeMap(cropfieldItem, geotiffItem, plantingDate, - measurementDate, input.InputVariable); + measurementDate, input.InputVariable, input.InputLayerName); if (uptakeMapItem == null) { _logger.LogError("Something went wrong with creating the uptakeMap"); @@ -154,8 +229,7 @@ namespace FarmmapsNbs _logger.LogInformation("Calculating application map"); var applicationMapItem = await _nitrogenService.CalculateApplicationMap(cropfieldItem, geotiffItem, plantingDate, - measurementDate, - input.InputVariable, targetNData.TargetN); + measurementDate, input.InputVariable, targetNData.TargetN, input.InputLayerName); if (applicationMapItem == null) { @@ -189,5 +263,28 @@ namespace FarmmapsNbs } + + // Functions to save previously created cropfields + 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/FarmmapsNbs/NitrogenInput.json b/FarmmapsNbs/NitrogenInput.json index 7f69b17..ef495d1 100644 --- a/FarmmapsNbs/NitrogenInput.json +++ b/FarmmapsNbs/NitrogenInput.json @@ -1,126 +1,195 @@ [ - { - "file": "Scan_1_20190605.json", - "inputVariable": "irmi", - "outputFileName": "vranbs1", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-05", - "potatoPurposeType": "consumption", - "targetYield": 45, - "geometryJson": { "type": "Polygon", "coordinates": [ [ [ 3.40843828875524, 50.638966444680605 ], [ 3.408953272886064, 50.639197789621612 ], [ 3.409242951459603, 50.639469958681836 ], [ 3.409328782148028, 50.639612846807708 ], [ 3.409457528180712, 50.639789755314411 ], [ 3.409639918393741, 50.640014292074966 ], [ 3.409833037442765, 50.640211611372706 ], [ 3.410069071836049, 50.640395321698435 ], [ 3.410380208081761, 50.640572227259661 ], [ 3.410605513638958, 50.640715112034222 ], [ 3.411925160474145, 50.641177783561204 ], [ 3.411935889310142, 50.640728720085136 ], [ 3.412590348309737, 50.63948356709389 ], [ 3.413244807309242, 50.638224772339846 ], [ 3.413400375432099, 50.637901562841307 ], [ 3.413539850300779, 50.637449065809889 ], [ 3.413475477284437, 50.637418445552932 ], [ 3.40999396998362, 50.637449065810451 ], [ 3.409940325803365, 50.638102293212661 ], [ 3.409575545377398, 50.638483338338325 ], [ 3.409060561246574, 50.638707881340494 ], [ 3.40843828875524, 50.638966444680605 ] ] ] } - }, - { - "file": "Scan_1_20190605.zip", - "inputVariable": "irmi", - "outputFileName": "vranbs2", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-05", - "potatoPurposeType": "starch", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] + { + "file": "", // keep emptpy to use satellite image + "inputVariable": "wdvi", + "InputLayerName": "wdvi", + "outputFileName": "rtest1", + "plantingDate": "2020-05-01", + "measurementDate": "2020-06-14", + "potatoPurposeType": "consumption", + "targetYield": 60, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 4.960707146896585, 52.800583669708487 ], + [ 4.960645975538824, 52.800470217610922 ], + [ 4.962140695752897, 52.799177147194797 ], + [ 4.967523821195745, 52.801502400041208 ], + [ 4.966336768950911, 52.802543735879809 ], + [ 4.961711880764330, 52.801009996856429 ], + [ 4.960707146896585, 52.800583669708487 ] + ] + ] + } } - }, - { - "file": "Scan_1_20190605.zip", - "inputVariable": "irmi", - "outputFileName": "vranbs3", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-20", - "potatoPurposeType": "starch", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] - } - }, - { - "file": "Scan_1_20190605.zip", - "inputVariable": "irmi", - "outputFileName": "vranbs4", - "plantingDate": "2019-04-18", - "measurementDate": "2019-07-03", - "potatoPurposeType": "starch", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] - } - } + //, + //{ + // "file": "", // keep emptpy to use satellite image + // "inputVariable": "wdvi", + // "InputLayerName": "wdvi", + // "outputFileName": "mtest1", + // "plantingDate": "2020-04-01", + // "measurementDate": "2020-06-24", + // "potatoPurposeType": "consumption", + // "targetYield": 60, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.575458869234128, 51.308707885669762 ], + // [ 3.575508957999423, 51.30878478562019 ], + // [ 3.576188404403633, 51.309372997559777 ], + // [ 3.576188872410267, 51.309374219701091 ], + // [ 3.576210290749152, 51.309430091473608 ], + // [ 3.57621266537704, 51.309483685674898 ], + // [ 3.576213477455834, 51.309502027981374 ], + // [ 3.577543136860447, 51.310682367527122 ], + // [ 3.57796629328866, 51.31104321405175 ], + // [ 3.578442479292087, 51.311449273042747 ], + // [ 3.57866702353106, 51.311636072786726 ], + // [ 3.57880446997978, 51.31157117599529 ], + // [ 3.579155910205885, 51.311863542729718 ], + // [ 3.579175814007489, 51.311875435159394 ], + // [ 3.579293885246395, 51.311936532835396 ], + // [ 3.579413896180069, 51.311998649478575 ], + // [ 3.579514543462617, 51.312041110734917 ], + // [ 3.579611760655688, 51.312082118352606 ], + // [ 3.579635115371588, 51.312093949652223 ], + // [ 3.579793143414486, 51.312189437140432 ], + // [ 3.579966991648108, 51.312286148850511 ], + // [ 3.580079704980967, 51.312332458349751 ], + // [ 3.580203717638148, 51.312336471368539 ], + // [ 3.580307101018293, 51.312330239539847 ], + // [ 3.580383836270609, 51.312317097185243 ], + // [ 3.580505207977176, 51.312279163554869 ], + // [ 3.580610387713855, 51.312233723091026 ], + // [ 3.5806309754483, 51.312226093729677 ], + // [ 3.580638516049738, 51.312223727082049 ], + // [ 3.58075536599681, 51.312186990706344 ], + // [ 3.580787745633303, 51.312176820129551 ], + // [ 3.580829682241423, 51.312167665428959 ], + // [ 3.58086828456562, 51.312162614898625 ], + // [ 3.580980493636721, 51.312147935609723 ], + // [ 3.581014352632766, 51.312145662592656 ], + // [ 3.581028980583245, 51.312145592849248 ], + // [ 3.581119189823368, 51.312145103215911 ], + // [ 3.581195330198145, 51.312144693075908 ], + // [ 3.581243537809229, 51.312148741603245 ], + // [ 3.58132480221972, 51.312163110548383 ], + // [ 3.581426517039001, 51.312181089466016 ], + // [ 3.581448095953263, 51.312184910295024 ], + // [ 3.581474337475052, 51.312191038736145 ], + // [ 3.581709405982819, 51.312260068944951 ], + // [ 3.581727319558337, 51.312266163697757 ], + // [ 3.581753583356718, 51.312276407881612 ], + // [ 3.581841655772683, 51.312310743468075 ], + // [ 3.581878851795624, 51.312325234405343 ], + // [ 3.581905889860924, 51.312338260798654 ], + // [ 3.581906254501594, 51.312338472113815 ], + // [ 3.582048236499295, 51.312422439022804 ], + // [ 3.58189849928915, 51.312481944577037 ], + // [ 3.583044298383354, 51.312095780444281 ], + // [ 3.582984006671231, 51.312017283925279 ], + // [ 3.582743535862999, 51.311699343434064 ], + // [ 3.582628599916243, 51.311582190756774 ], + // [ 3.581446834435509, 51.310511259982569 ], + // [ 3.580621864908701, 51.309767270412806 ], + // [ 3.579610575760466, 51.308860440593946 ], + // [ 3.579112608916012, 51.308394999612226 ], + // [ 3.578688808506157, 51.307968441218165 ], + // [ 3.578394256007207, 51.307644098092617 ], + // [ 3.578355980318371, 51.307607614964702 ], + // [ 3.578217977585775, 51.307654179547846 ], + // [ 3.577480636332469, 51.307921035607997 ], + // [ 3.575695560441006, 51.308577167973212 ], + // [ 3.575668643609169, 51.30855384769157 ], + // [ 3.575666204524265, 51.308551734020703 ], + // [ 3.575506397192348, 51.308609906947261 ], + // [ 3.575459139533024, 51.308653178431456 ], + // [ 3.575458869234128, 51.308707885669762 ] + // ] + // ] + // } + //} + //, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "InputLayerName": "IRMI", + // "outputFileName": "vranbs3", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-06-20", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } + //} + //, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "InputLayerName": "", + // "outputFileName": "vranbs4", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-07-03", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } + //} ] \ No newline at end of file diff --git a/FarmmapsNbs/NitrogenService.cs b/FarmmapsNbs/NitrogenService.cs index f4e392c..992a3d3 100644 --- a/FarmmapsNbs/NitrogenService.cs +++ b/FarmmapsNbs/NitrogenService.cs @@ -74,7 +74,7 @@ namespace FarmmapsNbs var item = await _farmmapsApiService.GetItemAsync(targetNItem.Code); return item.Data.ToObject(); } - + /// /// Calculates the uptake map based on the given inputs /// @@ -82,9 +82,11 @@ namespace FarmmapsNbs /// /// The date the crop is planted /// The date the measurements are taken + /// Data type, could be yara, ci, irmi or wdvi + /// Column name in which the sensor value is stored /// public async Task CalculateUptakeMap(Item cropfieldItem, Item inputItem, DateTime plantingDate, - DateTime measurementDate, string inputType) + DateTime measurementDate, string inputType, string inputLayerName) { var nbsUptakeMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; nbsUptakeMapRequest.attributes["operation"] = "uptake"; @@ -92,10 +94,9 @@ namespace FarmmapsNbs 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. + if (!(string.IsNullOrEmpty(inputLayerName))) nbsUptakeMapRequest.attributes["inputLayerName"] = inputLayerName; + //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); @@ -116,9 +117,10 @@ namespace FarmmapsNbs var itemName = "VRANbs uptake"; var uptakeMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, - GEOTIFF_PROCESSED_ITEMTYPE, itemName, + GEOTIFF_PROCESSED_ITEMTYPE, itemName, i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && - i.Name.ToLower().Contains(itemName.ToLower())); + i.Name.ToLower().Contains(itemName.ToLower())); + if (uptakeMapItem == null) { _logger.LogError("Could not find the uptake geotiff child item under cropfield"); @@ -136,11 +138,11 @@ namespace FarmmapsNbs /// The farmmaps item containing the geotiff data /// The date the crop is planted /// The date the measurements are taken - /// The inputtype to use + /// The inputtype to use, could be yara, ci, irmi or wdvi /// The target nitrogen to use for the calculations /// public async Task CalculateApplicationMap(Item cropfieldItem, Item inputItem, DateTime plantingDate, - DateTime measurementDate, string inputType, double targetN) + DateTime measurementDate, string inputType, double targetN, string inputLayerName) { var nbsApplicationMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; nbsApplicationMapRequest.attributes["operation"] = "application"; @@ -150,7 +152,8 @@ namespace FarmmapsNbs nbsApplicationMapRequest.attributes["inputCode"] = inputItem.Code; nbsApplicationMapRequest.attributes["inputType"] = inputType.ToLower(); nbsApplicationMapRequest.attributes["targetN"] = targetN.ToString(CultureInfo.InvariantCulture); - + if (!(string.IsNullOrEmpty(inputLayerName))) nbsApplicationMapRequest.attributes["inputLayerName"] = inputLayerName; + string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsApplicationMapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => diff --git a/FarmmapsZonering/Models/InputParameter.cs b/FarmmapsZonering/Models/InputParameter.cs index 35f25d2..536ae76 100644 --- a/FarmmapsZonering/Models/InputParameter.cs +++ b/FarmmapsZonering/Models/InputParameter.cs @@ -1,4 +1,4 @@ -namespace FarmmapsHaulmkilling.Models +namespace FarmmapsZonering.Models { public class InputParameter { diff --git a/FarmmapsZonering/ZoneringApplication.cs b/FarmmapsZonering/ZoneringApplication.cs index 375b7f9..27ace6f 100644 --- a/FarmmapsZonering/ZoneringApplication.cs +++ b/FarmmapsZonering/ZoneringApplication.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -40,83 +41,33 @@ namespace FarmmapsZonering if (!Directory.Exists(DownloadFolder)) Directory.CreateDirectory(DownloadFolder); + // Load settings from previous cropfield LoadSettings(); + + // Read input data from separate file + var zoneringInputJson = File.ReadAllText("ZoneringInput.json"); + List zoneringInputs = JsonConvert.DeserializeObject>(zoneringInputJson); // !! 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(); - - // await SimpleAdditionAsync(roots); - await ZoningAsync(roots); - } - - private async Task SimpleAdditionAsync(List roots) - { - 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 VRA Zonering", 2020, - @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); - _settings.CropfieldItemCode = cropfieldItem.Code; - SaveSettings(); - } - else - { - _logger.LogInformation("Cropfield already exists trying to get"); - cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); - } - var inputOneItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, - Path.Combine("Data", "data_9001.tif"),"data_9001"); - if (inputOneItem == null) { - _logger.LogError("Could not find item for uploaded data"); - return; - } - var inputTwoItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, - Path.Combine("Data", "data_times_two_4326.tiff"), "data_times_two_4326"); - if (inputTwoItem == null) { - _logger.LogError("Could not find item for uploaded data"); - return; + foreach (var input in zoneringInputs) + { + try + { + await ZoningAsync(roots, input); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } } - var outputItem = await _zoneringService.CreateApplicationMapAsync(cropfieldItem, "[0] + [1]", new Output() - { - Name = "add", - Unit = "n/kg", - Quantity = "Nitrogen" - }, new InputParameter() - { - ItemCode = inputOneItem.Code, - LayerName = inputOneItem.Data["layers"][0]["name"].ToString() - }, - new InputParameter() - { - ItemCode = inputTwoItem.Code, - LayerName = inputTwoItem.Data["layers"][0]["name"].ToString() - }); - - _logger.LogInformation("Downloading output"); - await _farmmapsApiService.DownloadItemAsync(outputItem.Code, - Path.Combine(DownloadFolder, $"times_2_zonering.zip")); } - private async Task ZoningAsync(List roots) + private async Task ZoningAsync(List roots, ZoneringInput input) { var myDrive = roots.SingleOrDefault(r => r.Name == "My drive"); if (myDrive == null) @@ -136,6 +87,12 @@ namespace FarmmapsZonering if (string.IsNullOrEmpty(_settings.CropfieldItemCode)) { _logger.LogInformation("Creating cropfield"); + + //var cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, input.CropFieldName, input.cropYear + //$"VRA Poten cropfield {input.OutputFileName}", + //input.GeometryJson.ToString(Formatting.None)); + + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield VRA Zonering", 2020, @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); _settings.CropfieldItemCode = cropfieldItem.Code; From f3a2b338105ea8dc297be2ab5612d3482c590c5f Mon Sep 17 00:00:00 2001 From: Fedde Sijbrandij Date: Tue, 15 Dec 2020 16:28:19 +0100 Subject: [PATCH 02/35] update Blight --- FarmMapsBlight/BlightApplication.cs | 4 +- FarmMapsBlight/BlightService.cs | 6 +- FarmMapsBlight/FarmMapsBlight.csproj | 2 +- FarmmapsNbs/NitrogenInput.json | 230 +++++++++++++++++---------- 4 files changed, 149 insertions(+), 93 deletions(-) diff --git a/FarmMapsBlight/BlightApplication.cs b/FarmMapsBlight/BlightApplication.cs index 8267744..6b034d3 100644 --- a/FarmMapsBlight/BlightApplication.cs +++ b/FarmMapsBlight/BlightApplication.cs @@ -74,8 +74,8 @@ namespace FarmMapsBlight cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); } - DateTime adviceDate = DateTime.Now.Date; - DateTime plantingDate = new DateTime(2020, 3, 20); + DateTime adviceDate = new DateTime(2020, 7, 7);// DateTime.Now.Date; + DateTime plantingDate = new DateTime(2020, 4, 20); DateTime emergeDate = new DateTime(2020, 5, 20); var blightItem = await _blightService.CreateAdvice(cropfieldItem, adviceDate, plantingDate, emergeDate); diff --git a/FarmMapsBlight/BlightService.cs b/FarmMapsBlight/BlightService.cs index fb050fb..578a134 100644 --- a/FarmMapsBlight/BlightService.cs +++ b/FarmMapsBlight/BlightService.cs @@ -32,12 +32,12 @@ namespace FarmMapsBlight TaskType = "vnd.farmmaps.task.blight" }; - taskRequest.attributes["d"] = DateTime.Now.Date.ToString("o"); + taskRequest.attributes["d"] = adviceDate.ToUniversalTime().ToString("o"); taskRequest.attributes["plantingDate"] = plantingDate.ToUniversalTime().ToString("o"); taskRequest.attributes["emergeDate"] = emergeDate.ToUniversalTime().ToString("o"); List sprays = new List(); - sprays.Add(new Spray() { fungicideCode = "FLEX", SprayTime = new DateTime(2020, 9, 1), dose = 0.6, isVRA = false }); + sprays.Add(new Spray() { fungicideCode = "FLEX", SprayTime = new DateTime(2020, 7, 1), dose = 0.6, isVRA = false }); taskRequest.attributes["sprays"] = JsonConvert.SerializeObject(sprays); List irrigations = new List(); @@ -47,8 +47,8 @@ namespace FarmMapsBlight 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); + _logger.LogInformation($"Checking blight task status: { itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); diff --git a/FarmMapsBlight/FarmMapsBlight.csproj b/FarmMapsBlight/FarmMapsBlight.csproj index 052b891..37ce34b 100644 --- a/FarmMapsBlight/FarmMapsBlight.csproj +++ b/FarmMapsBlight/FarmMapsBlight.csproj @@ -11,7 +11,7 @@ - Never + always diff --git a/FarmmapsNbs/NitrogenInput.json b/FarmmapsNbs/NitrogenInput.json index 7f69b17..ab9ebc6 100644 --- a/FarmmapsNbs/NitrogenInput.json +++ b/FarmmapsNbs/NitrogenInput.json @@ -1,98 +1,12 @@ [ { "file": "Scan_1_20190605.json", - "inputVariable": "irmi", + "inputVariable": "irmi", "outputFileName": "vranbs1", "plantingDate": "2019-04-18", "measurementDate": "2019-06-05", "potatoPurposeType": "consumption", "targetYield": 45, - "geometryJson": { "type": "Polygon", "coordinates": [ [ [ 3.40843828875524, 50.638966444680605 ], [ 3.408953272886064, 50.639197789621612 ], [ 3.409242951459603, 50.639469958681836 ], [ 3.409328782148028, 50.639612846807708 ], [ 3.409457528180712, 50.639789755314411 ], [ 3.409639918393741, 50.640014292074966 ], [ 3.409833037442765, 50.640211611372706 ], [ 3.410069071836049, 50.640395321698435 ], [ 3.410380208081761, 50.640572227259661 ], [ 3.410605513638958, 50.640715112034222 ], [ 3.411925160474145, 50.641177783561204 ], [ 3.411935889310142, 50.640728720085136 ], [ 3.412590348309737, 50.63948356709389 ], [ 3.413244807309242, 50.638224772339846 ], [ 3.413400375432099, 50.637901562841307 ], [ 3.413539850300779, 50.637449065809889 ], [ 3.413475477284437, 50.637418445552932 ], [ 3.40999396998362, 50.637449065810451 ], [ 3.409940325803365, 50.638102293212661 ], [ 3.409575545377398, 50.638483338338325 ], [ 3.409060561246574, 50.638707881340494 ], [ 3.40843828875524, 50.638966444680605 ] ] ] } - }, - { - "file": "Scan_1_20190605.zip", - "inputVariable": "irmi", - "outputFileName": "vranbs2", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-05", - "potatoPurposeType": "starch", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] - } - }, - { - "file": "Scan_1_20190605.zip", - "inputVariable": "irmi", - "outputFileName": "vranbs3", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-20", - "potatoPurposeType": "starch", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] - } - }, - { - "file": "Scan_1_20190605.zip", - "inputVariable": "irmi", - "outputFileName": "vranbs4", - "plantingDate": "2019-04-18", - "measurementDate": "2019-07-03", - "potatoPurposeType": "starch", - "targetYield": 45, "geometryJson": { "type": "Polygon", "coordinates": [ @@ -121,6 +35,148 @@ [ 3.40843828875524, 50.638966444680605 ] ] ] + //}, + //{ + //"file": "[...].json", + //"inputVariable": "wdvi", + //"outputFileName": "20201211_Mahindra", + //"plantingDate": "2020-11-25", + //"measurementDate": "2020-12-08", + //"potatoPurposeType": "consumption", + //"targetYield": 45, + //"geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 75.4652734163369, 31.26328617861426 ], + // [ 75.46527659380338, 31.26437743805827 ], + // [ 75.46494267602188, 31.26438166160194 ], + // [ 75.46493257618651, 31.26462192072676 ], + // [ 75.46438242791261, 31.26462132638865 ], + // [ 75.46438225728252, 31.26334267015003 ], + // [ 75.46448543502072, 31.26333463263533 ], + // [ 75.46448842093658, 31.26302891147988 ], + // [ 75.46507631089699, 31.26299589154944 ], + // [ 75.46509039016291, 31.26329023051392 ], + // [ 75.4652734163369, 31.26328617861426 ] + // ] + // ] + //} } + + //}, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "outputFileName": "vranbs2", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-06-05", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } + //}, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "outputFileName": "vranbs3", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-06-20", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } + //}, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "outputFileName": "vranbs4", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-07-03", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } } ] \ No newline at end of file From 751b154bb9e60506f1658cd589fed84e1908a73a Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Wed, 16 Dec 2020 09:34:11 +0100 Subject: [PATCH 03/35] added settings-file to NBS --- FarmmapsNbs/Models/Settings.cs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 FarmmapsNbs/Models/Settings.cs diff --git a/FarmmapsNbs/Models/Settings.cs b/FarmmapsNbs/Models/Settings.cs new file mode 100644 index 0000000..41b3853 --- /dev/null +++ b/FarmmapsNbs/Models/Settings.cs @@ -0,0 +1,6 @@ +namespace FarmmapsNbs { + public class Settings { + public string CropfieldItemCode { get; set; } + public string SatelliteTaskCode { get; set; } + } +} \ No newline at end of file From 59bde7690357e740b514e70fb2ccdf8b1951b5d7 Mon Sep 17 00:00:00 2001 From: Fedde Sijbrandij Date: Wed, 16 Dec 2020 10:09:24 +0100 Subject: [PATCH 04/35] added zoninginput, file and data --- FarmmapsZonering/Data/Points.json | 45 ++++++++++++++++++++++++++++ FarmmapsZonering/Data/Polygons.json | 40 +++++++++++++++++++++++++ FarmmapsZonering/ZoneringsInput.json | 20 +++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 FarmmapsZonering/Data/Points.json create mode 100644 FarmmapsZonering/Data/Polygons.json create mode 100644 FarmmapsZonering/ZoneringsInput.json diff --git a/FarmmapsZonering/Data/Points.json b/FarmmapsZonering/Data/Points.json new file mode 100644 index 0000000..7342db1 --- /dev/null +++ b/FarmmapsZonering/Data/Points.json @@ -0,0 +1,45 @@ +{ +"type": "FeatureCollection", +"name": "Points", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "Data_whole": 9, "Data_decimal": 0.1 }, "geometry": { "type": "Point", "coordinates": [ 5.670697160803186, 52.529191329839897 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.97 }, "geometry": { "type": "Point", "coordinates": [ 5.671065563240634, 52.529190448643625 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 9, "Data_decimal": 0.09 }, "geometry": { "type": "Point", "coordinates": [ 5.671433965661168, 52.5291895663036 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.29 }, "geometry": { "type": "Point", "coordinates": [ 5.669222114450619, 52.528970163883578 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.05 }, "geometry": { "type": "Point", "coordinates": [ 5.669590515080236, 52.528969287267856 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.35 }, "geometry": { "type": "Point", "coordinates": [ 5.669958915693023, 52.528968409508401 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.59 }, "geometry": { "type": "Point", "coordinates": [ 5.670327316288964, 52.528967530605286 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.16 }, "geometry": { "type": "Point", "coordinates": [ 5.670695716868035, 52.528966650558438 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 7, "Data_decimal": 0.57 }, "geometry": { "type": "Point", "coordinates": [ 5.671064117430213, 52.528965769367922 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 6, "Data_decimal": 0.4 }, "geometry": { "type": "Point", "coordinates": [ 5.671432517975476, 52.528964887033666 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.32 }, "geometry": { "type": "Point", "coordinates": [ 5.669220678033353, 52.528745484567601 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.16 }, "geometry": { "type": "Point", "coordinates": [ 5.669589076787721, 52.528744607957577 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 7, "Data_decimal": 0.36 }, "geometry": { "type": "Point", "coordinates": [ 5.669957475525263, 52.528743730203921 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.33 }, "geometry": { "type": "Point", "coordinates": [ 5.670325874245955, 52.528742851306561 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.86 }, "geometry": { "type": "Point", "coordinates": [ 5.670694272949777, 52.528741971265497 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 6, "Data_decimal": 0.61 }, "geometry": { "type": "Point", "coordinates": [ 5.671062671636706, 52.528741090080729 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 9, "Data_decimal": 0.7 }, "geometry": { "type": "Point", "coordinates": [ 5.671431070306722, 52.528740207752286 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 4, "Data_decimal": 0.73 }, "geometry": { "type": "Point", "coordinates": [ 5.669219241632893, 52.528520805240142 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 5, "Data_decimal": 0.54 }, "geometry": { "type": "Point", "coordinates": [ 5.669587638512033, 52.528519928635895 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.11 }, "geometry": { "type": "Point", "coordinates": [ 5.669956035374347, 52.528519050887994 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.41 }, "geometry": { "type": "Point", "coordinates": [ 5.670324432219813, 52.528518171996353 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.17 }, "geometry": { "type": "Point", "coordinates": [ 5.670692829048409, 52.52851729196108 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 5, "Data_decimal": 0.8 }, "geometry": { "type": "Point", "coordinates": [ 5.671061225860114, 52.528516410782132 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 4, "Data_decimal": 0.11 }, "geometry": { "type": "Point", "coordinates": [ 5.671429622654903, 52.528515528459451 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.31 }, "geometry": { "type": "Point", "coordinates": [ 5.669217805249236, 52.528296125901214 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 9, "Data_decimal": 0.07 }, "geometry": { "type": "Point", "coordinates": [ 5.669586200253172, 52.528295249302751 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.54 }, "geometry": { "type": "Point", "coordinates": [ 5.669954595240281, 52.528294371560584 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 8, "Data_decimal": 0.82 }, "geometry": { "type": "Point", "coordinates": [ 5.670322990210543, 52.528293492674734 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 7, "Data_decimal": 0.54 }, "geometry": { "type": "Point", "coordinates": [ 5.670691385163935, 52.528292612645224 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.35 }, "geometry": { "type": "Point", "coordinates": [ 5.671059780100435, 52.528291731472009 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.35 }, "geometry": { "type": "Point", "coordinates": [ 5.671428175020021, 52.528290849155169 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 8, "Data_decimal": 0.13 }, "geometry": { "type": "Point", "coordinates": [ 5.669216368882385, 52.528071446550889 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.51 }, "geometry": { "type": "Point", "coordinates": [ 5.669584762011135, 52.528070569958146 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 7, "Data_decimal": 0.52 }, "geometry": { "type": "Point", "coordinates": [ 5.669953155123062, 52.528069692221734 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 8, "Data_decimal": 0.86 }, "geometry": { "type": "Point", "coordinates": [ 5.670321548218141, 52.528068813341662 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.54 }, "geometry": { "type": "Point", "coordinates": [ 5.670689941296351, 52.528067933317907 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 7, "Data_decimal": 0.54 }, "geometry": { "type": "Point", "coordinates": [ 5.671058334357669, 52.528067052150526 ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.33 }, "geometry": { "type": "Point", "coordinates": [ 5.671426727402074, 52.528066169839413 ] } } +] +} diff --git a/FarmmapsZonering/Data/Polygons.json b/FarmmapsZonering/Data/Polygons.json new file mode 100644 index 0000000..03aee28 --- /dev/null +++ b/FarmmapsZonering/Data/Polygons.json @@ -0,0 +1,40 @@ +{ +"type": "FeatureCollection", +"name": "Polygons", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "Data_whole": 4, "Data_decimal": 0.17 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670445415146707, 52.529088625025459 ], [ 5.670436500109911, 52.529205353741368 ], [ 5.67100275425158, 52.529226856274043 ], [ 5.670445415146707, 52.529088625025459 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.17 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670474664765167, 52.528705646190737 ], [ 5.670509421984359, 52.528695389390876 ], [ 5.670475899473076, 52.528689479586824 ], [ 5.670474664765167, 52.528705646190737 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 8, "Data_decimal": 0.66 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670529486595214, 52.527987838516857 ], [ 5.670513853610409, 52.528192528445118 ], [ 5.671080652223759, 52.528222889234513 ], [ 5.671037195788521, 52.52802077466923 ], [ 5.670529486595214, 52.527987838516857 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 6, "Data_decimal": 0.87 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.671088061587392, 52.528257349960555 ], [ 5.670500031678031, 52.528373505423083 ], [ 5.670475899473076, 52.528689479586824 ], [ 5.670509421984359, 52.528695389390876 ], [ 5.67114210124075, 52.528508686753064 ], [ 5.671088061587392, 52.528257349960555 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.97 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.671208751860665, 52.528818676769426 ], [ 5.671298932270745, 52.529238103127824 ], [ 5.671622542615743, 52.529250391677266 ], [ 5.671678791037005, 52.528901541665995 ], [ 5.671208751860665, 52.528818676769426 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 8, "Data_decimal": 0.34 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.671805511114944, 52.528115629819482 ], [ 5.671223720783484, 52.528230552757869 ], [ 5.671782157848563, 52.528260465657667 ], [ 5.671805511114944, 52.528115629819482 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.93 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.671223720783484, 52.528230552757869 ], [ 5.671088061587392, 52.528257349960555 ], [ 5.67114210124075, 52.528508686753064 ], [ 5.671772111651965, 52.528322771687201 ], [ 5.671782157848563, 52.528260465657667 ], [ 5.671223720783484, 52.528230552757869 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 9, "Data_decimal": 0.88 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669598765293697, 52.528964122725455 ], [ 5.669756159705026, 52.52891767588612 ], [ 5.66960263338403, 52.528879598286942 ], [ 5.669598765293697, 52.528964122725455 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.65 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.66964340911196, 52.528542716867584 ], [ 5.669617818752811, 52.528547771814544 ], [ 5.66960263338403, 52.528879598286942 ], [ 5.669756159705026, 52.52891767588612 ], [ 5.670010186520852, 52.528842712978594 ], [ 5.670075070608809, 52.528618816025038 ], [ 5.66964340911196, 52.528542716867584 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.12 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669646065617165, 52.527930529158823 ], [ 5.669636227085067, 52.52814551803705 ], [ 5.670203425137223, 52.528175900222543 ], [ 5.670262935417118, 52.527970546787003 ], [ 5.669646065617165, 52.527930529158823 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 4, "Data_decimal": 0.36 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670124673219705, 52.528447651201468 ], [ 5.66964340911196, 52.528542716867584 ], [ 5.670075070608809, 52.528618816025038 ], [ 5.670124673219705, 52.528447651201468 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.25 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.66997288555807, 52.528971428233667 ], [ 5.669910879416426, 52.5291853941944 ], [ 5.670436500109911, 52.529205353741368 ], [ 5.670445415146707, 52.529088625025459 ], [ 5.66997288555807, 52.528971428233667 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 4, "Data_decimal": 0.32 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670010186520852, 52.528842712978594 ], [ 5.670474664765167, 52.528705646190737 ], [ 5.670475899473076, 52.528689479586824 ], [ 5.670075070608809, 52.528618816025038 ], [ 5.670010186520852, 52.528842712978594 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 5, "Data_decimal": 0.41 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670262935417118, 52.527970546787003 ], [ 5.670203425137223, 52.528175900222543 ], [ 5.670513853610409, 52.528192528445118 ], [ 5.670529486595214, 52.527987838516857 ], [ 5.670262935417118, 52.527970546787003 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 7, "Data_decimal": 0.8 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670500031678031, 52.528373505423083 ], [ 5.670124673219705, 52.528447651201468 ], [ 5.670075070608809, 52.528618816025038 ], [ 5.670475899473076, 52.528689479586824 ], [ 5.670500031678031, 52.528373505423083 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 9, "Data_decimal": 0.35 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.668970013365158, 52.529149666415094 ], [ 5.669598765293697, 52.528964122725455 ], [ 5.66960263338403, 52.528879598286942 ], [ 5.668980939909752, 52.528725405867227 ], [ 5.668923420100609, 52.52914789711555 ], [ 5.668970013365158, 52.529149666415094 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 4, "Data_decimal": 0.69 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669617818752811, 52.528547771814544 ], [ 5.668988191186747, 52.528672144197635 ], [ 5.668980939909752, 52.528725405867227 ], [ 5.66960263338403, 52.528879598286942 ], [ 5.669617818752811, 52.528547771814544 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.43 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669636227085067, 52.52814551803705 ], [ 5.669646065617165, 52.527930529158823 ], [ 5.669094033437861, 52.527894717682074 ], [ 5.669064060973, 52.528114869735482 ], [ 5.669636227085067, 52.52814551803705 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 6, "Data_decimal": 0.6 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669618253034461, 52.528538282012228 ], [ 5.669636227085067, 52.52814551803705 ], [ 5.669064060973, 52.528114869735482 ], [ 5.669020756513456, 52.528432947202063 ], [ 5.669618253034461, 52.528538282012228 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.39 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670509421984359, 52.528695389390876 ], [ 5.670474664765167, 52.528705646190737 ], [ 5.670445415146707, 52.529088625025459 ], [ 5.67100275425158, 52.529226856274043 ], [ 5.671298932270745, 52.529238103127824 ], [ 5.671208751860665, 52.528818676769426 ], [ 5.670509421984359, 52.528695389390876 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 3, "Data_decimal": 0.93 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.67114210124075, 52.528508686753064 ], [ 5.670509421984359, 52.528695389390876 ], [ 5.671208751860665, 52.528818676769426 ], [ 5.67114210124075, 52.528508686753064 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 5, "Data_decimal": 0.2 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.671772111651965, 52.528322771687201 ], [ 5.67114210124075, 52.528508686753064 ], [ 5.671208751860665, 52.528818676769426 ], [ 5.671678791037005, 52.528901541665995 ], [ 5.671772111651965, 52.528322771687201 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 6, "Data_decimal": 0.64 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669756159705026, 52.52891767588612 ], [ 5.669598765293697, 52.528964122725455 ], [ 5.669589198259692, 52.529173178902582 ], [ 5.669910879416426, 52.5291853941944 ], [ 5.66997288555807, 52.528971428233667 ], [ 5.669756159705026, 52.52891767588612 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 7, "Data_decimal": 0.2 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670010186520852, 52.528842712978594 ], [ 5.669756159705026, 52.52891767588612 ], [ 5.66997288555807, 52.528971428233667 ], [ 5.670010186520852, 52.528842712978594 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.66 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670474664765167, 52.528705646190737 ], [ 5.670010186520852, 52.528842712978594 ], [ 5.66997288555807, 52.528971428233667 ], [ 5.670445415146707, 52.529088625025459 ], [ 5.670474664765167, 52.528705646190737 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.06 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669598765293697, 52.528964122725455 ], [ 5.668970013365158, 52.529149666415094 ], [ 5.669589198259692, 52.529173178902582 ], [ 5.669598765293697, 52.528964122725455 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.62 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670500031678031, 52.528373505423083 ], [ 5.671088061587392, 52.528257349960555 ], [ 5.671080652223759, 52.528222889234513 ], [ 5.670513853610409, 52.528192528445118 ], [ 5.670500031678031, 52.528373505423083 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 1, "Data_decimal": 0.58 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.671223720783484, 52.528230552757869 ], [ 5.671805511114944, 52.528115629819482 ], [ 5.671812693849701, 52.528071082842963 ], [ 5.671037195788521, 52.52802077466923 ], [ 5.671080652223759, 52.528222889234513 ], [ 5.671223720783484, 52.528230552757869 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 2, "Data_decimal": 0.83 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.671088061587392, 52.528257349960555 ], [ 5.671223720783484, 52.528230552757869 ], [ 5.671080652223759, 52.528222889234513 ], [ 5.671088061587392, 52.528257349960555 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 5, "Data_decimal": 0.45 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.669617818752811, 52.528547771814544 ], [ 5.66964340911196, 52.528542716867584 ], [ 5.669618253034461, 52.528538282012228 ], [ 5.669617818752811, 52.528547771814544 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 4, "Data_decimal": 0.34 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.66964340911196, 52.528542716867584 ], [ 5.670124673219705, 52.528447651201468 ], [ 5.670203425137223, 52.528175900222543 ], [ 5.669636227085067, 52.52814551803705 ], [ 5.669618253034461, 52.528538282012228 ], [ 5.66964340911196, 52.528542716867584 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 10, "Data_decimal": 0.68 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.670124673219705, 52.528447651201468 ], [ 5.670500031678031, 52.528373505423083 ], [ 5.670513853610409, 52.528192528445118 ], [ 5.670203425137223, 52.528175900222543 ], [ 5.670124673219705, 52.528447651201468 ] ] ] } }, +{ "type": "Feature", "properties": { "Data_whole": 9, "Data_decimal": 0.97 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.668988191186747, 52.528672144197635 ], [ 5.669617818752811, 52.528547771814544 ], [ 5.669618253034461, 52.528538282012228 ], [ 5.669020756513456, 52.528432947202063 ], [ 5.668988191186747, 52.528672144197635 ] ] ] } } +] +} diff --git a/FarmmapsZonering/ZoneringsInput.json b/FarmmapsZonering/ZoneringsInput.json new file mode 100644 index 0000000..f3ac9f6 --- /dev/null +++ b/FarmmapsZonering/ZoneringsInput.json @@ -0,0 +1,20 @@ +[ + { + "File": "Points.json", + "OutputFileName": "Zoning", + "FieldName": "Data_whole", + "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 ab9a23221f75a7396e21e801473165f6e3aa5e55 Mon Sep 17 00:00:00 2001 From: Fedde Sijbrandij Date: Wed, 16 Dec 2020 10:23:24 +0100 Subject: [PATCH 05/35] kleine update zonering --- FarmmapsZonering/Models/ZoneringInput.cs | 14 ++++++++++++++ .../{ZoneringsInput.json => ZoneringInput.json} | 0 2 files changed, 14 insertions(+) create mode 100644 FarmmapsZonering/Models/ZoneringInput.cs rename FarmmapsZonering/{ZoneringsInput.json => ZoneringInput.json} (100%) diff --git a/FarmmapsZonering/Models/ZoneringInput.cs b/FarmmapsZonering/Models/ZoneringInput.cs new file mode 100644 index 0000000..7ac072b --- /dev/null +++ b/FarmmapsZonering/Models/ZoneringInput.cs @@ -0,0 +1,14 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsZonering { + public class ZoneringInput { + public bool UseShadow { get; set; } + + public string File { get; set; } + public string OutputFileName { get; set; } + public string FieldName { get; set; } + public JObject GeometryJson { get; set; } + + } +} \ No newline at end of file diff --git a/FarmmapsZonering/ZoneringsInput.json b/FarmmapsZonering/ZoneringInput.json similarity index 100% rename from FarmmapsZonering/ZoneringsInput.json rename to FarmmapsZonering/ZoneringInput.json From 764df7ce9111c2a24207ed836fb43fc9fc507b2e Mon Sep 17 00:00:00 2001 From: Riepma Date: Fri, 18 Dec 2020 10:22:23 +0100 Subject: [PATCH 06/35] "Inputfile created for zonering" --- FarmmapsZonering/FarmmapsZonering.csproj | 3 ++ FarmmapsZonering/Models/Settings.cs | 3 ++ FarmmapsZonering/Models/ZoneringInput.cs | 20 +++++++++++ FarmmapsZonering/Services/ZoneringService.cs | 2 +- FarmmapsZonering/ZoneringApplication.cs | 35 ++++++++++---------- FarmmapsZonering/ZoneringInput.json | 29 ++++++++++++++++ 6 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 FarmmapsZonering/Models/ZoneringInput.cs create mode 100644 FarmmapsZonering/ZoneringInput.json diff --git a/FarmmapsZonering/FarmmapsZonering.csproj b/FarmmapsZonering/FarmmapsZonering.csproj index dcc6344..10ba86c 100644 --- a/FarmmapsZonering/FarmmapsZonering.csproj +++ b/FarmmapsZonering/FarmmapsZonering.csproj @@ -15,6 +15,9 @@ Always + + PreserveNewest + diff --git a/FarmmapsZonering/Models/Settings.cs b/FarmmapsZonering/Models/Settings.cs index c3beb90..a44ce8d 100644 --- a/FarmmapsZonering/Models/Settings.cs +++ b/FarmmapsZonering/Models/Settings.cs @@ -3,5 +3,8 @@ namespace FarmmapsHaulmkilling.Models public class Settings { public string CropfieldItemCode { get; set; } + public string CropfieldName { get; set; } + + } } \ No newline at end of file diff --git a/FarmmapsZonering/Models/ZoneringInput.cs b/FarmmapsZonering/Models/ZoneringInput.cs new file mode 100644 index 0000000..52d022c --- /dev/null +++ b/FarmmapsZonering/Models/ZoneringInput.cs @@ -0,0 +1,20 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsZonering.Models +{ + public class ZoneringInput + { + public string InputItemOne { get; set; } + public string InputItemTwo { get; set; } + public string Formula { get; set; } + public string OutputFileName { get; set; } + public string CropFieldName { get; set; } + public string LayerName { get; set; } + public string CalculatedQuantity { get; set; } + public string CalculatedUnit { get; set; } + public bool CreateNewCropfield { get; set; } + public int CropYear { get; set; } + public JObject GeometryJson { get; set; } + } +} \ No newline at end of file diff --git a/FarmmapsZonering/Services/ZoneringService.cs b/FarmmapsZonering/Services/ZoneringService.cs index d736240..67e5e49 100644 --- a/FarmmapsZonering/Services/ZoneringService.cs +++ b/FarmmapsZonering/Services/ZoneringService.cs @@ -50,7 +50,7 @@ namespace FarmmapsZonering.Services } var itemName = $"VRAZonering"; - var applianceMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, + 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())); diff --git a/FarmmapsZonering/ZoneringApplication.cs b/FarmmapsZonering/ZoneringApplication.cs index 27ace6f..0302e03 100644 --- a/FarmmapsZonering/ZoneringApplication.cs +++ b/FarmmapsZonering/ZoneringApplication.cs @@ -84,17 +84,14 @@ namespace FarmmapsZonering } Item cropfieldItem; - if (string.IsNullOrEmpty(_settings.CropfieldItemCode)) + if (string.IsNullOrEmpty(_settings.CropfieldItemCode) || input.CreateNewCropfield == true) { _logger.LogInformation("Creating cropfield"); - //var cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, input.CropFieldName, input.cropYear - //$"VRA Poten cropfield {input.OutputFileName}", - //input.GeometryJson.ToString(Formatting.None)); - - - cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield VRA Zonering", 2020, - @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, input.CropFieldName, input.CropYear, + input.GeometryJson.ToString(Formatting.None)); + + _settings.CropfieldName = cropfieldItem.Name; _settings.CropfieldItemCode = cropfieldItem.Code; SaveSettings(); } @@ -105,24 +102,25 @@ namespace FarmmapsZonering } var inputOneItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, - Path.Combine("Data", "data_9001.tif"),"data_9001"); + Path.Combine("Data", $"{input.InputItemOne}"),Path.GetFileNameWithoutExtension($"{input.InputItemOne}")); if (inputOneItem == null) { _logger.LogError("Could not find item for uploaded data"); return; } - var inputTwoItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, - Path.Combine("Data", "data_times_two_4326.tiff"), "data_times_two_4326"); + var inputTwoItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, + Path.Combine("Data", $"{input.InputItemTwo}"), Path.GetFileNameWithoutExtension($"{input.InputItemTwo}")); if (inputTwoItem == null) { _logger.LogError("Could not find item for uploaded data"); return; } - var outputItem = await _zoneringService.CreateApplicationMapAsync(cropfieldItem, "if [0] >= 1.28 then [1] else 0", new Output() + var outputItem = await _zoneringService.CreateApplicationMapAsync(cropfieldItem, input.Formula, new Output() { - Name = "Remove", - Unit = "m&m", - Quantity = "Nonsense" + Name = input.LayerName, + Quantity = input.CalculatedQuantity, + Unit = input.CalculatedUnit, + }, new InputParameter() { ItemCode = inputOneItem.Code, @@ -134,9 +132,12 @@ namespace FarmmapsZonering LayerName = inputTwoItem.Data["layers"][0]["name"].ToString() }); - _logger.LogInformation("Downloading output"); + _logger.LogInformation("Downloading output"); + + + await _farmmapsApiService.DownloadItemAsync(outputItem.Code, - Path.Combine(DownloadFolder, $"NonsenseRemove.zip")); + Path.Combine(DownloadFolder, $"{input.OutputFileName}.zip")); } private void LoadSettings() diff --git a/FarmmapsZonering/ZoneringInput.json b/FarmmapsZonering/ZoneringInput.json new file mode 100644 index 0000000..366cd72 --- /dev/null +++ b/FarmmapsZonering/ZoneringInput.json @@ -0,0 +1,29 @@ +[ + { + "InputItemOne": "20201106_Sentinel2_L2A_B04.tiff", + "InputItemTwo": "20201106_Sentinel2_L2A_B08.tiff", + "Formula": "([1]-[0])/([1]+[0])", + "CreatedLayerName": "Biomassa", + "CalculatedQuantity": "NDVI", + "CalculatedUnit": "ndviValue", + + "OutputFileName": "FullField_NDVI", + "CropFieldName": "FullField", + "CreateNewCropfield": false, + "CropYear": 2020, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + + [ + [ 4.9593709, 52.8014339 ], + [ 4.9675488, 52.7943149 ], + [ 4.9735195, 52.7968665 ], + [ 4.9667833, 52.8030414 ], + [ 4.9593709, 52.8014339 ] + ] + + ] + } + } +] From 693c82db2e54385bd7a0c4aeb2776314860fc38d Mon Sep 17 00:00:00 2001 From: jits Date: Fri, 18 Dec 2020 14:03:35 +0100 Subject: [PATCH 07/35] update zonering testData --- FarmmapsZonering/Data/sentinel2_L2A_B04.tiff | Bin 0 -> 38207 bytes FarmmapsZonering/Data/sentinel2_L2A_B08.tiff | Bin 0 -> 42461 bytes FarmmapsZonering/FarmmapsZonering.csproj | 4 - FarmmapsZonering/ZoneringInput.json | 106 +++++++++++++------ 4 files changed, 76 insertions(+), 34 deletions(-) create mode 100644 FarmmapsZonering/Data/sentinel2_L2A_B04.tiff create mode 100644 FarmmapsZonering/Data/sentinel2_L2A_B08.tiff diff --git a/FarmmapsZonering/Data/sentinel2_L2A_B04.tiff b/FarmmapsZonering/Data/sentinel2_L2A_B04.tiff new file mode 100644 index 0000000000000000000000000000000000000000..a4a5c1e3e176b40736ef6d827c7b7c2e46b9af47 GIT binary patch literal 38207 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 literal 0 HcmV?d00001 diff --git a/FarmmapsZonering/FarmmapsZonering.csproj b/FarmmapsZonering/FarmmapsZonering.csproj index 10ba86c..b9ae3fa 100644 --- a/FarmmapsZonering/FarmmapsZonering.csproj +++ b/FarmmapsZonering/FarmmapsZonering.csproj @@ -24,8 +24,4 @@ - - - - diff --git a/FarmmapsZonering/ZoneringInput.json b/FarmmapsZonering/ZoneringInput.json index a2ebd87..9063113 100644 --- a/FarmmapsZonering/ZoneringInput.json +++ b/FarmmapsZonering/ZoneringInput.json @@ -1,47 +1,93 @@ [ - { - "InputItemOne": "20201106_Sentinel2_L2A_B04.tiff", - "InputItemTwo": "20201106_Sentinel2_L2A_B08.tiff", - "Formula": "([1]-[0])/([1]+[0])", - "CreatedLayerName": "Biomassa", - "CalculatedQuantity": "NDVI", - "CalculatedUnit": "ndviValue", + "InputItemOne": "data_9001.tif", + "InputItemTwo": "data_times_two_4326.tiff", + "Formula": "if [0] >= 1.28 then [1] else 0", + "CreatedLayerName": "Remove", + "CalculatedQuantity": "Nonsense", + "CalculatedUnit": "m&m", - "OutputFileName": "FullField_NDVI", - "CropFieldName": "FullField", - "CreateNewCropfield": false, + "OutputFileName": "NonsenseRemove", + "CropFieldName": "Cropfield VRA Zonering", + "CreateNewCropfield": true, "CropYear": 2020, "geometryJson": { "type": "Polygon", "coordinates": [ [ - [ 4.9593709, 52.8014339 ], - [ 4.9675488, 52.7943149 ], - [ 4.9735195, 52.7968665 ], - [ 4.9667833, 52.8030414 ], - [ 4.9593709, 52.8014339 ] + [ 5.670991253771027, 52.796788997702613 ], + [ 5.671526456638633, 52.797291618546666 ], + [ 5.671275936147413, 52.797422436717852 ], + [ 5.671959173850738, 52.798269302728798 ], + [ 5.670649634919365, 52.798778791408822 ], + [ 5.671503682048522, 52.799591206957416 ], + [ 5.675159003761311, 52.798193567415474 ], + [ 5.673029579585948, 52.796024727480535 ], + [ 5.670991253771027, 52.796788997702613 ] + ] ] } } - //{ - // "File": "Points.json", - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 5.66886041703652044, 52.52929999060298627 ], - // [ 5.6716230923214912, 52.52946316399909676 ], - // [ 5.67185376229668581, 52.5280565894154563 ], - // [ 5.66903207841337231, 52.52790646510525363 ], - // [ 5.66886041703652044, 52.52929999060298627 ] - // ] - // ] - // } - //} + //{ + // "InputItemOne": "sentinel2_L2A_B04.tiff", + // "InputItemTwo": "sentinel2_L2A_B08.tiff", + // //"InputItemOne": "data_9001.tif", + // //"InputItemTwo": "data_times_two_4326.tiff", + // "Formula": "([1]-[0])/([1]+[0])", + // "CreatedLayerName": "Biomassa", + // "CalculatedQuantity": "NDVI", + // "CalculatedUnit": "ndviValue", + + // "OutputFileName": "FullField_NDVI", + // "CropFieldName": "FullField", + // "CreateNewCropfield": true, + // "CropYear": 2020, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + + // [ + // [ 4.958640119816389, 52.80350324727047 ], + // [ 4.958730764622494, 52.793849575420225 ], + // [ 4.974049736854337, 52.793804253017171 ], + // [ 4.973959092048232, 52.803457924867416 ], + // [ 4.958640119816389, 52.80350324727047 ] + + // ] + + // ] + // } + //}, + + //{ + + // "InputItemOne": "Points.json", + // "Formula": "[0]*100", + // "CreatedLayerName": "PointsX100", + // "CalculatedQuantity": "NDVI", + // "CalculatedUnit": "PointsX100", + + // "OutputFileName": "PointsX100", + // "CropFieldName": "PointsField", + // "CreateNewCropfield": true, + // "CropYear": 2020, + + // "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 bb14c4e4b013067fddee396bd7df97febbfdf04c Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Thu, 28 Jan 2021 15:08:32 +0100 Subject: [PATCH 08/35] added vandersat data to sample code --- FarmmapsApi/Constants.cs | 2 + FarmmapsApi/Services/GeneralService.cs | 284 +++++++++++++------ FarmmapsNbs/Models/NitrogenInput.cs | 2 + FarmmapsNbs/Models/Settings.cs | 3 + FarmmapsNbs/NbsApplication.cs | 138 +++++---- FarmmapsPoten/PotenApplication.cs | 76 ++--- FarmmapsZonering/Models/Settings.cs | 3 + FarmmapsZonering/Models/ZoneringInput.cs | 6 +- FarmmapsZonering/Services/ZoneringService.cs | 5 +- FarmmapsZonering/ZoneringApplication.cs | 123 +++++--- FarmmapsZonering/ZoneringInput.json | 3 + 11 files changed, 431 insertions(+), 214 deletions(-) diff --git a/FarmmapsApi/Constants.cs b/FarmmapsApi/Constants.cs index b8ed6e0..b061c18 100644 --- a/FarmmapsApi/Constants.cs +++ b/FarmmapsApi/Constants.cs @@ -18,10 +18,12 @@ namespace FarmmapsApiSamples public const string VRAPLANTING_TASK = "vnd.farmmaps.task.vrapoten"; public const string VRAZONERING_TASK = "vnd.farmmaps.task.vrazonering"; public const string SATELLITE_TASK = "vnd.farmmaps.task.satellite"; + public const string VANDERSAT_TASK = "vnd.farmmaps.task.vandersat"; 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"; + public const string WATBAL_TASK = "vnd.farmmaps.task.watbal"; } } diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 3a04ca2..6bb9c76 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -12,23 +12,19 @@ using static FarmmapsApiSamples.Constants; namespace FarmmapsApi.Services { - public class GeneralService - { + public class GeneralService { private readonly ILogger _logger; private readonly FarmmapsApiService _farmmapsApiService; - public GeneralService(ILogger logger, 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 = "{}") - { + string fieldGeomJson, string data = "{}") { var currentYear = new DateTime(year, 1, 1); - var cropfieldItemRequest = new ItemRequest() - { + var cropfieldItemRequest = new ItemRequest() { ParentCode = parentItemCode, ItemType = CROPFIELD_ITEMTYPE, Name = name, @@ -41,8 +37,7 @@ namespace FarmmapsApi.Services return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest); } - public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName) - { + public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName) { var startUpload = DateTime.UtcNow; var result = await _farmmapsApiService.UploadFile(filePath, root.Code, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); @@ -55,8 +50,7 @@ namespace FarmmapsApi.Services i.Name.ToLower().Contains(itemName.ToLower())); } - public async Task UploadZipWithShapeAsync(UserRoot root, string filePath, string itemName) - { + public async Task UploadZipWithShapeAsync(UserRoot root, string filePath, string itemName) { var startUpload = DateTime.UtcNow; var result = await _farmmapsApiService.UploadFile(filePath, root.Code, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); @@ -66,11 +60,10 @@ namespace FarmmapsApi.Services return await FindChildItemAsync(root.Code, SHAPE_PROCESSED_ITEMTYPE, itemName, i => i.Created >= startUpload && - i.Name.ToLower().Contains(itemName.ToLower()));; + i.Name.ToLower().Contains(itemName.ToLower())); ; } - public async Task ShapeToGeotiff(Item shapeItem) - { + public async Task ShapeToGeotiff(Item shapeItem) { await RunAndWaitForTask(shapeItem, "vnd.farmmaps.task.shapetogeotiff"); // the parent of the shape item is now the tiff item @@ -79,14 +72,12 @@ namespace FarmmapsApi.Services } - public async Task GeotiffToShape(Item tiffItem) - { - var taskmapRequest = new TaskRequest {TaskType = TASKMAP_TASK}; + 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) => - { + 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) @@ -94,8 +85,7 @@ namespace FarmmapsApi.Services }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(tiffItem.Code, itemTaskCode); - if (itemTask.State == ItemTaskState.Error) - { + if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } @@ -104,8 +94,7 @@ namespace FarmmapsApi.Services var itemName = "Taskmap"; var taskMapItem = await FindChildItemAsync(tiffItem.Code, SHAPE_PROCESSED_ITEMTYPE, itemName); - if (taskMapItem == null) - { + if (taskMapItem == null) { _logger.LogError("Could not find the shape taskmap as a child item under the input"); return null; } @@ -115,18 +104,15 @@ namespace FarmmapsApi.Services public async Task RunAndWaitForTask(Item subjectItem, string taskIdentifier, - Action configureCallback = null, int retrySeconds = 3) - { - var taskRequest = new TaskRequest() - { + 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) => - { + await PollTask(TimeSpan.FromSeconds(retrySeconds), async (tokenSource) => { _logger.LogInformation($"Checking {taskIdentifier} task status"); var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(subjectItem.Code, taskCode); if (itemTaskStatus.IsFinished) @@ -139,28 +125,24 @@ namespace FarmmapsApi.Services } public async Task FindChildItemAsync(string parentCode, string itemType, string containsName, - Func filter = null, int maxTries = 10) - { + Func filter = null, int maxTries = 10) { Item dataItem = null; int tries = 0; - await PollTask(TimeSpan.FromSeconds(3), async source => - { + 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) - { + if (dataItem != null || tries == maxTries) { source.Cancel(); } tries++; }); - if (dataItem == null) - { + if (dataItem == null) { _logger.LogError("dataItem not found"); return null; } @@ -169,14 +151,12 @@ namespace FarmmapsApi.Services return dataItem; } - public async Task RunBofekTask(Item cropfieldItem) - { - var taskmapRequest = new TaskRequest {TaskType = BOFEK_TASK}; + 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) => - { + 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) @@ -184,8 +164,7 @@ namespace FarmmapsApi.Services }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); - if (itemTask.State == ItemTaskState.Error) - { + if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } @@ -194,8 +173,7 @@ namespace FarmmapsApi.Services var itemName = "bofek"; var bofekItem = await FindChildItemAsync(cropfieldItem.Code, SHAPE_PROCESSED_ITEMTYPE, itemName); - if (bofekItem == null) - { + if (bofekItem == null) { _logger.LogError("Could not find the BOFEK data as a child item under the cropfield"); return null; } @@ -203,14 +181,12 @@ namespace FarmmapsApi.Services return bofekItem; } - public async Task RunAhnTask(Item cropfieldItem) - { - var taskmapRequest = new TaskRequest {TaskType = AHN_TASK}; + 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) => - { + 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) @@ -218,8 +194,7 @@ namespace FarmmapsApi.Services }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); - if (itemTask.State == ItemTaskState.Error) - { + if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } @@ -228,8 +203,7 @@ namespace FarmmapsApi.Services var itemName = "ahn"; var ahnItem = await FindChildItemAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE, itemName); - if (ahnItem == null) - { + if (ahnItem == null) { _logger.LogError("Could not find the AHN data as a child item under the cropfield"); return null; } @@ -237,14 +211,12 @@ namespace FarmmapsApi.Services return ahnItem; } - public async Task RunShadowTask(Item cropfieldItem) - { - var taskmapRequest = new TaskRequest {TaskType = SHADOW_TASK}; + 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) => - { + 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) @@ -252,8 +224,7 @@ namespace FarmmapsApi.Services }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); - if (itemTask.State == ItemTaskState.Error) - { + if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } @@ -262,8 +233,7 @@ namespace FarmmapsApi.Services var itemName = "shadow"; var shadowItem = await FindChildItemAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE, itemName); - if (shadowItem == null) - { + if (shadowItem == null) { _logger.LogError("Could not find the shadow data as a child item under the cropfield"); return null; } @@ -272,27 +242,24 @@ namespace FarmmapsApi.Services } - public async Task RunSatelliteTask(Item cropfieldItem) - { + 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) => - { + 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) - { + if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong when trying to process satellite data; {itemTask.Message}"); } @@ -301,8 +268,7 @@ namespace FarmmapsApi.Services } - public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode) - { + public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode, string FieldName, bool StoreStatistics) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); @@ -314,8 +280,7 @@ namespace FarmmapsApi.Services taskStatus.Finished <= item.Created.Value.AddHours(1)); - if (temporalItem == null) - { + if (temporalItem == null) { _logger.LogError("Temporal item not found"); } @@ -325,19 +290,18 @@ namespace FarmmapsApi.Services _logger.LogInformation("Available satellite images:"); var count = 0; TimeSpan.FromSeconds(0.5); - foreach (var item in satelliteTiffs) - { + foreach (var item in satelliteTiffs) { Console.WriteLine($"Satellite image #{count}: {item.DataDate}"); count++; } + _logger.LogInformation("Enter satellite image number for NBS application"); int elment = Int32.Parse(Console.ReadLine()); var selectedSatelliteItem = satelliteTiffs[elment]; - if (selectedSatelliteItem == null) - { + if (selectedSatelliteItem == null) { _logger.LogError("Satellite item not found"); } @@ -350,8 +314,168 @@ namespace FarmmapsApi.Services } + //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 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 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 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 == VANDERSAT_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; + } } } \ No newline at end of file diff --git a/FarmmapsNbs/Models/NitrogenInput.cs b/FarmmapsNbs/Models/NitrogenInput.cs index 87fb186..7d29e6e 100644 --- a/FarmmapsNbs/Models/NitrogenInput.cs +++ b/FarmmapsNbs/Models/NitrogenInput.cs @@ -14,6 +14,8 @@ namespace FarmmapsNbs.Models public int TargetYield { get; set; } public JObject GeometryJson { get; set; } public string InputLayerName { get; set; } + public string fieldName{ get; set; } + public bool storeSatelliteStatistics { get; set; } } diff --git a/FarmmapsNbs/Models/Settings.cs b/FarmmapsNbs/Models/Settings.cs index 41b3853..40cd25d 100644 --- a/FarmmapsNbs/Models/Settings.cs +++ b/FarmmapsNbs/Models/Settings.cs @@ -2,5 +2,8 @@ public class Settings { public string CropfieldItemCode { get; set; } public string SatelliteTaskCode { get; set; } + public string VanDerSatTaskCode { get; set; } + public string WatBalTaskCode { get; set; } + } } \ No newline at end of file diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index 23c4ce0..7a74696 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -38,16 +38,11 @@ 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)) 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(); @@ -67,10 +62,16 @@ namespace FarmmapsNbs private async Task Process(List roots, NitrogenInput input) { + // !!specify if you are using an already created cropfield: - bool useCreatedCropfield = false; + bool useCreatedCropfield = true; var plantingDate = input.PlantingDate; + var FieldName = input.fieldName; + bool StoreStatistics = input.storeSatelliteStatistics; var measurementDate = input.MeasurementDate; + string settingsfile = $"Settings_{FieldName}.json"; + + LoadSettings(settingsfile); var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); if (uploadedRoot == null) @@ -85,8 +86,7 @@ namespace FarmmapsNbs _logger.LogError("Could not find a needed root item"); return; } - - + // Use already created cropfield or create new one Item cropfieldItem; if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) @@ -95,7 +95,7 @@ namespace FarmmapsNbs cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, $"VRA NBS cropfield {input.OutputFileName}", plantingDate.Year, input.GeometryJson.ToString(Formatting.None)); _settings.CropfieldItemCode = cropfieldItem.Code; - SaveSettings(); + SaveSettings(settingsfile); } else { @@ -103,40 +103,59 @@ namespace FarmmapsNbs cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); } - - var geotiffItem = (Item)null; // No file input, use most recent satellite image - if (string.IsNullOrEmpty(input.File)) - { + if (string.IsNullOrEmpty(input.File)) { _logger.LogInformation("No specific data given, retrieving most recent satellite image"); // check if satellite task not yet done, do here and save taskcode - if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) - { + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) { var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); _settings.SatelliteTaskCode = satelliteTaskCode; - SaveSettings(); + SaveSettings(settingsfile); } // Select a particular satellite item from satelliteTask - Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode); + Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode, FieldName, StoreStatistics); + + var satelliteBand = satalliteItem.Data["layers"][0]["name"]; + var satelliteStatistics = satalliteItem.Data["layers"][0]["renderer"]["band"]["statistics"]; + Console.WriteLine($"Satellite image date: {satalliteItem.DataDate}"); + //Console.WriteLine($"Satellite image statistics for band {satelliteBand}: {satelliteStatistics}"); + + //Store data to csv + if (StoreStatistics==true) { + var SatelliteFile = $"C:\\Akkerweb\\DataSatellite_{FieldName}.csv"; + var NewLineField = $"\"Field\":{FieldName}" + Environment.NewLine; + var NewLineDate = $"\"date\":{satalliteItem.DataDate}" + Environment.NewLine; + var i = 0; + foreach (var item in satelliteStatistics) { + //var NewLines2; + if (i == 0) { + File.AppendAllText(SatelliteFile, NewLineDate); + i++; + } + File.AppendAllText(SatelliteFile, $"{item}" + Environment.NewLine); + + } + } // must be wdvi[1] var inputType = (satalliteItem.Data["layers"] as JArray)?[1]["name"].ToString(); - if (string.IsNullOrEmpty(inputType)) - { + if (string.IsNullOrEmpty(inputType)) { _logger.LogError("Could not get the input type name from the satellite item"); return; } // download the geotiff + var SatelliteImageDate = (DateTime)satalliteItem.DataDate; + var SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); _logger.LogInformation("Downloading geotiff file"); await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, - Path.Combine(DownloadFolder, $"nbs_inputSatelliteGeotiff_{input.OutputFileName}.zip")); + Path.Combine(DownloadFolder, $"nbs_inputSatelliteGeotiff_{input.OutputFileName}_{inputType}_{SatelliteDate}.zip")); // overwrite measurement date by date of satellite item measurementDate = satalliteItem.DataDate.Value; @@ -147,20 +166,19 @@ namespace FarmmapsNbs } - // (geo)tiff input: else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { - _logger.LogInformation("input = tiff data"); - var dataPath = Path.Combine("Data", input.File); - geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, - Path.GetFileNameWithoutExtension(input.File)); + _logger.LogInformation("input = tiff data"); + var dataPath = Path.Combine("Data", input.File); + geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, + Path.GetFileNameWithoutExtension(input.File)); + + if (geotiffItem == null) { + _logger.LogError("Could not find item for uploaded data"); + return; + } + } - if (geotiffItem == null) { - _logger.LogError("Could not find item for uploaded data"); - return; - } - } - // json/shape input else { var isGeoJson = input.File.Contains("json"); @@ -187,22 +205,32 @@ namespace FarmmapsNbs } - - + //// check if vandersat task not yet done, do here and save taskcode + //if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.VanDerSatTaskCode)) { + // var VanDerSatTaskCode = await _generalService.RunVanDerSatTask(cropfieldItem); + // _settings.VanDerSatTaskCode = VanDerSatTaskCode; + // SaveSettings(settingsfile); + //} + + //// Select a particular image item from VanDerSat + //Item VanDerSatItem = await _generalService.FindVanDerSatItem(cropfieldItem, _settings.VanDerSatTaskCode, FieldName, StoreStatistics); + //// download the geotiff + //_logger.LogInformation("Downloading geotiff file"); + //await _farmmapsApiService.DownloadItemAsync(VanDerSatItem.Code, + // Path.Combine(DownloadFolder, $"nbs_VanDerSatGeotiff_{input.OutputFileName}.zip")); _logger.LogInformation($"Calculating targetN with targetYield: {input.TargetYield}"); var targetNItem = await _nitrogenService.CreateTargetNItem(cropfieldItem); var targetNData = await _nitrogenService.CalculateTargetN(cropfieldItem, targetNItem, plantingDate, measurementDate, input.PotatoPurposeType, input.TargetYield); - if (targetNData == null) - { + if (targetNData == null) { _logger.LogError("Something went wrong with TargetN calculation"); return; } - + _logger.LogInformation($"TargetN: {targetNData.TargetN}"); ////Option to manually adjust the Target N, for test purposes only! //targetNData.TargetN = 225; @@ -215,12 +243,11 @@ namespace FarmmapsNbs var uptakeMapItem = await _nitrogenService.CalculateUptakeMap(cropfieldItem, geotiffItem, plantingDate, measurementDate, input.InputVariable, input.InputLayerName); - if (uptakeMapItem == null) - { + if (uptakeMapItem == null) { _logger.LogError("Something went wrong with creating the uptakeMap"); return; } - + _logger.LogInformation("Downloading uptake map"); await _farmmapsApiService.DownloadItemAsync(uptakeMapItem.Code, Path.Combine(DownloadFolder, $"{input.OutputFileName}.uptake.zip")); @@ -231,8 +258,7 @@ namespace FarmmapsNbs await _nitrogenService.CalculateApplicationMap(cropfieldItem, geotiffItem, plantingDate, measurementDate, input.InputVariable, targetNData.TargetN, input.InputLayerName); - if (applicationMapItem == null) - { + if (applicationMapItem == null) { _logger.LogError("Something went wrong with creating the applicationMap"); return; } @@ -244,15 +270,13 @@ namespace FarmmapsNbs //transforming tiff to shape var tiffItem = applicationMapItem; - if (tiffItem == null) - { + 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) - { + if (taskmap == null) { _logger.LogError("Something went wrong with geotiff to shape transformation"); return; } @@ -264,12 +288,13 @@ namespace FarmmapsNbs } + // Functions to save previously created cropfields - private void LoadSettings() + private void LoadSettings(string file) { - if (File.Exists(SettingsFile)) + if (File.Exists(file)) { - var jsonText = File.ReadAllText(SettingsFile); + var jsonText = File.ReadAllText(file); _settings = JsonConvert.DeserializeObject(jsonText); } else @@ -278,13 +303,22 @@ namespace FarmmapsNbs } } - private void SaveSettings() + private void SaveSettings(string file) { if (_settings == null) return; var json = JsonConvert.SerializeObject(_settings); - File.WriteAllText(SettingsFile, json); + File.WriteAllText(file, json); } - } -} \ No newline at end of file + private void SaveInfo(string file) { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + + } + + } + } \ No newline at end of file diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 0e961ed..42a4ac3 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -14,9 +14,7 @@ using static FarmmapsApiSamples.Constants; namespace FarmmapsVRApoten { - public class PotenApplication : IApplication - - { + public class PotenApplication : IApplication { private const string DownloadFolder = "Downloads"; private readonly ILogger _logger; @@ -26,16 +24,14 @@ namespace FarmmapsVRApoten public PotenApplication(ILogger logger, FarmmapsApiService farmmapsApiService, - GeneralService generalService, PotenService potenService) - { + GeneralService generalService, PotenService potenService) { _logger = logger; _farmmapsApiService = farmmapsApiService; _generalService = generalService; _potenService = potenService; } - public async Task RunAsync() - { + public async Task RunAsync() { // read field data from separate json file var VRAPotenInputJson = File.ReadAllText("PotenInput.json"); List potenInputs = JsonConvert.DeserializeObject>(VRAPotenInputJson); @@ -48,36 +44,29 @@ namespace FarmmapsVRApoten await _farmmapsApiService.GetCurrentUserCodeAsync(); var roots = await _farmmapsApiService.GetCurrentUserRootsAsync(); - foreach (var input in potenInputs) - { - try - { + foreach (var input in potenInputs) { + try { await Process(roots, input); - } - catch (Exception ex) - { + } catch (Exception ex) { _logger.LogError(ex.Message); } } } - private async Task Process(List roots, PotenInput input) - { + 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) - { + if (myDrive == null) { _logger.LogError("Could not find a needed root item"); return; } var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); - if (uploadedRoot == null) - { + if (uploadedRoot == null) { _logger.LogError("Could not find a needed root item"); return; } @@ -89,12 +78,10 @@ namespace FarmmapsVRApoten input.GeometryJson.ToString(Formatting.None)); //Calculating shadow map - if (useShadow) - { + if (useShadow) { _logger.LogInformation("Calculate shadow map for field"); var shadowItem = await _generalService.RunShadowTask(cropfieldItem); - if (shadowItem == null) - { + if (shadowItem == null) { _logger.LogError("Something went wrong while obtaining the shadow map"); return; } @@ -104,21 +91,30 @@ namespace FarmmapsVRApoten Path.Combine(DownloadFolder, $"{input.OutputFileName}.shadow.zip")); } + //Calculating AHN map + _logger.LogInformation("retreiving AHN map for field"); + var AHNItem = await _generalService.RunAhnTask(cropfieldItem); + if (AHNItem == null) { + _logger.LogError("Something went wrong while obtaining the AHN map"); + return; + } + _logger.LogInformation("Downloading AHN map"); + await _farmmapsApiService.DownloadItemAsync(AHNItem.Code, + Path.Combine(DownloadFolder, $"{input.OutputFileName}_AHN.zip")); + _logger.LogInformation("Looking for local data to use"); var localDataAvailable = input.File; - var geotiffItem = (Item) null; + var geotiffItem = (Item)null; - if (String.IsNullOrEmpty(localDataAvailable)) - { + 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) - { + if (bofekItem == null) { _logger.LogError("Something went wrong while obtaining the BOFEK data"); return; } @@ -126,10 +122,7 @@ namespace FarmmapsVRApoten _logger.LogInformation("Downloading Bofek map"); await _farmmapsApiService.DownloadItemAsync(bofekItem.Code, Path.Combine(DownloadFolder, $"{input.OutputFileName}.BOFEK.zip")); - } - - - else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { + } else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { _logger.LogInformation("input = tiff data"); var dataPath = Path.Combine("Data", input.File); geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, @@ -139,10 +132,7 @@ namespace FarmmapsVRApoten _logger.LogError("Could not find item for uploaded data"); return; } - } - - else - { + } else { var isGeoJson = input.File.Contains("json"); var dataPath = Path.Combine("Data", input.File); var shapeItem = isGeoJson @@ -151,8 +141,7 @@ namespace FarmmapsVRApoten : await _generalService.UploadZipWithShapeAsync(uploadedRoot, dataPath, Path.GetFileNameWithoutExtension(input.File)); - if (shapeItem == null) - { + if (shapeItem == null) { _logger.LogError("Something went wrong while searching for the shape file"); return; } @@ -161,8 +150,7 @@ namespace FarmmapsVRApoten _logger.LogInformation($"Converting shape to geotiff"); geotiffItem = await _generalService.ShapeToGeotiff(shapeItem); - if (geotiffItem == null) - { + if (geotiffItem == null) { _logger.LogError("Something went wrong with shape to geotiff transformation"); return; } @@ -179,8 +167,7 @@ namespace FarmmapsVRApoten var applianceMapItem = await _potenService.CalculateApplicationMapAsync(cropfieldItem, geotiffItem, meanDensity, variation); - if (applianceMapItem == null) - { + if (applianceMapItem == null) { return; } @@ -195,8 +182,7 @@ namespace FarmmapsVRApoten _logger.LogInformation($"Converting geotiff to shape"); var taskmap = await _generalService.GeotiffToShape(applianceMapItem); - if (taskmap == null) - { + if (taskmap == null) { _logger.LogError("Something went wrong with geotiff to shape transformation"); return; } diff --git a/FarmmapsZonering/Models/Settings.cs b/FarmmapsZonering/Models/Settings.cs index c3beb90..8644922 100644 --- a/FarmmapsZonering/Models/Settings.cs +++ b/FarmmapsZonering/Models/Settings.cs @@ -3,5 +3,8 @@ namespace FarmmapsHaulmkilling.Models public class Settings { public string CropfieldItemCode { get; set; } + public string SatelliteTaskCode { get; set; } + public string VanDerSatTaskCode { get; set; } + public string WatBalTaskCode { get; set; } } } \ No newline at end of file diff --git a/FarmmapsZonering/Models/ZoneringInput.cs b/FarmmapsZonering/Models/ZoneringInput.cs index 7ac072b..26f8414 100644 --- a/FarmmapsZonering/Models/ZoneringInput.cs +++ b/FarmmapsZonering/Models/ZoneringInput.cs @@ -4,11 +4,13 @@ using Newtonsoft.Json.Linq; namespace FarmmapsZonering { public class ZoneringInput { public bool UseShadow { get; set; } - public string File { get; set; } public string OutputFileName { get; set; } - public string FieldName { get; set; } + public string fieldName { get; set; } public JObject GeometryJson { get; set; } + public bool GetWatBal { get; set; } + public bool GetVanDerSat { get; set; } + public bool storeVanDerSatStatistics { get; set; } } } \ No newline at end of file diff --git a/FarmmapsZonering/Services/ZoneringService.cs b/FarmmapsZonering/Services/ZoneringService.cs index d736240..d45564f 100644 --- a/FarmmapsZonering/Services/ZoneringService.cs +++ b/FarmmapsZonering/Services/ZoneringService.cs @@ -52,9 +52,10 @@ namespace FarmmapsZonering.Services var itemName = $"VRAZonering"; var applianceMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE, itemName, - i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && + //i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && + // i.Name.ToLower().Contains(itemName.ToLower())); + i => i.Name.ToLower().Contains(itemName.ToLower())); - if (applianceMapItem == null) { _logger.LogError("Could not find the VRAZonering geotiff child item under cropfield"); diff --git a/FarmmapsZonering/ZoneringApplication.cs b/FarmmapsZonering/ZoneringApplication.cs index 27ace6f..9d3f0b4 100644 --- a/FarmmapsZonering/ZoneringApplication.cs +++ b/FarmmapsZonering/ZoneringApplication.cs @@ -41,9 +41,6 @@ namespace FarmmapsZonering if (!Directory.Exists(DownloadFolder)) Directory.CreateDirectory(DownloadFolder); - // Load settings from previous cropfield - LoadSettings(); - // Read input data from separate file var zoneringInputJson = File.ReadAllText("ZoneringInput.json"); List zoneringInputs = JsonConvert.DeserializeObject>(zoneringInputJson); @@ -76,6 +73,16 @@ namespace FarmmapsZonering return; } + bool useCreatedCropfield = true; + bool GetWatBal = input.GetWatBal; + bool getVanDerSat = true;// input.GetVanDerSat; + bool StoreVanDerSatStatistics = true;// input.storeVanDerSatStatistics; + var FieldName = input.fieldName; + string settingsfile = $"Settings_{FieldName}.json"; + + // Load settings from previous cropfield + LoadSettings(settingsfile); + var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); if (uploadedRoot == null) { @@ -91,38 +98,85 @@ namespace FarmmapsZonering //var cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, input.CropFieldName, input.cropYear //$"VRA Poten cropfield {input.OutputFileName}", //input.GeometryJson.ToString(Formatting.None)); - - - cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield VRA Zonering", 2020, - @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Perceel mestsilo", 2020, + @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 6.48270347, 53.25953201 ], [ 6.48257178, 53.25858122 ], [ 6.48781397, 53.25797859 ], [ 6.48793320, 53.25928036 ], [ 6.48270347, 53.25953201 ] ] ] }"); _settings.CropfieldItemCode = cropfieldItem.Code; - SaveSettings(); - } + SaveSettings(settingsfile); + + //cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield VRA Zonering", 2020, + // @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); + //_settings.CropfieldItemCode = cropfieldItem.Code; + //SaveSettings(); + } else { _logger.LogInformation("Cropfield already exists trying to get"); cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); } - - var inputOneItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, - Path.Combine("Data", "data_9001.tif"),"data_9001"); + + if (GetWatBal==true) { + ////Run watbal + //if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.WatBalTaskCode)) { + // var WatBalTaskCode = await _generalService.RunWatBalTask(cropfieldItem); + // _settings.WatBalTaskCode = WatBalTaskCode; + // SaveSettings(settingsfile); + //} + + //// Get watbal data + //Item WatBalItem = await _generalService.FindWatBalItem(cropfieldItem, _settings.WatBalTaskCode, FieldName, StoreStatistics); + } + + if (getVanDerSat==true) { + // check if vandersat task not yet done, do here and save taskcode + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.VanDerSatTaskCode)) { + var VanDerSatTaskCode = await _generalService.RunVanDerSatTask(cropfieldItem); + _settings.VanDerSatTaskCode = VanDerSatTaskCode; + SaveSettings(settingsfile); + } + + // Select a particular image item from VanDerSat + Item VanDerSatItem = await _generalService.FindVanDerSatItem(cropfieldItem, _settings.VanDerSatTaskCode, FieldName, StoreVanDerSatStatistics); + + + // download the geotiff + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(VanDerSatItem.Code, + Path.Combine(DownloadFolder, $"nbs_VanDerSatGeotiff_{input.OutputFileName}.zip")); + } + + var inputOneItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, + Path.Combine("Data", "data_9001.tif"), "data_9001"); if (inputOneItem == null) { _logger.LogError("Could not find item for uploaded data"); return; } - - var inputTwoItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, + + var inputTwoItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, Path.Combine("Data", "data_times_two_4326.tiff"), "data_times_two_4326"); if (inputTwoItem == null) { _logger.LogError("Could not find item for uploaded data"); return; } - var outputItem = await _zoneringService.CreateApplicationMapAsync(cropfieldItem, "if [0] >= 1.28 then [1] else 0", new Output() + //Formule kan meerdere inputs aan + // Met blokhaken en een nummer specificeer je een input: + //[0], [10]. etc... + // Aan de hand van de volgorde van input wordt momenteel bepaald welk nummer bij welk input hoort. + //[0] is dus de eerst tiff opgegeven.Als het goed is maakt het nummer nu niks uit, dus als er 2 inputs zijn en in de formule staan[0] en[10] dan zal die nog steeds werken. + //De volgorde van de tiff kun je verslepen, bij de formule inputs kun je de input van het ene[0] slot naar het andere slepen[10] + //Functies: abs, min en max + //Constanten: pi en e + + //if ([0] -[1])/ ([0] +[1]) < 0 then 0 + //else if ([0] -[1])/ ([0] +[1]) > 1 then 1 + //else ([0] -[1]) / ([0] +[1]) + + var outputItem = await _zoneringService.CreateApplicationMapAsync(cropfieldItem, "[0] >= 1.28 then [1] else 0", new Output() + { - Name = "Remove", - Unit = "m&m", - Quantity = "Nonsense" + Name = "Test", + Unit = "index", + Quantity = "kg" }, new InputParameter() { ItemCode = inputOneItem.Code, @@ -130,35 +184,38 @@ namespace FarmmapsZonering }, new InputParameter() { - ItemCode = inputTwoItem.Code, - LayerName = inputTwoItem.Data["layers"][0]["name"].ToString() + ItemCode = inputOneItem.Code, + LayerName = inputOneItem.Data["layers"][0]["name"].ToString() }); _logger.LogInformation("Downloading output"); await _farmmapsApiService.DownloadItemAsync(outputItem.Code, - Path.Combine(DownloadFolder, $"NonsenseRemove.zip")); + Path.Combine(DownloadFolder, $"Test.zip")); } - private void LoadSettings() - { - if (File.Exists(SettingsFile)) - { - var jsonText = File.ReadAllText(SettingsFile); + // Functions to save previously created cropfields + private void LoadSettings(string file) { + if (File.Exists(file)) { + var jsonText = File.ReadAllText(file); _settings = JsonConvert.DeserializeObject(jsonText); - } - else - { + } else { _settings = new Settings(); } } - private void SaveSettings() - { + private void SaveSettings(string file) { if (_settings == null) return; var json = JsonConvert.SerializeObject(_settings); - File.WriteAllText(SettingsFile, json); + File.WriteAllText(file, json); + } + private void SaveInfo(string file) { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + } } - } } \ No newline at end of file diff --git a/FarmmapsZonering/ZoneringInput.json b/FarmmapsZonering/ZoneringInput.json index f3ac9f6..33960da 100644 --- a/FarmmapsZonering/ZoneringInput.json +++ b/FarmmapsZonering/ZoneringInput.json @@ -4,6 +4,9 @@ "OutputFileName": "Zoning", "FieldName": "Data_whole", "UseShadow": false, + "GetWatBal": true, + "GetVanDerSat": true, + "storeVanDerSatStatistics": true, "geometryJson": { "type": "Polygon", "coordinates": [ From c02b232837f59d01852f246274d00476506a7e0b Mon Sep 17 00:00:00 2001 From: Riepma Date: Fri, 12 Feb 2021 17:10:25 +0100 Subject: [PATCH 09/35] Added Taskmap sample code to potenAPI --- FarmmapsApi/Services/GeneralService.cs | 48 ++++++++++++++++++++- FarmmapsPoten/FarmmapsPoten.csproj | 3 -- FarmmapsPoten/Models/PotenInput.cs | 13 +++++- FarmmapsPoten/PotenApplication.cs | 35 +++++++++++++-- FarmmapsPoten/PotenInput.json | 59 +++++++++++++++++--------- FarmmapsPoten/PotenService.cs | 46 +++++++++++++++++++- 6 files changed, 170 insertions(+), 34 deletions(-) diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 6bb9c76..d1556b1 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -38,7 +38,7 @@ namespace FarmmapsApi.Services } public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName) { - var startUpload = DateTime.UtcNow; + var startUpload = DateTime.UtcNow.AddSeconds(-3); var result = await _farmmapsApiService.UploadFile(filePath, root.Code, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); @@ -74,6 +74,10 @@ namespace FarmmapsApi.Services 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); @@ -92,7 +96,7 @@ namespace FarmmapsApi.Services //the taskmap is a child of the input tiff var itemName = "Taskmap"; - var taskMapItem = await FindChildItemAsync(tiffItem.Code, + 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"); @@ -102,6 +106,46 @@ namespace FarmmapsApi.Services return taskMapItem; } + // Create taskmap based on width, height and direction + public async Task CreateTaskmap(Item tiffItem, string cellWidth, string cellHeight, string startPoint, string endPoint = null, string angle = null) + { + var taskmapRequest = new TaskRequest { TaskType = TASKMAP_TASK }; + taskmapRequest.attributes["inputCode"] = tiffItem.Code; + taskmapRequest.attributes["cellWidth"] = cellWidth; //metres + taskmapRequest.attributes["cellHeight"] = cellHeight; //metres + taskmapRequest.attributes["startPoint"] = startPoint; // Coordinates WGS84 + if (angle == null) taskmapRequest.attributes["endPoint"] = endPoint; // Coordinates WGS84 + if (endPoint == null) taskmapRequest.attributes["angle"] = angle; // degrees between 0.0 and 360.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 conversion to Taskmap; 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.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; + } + public async Task RunAndWaitForTask(Item subjectItem, string taskIdentifier, Action configureCallback = null, int retrySeconds = 3) { diff --git a/FarmmapsPoten/FarmmapsPoten.csproj b/FarmmapsPoten/FarmmapsPoten.csproj index 1ff2fe3..f76d9da 100644 --- a/FarmmapsPoten/FarmmapsPoten.csproj +++ b/FarmmapsPoten/FarmmapsPoten.csproj @@ -12,9 +12,6 @@ Always - - PreserveNewest - diff --git a/FarmmapsPoten/Models/PotenInput.cs b/FarmmapsPoten/Models/PotenInput.cs index 3bb03be..dc97b6b 100644 --- a/FarmmapsPoten/Models/PotenInput.cs +++ b/FarmmapsPoten/Models/PotenInput.cs @@ -5,14 +5,23 @@ 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 bool UseShadow { get; set; } + public bool ConvertToCountPerArea { get; set; } + public float Rijbreedte_m { get; set; } public JObject GeometryJson { get; set; } + + public bool GenerateTaskmap { get; set; } + public string CellWidth { get; set; } + public string CellHeight { get; set; } + public JObject StartPoint { get; set; } + public JObject EndPoint { get; set; } + public string Angle { get; set; } + } } \ No newline at end of file diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 42a4ac3..b3dad38 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -58,6 +58,8 @@ namespace FarmmapsVRApoten var variation = input.Variation; var fieldName = input.FieldName; bool useShadow = input.UseShadow; + bool convertToCountPerArea = input.ConvertToCountPerArea; + float rijBreedte_m = input.Rijbreedte_m; var myDrive = roots.SingleOrDefault(r => r.Name == "My drive"); if (myDrive == null) { @@ -94,7 +96,8 @@ namespace FarmmapsVRApoten //Calculating AHN map _logger.LogInformation("retreiving AHN map for field"); var AHNItem = await _generalService.RunAhnTask(cropfieldItem); - if (AHNItem == null) { + if (AHNItem == null) + { _logger.LogError("Something went wrong while obtaining the AHN map"); return; } @@ -160,6 +163,8 @@ namespace FarmmapsVRApoten Path.Combine(DownloadFolder, $"VRApoten_inputGeotiff_{input.OutputFileName}.zip")); } + + // create appliance map _logger.LogInformation("Calculating application map"); @@ -180,13 +185,35 @@ namespace FarmmapsVRApoten ? "Download application map completed." : "Something went wrong while downloading."); - _logger.LogInformation($"Converting geotiff to shape"); - var taskmap = await _generalService.GeotiffToShape(applianceMapItem); - if (taskmap == null) { + + // if convertToCountPerArea == True, than recalculate pootafstand in cm to # of poters/m2 from the geotiffItem with the use of the zoneringsTask + if (convertToCountPerArea) + { + applianceMapItem = + await _potenService.ConvertToCountPerAreaTroughZonering(cropfieldItem, applianceMapItem, input.Rijbreedte_m); + + } + + + //GEOTIFF TO Taskmap + _logger.LogInformation($"Converting geotiff to taskmap"); + var taskmap = await _generalService.CreateTaskmap(applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + input.EndPoint.ToString(Formatting.None), input.Angle); + if (taskmap == null) + { _logger.LogError("Something went wrong with geotiff to shape transformation"); return; } + + ////GEOTIFF TO SHAPE + //_logger.LogInformation($"Converting geotiff to shape"); + //var geotiffToShapeItem= 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")); diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index e67acba..7fb8b26 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -1,24 +1,41 @@ [ - { - "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 ] - ] - ] + { + "File": "PlantingSampleDataLutum.zip", + //"File": "Lutum_SampleDataPlanting.zip", + "OutputFileName": "20210212_vraPoten_SampleData_TASKMAP_ENDPOINT", + "FieldName": "lutum", + "PlantingYear": 2020, + "MeanDensity": "30", + "Variation": "20", + "UseShadow": false, + "ConvertToCountPerArea": false, + "Rijbreedte_m": 0.75, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 5.66886041703652044, 52.52929999060298627 ], + [ 5.6716230923214912, 52.52946316399909676 ], + [ 5.67185376229668581, 52.5280565894154563 ], + [ 5.66903207841337231, 52.52790646510525363 ], + [ 5.66886041703652044, 52.52929999060298627 ] + ] + ] + }, + + "GenerateTaskmap": true, + "CellWidth": "3", + "CellHeight": "10", + "StartPoint": { + "type": "Point", + "coordinates": [ 5.66886041703652044, 52.52929999060298627 ] + }, + "EndPoint": { + "type": "Point", + "coordinates": [ 5.6716230923214912, 52.52946316399909676 ] + } // if no angle + + //"Angle": "317.0" // if no endpoint } - } + ] diff --git a/FarmmapsPoten/PotenService.cs b/FarmmapsPoten/PotenService.cs index 7a839c2..c77f393 100644 --- a/FarmmapsPoten/PotenService.cs +++ b/FarmmapsPoten/PotenService.cs @@ -82,10 +82,52 @@ namespace FarmmapsVRApoten return applianceMapItem; - } + } - } + // Extra task making use of the zonering task to convert the planting distance in cm to number of seeds per m2 + public async Task ConvertToCountPerAreaTroughZonering(Item cropfieldItem, Item geotiffItem, float rijBreedte_m) + { + var zoneringTaskRequest = new TaskRequest() { TaskType = VRAZONERING_TASK }; + zoneringTaskRequest.attributes["formula"] = $"((100/[0])/{rijBreedte_m.ToString()})"; + zoneringTaskRequest.attributes["output"] = "{\"Name\":\"CountPerAreaConversion\",\"Quantity\":\"CountPerArea\",\"Unit\":\"#/m2\"}"; + zoneringTaskRequest.attributes["inputs"] = $"{{\"ItemCode\":{geotiffItem.Code},\"LayerName\":null\"}}"; + + var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, zoneringTaskRequest); + + await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => + { + var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); + _logger.LogInformation($"Waiting on convertion to Count per area through zoneringTast; 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 = $"VRAZonering"; + 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())); + i => + i.Name.ToLower().Contains(itemName.ToLower())); + if (applianceMapItem == null) + { + _logger.LogError("Could not find the converted to count per area geotiff child item under cropfield"); + return null; + } + + return applianceMapItem; + } + + + } } From 101b683daa9751d4b363ab569be7575c8ba49cd5 Mon Sep 17 00:00:00 2001 From: Riepma Date: Mon, 15 Feb 2021 17:06:09 +0100 Subject: [PATCH 10/35] Update zonering to find the right output files Added taskmap creator to NBS --- FarmmapsApi/Services/GeneralService.cs | 9 +- FarmmapsNbs/InputData-NBS.json | 40 +++++ FarmmapsNbs/Models/NitrogenInput.cs | 7 + FarmmapsNbs/NbsApplication.cs | 22 ++- ...0210215_vraPoten_SampleData_CovertArea.tif | Bin 0 -> 142584 bytes FarmmapsZonering/FarmmapsZonering.csproj | 4 + FarmmapsZonering/Services/ZoneringService.cs | 12 +- FarmmapsZonering/ZoneringApplication.cs | 43 ++--- FarmmapsZonering/ZoneringInput.json | 152 +++++++++++------- 9 files changed, 194 insertions(+), 95 deletions(-) create mode 100644 FarmmapsNbs/InputData-NBS.json create mode 100644 FarmmapsZonering/Data/VRApoten_appliancemap_20210215_vraPoten_SampleData_CovertArea.tif diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index d1556b1..4e1f9b0 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -107,26 +107,27 @@ namespace FarmmapsApi.Services } // Create taskmap based on width, height and direction - public async Task CreateTaskmap(Item tiffItem, string cellWidth, string cellHeight, string startPoint, string endPoint = null, string angle = null) + public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string cellWidth, string cellHeight, string startPoint, string endPoint = null, string angle = null) { var taskmapRequest = new TaskRequest { TaskType = TASKMAP_TASK }; taskmapRequest.attributes["inputCode"] = tiffItem.Code; + taskmapRequest.attributes["operation"] = "shape"; // 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 if (angle == null) taskmapRequest.attributes["endPoint"] = endPoint; // Coordinates WGS84 if (endPoint == null) taskmapRequest.attributes["angle"] = angle; // degrees between 0.0 and 360.0 - string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(tiffItem.Code, taskmapRequest); + string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { - var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(tiffItem.Code, itemTaskCode); + 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(tiffItem.Code, itemTaskCode); + var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); diff --git a/FarmmapsNbs/InputData-NBS.json b/FarmmapsNbs/InputData-NBS.json new file mode 100644 index 0000000..3fdad59 --- /dev/null +++ b/FarmmapsNbs/InputData-NBS.json @@ -0,0 +1,40 @@ +[ + { + "UseCreatedCropfield": false, + "storeStatistics": false, + "file": "20210127_WDVI_plus03.tif", + "inputVariable": "wdvi", + //"InputLayerName": "Band 1", + "outputFileName": "2021.02.15.Hapreet_Singh_0127-wdvi03_2", + "plantingDate": "2020-04-15", + "measurementDate": "2020-06-27", + "potatoPurposeType": "consumption", + "targetYield": 27, + "fieldName": "Mahindra-Hapreet-Singh", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 75.929090780177305, 31.639314978348551 ], + [ 75.931353489378182, 31.639409065494881 ], + [ 75.931432810729120, 31.638956841735819 ], + [ 75.929072617175663, 31.638879365370279 ], + [ 75.929090780177305, 31.639314978348551 ] + ] + ] + }, + "GenerateTaskmap": true, + "CellWidth": "3", + "CellHeight": "10", + "StartPoint": { + "type": "Point", + "coordinates": [ 75.931432810729120, 31.638956841735819 ] + }, + "EndPoint": { + "type": "Point", + "coordinates": [ 75.929072617175663, 31.638879365370279 ] + } // if no angle + + //"Angle": "317.0" // if no endpoint + } + ] \ No newline at end of file diff --git a/FarmmapsNbs/Models/NitrogenInput.cs b/FarmmapsNbs/Models/NitrogenInput.cs index 7d29e6e..61326ea 100644 --- a/FarmmapsNbs/Models/NitrogenInput.cs +++ b/FarmmapsNbs/Models/NitrogenInput.cs @@ -5,6 +5,7 @@ namespace FarmmapsNbs.Models { public class NitrogenInput { + public bool UseCreatedCropfield { get; set; } public string File { get; set; } public string InputVariable { get; set; } public string OutputFileName { get; set; } @@ -16,6 +17,12 @@ namespace FarmmapsNbs.Models public string InputLayerName { get; set; } public string fieldName{ get; set; } public bool storeSatelliteStatistics { get; set; } + public bool GenerateTaskmap { get; set; } + public string CellWidth { get; set; } + public string CellHeight { get; set; } + public JObject StartPoint { get; set; } + public JObject EndPoint { get; set; } + public string Angle { get; set; } } diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index 7a74696..109843c 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -37,7 +37,7 @@ namespace FarmmapsNbs public async Task RunAsync() { - var nitrogenInputJson = File.ReadAllText("NitrogenInput.json"); + var nitrogenInputJson = File.ReadAllText("InputData-NBS.json"); //NitrogenInput.json List nitrogenInputs = JsonConvert.DeserializeObject>(nitrogenInputJson); if (!Directory.Exists(DownloadFolder)) @@ -64,7 +64,7 @@ namespace FarmmapsNbs { // !!specify if you are using an already created cropfield: - bool useCreatedCropfield = true; + bool useCreatedCropfield = input. UseCreatedCropfield; var plantingDate = input.PlantingDate; var FieldName = input.fieldName; bool StoreStatistics = input.storeSatelliteStatistics; @@ -274,13 +274,25 @@ namespace FarmmapsNbs _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.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; + //} + + //ApplicationMap (GEOTIFF) To Taskmap + _logger.LogInformation($"Converting geotiff to taskmap"); + var taskmap = await _generalService.CreateTaskmap(cropfieldItem, tiffItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + input.EndPoint.ToString(Formatting.None), input.Angle); + 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")); diff --git a/FarmmapsZonering/Data/VRApoten_appliancemap_20210215_vraPoten_SampleData_CovertArea.tif b/FarmmapsZonering/Data/VRApoten_appliancemap_20210215_vraPoten_SampleData_CovertArea.tif new file mode 100644 index 0000000000000000000000000000000000000000..3fe1332555fde7e6ace21e74e92baab6a57aa61b GIT binary patch literal 142584 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 + + + + diff --git a/FarmmapsZonering/Services/ZoneringService.cs b/FarmmapsZonering/Services/ZoneringService.cs index d379122..c6d717e 100644 --- a/FarmmapsZonering/Services/ZoneringService.cs +++ b/FarmmapsZonering/Services/ZoneringService.cs @@ -49,13 +49,11 @@ namespace FarmmapsZonering.Services return null; } - var itemName = $"VRAZonering"; - 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())); - i => - i.Name.ToLower().Contains(itemName.ToLower())); + var applianceMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, + GEOTIFF_PROCESSED_ITEMTYPE, output.Name, + i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && + i.Name.ToLower().Contains(output.Name.ToLower())); + if (applianceMapItem == null) { _logger.LogError("Could not find the VRAZonering geotiff child item under cropfield"); diff --git a/FarmmapsZonering/ZoneringApplication.cs b/FarmmapsZonering/ZoneringApplication.cs index d53fd46..862f62a 100644 --- a/FarmmapsZonering/ZoneringApplication.cs +++ b/FarmmapsZonering/ZoneringApplication.cs @@ -73,7 +73,7 @@ namespace FarmmapsZonering return; } - bool useCreatedCropfield = true; + bool useCreatedCropfield = false; bool GetWatBal = input.GetWatBal; bool getVanDerSat = input.GetVanDerSat; bool StoreVanDerSatStatistics = input.storeVanDerSatStatistics; @@ -91,7 +91,7 @@ namespace FarmmapsZonering } Item cropfieldItem; - if (string.IsNullOrEmpty(_settings.CropfieldItemCode) || input.CreateNewCropfield == true) + if (input.CreateNewCropfield == true) // || string.IsNullOrEmpty(_settings.CropfieldItemCode) ## CHECK IT!! { _logger.LogInformation("Creating cropfield"); @@ -103,8 +103,8 @@ namespace FarmmapsZonering _settings.CropfieldItemCode = cropfieldItem.Code; SaveSettings(settingsfile); - cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield VRA Zonering", 2020, - @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); + //cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield VRA Zonering", 2020, + // @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); _settings.CropfieldItemCode = cropfieldItem.Code; SaveSettings(settingsfile); } @@ -147,19 +147,24 @@ namespace FarmmapsZonering var inputOneItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, Path.Combine("Data", $"{input.InputItemOne}"), Path.GetFileNameWithoutExtension($"{input.InputItemOne}")); - if (inputOneItem == null) { + if (inputOneItem == null) + { _logger.LogError("Could not find item for uploaded data"); return; } - - var inputTwoItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, - Path.Combine("Data", $"{input.InputItemTwo}"), Path.GetFileNameWithoutExtension($"{input.InputItemTwo}")); - if (inputTwoItem == null) { - _logger.LogError("Could not find item for uploaded data"); - return; - } + //var inputTwoItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, + // Path.Combine("Data", $"{input.InputItemTwo}"), Path.GetFileNameWithoutExtension($"{input.InputItemTwo}")); + + //if (inputTwoItem == null) + //{ + // _logger.LogError("Could not find item for uploaded data"); + // return; + //} + + //var inputOneItem = input.InputItemOne; + //var inputTwoItem = input.InputItemTwo; var outputItem = await _zoneringService.CreateApplicationMapAsync(cropfieldItem, input.Formula, new Output() { @@ -172,12 +177,14 @@ namespace FarmmapsZonering { ItemCode = inputOneItem.Code, LayerName = inputOneItem.Data["layers"][0]["name"].ToString() - }, - new InputParameter() - { - ItemCode = inputOneItem.Code, - LayerName = inputOneItem.Data["layers"][0]["name"].ToString() - }); + } + //, + //new InputParameter() + //{ + // //ItemCode = inputOneItem.Code, + // //LayerName = inputOneItem.Data["layers"][0]["name"].ToString() + //} + );; _logger.LogInformation("Downloading output"); diff --git a/FarmmapsZonering/ZoneringInput.json b/FarmmapsZonering/ZoneringInput.json index dc23cc3..984289f 100644 --- a/FarmmapsZonering/ZoneringInput.json +++ b/FarmmapsZonering/ZoneringInput.json @@ -1,71 +1,101 @@ [ - //Formule kan meerdere inputs aan - // Met blokhaken en een nummer specificeer je een input: - //[0], [10]. etc... - // Aan de hand van de volgorde van input wordt momenteel bepaald welk nummer bij welk input hoort. - //[0] is dus de eerst tiff opgegeven.Als het goed is maakt het nummer nu niks uit, dus als er 2 inputs zijn en in de formule staan[0] en[10] dan zal die nog steeds werken. - //De volgorde van de tiff kun je verslepen, bij de formule inputs kun je de input van het ene[0] slot naar het andere slepen[10] - //Functies: abs, min en max - //Constanten: pi en e + //Formule kan meerdere inputs aan + // Met blokhaken en een nummer specificeer je een input: + //[0], [10]. etc... + // Aan de hand van de volgorde van input wordt momenteel bepaald welk nummer bij welk input hoort. + //[0] is dus de eerst tiff opgegeven.Als het goed is maakt het nummer nu niks uit, dus als er 2 inputs zijn en in de formule staan[0] en[10] dan zal die nog steeds werken. + //De volgorde van de tiff kun je verslepen, bij de formule inputs kun je de input van het ene[0] slot naar het andere slepen[10] + //Functies: abs, min en max + //Constanten: pi en e - //if ([0] -[1])/ ([0] +[1]) < 0 then 0 - //else if ([0] -[1])/ ([0] +[1]) > 1 then 1 - //else ([0] -[1]) / ([0] +[1]) + //if ([0] -[1])/ ([0] +[1]) < 0 then 0 + //else if ([0] -[1])/ ([0] +[1]) > 1 then 1 + //else ([0] -[1]) / ([0] +[1]) - //{ - // "InputItemOne": "20201106_Sentinel2_L2A_B04.tiff", - // "InputItemTwo": "20201106_Sentinel2_L2A_B08.tiff", - // "Formula": "([1]-[0])/([1]+[0])", - // "CreatedLayerName": "Biomassa", - // "CalculatedQuantity": "NDVI", - // "CalculatedUnit": "ndviValue", + //{ + // "InputItemOne": "20201106_Sentinel2_L2A_B04.tiff", + // "InputItemTwo": "20201106_Sentinel2_L2A_B08.tiff", + // "Formula": "([1]-[0])/([1]+[0])", + // "CreatedLayerName": "Biomassa", + // "CalculatedQuantity": "NDVI", + // "CalculatedUnit": "ndviValue", - // "OutputFileName": "FullField_NDVI", - // "CropFieldName": "FullField", - // "CreateNewCropfield": false, - // "CropYear": 2020, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ + // "OutputFileName": "FullField_NDVI", + // "CropFieldName": "FullField", + // "CreateNewCropfield": false, + // "CropYear": 2020, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ - // [ - // [ 4.9593709, 52.8014339 ], - // [ 4.9675488, 52.7943149 ], - // [ 4.9735195, 52.7968665 ], - // [ 4.9667833, 52.8030414 ], - // [ 4.9593709, 52.8014339 ] - // ] + // [ + // [ 4.9593709, 52.8014339 ], + // [ 4.9675488, 52.7943149 ], + // [ 4.9735195, 52.7968665 ], + // [ 4.9667833, 52.8030414 ], + // [ 4.9593709, 52.8014339 ] + // ] - // ] - // }, - { - "InputItemOne": "data_9001.tif", - "InputItemTwo": "data_times_two_4326.tiff", - "Formula": "if [0] >= 1.28 then [1] else 0", - "CreatedLayerName": "Biomassa", - "CalculatedQuantity": "NDVI", - "CalculatedUnit": "ndviValue", - "OutputFileName": "Zoning", - "CropFieldName": "Data_whole", - "UseShadow": false, - "GetWatBal": false, - "GetVanDerSat": true, - "storeVanDerSatStatistics": true, - "CropYear": 2020, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 5.66886041703652044, 52.52929999060298627 ], - [ 5.6716230923214912, 52.52946316399909676 ], - [ 5.67185376229668581, 52.5280565894154563 ], - [ 5.66903207841337231, 52.52790646510525363 ], - [ 5.66886041703652044, 52.52929999060298627 ] - ] - ] + // ] + // } + //, + //{ + // "InputItemOne": "data_9001.tif", + // "InputItemTwo": "data_times_two_4326.tiff", + // "Formula": "if [0] >= 1.28 then [1] else 0", + // "CreatedLayerName": "Biomassa", + // "CalculatedQuantity": "NDVI", + // "CalculatedUnit": "ndviValue", + // "OutputFileName": "Zoning", + // "CropFieldName": "Data_whole", + // "UseShadow": false, + // "GetWatBal": false, + // "GetVanDerSat": false, + // "storeVanDerSatStatistics": false, + // "CropYear": 2020, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 5.66886041703652044, 52.52929999060298627 ], + // [ 5.6716230923214912, 52.52946316399909676 ], + // [ 5.67185376229668581, 52.5280565894154563 ], + // [ 5.66903207841337231, 52.52790646510525363 ], + // [ 5.66886041703652044, 52.52929999060298627 ] + // ] + // ] + // } + //}, + { + "InputItemOne": "VRApoten_appliancemap_20210215_vraPoten_SampleData_CovertArea.tif", + //"InputItemTwo": "", + "Formula": "((100/[0])/0.75)", + "LayerName": "CountPerAreaConversion", + "CalculatedQuantity": "CountPerArea", + "CalculatedUnit": "#/m2", + "OutputFileName": "CountPerAreaConversionOutput", + "CropFieldName": "ZoningCpA", + "CreateNewCropfield": true, + "UseShadow": false, + "GetWatBal": false, + "GetVanDerSat": false, + "storeVanDerSatStatistics": false, + "CropYear": 2020, + "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 3ad594d83fe1cf1114914e9d1d8bb315f4b0247a Mon Sep 17 00:00:00 2001 From: Riepma Date: Tue, 16 Feb 2021 09:46:50 +0100 Subject: [PATCH 11/35] Created separate project for areaConversion VRA poten Updated VRA poten sample code --- FarmmapsPoten/FarmmapsPoten.csproj | 3 + FarmmapsPoten/Models/PotenInput.cs | 3 +- FarmmapsPoten/PotenApplication.cs | 27 +- FarmmapsPoten/PotenInput.json | 5 +- FarmmapsPoten/PotenService.cs | 59 +--- .../Data/PlantingSampleDataLutum.json | 302 ++++++++++++++++++ .../Data/PlantingSampleDataLutum.zip | Bin 0 -> 5865 bytes .../FarmmapsPoten_AVRapi.csproj | 24 ++ FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs | 27 ++ FarmmapsPoten_AVRapi/PotenApplicationAVR.cs | 228 +++++++++++++ FarmmapsPoten_AVRapi/PotenInputAVR.json | 41 +++ FarmmapsPoten_AVRapi/PotenServiceAVR.cs | 120 +++++++ FarmmapsPoten_AVRapi/Program.cs | 23 ++ FarmmapsPoten_AVRapi/appsettings.json | 10 + 14 files changed, 783 insertions(+), 89 deletions(-) create mode 100644 FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json create mode 100644 FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.zip create mode 100644 FarmmapsPoten_AVRapi/FarmmapsPoten_AVRapi.csproj create mode 100644 FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs create mode 100644 FarmmapsPoten_AVRapi/PotenApplicationAVR.cs create mode 100644 FarmmapsPoten_AVRapi/PotenInputAVR.json create mode 100644 FarmmapsPoten_AVRapi/PotenServiceAVR.cs create mode 100644 FarmmapsPoten_AVRapi/Program.cs create mode 100644 FarmmapsPoten_AVRapi/appsettings.json diff --git a/FarmmapsPoten/FarmmapsPoten.csproj b/FarmmapsPoten/FarmmapsPoten.csproj index f76d9da..fd6dc18 100644 --- a/FarmmapsPoten/FarmmapsPoten.csproj +++ b/FarmmapsPoten/FarmmapsPoten.csproj @@ -12,6 +12,9 @@ Always + + PreserveNewest + diff --git a/FarmmapsPoten/Models/PotenInput.cs b/FarmmapsPoten/Models/PotenInput.cs index dc97b6b..52bda2d 100644 --- a/FarmmapsPoten/Models/PotenInput.cs +++ b/FarmmapsPoten/Models/PotenInput.cs @@ -5,6 +5,7 @@ namespace FarmmapsPoten.Models { public class PotenInput { + public string File { get; set; } public string OutputFileName { get; set; } public string FieldName { get; set; } @@ -12,8 +13,6 @@ namespace FarmmapsPoten.Models public string MeanDensity { get; set; } public string Variation { get; set; } public bool UseShadow { get; set; } - public bool ConvertToCountPerArea { get; set; } - public float Rijbreedte_m { get; set; } public JObject GeometryJson { get; set; } public bool GenerateTaskmap { get; set; } diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index b3dad38..637b9c3 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -58,8 +58,6 @@ namespace FarmmapsVRApoten var variation = input.Variation; var fieldName = input.FieldName; bool useShadow = input.UseShadow; - bool convertToCountPerArea = input.ConvertToCountPerArea; - float rijBreedte_m = input.Rijbreedte_m; var myDrive = roots.SingleOrDefault(r => r.Name == "My drive"); if (myDrive == null) { @@ -93,18 +91,6 @@ namespace FarmmapsVRApoten Path.Combine(DownloadFolder, $"{input.OutputFileName}.shadow.zip")); } - //Calculating AHN map - _logger.LogInformation("retreiving AHN map for field"); - var AHNItem = await _generalService.RunAhnTask(cropfieldItem); - if (AHNItem == null) - { - _logger.LogError("Something went wrong while obtaining the AHN map"); - return; - } - _logger.LogInformation("Downloading AHN map"); - await _farmmapsApiService.DownloadItemAsync(AHNItem.Code, - Path.Combine(DownloadFolder, $"{input.OutputFileName}_AHN.zip")); - _logger.LogInformation("Looking for local data to use"); var localDataAvailable = input.File; @@ -185,23 +171,14 @@ namespace FarmmapsVRApoten ? "Download application map completed." : "Something went wrong while downloading."); - - // if convertToCountPerArea == True, than recalculate pootafstand in cm to # of poters/m2 from the geotiffItem with the use of the zoneringsTask - if (convertToCountPerArea) - { - applianceMapItem = - await _potenService.ConvertToCountPerAreaTroughZonering(cropfieldItem, applianceMapItem, input.Rijbreedte_m); - - } - //GEOTIFF TO Taskmap _logger.LogInformation($"Converting geotiff to taskmap"); - var taskmap = await _generalService.CreateTaskmap(applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + var taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), input.EndPoint.ToString(Formatting.None), input.Angle); if (taskmap == null) { - _logger.LogError("Something went wrong with geotiff to shape transformation"); + _logger.LogError("Something went wrong with geotiff to taskmap transformation"); return; } diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index 7fb8b26..70691fd 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -1,15 +1,12 @@ [ { "File": "PlantingSampleDataLutum.zip", - //"File": "Lutum_SampleDataPlanting.zip", - "OutputFileName": "20210212_vraPoten_SampleData_TASKMAP_ENDPOINT", + "OutputFileName": "20210216_vraPoten_SampleData", "FieldName": "lutum", "PlantingYear": 2020, "MeanDensity": "30", "Variation": "20", "UseShadow": false, - "ConvertToCountPerArea": false, - "Rijbreedte_m": 0.75, "geometryJson": { "type": "Polygon", "coordinates": [ diff --git a/FarmmapsPoten/PotenService.cs b/FarmmapsPoten/PotenService.cs index c77f393..02acda9 100644 --- a/FarmmapsPoten/PotenService.cs +++ b/FarmmapsPoten/PotenService.cs @@ -25,24 +25,13 @@ namespace FarmmapsVRApoten _generalService = generalService; } - public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem,string meanDensity, string variation) + 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}"); @@ -50,8 +39,6 @@ namespace FarmmapsVRApoten _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}"); @@ -60,7 +47,6 @@ namespace FarmmapsVRApoten }); - var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); if (itemTask.State == ItemTaskState.Error) { @@ -84,49 +70,6 @@ namespace FarmmapsVRApoten } - - // Extra task making use of the zonering task to convert the planting distance in cm to number of seeds per m2 - public async Task ConvertToCountPerAreaTroughZonering(Item cropfieldItem, Item geotiffItem, float rijBreedte_m) - { - var zoneringTaskRequest = new TaskRequest() { TaskType = VRAZONERING_TASK }; - zoneringTaskRequest.attributes["formula"] = $"((100/[0])/{rijBreedte_m.ToString()})"; - zoneringTaskRequest.attributes["output"] = "{\"Name\":\"CountPerAreaConversion\",\"Quantity\":\"CountPerArea\",\"Unit\":\"#/m2\"}"; - zoneringTaskRequest.attributes["inputs"] = $"{{\"ItemCode\":{geotiffItem.Code},\"LayerName\":null\"}}"; - - var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, zoneringTaskRequest); - - await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => - { - var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); - _logger.LogInformation($"Waiting on convertion to Count per area through zoneringTast; 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 = $"VRAZonering"; - 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())); - i => - i.Name.ToLower().Contains(itemName.ToLower())); - if (applianceMapItem == null) - { - _logger.LogError("Could not find the converted to count per area geotiff child item under cropfield"); - return null; - } - - return applianceMapItem; - } - - } } diff --git a/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json b/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json new file mode 100644 index 0000000..7414170 --- /dev/null +++ b/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json @@ -0,0 +1,302 @@ +{ +"type": "FeatureCollection", +"name": "PlantingSampleDataLutum", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669006678383711, 52.529290221274948 ] } }, +{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669006104248043, 52.529200349555097 ] } }, +{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669005530115061, 52.529110477833399 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669004955984766, 52.529020606109889 ] } }, +{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669004381857157, 52.528930734384545 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669003807732236, 52.528840862657354 ] } }, +{ "type": "Feature", "properties": { "lutum": 12 }, "geometry": { "type": "Point", "coordinates": [ 5.66900323361, 52.528750990928309 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669002659490453, 52.528661119197444 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669002085373592, 52.528571247464789 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669001511259415, 52.528481375730252 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669000937147927, 52.528391503993873 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669000363039124, 52.52830163225569 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669154039708354, 52.529289871032709 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669153465272641, 52.529199999313761 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669152890839613, 52.529110127592965 ] } }, +{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669152316409275, 52.529020255870378 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669151741981626, 52.528930384145944 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669151167556664, 52.528840512419642 ] } }, +{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.66915059313439, 52.528750640691563 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669150018714802, 52.528660768961629 ] } }, +{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669149444297904, 52.528570897229841 ] } }, +{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669148869883696, 52.528481025496269 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669148295472175, 52.528391153760822 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.66914772106334, 52.528301282023527 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669147146657193, 52.528211410284435 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669146572253736, 52.528121538543502 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669145997852967, 52.528031666800764 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669145423454884, 52.527941795056144 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669301401030309, 52.529289520607428 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.66930082629455, 52.52919964888941 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669300251561478, 52.529109777169573 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669299676831097, 52.529019905447882 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669299102103404, 52.528930033724393 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669298527378404, 52.528840161999021 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669297952656091, 52.528750290271873 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669297377936467, 52.528660418542835 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669296803219534, 52.528570546811963 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669296228505289, 52.528480675079251 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669295653793735, 52.528390803344763 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669295079084868, 52.52830093160842 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669294504378692, 52.528211059870216 ] } }, +{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669293929675204, 52.528121188130193 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669293354974407, 52.528031316388351 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669292780276298, 52.527941444644689 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669448762349575, 52.529289169999203 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669448187313771, 52.529199298282116 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669447612280653, 52.529109426563181 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669447037250229, 52.529019554842385 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669446462222496, 52.52892968311982 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669445887197454, 52.528839811395386 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669445312175103, 52.528749939669105 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669444737155444, 52.528660067941011 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669444162138473, 52.528570196211071 ] } }, +{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669443587124193, 52.528480324479311 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669443012112604, 52.528390452745711 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669442437103707, 52.528300581010306 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669441862097501, 52.528210709273012 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669441287093983, 52.52812083753394 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669440712093158, 52.528030965793008 ] } }, +{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669440137095023, 52.527941094050242 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669596123666151, 52.529288819207963 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.6695955483303, 52.5291989474918 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669594972997138, 52.529109075773768 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669594397666672, 52.529019204053945 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669593822338896, 52.52892933233224 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669593247013813, 52.528839460608751 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669592671691423, 52.528749588883372 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669592096371725, 52.528659717156224 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.66959152105472, 52.528569845427207 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669590945740404, 52.528479973696371 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669590370428786, 52.528390101963666 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669589795119855, 52.528300230229171 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669589219813616, 52.528210358492849 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669588644510072, 52.528120486754638 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669588069209219, 52.52803061501465 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669587493911057, 52.527940743272772 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669743484980033, 52.52928846823373 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669742909344135, 52.529198596518491 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669742333710931, 52.529108724801389 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669741758080422, 52.529018853082476 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669741182452603, 52.528928981361695 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669740606827482, 52.528839109639122 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669740031205053, 52.528749237914688 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669739455585318, 52.528659366188421 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669738879968274, 52.528569494460349 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669738304353925, 52.528479622730416 ] } }, +{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.66973772874227, 52.528389750998656 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.66973715313331, 52.528299879265084 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669736577527043, 52.528210007529651 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669736001923469, 52.528120135792392 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669735426322587, 52.528030264053307 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669890846291222, 52.529288117076518 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669890270355278, 52.529198245362174 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669889694422031, 52.529108373646018 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669889118491477, 52.529018501928007 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669888542563618, 52.528928630208178 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.669887966638455, 52.528838758486494 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.669887390715987, 52.528748886762969 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.669886814796213, 52.528659015037654 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669886238879136, 52.528569143310477 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669885662964753, 52.528479271581489 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669885087053065, 52.528389399850646 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669884511144072, 52.528299528117998 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669883935237774, 52.528209656383481 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.66988335933417, 52.528119784647131 ] } }, +{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669882783433263, 52.528029912908977 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670038207599717, 52.529287765736285 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670037631363726, 52.529197894022879 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670037055130432, 52.529108022307639 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670036478899838, 52.529018150590574 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670035902671936, 52.528928278871632 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670035326446734, 52.528838407150914 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670034750224229, 52.528748535428306 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670034174004417, 52.528658663703865 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670033597787302, 52.528568791977627 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670033021572887, 52.528478920249526 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670032445361164, 52.528389048519642 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670031869152139, 52.528299176787897 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670031292945811, 52.528209305054318 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670030716742176, 52.528119433318885 ] } }, +{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670030140541242, 52.52802956158164 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670186145444245, 52.529377285923772 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670185568905514, 52.52928741421308 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670184992369477, 52.52919754250059 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670184415836139, 52.529107670786267 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670183839305502, 52.529017799070111 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670183262777561, 52.528927927352115 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670182686252317, 52.528838055632299 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670182109729772, 52.528748183910643 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670181533209922, 52.52865831218714 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670180956692772, 52.528568440461825 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670180380178322, 52.528478568734663 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670179803666567, 52.52838869700566 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670179227157511, 52.528298825274803 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670178650651151, 52.528208953542183 ] } }, +{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.67017807414749, 52.528119081807688 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670177497646527, 52.528029210071367 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670333507047393, 52.529376934216629 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670332930208612, 52.529287062506896 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670332353372531, 52.529197190795308 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670331776539151, 52.52910731908193 ] } }, +{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670331199708469, 52.529017447366684 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670330622880485, 52.528927575649611 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670330046055202, 52.528837703930698 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670329469232617, 52.528747832209973 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670328892412731, 52.528657960487401 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670328315595544, 52.528568088762988 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.67032773878106, 52.528478217036756 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670327161969271, 52.528388345308677 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670326585160184, 52.528298473578765 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670326008353794, 52.52820860184702 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670325431550103, 52.528118730113469 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670324854749113, 52.528028858378079 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.67048086864784, 52.529376582326506 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670480291509012, 52.529286710617704 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670479714372884, 52.529196838907055 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670479137239461, 52.529106967194558 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670478560108735, 52.529017095480263 ] } }, +{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670477982980711, 52.5289272237641 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670477405855387, 52.528837352046096 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670476828732764, 52.528747480326295 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.67047625161284, 52.528657608604661 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670475674495618, 52.528567736881193 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670475097381098, 52.528477865155843 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670474520269277, 52.528387993428701 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670473943160156, 52.528298121699713 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670473366053738, 52.52820824996892 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670472788950019, 52.528118378236279 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670472211849, 52.528028506501805 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670628230245586, 52.529376230253412 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670627652806711, 52.529286358545527 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670627075370538, 52.529196486835779 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670626497937069, 52.529106615124242 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670625920506301, 52.52901674341085 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670625343078235, 52.528926871695603 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670624765652872, 52.528836999978552 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670624188230208, 52.528747128259667 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670623610810248, 52.528657256538921 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670623033392991, 52.528567384816377 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670622455978436, 52.528477513091964 ] } }, +{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670621878566583, 52.528387641365747 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670621301157429, 52.52829776963771 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.67062072375098, 52.528207897907812 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670620146347232, 52.528118026176088 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670619568946186, 52.528028154442524 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670775591840631, 52.529375877997303 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670775014101707, 52.529286006290334 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670774436365489, 52.529196134581525 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670773858631976, 52.529106262870911 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670773280901165, 52.52901639115845 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670772703173058, 52.52892651944412 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670772125447654, 52.528836647727985 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670771547724952, 52.528746776010017 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670770970004954, 52.528656904290216 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670770392287662, 52.528567032568581 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670769814573072, 52.528477160845142 ] } }, +{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670769236861187, 52.528387289119834 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670768659152, 52.528297417392693 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670768081445522, 52.528207545663754 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670767503741745, 52.528117673932925 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670766926040671, 52.528027802200249 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.67092295343297, 52.529375525558216 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670922375394001, 52.52928565385217 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670921797357738, 52.529195782144292 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670921219324179, 52.529105910434581 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.670920641293326, 52.529016038723007 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670920063265176, 52.528926167009637 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670919485239732, 52.528836295294447 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670918907216992, 52.52874642357736 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670918329196958, 52.528656551858511 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670917751179629, 52.528566680137807 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.670917173165005, 52.52847680841527 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670916595153083, 52.528386936690872 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.67091601714387, 52.528297064964669 ] } }, +{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670915439137358, 52.528207193236646 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670914861133554, 52.528117321506784 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.670914283132454, 52.528027449775095 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671070315022605, 52.529375172936099 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671069736683591, 52.529285301230992 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671069158347279, 52.529195429524037 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.671068580013677, 52.529105557815264 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671068001682782, 52.529015686104621 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671067423354591, 52.52892581439216 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671066845029107, 52.528835942677908 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.67106626670633, 52.528746070961773 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671065688386258, 52.528656199243834 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671065110068891, 52.528566327524032 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671064531754232, 52.528476455802441 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.67106395344228, 52.528386584078959 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671063375133032, 52.528296712353679 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671062796826493, 52.528206840626574 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671062218522659, 52.528116968897628 ] } }, +{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.671061640221531, 52.528027097166849 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671217676609535, 52.529374820131018 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671217097970472, 52.52928494842682 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671216519334117, 52.529195076720818 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671215940700469, 52.529105205012932 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67121536206953, 52.529015333303256 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671214783441298, 52.52892546159174 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671214204815774, 52.528835589878348 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67121362619296, 52.528745718163179 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671213047572849, 52.528655846446135 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671212468955447, 52.528565974727286 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671211890340752, 52.528476103006554 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671211311728769, 52.52838623128406 ] } }, +{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.67121073311949, 52.528296359559697 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671210154512919, 52.528206487833543 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671209575909057, 52.528116616105528 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671208997307901, 52.528026744375651 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671365038193755, 52.529374467142929 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671364459254646, 52.529284595439655 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671363880318245, 52.529194723734562 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671363301384553, 52.529104852027658 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671362722453573, 52.529014980318877 ] } }, +{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671362143525299, 52.528925108608256 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671361564599734, 52.528835236895866 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671360985676879, 52.528745365181578 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671360406756732, 52.528655493465472 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671359827839296, 52.528565621747525 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671359248924568, 52.528475750027788 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671358670012548, 52.528385878306167 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671358091103239, 52.528296006582757 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671357512196637, 52.528206134857442 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671356933292747, 52.528116263130357 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671512399775269, 52.529374113971883 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671511820536112, 52.529284242269519 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671511241299665, 52.529194370565364 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67151066206593, 52.52910449885934 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671510082834905, 52.529014627151504 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.67150950360659, 52.528924755441842 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671508924380984, 52.528834883730326 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671508345158092, 52.528745012016955 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671507765937908, 52.528655140301815 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671507186720435, 52.528565268584806 ] } }, +{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671506607505673, 52.528475396865979 ] } }, +{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671506028293621, 52.528385525145282 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67150544908428, 52.528295653422774 ] } }, +{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671504869877649, 52.52820578169846 ] } }, +{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671504290673729, 52.528115909972264 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671658602278375, 52.52919401721315 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671658022744594, 52.529104145508029 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671657443213525, 52.529014273801138 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.67165686368517, 52.528924402092414 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671656284159525, 52.52883453038185 ] } }, +{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671655704636593, 52.528744658669403 ] } }, +{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671655125116374, 52.528654786955173 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671654545598865, 52.528564915239095 ] } }, +{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671653966084067, 52.528475043521155 ] } }, +{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671653386571983, 52.528385171801425 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.67165280706261, 52.528295300079861 ] } }, +{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.67165222755595, 52.528205428356415 ] } }, +{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671651648051999, 52.528115556631185 ] } }, +{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671800165038228, 52.528294946553906 ] } }, +{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671799585231536, 52.528205074831419 ] } }, +{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671799005427561, 52.528115203107113 ] } } +] +} diff --git a/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.zip b/FarmmapsPoten_AVRapi/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_AVRapi/FarmmapsPoten_AVRapi.csproj b/FarmmapsPoten_AVRapi/FarmmapsPoten_AVRapi.csproj new file mode 100644 index 0000000..edb3d3c --- /dev/null +++ b/FarmmapsPoten_AVRapi/FarmmapsPoten_AVRapi.csproj @@ -0,0 +1,24 @@ + + + + Exe + netcoreapp3.1 + + + + + Always + + + Always + + + PreserveNewest + + + + + + + + diff --git a/FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs b/FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs new file mode 100644 index 0000000..0bb2876 --- /dev/null +++ b/FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs @@ -0,0 +1,27 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsPoten_AVRapi.Models +{ + public class PotenInputAVR + { + 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 bool UseShadow { get; set; } + public bool ConvertToCountPerArea { get; set; } + public string Rijbreedte_m { get; set; } + public JObject GeometryJson { get; set; } + + public bool GenerateTaskmap { get; set; } + public string CellWidth { get; set; } + public string CellHeight { get; set; } + public JObject StartPoint { get; set; } + public JObject EndPoint { get; set; } + public string Angle { get; set; } + + } +} \ No newline at end of file diff --git a/FarmmapsPoten_AVRapi/PotenApplicationAVR.cs b/FarmmapsPoten_AVRapi/PotenApplicationAVR.cs new file mode 100644 index 0000000..295ad21 --- /dev/null +++ b/FarmmapsPoten_AVRapi/PotenApplicationAVR.cs @@ -0,0 +1,228 @@ +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_AVRapi.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static FarmmapsApiSamples.Constants; + + +namespace FarmmapsVRApoten +{ + public class PotenApplicationAVR : IApplication + { + private const string DownloadFolder = "Downloads"; + + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly PotenServiceAVR _potenService; + private readonly GeneralService _generalService; + + + public PotenApplicationAVR(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, PotenServiceAVR potenService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _potenService = potenService; + } + + public async Task RunAsync() + { + // read field data from separate json file + var VRAPotenInputJson = File.ReadAllText("PotenInputAVR.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, PotenInputAVR input) + { + var meanDensity = input.MeanDensity; + var variation = input.Variation; + var fieldName = input.FieldName; + bool useShadow = input.UseShadow; + bool convertToCountPerArea = input.ConvertToCountPerArea; + string rijBreedte_m = input.Rijbreedte_m; + + 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 if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) + { + _logger.LogInformation("input = tiff data"); + var dataPath = Path.Combine("Data", input.File); + geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, + Path.GetFileNameWithoutExtension(input.File)); + + if (geotiffItem == null) + { + _logger.LogError("Could not find item for uploaded data"); + return; + } + } + 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."); + + + // if convertToCountPerArea == True, than recalculate pootafstand in cm to # of poters/m2 from the geotiffItem with the use of the zoneringsTask + if (convertToCountPerArea) + { + applianceMapItem = + await _potenService.ConvertToCountPerAreaTroughZonering(cropfieldItem, applianceMapItem, input.Rijbreedte_m); + + } + + + //Appliancemap (GEOTIFF) TO Taskmap + if (input.GenerateTaskmap == true) + { + _logger.LogInformation($"Converting geotiff to taskmap"); + var taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + input.EndPoint.ToString(Formatting.None), input.Angle); + 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_AVRapi/PotenInputAVR.json b/FarmmapsPoten_AVRapi/PotenInputAVR.json new file mode 100644 index 0000000..757844a --- /dev/null +++ b/FarmmapsPoten_AVRapi/PotenInputAVR.json @@ -0,0 +1,41 @@ +[ + { + "File": "PlantingSampleDataLutum.zip", + "OutputFileName": "20210216_SampleData_CovertArea", + "FieldName": "lutum", + "PlantingYear": 2021, + "MeanDensity": "30", + "Variation": "20", + "UseShadow": false, + "ConvertToCountPerArea": true, + "Rijbreedte_m": "0.75", //as string + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 5.66886041703652044, 52.52929999060298627 ], + [ 5.6716230923214912, 52.52946316399909676 ], + [ 5.67185376229668581, 52.5280565894154563 ], + [ 5.66903207841337231, 52.52790646510525363 ], + [ 5.66886041703652044, 52.52929999060298627 ] + ] + ] + }, + + // if taskmap is not generated, output is only geotiff + "GenerateTaskmap": true, + "CellWidth": "3", + "CellHeight": "10", + "StartPoint": { + "type": "Point", + "coordinates": [ 5.66886041703652044, 52.52929999060298627 ] + }, + "EndPoint": { + "type": "Point", + "coordinates": [ 5.6716230923214912, 52.52946316399909676 ] + } // if no angle + + //"Angle": "317.0" // if no endpoint + } + +] diff --git a/FarmmapsPoten_AVRapi/PotenServiceAVR.cs b/FarmmapsPoten_AVRapi/PotenServiceAVR.cs new file mode 100644 index 0000000..42ec93e --- /dev/null +++ b/FarmmapsPoten_AVRapi/PotenServiceAVR.cs @@ -0,0 +1,120 @@ +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 PotenServiceAVR + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly GeneralService _generalService; + + public PotenServiceAVR(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); + _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; + + } + + + // Extra task making use of the zonering task to convert the planting distance in cm to number of seeds per m2 !!! mind the hardcoded values !!! + public async Task ConvertToCountPerAreaTroughZonering(Item cropfieldItem, Item geotiffItem, string rijBreedte_m) + { + var zoneringTaskRequest = new TaskRequest() { TaskType = VRAZONERING_TASK }; + zoneringTaskRequest.attributes["formula"] = $"((100/[0])/{rijBreedte_m})"; + zoneringTaskRequest.attributes["output"] = "{\"Name\":\"CountPerAreaConversion\",\"Quantity\":\"CountPerArea\",\"Unit\":\"#/m2\"}"; + zoneringTaskRequest.attributes["inputs"] = $"[{{\"ItemCode\":\"{geotiffItem.Code}\",\"LayerName\":\"Appliancemap\"}}]"; + + var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, zoneringTaskRequest); + + await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => + { + var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); + _logger.LogInformation($"Waiting on convertion to Count per area through zoneringTast; 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 applianceMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, + GEOTIFF_PROCESSED_ITEMTYPE, "CountPerAreaConversion", + i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && + i.Name.ToLower().Contains("CountPerAreaConversion".ToLower())); + + if (applianceMapItem == null) + { + _logger.LogError("Could not find the converted to count per area geotiff child item under cropfield"); + return null; + } + + return applianceMapItem; + } + + + } +} + + diff --git a/FarmmapsPoten_AVRapi/Program.cs b/FarmmapsPoten_AVRapi/Program.cs new file mode 100644 index 0000000..1003080 --- /dev/null +++ b/FarmmapsPoten_AVRapi/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 diff --git a/FarmmapsPoten_AVRapi/appsettings.json b/FarmmapsPoten_AVRapi/appsettings.json new file mode 100644 index 0000000..3e75982 --- /dev/null +++ b/FarmmapsPoten_AVRapi/appsettings.json @@ -0,0 +1,10 @@ +{ + "Authority": "https://accounts.test.farmmaps.eu/", + "Endpoint": "https://test.farmmaps.eu/", + "BasePath": "api/v1", + "DiscoveryEndpointUrl": "https://accounts.test.farmmaps.eu/.well-known/openid-configuration", + "RedirectUri": "http://example.nl/api", + "ClientId": "", + "ClientSecret": "", + "Scopes": [ "api" ] +} \ No newline at end of file From 6debe8208596e97e6f52be3b2f2fa101a3f431e4 Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Mon, 1 Mar 2021 14:04:18 +0100 Subject: [PATCH 12/35] Updated Blight and Zonering --- FarmMapsBlight/BlightService.cs | 7 +++- FarmMapsBlight/FarmMapsBlight.csproj | 2 +- FarmMapsBlight/Models/Fungicide.cs | 44 ++++++++++++++++++++ FarmMapsBlight/Models/Spray.cs | 3 +- FarmmapsApi/Services/GeneralService.cs | 8 ++-- FarmmapsZonering/Models/ZoneringInput.cs | 2 +- FarmmapsZonering/Services/ZoneringService.cs | 21 ++++++---- FarmmapsZonering/ZoneringApplication.cs | 24 +++++------ 8 files changed, 80 insertions(+), 31 deletions(-) create mode 100644 FarmMapsBlight/Models/Fungicide.cs diff --git a/FarmMapsBlight/BlightService.cs b/FarmMapsBlight/BlightService.cs index 578a134..cead99f 100644 --- a/FarmMapsBlight/BlightService.cs +++ b/FarmMapsBlight/BlightService.cs @@ -36,8 +36,13 @@ namespace FarmMapsBlight taskRequest.attributes["plantingDate"] = plantingDate.ToUniversalTime().ToString("o"); taskRequest.attributes["emergeDate"] = emergeDate.ToUniversalTime().ToString("o"); + var fungicide1 = "{\"ai1\": \"propamocarb\", \"ai2\": \"fluopicolide\", \"ai3\": \"cymoxanil\", \"code\": \"infinito12curz\", \"name\": \"infinito 1,2 l + curzate partner 0,2 kg\", \"maxdose\": \"1\", \"mindose\": \"1\", \"safedays\": \"14\", \"emergence\": true, \"newgrowth\": \"1\", \"contentai1\": \"525.2\", \"contentai2\": \"62.5\", \"contentai3\": \"600\", \"fastgrowth\": true, \"contentunit\": \"l/ha + kg/ha\", \"rainfastness\": \"2.5\", \"tuberfilling\": true, \"aidescription\": \"(propamocarb + fluopicolide) 1.2 l/ha + cymoxanil 0.2 kg/ha\", \"curativescore\": \"2\", \"dryingtimemax\": \"2\", \"dryingtimemin\": \"2\", \"vracompatible\": false, \"maxapplications\": \"4\", \"preventivescore\": \"3\", \"recommendeddose\": \"1\", \"tuberprotection\": \"3.3\", \"eradicativescore\": \"2\", \"earlytubersetting\": true, \"protectioncategory\": \"2\", \"applicationrateunit\": null, \"ctgbregistrationnumber\": \"12927 n + 12755 n\"}"; + var fungicide2 = "{\"ai1\": \"fluazinam\", \"ai2\": \"cymoxanil\", \"ai3\": null, \"code\": \"kunshi\", \"name\": \"kunshi\", \"maxdose\": \"0.5\", \"mindose\": \"0.4\", \"safedays\": \"1\", \"emergence\": false, \"newgrowth\": \"1\", \"contentai1\": \"375\", \"contentai2\": \"250\", \"contentai3\": \"0\", \"fastgrowth\": true, \"contentunit\": \"g/kg\", \"rainfastness\": \"2.5\", \"tuberfilling\": false, \"aidescription\": \"(fluazinam + cymoxanil) 0.5 kg/ha\", \"curativescore\": \"2\", \"dryingtimemax\": \"2\", \"dryingtimemin\": \"1\", \"vracompatible\": true, \"maxapplications\": \"5\", \"preventivescore\": \"2.9\", \"recommendeddose\": \"0.5\", \"tuberprotection\": \"3.3\", \"eradicativescore\": \"1\", \"earlytubersetting\": false, \"protectioncategory\": \"1\", \"applicationrateunit\": \"kg/ha\", \"ctgbregistrationnumber\": \"14371 n\"}"; + List sprays = new List(); - sprays.Add(new Spray() { fungicideCode = "FLEX", SprayTime = new DateTime(2020, 7, 1), dose = 0.6, isVRA = false }); + sprays.Add(new Spray() { fungicide = JsonConvert.DeserializeObject(fungicide1), sprayTime = new DateTime(2020, 6, 24), dose = 0.6, isVRA = false }); + sprays.Add(new Spray() { fungicide = JsonConvert.DeserializeObject(fungicide2), sprayTime = new DateTime(2020, 7, 2), dose = 0.6, isVRA = false }); + //sprays.Add(new Spray() { fungicideCode = "FLEX", SprayTime = new DateTime(2020, 7, 1), dose = 0.6, isVRA = false }); taskRequest.attributes["sprays"] = JsonConvert.SerializeObject(sprays); List irrigations = new List(); diff --git a/FarmMapsBlight/FarmMapsBlight.csproj b/FarmMapsBlight/FarmMapsBlight.csproj index 37ce34b..666c07d 100644 --- a/FarmMapsBlight/FarmMapsBlight.csproj +++ b/FarmMapsBlight/FarmMapsBlight.csproj @@ -11,7 +11,7 @@ - always + >always diff --git a/FarmMapsBlight/Models/Fungicide.cs b/FarmMapsBlight/Models/Fungicide.cs new file mode 100644 index 0000000..1ca71fe --- /dev/null +++ b/FarmMapsBlight/Models/Fungicide.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FarmMapsBlight.Models { + public class Fungicide { + public string code { get; set; } + public string name { get; set; } + public string ctgbregistrationnumber { get; set; } + public string ai1 { get; set; } + public string ai2 { get; set; } + public string ai3 { get; set; } + public double contentai1 { get; set; } + public double contentai2 { get; set; } + public double contentai3 { get; set; } + public string contentunit { get; set; } + public string aidescription { get; set; } + public int protectioncategory { get; set; } + public double preventivescore { get; set; } + public double curativescore { get; set; } + public string applicationrateunit { get; set; } + public double recommendeddose { get; set; } + public double newgrowth { get; set; } + public double tuberprotection { get; set; } + public double dryingtimemin { get; set; } + public double dryingtimemax { get; set; } + public double rainfastness { get; set; } + public double mindose { get; set; } + public double maxdose { get; set; } + public int? maxapplications { get; set; } + public bool vracompatible { get; set; } + public double eradicativescore { get; set; } + public bool emergence { get; set; } + public bool fastgrowth { get; set; } + public bool earlytubersetting { get; set; } + public bool tuberfilling { get; set; } + public int? safedays { get; set; } + public double takeback { + get { + return curativescore * 6; + } + } + } +} \ No newline at end of file diff --git a/FarmMapsBlight/Models/Spray.cs b/FarmMapsBlight/Models/Spray.cs index f0d2f29..206b849 100644 --- a/FarmMapsBlight/Models/Spray.cs +++ b/FarmMapsBlight/Models/Spray.cs @@ -4,8 +4,9 @@ namespace FarmMapsBlight.Models { public class Spray { - public DateTime SprayTime { get; set; } + public DateTime sprayTime { get; set; } public string fungicideCode { get; set; } + public Fungicide fungicide { get; set; } public double dose { get; set; } public bool isVRA { get; set; } } diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 6bb9c76..728ed78 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -38,7 +38,7 @@ namespace FarmmapsApi.Services } public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName) { - var startUpload = DateTime.UtcNow; + var startUpload = DateTime.UtcNow.AddSeconds(-3); var result = await _farmmapsApiService.UploadFile(filePath, root.Code, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); @@ -90,9 +90,9 @@ namespace FarmmapsApi.Services return null; } - //the taskmap is a child of the input tiff + //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.Code, + 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"); @@ -131,8 +131,6 @@ namespace FarmmapsApi.Services 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) { diff --git a/FarmmapsZonering/Models/ZoneringInput.cs b/FarmmapsZonering/Models/ZoneringInput.cs index 5856ef7..ceed93c 100644 --- a/FarmmapsZonering/Models/ZoneringInput.cs +++ b/FarmmapsZonering/Models/ZoneringInput.cs @@ -10,7 +10,7 @@ namespace FarmmapsZonering.Models public string Formula { get; set; } public string OutputFileName { get; set; } public string CropFieldName { get; set; } - public string LayerName { get; set; } + public string CalculatedLayerName { get; set; } public string CalculatedQuantity { get; set; } public string CalculatedUnit { get; set; } public bool CreateNewCropfield { get; set; } diff --git a/FarmmapsZonering/Services/ZoneringService.cs b/FarmmapsZonering/Services/ZoneringService.cs index d379122..ce1cfd3 100644 --- a/FarmmapsZonering/Services/ZoneringService.cs +++ b/FarmmapsZonering/Services/ZoneringService.cs @@ -48,14 +48,19 @@ namespace FarmmapsZonering.Services _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } - - var itemName = $"VRAZonering"; - 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())); - i => - i.Name.ToLower().Contains(itemName.ToLower())); + + var uploadedFilesChildren = await _farmmapsApiService.GetItemChildrenAsync(cropfieldItem.Code); + //int last = uploadedFilesChildren.Count - 1; + //var applianceMapItem = uploadedFilesChildren[last]; + + + var itemName = output.Name; + //var itemName = $"VRAZonering"; + 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 VRAZonering geotiff child item under cropfield"); diff --git a/FarmmapsZonering/ZoneringApplication.cs b/FarmmapsZonering/ZoneringApplication.cs index d53fd46..2519944 100644 --- a/FarmmapsZonering/ZoneringApplication.cs +++ b/FarmmapsZonering/ZoneringApplication.cs @@ -73,7 +73,7 @@ namespace FarmmapsZonering return; } - bool useCreatedCropfield = true; + bool useCreatedCropfield = false; bool GetWatBal = input.GetWatBal; bool getVanDerSat = input.GetVanDerSat; bool StoreVanDerSatStatistics = input.storeVanDerSatStatistics; @@ -102,11 +102,6 @@ namespace FarmmapsZonering _settings.CropfieldName = cropfieldItem.Name; _settings.CropfieldItemCode = cropfieldItem.Code; SaveSettings(settingsfile); - - cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDrive.Code, "Cropfield VRA Zonering", 2020, - @"{ ""type"": ""Polygon"", ""coordinates"": [ [ [ 5.670991253771027, 52.796788997702613 ], [ 5.671526456638633, 52.797291618546666 ], [ 5.671275936147413, 52.797422436717852 ], [ 5.671959173850738, 52.798269302728798 ], [ 5.670649634919365, 52.798778791408822 ], [ 5.671503682048522, 52.799591206957416 ], [ 5.675159003761311, 52.798193567415474 ], [ 5.673029579585948, 52.796024727480535 ], [ 5.670991253771027, 52.796788997702613 ] ] ] }"); - _settings.CropfieldItemCode = cropfieldItem.Code; - SaveSettings(settingsfile); } else { @@ -163,7 +158,7 @@ namespace FarmmapsZonering var outputItem = await _zoneringService.CreateApplicationMapAsync(cropfieldItem, input.Formula, new Output() { - Name = input.LayerName, + Name = input.CalculatedLayerName, Quantity = input.CalculatedQuantity, Unit = input.CalculatedUnit, @@ -175,17 +170,18 @@ namespace FarmmapsZonering }, new InputParameter() { - ItemCode = inputOneItem.Code, - LayerName = inputOneItem.Data["layers"][0]["name"].ToString() + ItemCode = inputTwoItem.Code, + LayerName = inputTwoItem.Data["layers"][0]["name"].ToString() }); - _logger.LogInformation("Downloading output"); - - - + _logger.LogInformation("Downloading output"); + _logger.LogInformation($"outputitem: {outputItem} with code {outputItem.Code} and date {outputItem.DataDate}"); + //_logger.LogInformation($"Data: {outputItem.Data}"); + await _farmmapsApiService.DownloadItemAsync(outputItem.Code, - Path.Combine(DownloadFolder, $"{input.OutputFileName}.zip")); + Path.Combine(DownloadFolder, $"{input.OutputFileName}.zoning.zip")); + } // Functions to save previously created cropfields From cf5ac33b3fa24968aeb1c93cc3a7b6d5336d4055 Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Mon, 1 Mar 2021 14:33:29 +0100 Subject: [PATCH 13/35] added updaten zoning input --- FarmmapsZonering/ZoneringInput.json | 61 ++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/FarmmapsZonering/ZoneringInput.json b/FarmmapsZonering/ZoneringInput.json index dc23cc3..705c5a7 100644 --- a/FarmmapsZonering/ZoneringInput.json +++ b/FarmmapsZonering/ZoneringInput.json @@ -1,4 +1,4 @@ -[ +//[ //Formule kan meerdere inputs aan // Met blokhaken en een nummer specificeer je een input: @@ -39,33 +39,64 @@ // ] // }, +[ + + //{ + // "InputItemOne": "20210127_L2A_B08_(Raw).tiff", + // "InputItemTwo": "20210127_L2A_B04_(Raw).tiff", + // "Formula": "([0]-[1])/([0]+[1])+0.3", + // "CalculatedLayerName": "Biomassa", + // "CalculatedQuantity": "NDVI", + // "CalculatedUnit": "wdviValue", + // "OutputFileName": "20210212_NDVI-test", + // "CropFieldName": "Mahindra-Hapreet_Singh-2", + // "UseShadow": false, + // "GetWatBal": false, + // "GetVanDerSat": false, + // "storeVanDerSatStatistics": false, + // "CropYear": 2020, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 31.63887936537028, 75.92907261717566 ], + // [ 31.63895684173582, 75.93143281072912 ], + // [ 31.63940906549488, 75.93135348937818 ], + // [ 31.63931497834855, 75.9290907801773 ], + // [ 31.63887936537028, 75.92907261717566 ] + // ] + // ] + + // } + //} + { - "InputItemOne": "data_9001.tif", - "InputItemTwo": "data_times_two_4326.tiff", - "Formula": "if [0] >= 1.28 then [1] else 0", - "CreatedLayerName": "Biomassa", + "InputItemOne": "2020-04-22_sentinel-2_l2a_b08.tiff", + "InputItemTwo": "2020-04-22_sentinel-2_l2a_b04.tiff", + "Formula": "([0]-[1])/([0]+[1])", + "CalculatedLayerName": "Biomassa", "CalculatedQuantity": "NDVI", "CalculatedUnit": "ndviValue", - "OutputFileName": "Zoning", - "CropFieldName": "Data_whole", + "OutputFileName": "20210212_NDVI-test_FS", + "CropFieldName": "MestSiloAduard", "UseShadow": false, "GetWatBal": false, - "GetVanDerSat": true, - "storeVanDerSatStatistics": true, + "GetVanDerSat": false, + "storeVanDerSatStatistics": false, "CropYear": 2020, "geometryJson": { "type": "Polygon", "coordinates": [ [ - [ 5.66886041703652044, 52.52929999060298627 ], - [ 5.6716230923214912, 52.52946316399909676 ], - [ 5.67185376229668581, 52.5280565894154563 ], - [ 5.66903207841337231, 52.52790646510525363 ], - [ 5.66886041703652044, 52.52929999060298627 ] + [ 6.48274677, 53.25861525 ], + [ 6.48740108, 53.25799841 ], + [ 6.48756130, 53.25934424 ], + [ 6.48285892, 53.25953650 ], + [ 6.48274677, 53.25861525 ] ] ] } } - + ] From 39a20247181712a58038129c7366ef43ea58cc4f Mon Sep 17 00:00:00 2001 From: Riepma Date: Mon, 1 Mar 2021 16:31:31 +0100 Subject: [PATCH 14/35] update poten app with centered and countPerArea --- FarmmapsApi/Services/GeneralService.cs | 3 ++- FarmmapsApiSamples.sln | 10 ++++++++-- FarmmapsPoten/Models/PotenInput.cs | 2 ++ FarmmapsPoten/PotenApplication.cs | 7 ++++--- FarmmapsPoten/PotenInput.json | 12 ++++++++++-- FarmmapsPoten/PotenService.cs | 5 +++-- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 4e1f9b0..f61ba40 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -107,7 +107,7 @@ namespace FarmmapsApi.Services } // Create taskmap based on width, height and direction - public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string cellWidth, string cellHeight, string startPoint, string endPoint = null, string angle = null) + public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string cellWidth, string cellHeight, string startPoint, string centered = "false", string endPoint = null, string angle = null) { var taskmapRequest = new TaskRequest { TaskType = TASKMAP_TASK }; taskmapRequest.attributes["inputCode"] = tiffItem.Code; @@ -115,6 +115,7 @@ namespace FarmmapsApi.Services taskmapRequest.attributes["cellWidth"] = cellWidth; //metres taskmapRequest.attributes["cellHeight"] = cellHeight; //metres taskmapRequest.attributes["startPoint"] = startPoint; // Coordinates WGS84 + taskmapRequest.attributes["centered"] = centered; if (angle == null) taskmapRequest.attributes["endPoint"] = endPoint; // Coordinates WGS84 if (endPoint == null) taskmapRequest.attributes["angle"] = angle; // degrees between 0.0 and 360.0 diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index 65894a6..62501d5 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -19,9 +19,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsHaulmkilling", "Far EndProject 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}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmMapsBlight", "FarmMapsBlight\FarmMapsBlight.csproj", "{892E0932-5D11-4A37-979E-CEDB39C2E181}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsZonering", "FarmmapsZonering\FarmmapsZonering.csproj", "{91A58C4A-4A80-4079-B43D-9B851206194F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsZonering", "FarmmapsZonering\FarmmapsZonering.csproj", "{91A58C4A-4A80-4079-B43D-9B851206194F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsPoten_AVRapi", "FarmmapsPoten_AVRapi\FarmmapsPoten_AVRapi.csproj", "{E220AA18-D735-4B07-859B-C0DA11CD1E8B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -57,6 +59,10 @@ Global {91A58C4A-4A80-4079-B43D-9B851206194F}.Debug|Any CPU.Build.0 = Debug|Any CPU {91A58C4A-4A80-4079-B43D-9B851206194F}.Release|Any CPU.ActiveCfg = Release|Any CPU {91A58C4A-4A80-4079-B43D-9B851206194F}.Release|Any CPU.Build.0 = Release|Any CPU + {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FarmmapsPoten/Models/PotenInput.cs b/FarmmapsPoten/Models/PotenInput.cs index 52bda2d..46a7e11 100644 --- a/FarmmapsPoten/Models/PotenInput.cs +++ b/FarmmapsPoten/Models/PotenInput.cs @@ -13,11 +13,13 @@ namespace FarmmapsPoten.Models public string MeanDensity { get; set; } public string Variation { get; set; } public bool UseShadow { get; set; } + public bool CountPerArea { get; set; } public JObject GeometryJson { get; set; } public bool GenerateTaskmap { get; set; } public string CellWidth { get; set; } public string CellHeight { get; set; } + public string Centered { get; set; } public JObject StartPoint { get; set; } public JObject EndPoint { get; set; } public string Angle { get; set; } diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 637b9c3..2ebed6f 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -58,6 +58,7 @@ namespace FarmmapsVRApoten var variation = input.Variation; var fieldName = input.FieldName; bool useShadow = input.UseShadow; + bool countPerArea = input.CountPerArea; var myDrive = roots.SingleOrDefault(r => r.Name == "My drive"); if (myDrive == null) { @@ -156,7 +157,7 @@ namespace FarmmapsVRApoten // INPUT IS NEEDED as GEOTIFF var applianceMapItem = - await _potenService.CalculateApplicationMapAsync(cropfieldItem, geotiffItem, meanDensity, variation); + await _potenService.CalculateApplicationMapAsync(cropfieldItem, geotiffItem, meanDensity, variation, countPerArea); if (applianceMapItem == null) { return; @@ -174,8 +175,8 @@ namespace FarmmapsVRApoten //GEOTIFF TO Taskmap _logger.LogInformation($"Converting geotiff to taskmap"); - var taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - input.EndPoint.ToString(Formatting.None), input.Angle); + var taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle); if (taskmap == null) { _logger.LogError("Something went wrong with geotiff to taskmap transformation"); diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index 70691fd..3fd173c 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -7,6 +7,7 @@ "MeanDensity": "30", "Variation": "20", "UseShadow": false, + "CountPerArea": true, "geometryJson": { "type": "Polygon", "coordinates": [ @@ -23,13 +24,20 @@ "GenerateTaskmap": true, "CellWidth": "3", "CellHeight": "10", + "Centered": "true", "StartPoint": { "type": "Point", - "coordinates": [ 5.66886041703652044, 52.52929999060298627 ] + //"coordinates": [ 5.669032078413372, 52.527906465105254 ] // 1 + //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 + //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 + "coordinates": [ 5.671853762296686, 52.528056589415456 ] // 4 }, "EndPoint": { "type": "Point", - "coordinates": [ 5.6716230923214912, 52.52946316399909676 ] + "coordinates": [ 5.669032078413372, 52.527906465105254 ] // 1 + //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 + //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 + //"coordinates": [ 5.671853762296686, 52.528056589415456 ] // 4 } // if no angle //"Angle": "317.0" // if no endpoint diff --git a/FarmmapsPoten/PotenService.cs b/FarmmapsPoten/PotenService.cs index 02acda9..e3d66e5 100644 --- a/FarmmapsPoten/PotenService.cs +++ b/FarmmapsPoten/PotenService.cs @@ -25,13 +25,14 @@ namespace FarmmapsVRApoten _generalService = generalService; } - public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem, string meanDensity, string variation) + public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem, string meanDensity, string variation, bool countPerArea) { var potenApplicationMapRequest = new TaskRequest() { TaskType = VRAPLANTING_TASK }; if (inputItem != null) {potenApplicationMapRequest.attributes["inputCode"] = inputItem.Code; } potenApplicationMapRequest.attributes["meanDensity"] = meanDensity; potenApplicationMapRequest.attributes["variation"] = variation; - + potenApplicationMapRequest.attributes["variation"] = variation; + potenApplicationMapRequest.attributes["countPerArea"] = countPerArea.ToString(); var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, potenApplicationMapRequest); _logger.LogInformation($"itemTaskCode: {taskCode}"); From f71f072be75ad4131e4c71ddcb01be7761900fe3 Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Tue, 9 Mar 2021 14:15:32 +0100 Subject: [PATCH 15/35] update poten; schaduw moet nu als attribute worden meegegeven --- FarmmapsApi/Services/GeneralService.cs | 7 +- FarmmapsNbs/NitrogenService.cs | 9 +-- FarmmapsPoten/Data/BBL-lutum.tif | Bin 0 -> 227054 bytes FarmmapsPoten/PotenApplication.cs | 2 +- FarmmapsPoten/PotenInput.json | 95 ++++++++++++++----------- FarmmapsPoten/PotenService.cs | 3 +- 6 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 FarmmapsPoten/Data/BBL-lutum.tif diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 1f92d7e..b494fce 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -64,11 +64,12 @@ namespace FarmmapsApi.Services } public async Task ShapeToGeotiff(Item shapeItem) { + var startUpload = DateTime.UtcNow.AddSeconds(-3); await RunAndWaitForTask(shapeItem, "vnd.farmmaps.task.shapetogeotiff"); - // the parent of the shape item is now the tiff item - shapeItem = await _farmmapsApiService.GetItemAsync(shapeItem.Code); - return await _farmmapsApiService.GetItemAsync(shapeItem.ParentCode); + return await FindChildItemAsync(shapeItem.ParentCode, GEOTIFF_PROCESSED_ITEMTYPE, shapeItem.Name, + i => i.Created >= startUpload && + i.Name.ToLower().Contains(shapeItem.Name.ToLower())); } diff --git a/FarmmapsNbs/NitrogenService.cs b/FarmmapsNbs/NitrogenService.cs index 992a3d3..96416ef 100644 --- a/FarmmapsNbs/NitrogenService.cs +++ b/FarmmapsNbs/NitrogenService.cs @@ -104,12 +104,13 @@ namespace FarmmapsNbs await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); + _logger.LogInformation($"Calculating uptake map; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) - tokenSource.Cancel(); + tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); - if(itemTask.State == ItemTaskState.Error) + if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; @@ -159,7 +160,7 @@ namespace FarmmapsNbs await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); - + _logger.LogInformation($"Calculating application map; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); @@ -175,7 +176,7 @@ namespace FarmmapsNbs var itemName = $"VRANbs application"; var applicationMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, - GEOTIFF_PROCESSED_ITEMTYPE, itemName, + GEOTIFF_PROCESSED_ITEMTYPE, itemName, i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && i.Name.ToLower().Contains(itemName.ToLower())); if (applicationMapItem == null) diff --git a/FarmmapsPoten/Data/BBL-lutum.tif b/FarmmapsPoten/Data/BBL-lutum.tif new file mode 100644 index 0000000000000000000000000000000000000000..d090b0130e85559ee7777dfa9cbc3ef4e9df146a GIT binary patch literal 227054 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 CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem, string meanDensity, string variation, bool countPerArea) + public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem, string meanDensity, string variation, bool countPerArea, bool useShadow) { var potenApplicationMapRequest = new TaskRequest() { TaskType = VRAPLANTING_TASK }; if (inputItem != null) {potenApplicationMapRequest.attributes["inputCode"] = inputItem.Code; } @@ -33,6 +33,7 @@ namespace FarmmapsVRApoten potenApplicationMapRequest.attributes["variation"] = variation; potenApplicationMapRequest.attributes["variation"] = variation; potenApplicationMapRequest.attributes["countPerArea"] = countPerArea.ToString(); + potenApplicationMapRequest.attributes["useShadow"] = countPerArea.ToString(); var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, potenApplicationMapRequest); _logger.LogInformation($"itemTaskCode: {taskCode}"); From b7589136935ebca3dd7ef3cf3d2a3d75302d7a3e Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Tue, 9 Mar 2021 14:18:54 +0100 Subject: [PATCH 16/35] update poten serice, foutje hersteld --- FarmmapsPoten/PotenService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FarmmapsPoten/PotenService.cs b/FarmmapsPoten/PotenService.cs index dd7b8b8..b3a6fe0 100644 --- a/FarmmapsPoten/PotenService.cs +++ b/FarmmapsPoten/PotenService.cs @@ -33,7 +33,7 @@ namespace FarmmapsVRApoten potenApplicationMapRequest.attributes["variation"] = variation; potenApplicationMapRequest.attributes["variation"] = variation; potenApplicationMapRequest.attributes["countPerArea"] = countPerArea.ToString(); - potenApplicationMapRequest.attributes["useShadow"] = countPerArea.ToString(); + potenApplicationMapRequest.attributes["useShadow"] = useShadow.ToString(); var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, potenApplicationMapRequest); _logger.LogInformation($"itemTaskCode: {taskCode}"); From 93469b605c97350368271a22f39b32295b641294 Mon Sep 17 00:00:00 2001 From: Riepma Date: Mon, 22 Mar 2021 10:35:55 +0100 Subject: [PATCH 17/35] Update NBS taskmap generator --- FarmmapsNbs/FarmmapsNbs.csproj | 24 --- FarmmapsNbs/NbsApplication.cs | 336 --------------------------------- 2 files changed, 360 deletions(-) delete mode 100644 FarmmapsNbs/FarmmapsNbs.csproj delete mode 100644 FarmmapsNbs/NbsApplication.cs diff --git a/FarmmapsNbs/FarmmapsNbs.csproj b/FarmmapsNbs/FarmmapsNbs.csproj deleted file mode 100644 index 05c7803..0000000 --- a/FarmmapsNbs/FarmmapsNbs.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - netcoreapp3.0 - - - - - Always - - - Always - - - Always - - - - - - - - diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs deleted file mode 100644 index 109843c..0000000 --- a/FarmmapsNbs/NbsApplication.cs +++ /dev/null @@ -1,336 +0,0 @@ -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 FarmmapsNbs.Models; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using static FarmmapsApiSamples.Constants; - -namespace FarmmapsNbs -{ - public class NbsApplication : IApplication - { - private const string DownloadFolder = "Downloads"; - private const string SettingsFile = "settings.json"; - - private readonly ILogger _logger; - private readonly FarmmapsApiService _farmmapsApiService; - private readonly NitrogenService _nitrogenService; - private readonly GeneralService _generalService; - - private Settings _settings; - - public NbsApplication(ILogger logger, FarmmapsApiService farmmapsApiService, - GeneralService generalService, NitrogenService nitrogenService) - { - _logger = logger; - _farmmapsApiService = farmmapsApiService; - _generalService = generalService; - _nitrogenService = nitrogenService; - } - - public async Task RunAsync() - { - var nitrogenInputJson = File.ReadAllText("InputData-NBS.json"); //NitrogenInput.json - List nitrogenInputs = JsonConvert.DeserializeObject>(nitrogenInputJson); - - 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 nitrogenInputs) - { - try - { - await Process(roots, input); - } - catch (Exception ex) - { - _logger.LogError(ex.Message); - } - } - } - - private async Task Process(List roots, NitrogenInput input) - { - - // !!specify if you are using an already created cropfield: - bool useCreatedCropfield = input. UseCreatedCropfield; - var plantingDate = input.PlantingDate; - var FieldName = input.fieldName; - bool StoreStatistics = input.storeSatelliteStatistics; - var measurementDate = input.MeasurementDate; - string settingsfile = $"Settings_{FieldName}.json"; - - LoadSettings(settingsfile); - - var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); - if (uploadedRoot == null) - { - _logger.LogError("Could not find a needed root item"); - return; - } - - var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive"); - if (myDriveRoot == null) - { - _logger.LogError("Could not find a needed root item"); - return; - } - - // Use already created cropfield or create new one - Item cropfieldItem; - if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) - { - _logger.LogInformation("Creating cropfield"); - cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, - $"VRA NBS cropfield {input.OutputFileName}", plantingDate.Year, input.GeometryJson.ToString(Formatting.None)); - _settings.CropfieldItemCode = cropfieldItem.Code; - SaveSettings(settingsfile); - } - else - { - _logger.LogInformation("Cropfield already exists, trying to get it"); - cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); - } - - var geotiffItem = (Item)null; - - // No file input, use most recent satellite image - if (string.IsNullOrEmpty(input.File)) { - _logger.LogInformation("No specific data given, retrieving most recent satellite image"); - - // check if satellite task not yet done, do here and save taskcode - if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) { - var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); - _settings.SatelliteTaskCode = satelliteTaskCode; - SaveSettings(settingsfile); - } - - - // Select a particular satellite item from satelliteTask - Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode, FieldName, StoreStatistics); - - var satelliteBand = satalliteItem.Data["layers"][0]["name"]; - var satelliteStatistics = satalliteItem.Data["layers"][0]["renderer"]["band"]["statistics"]; - Console.WriteLine($"Satellite image date: {satalliteItem.DataDate}"); - //Console.WriteLine($"Satellite image statistics for band {satelliteBand}: {satelliteStatistics}"); - - //Store data to csv - if (StoreStatistics==true) { - var SatelliteFile = $"C:\\Akkerweb\\DataSatellite_{FieldName}.csv"; - var NewLineField = $"\"Field\":{FieldName}" + Environment.NewLine; - var NewLineDate = $"\"date\":{satalliteItem.DataDate}" + Environment.NewLine; - var i = 0; - foreach (var item in satelliteStatistics) { - //var NewLines2; - if (i == 0) { - File.AppendAllText(SatelliteFile, NewLineDate); - i++; - } - File.AppendAllText(SatelliteFile, $"{item}" + Environment.NewLine); - - } - } - - - // must be wdvi[1] - var inputType = (satalliteItem.Data["layers"] as JArray)?[1]["name"].ToString(); - if (string.IsNullOrEmpty(inputType)) { - _logger.LogError("Could not get the input type name from the satellite item"); - return; - } - - // download the geotiff - var SatelliteImageDate = (DateTime)satalliteItem.DataDate; - var SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); - _logger.LogInformation("Downloading geotiff file"); - await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, - Path.Combine(DownloadFolder, $"nbs_inputSatelliteGeotiff_{input.OutputFileName}_{inputType}_{SatelliteDate}.zip")); - - // overwrite measurement date by date of satellite item - measurementDate = satalliteItem.DataDate.Value; - - geotiffItem = satalliteItem; - - - } - - - // (geo)tiff input: - else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { - _logger.LogInformation("input = tiff data"); - var dataPath = Path.Combine("Data", input.File); - geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, - Path.GetFileNameWithoutExtension(input.File)); - - if (geotiffItem == null) { - _logger.LogError("Could not find item for uploaded data"); - return; - } - } - - // json/shape input - 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("Could not find item for uploaded data"); - return; - } - - _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, $"{input.OutputFileName}.input_geotiff.zip")); - } - - - //// check if vandersat task not yet done, do here and save taskcode - //if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.VanDerSatTaskCode)) { - // var VanDerSatTaskCode = await _generalService.RunVanDerSatTask(cropfieldItem); - // _settings.VanDerSatTaskCode = VanDerSatTaskCode; - // SaveSettings(settingsfile); - //} - - //// Select a particular image item from VanDerSat - //Item VanDerSatItem = await _generalService.FindVanDerSatItem(cropfieldItem, _settings.VanDerSatTaskCode, FieldName, StoreStatistics); - - - //// download the geotiff - //_logger.LogInformation("Downloading geotiff file"); - //await _farmmapsApiService.DownloadItemAsync(VanDerSatItem.Code, - // Path.Combine(DownloadFolder, $"nbs_VanDerSatGeotiff_{input.OutputFileName}.zip")); - - _logger.LogInformation($"Calculating targetN with targetYield: {input.TargetYield}"); - var targetNItem = await _nitrogenService.CreateTargetNItem(cropfieldItem); - var targetNData = await _nitrogenService.CalculateTargetN(cropfieldItem, targetNItem, plantingDate, - measurementDate, input.PotatoPurposeType, input.TargetYield); - - if (targetNData == null) { - _logger.LogError("Something went wrong with TargetN calculation"); - return; - } - - _logger.LogInformation($"TargetN: {targetNData.TargetN}"); - ////Option to manually adjust the Target N, for test purposes only! - //targetNData.TargetN = 225; - //_logger.LogInformation($"TargetN adjusted: {targetNData.TargetN}"); - - var targetNDataPath = Path.Combine(DownloadFolder, $"{input.OutputFileName}.targetn.json"); - await File.WriteAllTextAsync(targetNDataPath, JsonConvert.SerializeObject(targetNData, Formatting.Indented)); - - _logger.LogInformation("Calculating uptake map"); - var uptakeMapItem = - await _nitrogenService.CalculateUptakeMap(cropfieldItem, geotiffItem, plantingDate, - measurementDate, input.InputVariable, input.InputLayerName); - if (uptakeMapItem == null) { - _logger.LogError("Something went wrong with creating the uptakeMap"); - return; - } - - _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 = - await _nitrogenService.CalculateApplicationMap(cropfieldItem, geotiffItem, plantingDate, - measurementDate, input.InputVariable, targetNData.TargetN, input.InputLayerName); - - if (applicationMapItem == null) { - _logger.LogError("Something went wrong with creating the applicationMap"); - return; - } - _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; - //} - - //ApplicationMap (GEOTIFF) To Taskmap - _logger.LogInformation($"Converting geotiff to taskmap"); - var taskmap = await _generalService.CreateTaskmap(cropfieldItem, tiffItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - input.EndPoint.ToString(Formatting.None), input.Angle); - 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")); - - - } - - - // Functions to save previously created cropfields - private void LoadSettings(string file) - { - if (File.Exists(file)) - { - var jsonText = File.ReadAllText(file); - _settings = JsonConvert.DeserializeObject(jsonText); - } - else - { - _settings = new Settings(); - } - } - - private void SaveSettings(string file) - { - if (_settings == null) - return; - - var json = JsonConvert.SerializeObject(_settings); - File.WriteAllText(file, json); - } - private void SaveInfo(string file) { - if (_settings == null) - return; - - var json = JsonConvert.SerializeObject(_settings); - File.WriteAllText(file, json); - - } - - } - } \ No newline at end of file From 760f3fad6409a2809e3fa377db9c5b0d5e8d85ab Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Fri, 2 Apr 2021 16:15:44 +0200 Subject: [PATCH 18/35] added field polygon to data upload --- FarmmapsApi/Services/FarmmapsApiService.cs | 5 +++-- FarmmapsApi/Services/GeneralService.cs | 11 ++++++----- FarmmapsNbs/FarmmapsNbs.csproj | 3 +++ FarmmapsNbs/NbsApplication.cs | 6 +++--- FarmmapsPoten/PotenApplication.cs | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/FarmmapsApi/Services/FarmmapsApiService.cs b/FarmmapsApi/Services/FarmmapsApiService.cs index 1a2770b..2d6a7d7 100644 --- a/FarmmapsApi/Services/FarmmapsApiService.cs +++ b/FarmmapsApi/Services/FarmmapsApiService.cs @@ -283,7 +283,7 @@ namespace FarmmapsApi.Services /// /// /// - public async Task UploadFile(string filePath, string parentItemCode, + public async Task UploadFile(string filePath, string parentItemCode, string geoJsonString, Action progressCallback = null) { if (!File.Exists(filePath)) @@ -300,7 +300,8 @@ namespace FarmmapsApi.Services { Name = Path.GetFileName(filePath), ParentCode = parentItemCode, - Size = uploadStream.Length + Size = uploadStream.Length, + Geometry = JObject.Parse(geoJsonString) }; using var httpClient = CreateConfigurableHttpClient(_httpClient); diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index b494fce..db0aacf 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -37,9 +37,9 @@ namespace FarmmapsApi.Services return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest); } - public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName) { + public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName, string fieldGeomJson = null) { var startUpload = DateTime.UtcNow.AddSeconds(-3); - var result = await _farmmapsApiService.UploadFile(filePath, root.Code, + var result = await _farmmapsApiService.UploadFile(filePath, root.Code, fieldGeomJson, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); if (result.Progress.Status == UploadStatus.Failed) @@ -50,9 +50,9 @@ namespace FarmmapsApi.Services i.Name.ToLower().Contains(itemName.ToLower())); } - public async Task UploadZipWithShapeAsync(UserRoot root, string filePath, string itemName) { + public async Task UploadZipWithShapeAsync(UserRoot root, string filePath, string itemName, string fieldGeomJson = null) { var startUpload = DateTime.UtcNow; - var result = await _farmmapsApiService.UploadFile(filePath, root.Code, + var result = await _farmmapsApiService.UploadFile(filePath, root.Code, fieldGeomJson, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); if (result.Progress.Status == UploadStatus.Failed) @@ -108,7 +108,7 @@ namespace FarmmapsApi.Services } // Create taskmap based on width, height and direction - public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string cellWidth, string cellHeight, string startPoint, string centered = "false", string endPoint = null, string angle = null) + public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string cellWidth, string cellHeight, string startPoint, string centered = "false", string endPoint = null, string angle = null, string precision = null) { var taskmapRequest = new TaskRequest { TaskType = TASKMAP_TASK }; taskmapRequest.attributes["inputCode"] = tiffItem.Code; @@ -119,6 +119,7 @@ namespace FarmmapsApi.Services taskmapRequest.attributes["centered"] = centered; if (angle == null) taskmapRequest.attributes["endPoint"] = endPoint; // Coordinates WGS84 if (endPoint == null) taskmapRequest.attributes["angle"] = angle; // degrees between 0.0 and 360.0 + taskmapRequest.attributes["precision"] = precision; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); diff --git a/FarmmapsNbs/FarmmapsNbs.csproj b/FarmmapsNbs/FarmmapsNbs.csproj index 05c7803..7f211f6 100644 --- a/FarmmapsNbs/FarmmapsNbs.csproj +++ b/FarmmapsNbs/FarmmapsNbs.csproj @@ -14,6 +14,9 @@ Always + + + Always diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index 109843c..fe63c2b 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -171,7 +171,7 @@ namespace FarmmapsNbs _logger.LogInformation("input = tiff data"); var dataPath = Path.Combine("Data", input.File); geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, - Path.GetFileNameWithoutExtension(input.File)); + Path.GetFileNameWithoutExtension(input.File), input.GeometryJson.ToString(Formatting.None)); if (geotiffItem == null) { _logger.LogError("Could not find item for uploaded data"); @@ -184,8 +184,8 @@ namespace FarmmapsNbs 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)); + await _generalService.UploadDataAsync(uploadedRoot, SHAPE_PROCESSED_ITEMTYPE, dataPath, Path.GetFileNameWithoutExtension(input.File), input.GeometryJson.ToString(Formatting.None)) : + await _generalService.UploadZipWithShapeAsync(uploadedRoot, dataPath, Path.GetFileNameWithoutExtension(input.File), input.GeometryJson.ToString(Formatting.None)); if (shapeItem == null) { _logger.LogError("Could not find item for uploaded data"); diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 712dd9f..79c0192 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -176,7 +176,7 @@ namespace FarmmapsVRApoten //GEOTIFF TO Taskmap _logger.LogInformation($"Converting geotiff to taskmap"); var taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle); + input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle, input.Precision); if (taskmap == null) { _logger.LogError("Something went wrong with geotiff to taskmap transformation"); From 766674c731f1b0de1c1645e65340017f0ee88494 Mon Sep 17 00:00:00 2001 From: Riepma Date: Wed, 7 Apr 2021 10:38:42 +0200 Subject: [PATCH 19/35] ISOXML to taskmap --- FarmmapsApi/Services/GeneralService.cs | 18 +- FarmmapsNbs/FarmmapsNbs.csproj.user | 10 - FarmmapsNbs/InputData-NBS.json | 40 --- FarmmapsNbs/Models/NitrogenInput.cs | 29 -- FarmmapsNbs/Models/Settings.cs | 9 - FarmmapsNbs/Models/TargetNData.cs | 9 - FarmmapsNbs/NitrogenInput.json | 310 ------------------- FarmmapsNbs/NitrogenService.cs | 191 ------------ FarmmapsNbs/Program.cs | 23 -- FarmmapsNbs/appsettings.json | 10 - FarmmapsPoten/Data/PotenInput.json | 57 ++++ FarmmapsPoten/Models/PotenInput.cs | 2 + FarmmapsPoten/PotenApplication.cs | 37 ++- FarmmapsPoten/PotenInput.json | 413 ++++++++++++++++++++++--- 14 files changed, 463 insertions(+), 695 deletions(-) delete mode 100644 FarmmapsNbs/FarmmapsNbs.csproj.user delete mode 100644 FarmmapsNbs/InputData-NBS.json delete mode 100644 FarmmapsNbs/Models/NitrogenInput.cs delete mode 100644 FarmmapsNbs/Models/Settings.cs delete mode 100644 FarmmapsNbs/Models/TargetNData.cs delete mode 100644 FarmmapsNbs/NitrogenInput.json delete mode 100644 FarmmapsNbs/NitrogenService.cs delete mode 100644 FarmmapsNbs/Program.cs delete mode 100644 FarmmapsNbs/appsettings.json create mode 100644 FarmmapsPoten/Data/PotenInput.json diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index b494fce..64fad1d 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -108,18 +108,32 @@ namespace FarmmapsApi.Services } // Create taskmap based on width, height and direction - public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string cellWidth, string cellHeight, string startPoint, string centered = "false", string endPoint = null, string angle = null) + public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string outputType, string cellWidth, string cellHeight, + string startPoint, string ddiCode = "0001", string centered = "false", string endPoint = null, string angle = null, + string cropTypeName = null, string costumerName = null, string ProductGroupName = null, string productName = null, + string resolution = null, string unitScale = null, string maximumClasses = null) + { var taskmapRequest = new TaskRequest { TaskType = TASKMAP_TASK }; taskmapRequest.attributes["inputCode"] = tiffItem.Code; - taskmapRequest.attributes["operation"] = "shape"; // Currently onlye "shape" supported, if ISOXML is supported this should be an input + 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 (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) => { diff --git a/FarmmapsNbs/FarmmapsNbs.csproj.user b/FarmmapsNbs/FarmmapsNbs.csproj.user deleted file mode 100644 index c41d825..0000000 --- a/FarmmapsNbs/FarmmapsNbs.csproj.user +++ /dev/null @@ -1,10 +0,0 @@ - - - - false - FarmmapsNbs - - - ProjectDebugger - - \ No newline at end of file diff --git a/FarmmapsNbs/InputData-NBS.json b/FarmmapsNbs/InputData-NBS.json deleted file mode 100644 index 3fdad59..0000000 --- a/FarmmapsNbs/InputData-NBS.json +++ /dev/null @@ -1,40 +0,0 @@ -[ - { - "UseCreatedCropfield": false, - "storeStatistics": false, - "file": "20210127_WDVI_plus03.tif", - "inputVariable": "wdvi", - //"InputLayerName": "Band 1", - "outputFileName": "2021.02.15.Hapreet_Singh_0127-wdvi03_2", - "plantingDate": "2020-04-15", - "measurementDate": "2020-06-27", - "potatoPurposeType": "consumption", - "targetYield": 27, - "fieldName": "Mahindra-Hapreet-Singh", - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 75.929090780177305, 31.639314978348551 ], - [ 75.931353489378182, 31.639409065494881 ], - [ 75.931432810729120, 31.638956841735819 ], - [ 75.929072617175663, 31.638879365370279 ], - [ 75.929090780177305, 31.639314978348551 ] - ] - ] - }, - "GenerateTaskmap": true, - "CellWidth": "3", - "CellHeight": "10", - "StartPoint": { - "type": "Point", - "coordinates": [ 75.931432810729120, 31.638956841735819 ] - }, - "EndPoint": { - "type": "Point", - "coordinates": [ 75.929072617175663, 31.638879365370279 ] - } // if no angle - - //"Angle": "317.0" // if no endpoint - } - ] \ No newline at end of file diff --git a/FarmmapsNbs/Models/NitrogenInput.cs b/FarmmapsNbs/Models/NitrogenInput.cs deleted file mode 100644 index 61326ea..0000000 --- a/FarmmapsNbs/Models/NitrogenInput.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Newtonsoft.Json.Linq; - -namespace FarmmapsNbs.Models -{ - public class NitrogenInput - { - public bool UseCreatedCropfield { get; set; } - public string File { get; set; } - public string InputVariable { get; set; } - public string OutputFileName { get; set; } - public DateTime PlantingDate { get; set; } - public DateTime MeasurementDate { get; set; } - public string PotatoPurposeType { get; set; } - public int TargetYield { get; set; } - public JObject GeometryJson { get; set; } - public string InputLayerName { get; set; } - public string fieldName{ get; set; } - public bool storeSatelliteStatistics { get; set; } - public bool GenerateTaskmap { get; set; } - public string CellWidth { get; set; } - public string CellHeight { get; set; } - public JObject StartPoint { get; set; } - public JObject EndPoint { get; set; } - public string Angle { get; set; } - - - } -} \ No newline at end of file diff --git a/FarmmapsNbs/Models/Settings.cs b/FarmmapsNbs/Models/Settings.cs deleted file mode 100644 index 40cd25d..0000000 --- a/FarmmapsNbs/Models/Settings.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FarmmapsNbs { - public class Settings { - public string CropfieldItemCode { get; set; } - public string SatelliteTaskCode { get; set; } - public string VanDerSatTaskCode { get; set; } - public string WatBalTaskCode { get; set; } - - } -} \ No newline at end of file diff --git a/FarmmapsNbs/Models/TargetNData.cs b/FarmmapsNbs/Models/TargetNData.cs deleted file mode 100644 index d4aaea9..0000000 --- a/FarmmapsNbs/Models/TargetNData.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FarmmapsNbs.Models -{ - public class TargetNData - { - public double TSum { get; set; } - public int TargetYield { get; set; } - public double TargetN { get; set; } - } -} \ No newline at end of file diff --git a/FarmmapsNbs/NitrogenInput.json b/FarmmapsNbs/NitrogenInput.json deleted file mode 100644 index 6e2afe5..0000000 --- a/FarmmapsNbs/NitrogenInput.json +++ /dev/null @@ -1,310 +0,0 @@ -[ - { - "file": "Scan_1_20190605.json", - "inputVariable": "irmi", - "outputFileName": "vranbs1", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-05", - "potatoPurposeType": "consumption", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] - }, - //{ - //"file": "[...].json", - //"inputVariable": "wdvi", - //"outputFileName": "20201211_Mahindra", - //"plantingDate": "2020-11-25", - //"measurementDate": "2020-12-08", - //"potatoPurposeType": "consumption", - //"targetYield": 45, - //"geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 75.4652734163369, 31.26328617861426 ], - // [ 75.46527659380338, 31.26437743805827 ], - // [ 75.46494267602188, 31.26438166160194 ], - // [ 75.46493257618651, 31.26462192072676 ], - // [ 75.46438242791261, 31.26462132638865 ], - // [ 75.46438225728252, 31.26334267015003 ], - // [ 75.46448543502072, 31.26333463263533 ], - // [ 75.46448842093658, 31.26302891147988 ], - // [ 75.46507631089699, 31.26299589154944 ], - // [ 75.46509039016291, 31.26329023051392 ], - // [ 75.4652734163369, 31.26328617861426 ] - // ] - // ] - //} - //} - - //}, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - // "outputFileName": "vranbs2", - // "plantingDate": "2019-04-18", - // "measurementDate": "2019-06-05", - // "potatoPurposeType": "starch", - // "targetYield": 45, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 3.40843828875524, 50.638966444680605 ], - // [ 3.408953272886064, 50.639197789621612 ], - // [ 3.409242951459603, 50.639469958681836 ], - // [ 3.409328782148028, 50.639612846807708 ], - // [ 3.409457528180712, 50.639789755314411 ], - // [ 3.409639918393741, 50.640014292074966 ], - // [ 3.409833037442765, 50.640211611372706 ], - // [ 3.410069071836049, 50.640395321698435 ], - // [ 3.410380208081761, 50.640572227259661 ], - // [ 3.410605513638958, 50.640715112034222 ], - // [ 3.411925160474145, 50.641177783561204 ], - // [ 3.411935889310142, 50.640728720085136 ], - // [ 3.412590348309737, 50.63948356709389 ], - // [ 3.413244807309242, 50.638224772339846 ], - // [ 3.413400375432099, 50.637901562841307 ], - // [ 3.413539850300779, 50.637449065809889 ], - // [ 3.413475477284437, 50.637418445552932 ], - // [ 3.40999396998362, 50.637449065810451 ], - // [ 3.409940325803365, 50.638102293212661 ], - // [ 3.409575545377398, 50.638483338338325 ], - // [ 3.409060561246574, 50.638707881340494 ], - // [ 3.40843828875524, 50.638966444680605 ] - // ] - // ] - // } - //}, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - { - "file": "", // keep emptpy to use satellite image - "inputVariable": "wdvi", - "InputLayerName": "wdvi", - "outputFileName": "rtest1", - "plantingDate": "2020-05-01", - "measurementDate": "2020-06-14", - "potatoPurposeType": "consumption", - "targetYield": 60, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 4.960707146896585, 52.800583669708487 ], - [ 4.960645975538824, 52.800470217610922 ], - [ 4.962140695752897, 52.799177147194797 ], - [ 4.967523821195745, 52.801502400041208 ], - [ 4.966336768950911, 52.802543735879809 ], - [ 4.961711880764330, 52.801009996856429 ], - [ 4.960707146896585, 52.800583669708487 ] - ] - ] - } - } - //, - //{ - // "file": "", // keep emptpy to use satellite image - // "inputVariable": "wdvi", - // "InputLayerName": "wdvi", - // "outputFileName": "mtest1", - // "plantingDate": "2020-04-01", - // "measurementDate": "2020-06-24", - // "potatoPurposeType": "consumption", - // "targetYield": 60, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 3.575458869234128, 51.308707885669762 ], - // [ 3.575508957999423, 51.30878478562019 ], - // [ 3.576188404403633, 51.309372997559777 ], - // [ 3.576188872410267, 51.309374219701091 ], - // [ 3.576210290749152, 51.309430091473608 ], - // [ 3.57621266537704, 51.309483685674898 ], - // [ 3.576213477455834, 51.309502027981374 ], - // [ 3.577543136860447, 51.310682367527122 ], - // [ 3.57796629328866, 51.31104321405175 ], - // [ 3.578442479292087, 51.311449273042747 ], - // [ 3.57866702353106, 51.311636072786726 ], - // [ 3.57880446997978, 51.31157117599529 ], - // [ 3.579155910205885, 51.311863542729718 ], - // [ 3.579175814007489, 51.311875435159394 ], - // [ 3.579293885246395, 51.311936532835396 ], - // [ 3.579413896180069, 51.311998649478575 ], - // [ 3.579514543462617, 51.312041110734917 ], - // [ 3.579611760655688, 51.312082118352606 ], - // [ 3.579635115371588, 51.312093949652223 ], - // [ 3.579793143414486, 51.312189437140432 ], - // [ 3.579966991648108, 51.312286148850511 ], - // [ 3.580079704980967, 51.312332458349751 ], - // [ 3.580203717638148, 51.312336471368539 ], - // [ 3.580307101018293, 51.312330239539847 ], - // [ 3.580383836270609, 51.312317097185243 ], - // [ 3.580505207977176, 51.312279163554869 ], - // [ 3.580610387713855, 51.312233723091026 ], - // [ 3.5806309754483, 51.312226093729677 ], - // [ 3.580638516049738, 51.312223727082049 ], - // [ 3.58075536599681, 51.312186990706344 ], - // [ 3.580787745633303, 51.312176820129551 ], - // [ 3.580829682241423, 51.312167665428959 ], - // [ 3.58086828456562, 51.312162614898625 ], - // [ 3.580980493636721, 51.312147935609723 ], - // [ 3.581014352632766, 51.312145662592656 ], - // [ 3.581028980583245, 51.312145592849248 ], - // [ 3.581119189823368, 51.312145103215911 ], - // [ 3.581195330198145, 51.312144693075908 ], - // [ 3.581243537809229, 51.312148741603245 ], - // [ 3.58132480221972, 51.312163110548383 ], - // [ 3.581426517039001, 51.312181089466016 ], - // [ 3.581448095953263, 51.312184910295024 ], - // [ 3.581474337475052, 51.312191038736145 ], - // [ 3.581709405982819, 51.312260068944951 ], - // [ 3.581727319558337, 51.312266163697757 ], - // [ 3.581753583356718, 51.312276407881612 ], - // [ 3.581841655772683, 51.312310743468075 ], - // [ 3.581878851795624, 51.312325234405343 ], - // [ 3.581905889860924, 51.312338260798654 ], - // [ 3.581906254501594, 51.312338472113815 ], - // [ 3.582048236499295, 51.312422439022804 ], - // [ 3.58189849928915, 51.312481944577037 ], - // [ 3.583044298383354, 51.312095780444281 ], - // [ 3.582984006671231, 51.312017283925279 ], - // [ 3.582743535862999, 51.311699343434064 ], - // [ 3.582628599916243, 51.311582190756774 ], - // [ 3.581446834435509, 51.310511259982569 ], - // [ 3.580621864908701, 51.309767270412806 ], - // [ 3.579610575760466, 51.308860440593946 ], - // [ 3.579112608916012, 51.308394999612226 ], - // [ 3.578688808506157, 51.307968441218165 ], - // [ 3.578394256007207, 51.307644098092617 ], - // [ 3.578355980318371, 51.307607614964702 ], - // [ 3.578217977585775, 51.307654179547846 ], - // [ 3.577480636332469, 51.307921035607997 ], - // [ 3.575695560441006, 51.308577167973212 ], - // [ 3.575668643609169, 51.30855384769157 ], - // [ 3.575666204524265, 51.308551734020703 ], - // [ 3.575506397192348, 51.308609906947261 ], - // [ 3.575459139533024, 51.308653178431456 ], - // [ 3.575458869234128, 51.308707885669762 ] - // ] - // ] - // } - //} - //, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - // "InputLayerName": "IRMI", - // "outputFileName": "vranbs3", - // "plantingDate": "2019-04-18", - // "measurementDate": "2019-06-20", - // "potatoPurposeType": "starch", - // "targetYield": 45, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 3.40843828875524, 50.638966444680605 ], - // [ 3.408953272886064, 50.639197789621612 ], - // [ 3.409242951459603, 50.639469958681836 ], - // [ 3.409328782148028, 50.639612846807708 ], - // [ 3.409457528180712, 50.639789755314411 ], - // [ 3.409639918393741, 50.640014292074966 ], - // [ 3.409833037442765, 50.640211611372706 ], - // [ 3.410069071836049, 50.640395321698435 ], - // [ 3.410380208081761, 50.640572227259661 ], - // [ 3.410605513638958, 50.640715112034222 ], - // [ 3.411925160474145, 50.641177783561204 ], - // [ 3.411935889310142, 50.640728720085136 ], - // [ 3.412590348309737, 50.63948356709389 ], - // [ 3.413244807309242, 50.638224772339846 ], - // [ 3.413400375432099, 50.637901562841307 ], - // [ 3.413539850300779, 50.637449065809889 ], - // [ 3.413475477284437, 50.637418445552932 ], - // [ 3.40999396998362, 50.637449065810451 ], - // [ 3.409940325803365, 50.638102293212661 ], - // [ 3.409575545377398, 50.638483338338325 ], - // [ 3.409060561246574, 50.638707881340494 ], - // [ 3.40843828875524, 50.638966444680605 ] - // ] - // ] - // } - //}, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - //} - //, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - // "InputLayerName": "", - // "outputFileName": "vranbs4", - // "plantingDate": "2019-04-18", - // "measurementDate": "2019-07-03", - // "potatoPurposeType": "starch", - // "targetYield": 45, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 3.40843828875524, 50.638966444680605 ], - // [ 3.408953272886064, 50.639197789621612 ], - // [ 3.409242951459603, 50.639469958681836 ], - // [ 3.409328782148028, 50.639612846807708 ], - // [ 3.409457528180712, 50.639789755314411 ], - // [ 3.409639918393741, 50.640014292074966 ], - // [ 3.409833037442765, 50.640211611372706 ], - // [ 3.410069071836049, 50.640395321698435 ], - // [ 3.410380208081761, 50.640572227259661 ], - // [ 3.410605513638958, 50.640715112034222 ], - // [ 3.411925160474145, 50.641177783561204 ], - // [ 3.411935889310142, 50.640728720085136 ], - // [ 3.412590348309737, 50.63948356709389 ], - // [ 3.413244807309242, 50.638224772339846 ], - // [ 3.413400375432099, 50.637901562841307 ], - // [ 3.413539850300779, 50.637449065809889 ], - // [ 3.413475477284437, 50.637418445552932 ], - // [ 3.40999396998362, 50.637449065810451 ], - // [ 3.409940325803365, 50.638102293212661 ], - // [ 3.409575545377398, 50.638483338338325 ], - // [ 3.409060561246574, 50.638707881340494 ], - // [ 3.40843828875524, 50.638966444680605 ] - // ] - // ] - // } - - } - - //} - -] \ No newline at end of file diff --git a/FarmmapsNbs/NitrogenService.cs b/FarmmapsNbs/NitrogenService.cs deleted file mode 100644 index 96416ef..0000000 --- a/FarmmapsNbs/NitrogenService.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.Globalization; -using System.Threading.Tasks; -using FarmmapsApi.Models; -using FarmmapsApi.Services; -using FarmmapsNbs.Models; -using Microsoft.Extensions.Logging; -using static FarmmapsApi.Extensions; -using static FarmmapsApiSamples.Constants; - -namespace FarmmapsNbs -{ - public class NitrogenService - { - private readonly ILogger _logger; - private readonly FarmmapsApiService _farmmapsApiService; - private readonly GeneralService _generalService; - - public NitrogenService(ILogger logger, FarmmapsApiService farmmapsApiService, - GeneralService generalService) - { - _logger = logger; - _farmmapsApiService = farmmapsApiService; - _generalService = generalService; - } - - public async Task CreateTargetNItem(Item cropfieldItem) - { - var itemRequest = new ItemRequest() - { - ParentCode = cropfieldItem.ParentCode, - ItemType = USERINPUT_ITEMTYPE, - Name = "TargetN" - }; - return await _farmmapsApiService.CreateItemAsync(itemRequest); - } - - /// - /// Calculates TargetN, makes the assumption the cropfield and user.input(targetn) item have the same parent - /// - /// The cropfield to base the calculations on - /// The targetN item to save calculations in - /// The date the crop is planted - /// The date the measurements are taken - /// The crop purpose - /// The target yield input for the TargetN calculation - /// The TargetN - public async Task CalculateTargetN(Item cropfieldItem, Item targetNItem, DateTime plantingDate, - DateTime measurementDate, string purposeType, int targetYield) - { - var nbsTargetNRequest = new TaskRequest {TaskType = VRANBS_TASK}; - nbsTargetNRequest.attributes["operation"] = "targetn"; - nbsTargetNRequest.attributes["inputCode"] = targetNItem.Code; - 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); - - await PollTask(TimeSpan.FromSeconds(3), 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) - { - _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); - return null; - } - - var item = await _farmmapsApiService.GetItemAsync(targetNItem.Code); - return item.Data.ToObject(); - } - - /// - /// Calculates the uptake map based on the given inputs - /// - /// The cropfield to base the calculations on - /// - /// The date the crop is planted - /// The date the measurements are taken - /// Data type, could be yara, ci, irmi or wdvi - /// Column name in which the sensor value is stored - /// - public async Task CalculateUptakeMap(Item cropfieldItem, Item inputItem, DateTime plantingDate, - DateTime measurementDate, string inputType, string inputLayerName) - { - var nbsUptakeMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; - nbsUptakeMapRequest.attributes["operation"] = "uptake"; - nbsUptakeMapRequest.attributes["inputCode"] = inputItem.Code; - nbsUptakeMapRequest.attributes["plantingDate"] = plantingDate.ToString("o"); - nbsUptakeMapRequest.attributes["measurementDate"] = measurementDate.ToString("o"); - nbsUptakeMapRequest.attributes["inputType"] = inputType.ToLower(); - if (!(string.IsNullOrEmpty(inputLayerName))) nbsUptakeMapRequest.attributes["inputLayerName"] = inputLayerName; - //toevoeging FS. Kolom IRMI hernoemd als IMI. Deze wordt niet automatisch herkend. En moet dus gespecificeerd worden. - - - - string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsUptakeMapRequest); - - await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => - { - var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); - _logger.LogInformation($"Calculating uptake map; 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; - } - - var itemName = "VRANbs uptake"; - var uptakeMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, - GEOTIFF_PROCESSED_ITEMTYPE, itemName, - i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && - i.Name.ToLower().Contains(itemName.ToLower())); - - if (uptakeMapItem == null) - { - _logger.LogError("Could not find the uptake geotiff child item under cropfield"); - return null; - } - - return uptakeMapItem; - } - - - /// - /// Creates the nitrogen application map based on given input data - /// - /// The cropfield to base the calculations on - /// The farmmaps item containing the geotiff data - /// The date the crop is planted - /// The date the measurements are taken - /// The inputtype to use, could be yara, ci, irmi or wdvi - /// The target nitrogen to use for the calculations - /// - public async Task CalculateApplicationMap(Item cropfieldItem, Item inputItem, DateTime plantingDate, - DateTime measurementDate, string inputType, double targetN, string inputLayerName) - { - var nbsApplicationMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; - nbsApplicationMapRequest.attributes["operation"] = "application"; - nbsApplicationMapRequest.attributes["inputCode"] = inputItem.Code; - 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); - if (!(string.IsNullOrEmpty(inputLayerName))) nbsApplicationMapRequest.attributes["inputLayerName"] = inputLayerName; - - string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsApplicationMapRequest); - - await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => - { - var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); - _logger.LogInformation($"Calculating application map; 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; - } - - var itemName = $"VRANbs application"; - var applicationMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, - GEOTIFF_PROCESSED_ITEMTYPE, itemName, - i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && - i.Name.ToLower().Contains(itemName.ToLower())); - if (applicationMapItem == null) - { - _logger.LogError("Could not find the application map geotiff child item under cropfield"); - return null; - } - - return applicationMapItem; - } - } -} \ No newline at end of file diff --git a/FarmmapsNbs/Program.cs b/FarmmapsNbs/Program.cs deleted file mode 100644 index 596637a..0000000 --- a/FarmmapsNbs/Program.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using FarmmapsApi; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace FarmmapsNbs -{ - 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/FarmmapsNbs/appsettings.json b/FarmmapsNbs/appsettings.json deleted file mode 100644 index c5d440a..0000000 --- a/FarmmapsNbs/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Authority": "https://accounts.test.farmmaps.eu/", - "Endpoint": "https://test.farmmaps.eu/", - "BasePath": "api/v1", - "DiscoveryEndpointUrl": "https://accounts.test.farmmaps.eu/.well-known/openid-configuration", - "RedirectUri": "http://example.nl/api", - "ClientId": "", - "ClientSecret": "", - "Scopes": [ "api" ] -} \ No newline at end of file diff --git a/FarmmapsPoten/Data/PotenInput.json b/FarmmapsPoten/Data/PotenInput.json new file mode 100644 index 0000000..cc195c0 --- /dev/null +++ b/FarmmapsPoten/Data/PotenInput.json @@ -0,0 +1,57 @@ +[ + { + + "File": "BBL-lutum.tif", + "OutputFileName": "BBL-poten_30_10_Shadow-True_TifInput", + "FieldName": "lutum", + "PlantingYear": 2020, + "MeanDensity": "30", + "Variation": "20", + "UseShadow": true, + "CountPerArea": false, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 4.61098862747705418, 52.22974843124053734 ], + [ 4.61338362824790682, 52.22993593073885421 ], + [ 4.61949010053501397, 52.22667042162688489 ], + [ 4.61954106663577324, 52.22670579437124871 ], + [ 4.61953075937382085, 52.22671128094363269 ], + [ 4.61954202811175296, 52.22670563321562298 ], + [ 4.61901745087585081, 52.22634382962637289 ], + [ 4.61812211075603685, 52.22571430401869463 ], + [ 4.61736058962494678, 52.22611942369080396 ], + [ 4.61464150409827134, 52.2275669988328346 ], + [ 4.61407137489250019, 52.22787370291415243 ], + [ 4.61242270952427802, 52.2287649864655279 ], + [ 4.61261020576063618, 52.22887063061121182 ], + [ 4.61256645484349015, 52.22889713517798072 ], + [ 4.61229657126373027, 52.22904994233584119 ], + [ 4.61207777596369262, 52.2291602791168188 ], + [ 4.61174554282434013, 52.22933965721241378 ], + [ 4.61169310584479941, 52.22934596082312453 ], + [ 4.61143525446537428, 52.22949107699816551 ], + [ 4.61100843013417183, 52.22973689585098356 ], + [ 4.61098862747705418, 52.22974843124053734 ] + ] + ] + }, + + "GenerateTaskmap": true, + "CellWidth": "3", + "CellHeight": "10", + "Centered": "false", + "StartPoint": { + "type": "Point", + "coordinates": [ 4.61812211075603685, 52.22571430401869463 ] // BBL + }, + "EndPoint": { + "type": "Point", + "coordinates": [ 4.61242270952427802, 52.2287649864655279 ] // BBL + } // if no angle + + //"Angle": "317.0" // if no endpoint + } + +] diff --git a/FarmmapsPoten/Models/PotenInput.cs b/FarmmapsPoten/Models/PotenInput.cs index 46a7e11..57c71c4 100644 --- a/FarmmapsPoten/Models/PotenInput.cs +++ b/FarmmapsPoten/Models/PotenInput.cs @@ -17,6 +17,8 @@ namespace FarmmapsPoten.Models public JObject GeometryJson { get; set; } public bool GenerateTaskmap { get; set; } + public string OutputType { get; set; } + public string DdiCode { get; set; } public string CellWidth { get; set; } public string CellHeight { get; set; } public string Centered { get; set; } diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 712dd9f..e6b24f5 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -78,6 +78,10 @@ namespace FarmmapsVRApoten $"VRA Poten cropfield {input.OutputFileName}", input.PlantingYear, input.GeometryJson.ToString(Formatting.None)); + //Item cropfieldItem; + // _logger.LogInformation("Cropfield already exists, trying to get it"); + //cropfieldItem = await _farmmapsApiService.GetItemAsync("2927b80f63b946afb36821470b9c5c23"); + //Calculating shadow map if (useShadow) { _logger.LogInformation("Calculate shadow map for field"); @@ -172,29 +176,40 @@ namespace FarmmapsVRApoten ? "Download application map completed." : "Something went wrong while downloading."); + ////GEOTIFF TO SHAPE + //_logger.LogInformation($"Converting geotiff to shape"); + //var geotiffToShapeItem= await _generalService.GeotiffToShape(applianceMapItem); + //if (taskmap == null) { + // _logger.LogError("Something went wrong with geotiff to shape transformation"); + // return; + //} + + if(input.GenerateTaskmap) { //GEOTIFF TO Taskmap _logger.LogInformation($"Converting geotiff to taskmap"); - var taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle); + var taskmap = (Item)null; + if (input.OutputType == "isoxml") + { + taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.OutputType, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + input.DdiCode, input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle); + } else + { + taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.OutputType, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle); + } + + if (taskmap == null) { _logger.LogError("Something went wrong with geotiff to taskmap transformation"); return; } - - ////GEOTIFF TO SHAPE - //_logger.LogInformation($"Converting geotiff to shape"); - //var geotiffToShapeItem= 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/PotenInput.json b/FarmmapsPoten/PotenInput.json index d3e7ce7..4e68e22 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -1,57 +1,368 @@ [ - { + //{ - "File": "BBL-lutum.tif", - "OutputFileName": "BBL-poten_30_20_Shadow-True-v2_TifInput", - "FieldName": "lutum", - "PlantingYear": 2020, - "MeanDensity": "30", - "Variation": "20", - "UseShadow": true, - "CountPerArea": false, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 4.61098862747705418, 52.22974843124053734 ], - [ 4.61338362824790682, 52.22993593073885421 ], - [ 4.61949010053501397, 52.22667042162688489 ], - [ 4.61954106663577324, 52.22670579437124871 ], - [ 4.61953075937382085, 52.22671128094363269 ], - [ 4.61954202811175296, 52.22670563321562298 ], - [ 4.61901745087585081, 52.22634382962637289 ], - [ 4.61812211075603685, 52.22571430401869463 ], - [ 4.61736058962494678, 52.22611942369080396 ], - [ 4.61464150409827134, 52.2275669988328346 ], - [ 4.61407137489250019, 52.22787370291415243 ], - [ 4.61242270952427802, 52.2287649864655279 ], - [ 4.61261020576063618, 52.22887063061121182 ], - [ 4.61256645484349015, 52.22889713517798072 ], - [ 4.61229657126373027, 52.22904994233584119 ], - [ 4.61207777596369262, 52.2291602791168188 ], - [ 4.61174554282434013, 52.22933965721241378 ], - [ 4.61169310584479941, 52.22934596082312453 ], - [ 4.61143525446537428, 52.22949107699816551 ], - [ 4.61100843013417183, 52.22973689585098356 ], - [ 4.61098862747705418, 52.22974843124053734 ] - ] - ] - }, + // "File": "ClaassenVRA_Kruising_LutEmptyPointsErased_largerExtent.zip", + // "OutputFileName": "2021.03.31.ClaassenVRA_Kruising_Lut_EmptyPointsErased_largeExtent", + // "FieldName": "Lutum", + // "PlantingYear": 2021, + // "MeanDensity": "15", + // "Variation": "20", + // "UseShadow": true, + // "CountPerArea": false, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 6.3070655, 53.3623397 ], + // [ 6.3070875, 53.3623898 ], + // [ 6.3071092, 53.3624398 ], + // [ 6.3071312, 53.3624898 ], + // [ 6.3071531, 53.3625399 ], + // [ 6.3071749, 53.3625899 ], + // [ 6.3071968, 53.36264 ], + // [ 6.3072186, 53.36269 ], + // [ 6.3072393, 53.3627389 ], + // [ 6.3072598, 53.3627879 ], + // [ 6.3072803, 53.3628367 ], + // [ 6.3073008, 53.3628856 ], + // [ 6.3073215, 53.3629345 ], + // [ 6.307342, 53.3629834 ], + // [ 6.3073625, 53.3630324 ], + // [ 6.3073832, 53.3630812 ], + // [ 6.3074037, 53.3631301 ], + // [ 6.3074242, 53.363179 ], + // [ 6.3074447, 53.3632279 ], + // [ 6.3074654, 53.3632768 ], + // [ 6.3074859, 53.3633257 ], + // [ 6.3075064, 53.3633746 ], + // [ 6.3075271, 53.3634235 ], + // [ 6.3075476, 53.3634724 ], + // [ 6.3075681, 53.3635213 ], + // [ 6.3075886, 53.3635702 ], + // [ 6.3076093, 53.3636191 ], + // [ 6.3076609, 53.3637669 ], + // [ 6.3076735, 53.3638141 ], + // [ 6.3076717, 53.3638452 ], + // [ 6.3076566, 53.3638765 ], + // [ 6.3076265, 53.3639037 ], + // [ 6.3075848, 53.3639245 ], + // [ 6.3075321, 53.3639382 ], + // [ 6.3074494, 53.3639508 ], + // [ 6.3073665, 53.3639632 ], + // [ 6.3072835, 53.3639758 ], + // [ 6.3072006, 53.3639884 ], + // [ 6.3071179, 53.3640008 ], + // [ 6.307035, 53.3640134 ], + // [ 6.306952, 53.3640258 ], + // [ 6.3068693, 53.3640384 ], + // [ 6.3067864, 53.3640509 ], + // [ 6.3067035, 53.3640634 ], + // [ 6.3066207, 53.3640759 ], + // [ 6.3065378, 53.3640884 ], + // [ 6.3064549, 53.364101 ], + // [ 6.306372, 53.3641135 ], + // [ 6.3062892, 53.364126 ], + // [ 6.3062063, 53.3641385 ], + // [ 6.3061234, 53.364151 ], + // [ 6.305889, 53.3641875 ], + // [ 6.3058018, 53.3642004 ], + // [ 6.3057146, 53.3642134 ], + // [ 6.3056273, 53.3642265 ], + // [ 6.3055401, 53.3642395 ], + // [ 6.3054529, 53.3642525 ], + // [ 6.3053655, 53.3642655 ], + // [ 6.3052782, 53.3642785 ], + // [ 6.3051975, 53.3642908 ], + // [ 6.3051167, 53.3643032 ], + // [ 6.3050358, 53.3643154 ], + // [ 6.304955, 53.3643277 ], + // [ 6.3048741, 53.3643401 ], + // [ 6.3047933, 53.3643523 ], + // [ 6.3047126, 53.3643647 ], + // [ 6.3046316, 53.364377 ], + // [ 6.3045509, 53.3643893 ], + // [ 6.3044701, 53.3644016 ], + // [ 6.3043892, 53.3644139 ], + // [ 6.3043086, 53.3644261 ], + // [ 6.304228, 53.3644382 ], + // [ 6.3041476, 53.3644503 ], + // [ 6.304067, 53.3644624 ], + // [ 6.3039864, 53.3644746 ], + // [ 6.3039059, 53.3644868 ], + // [ 6.3038253, 53.3644989 ], + // [ 6.3037447, 53.3645111 ], + // [ 6.3036641, 53.3645232 ], + // [ 6.3035835, 53.3645353 ], + // [ 6.303503, 53.3645474 ], + // [ 6.3034224, 53.3645596 ], + // [ 6.3033418, 53.3645717 ], + // [ 6.3032344, 53.3645896 ], + // [ 6.3029993, 53.364623 ], + // [ 6.3028959, 53.3646389 ], + // [ 6.3027925, 53.3646549 ], + // [ 6.3026889, 53.3646708 ], + // [ 6.3025855, 53.3646868 ], + // [ 6.3024821, 53.3647026 ], + // [ 6.3024042, 53.3647144 ], + // [ 6.3023261, 53.3647261 ], + // [ 6.3022482, 53.3647378 ], + // [ 6.3021702, 53.3647495 ], + // [ 6.3020923, 53.3647612 ], + // [ 6.3020142, 53.364773 ], + // [ 6.3019363, 53.3647847 ], + // [ 6.3018585, 53.3647964 ], + // [ 6.3017804, 53.3648081 ], + // [ 6.3017025, 53.3648198 ], + // [ 6.3016245, 53.3648315 ], + // [ 6.3015466, 53.3648433 ], + // [ 6.3014687, 53.364855 ], + // [ 6.3013906, 53.3648667 ], + // [ 6.3013128, 53.3648784 ], + // [ 6.3012347, 53.3648901 ], + // [ 6.3011568, 53.3649018 ], + // [ 6.3010788, 53.3649136 ], + // [ 6.3010009, 53.3649253 ], + // [ 6.300923, 53.364937 ], + // [ 6.3008449, 53.3649487 ], + // [ 6.300767, 53.3649604 ], + // [ 6.300689, 53.3649721 ], + // [ 6.3006111, 53.3649839 ], + // [ 6.300533, 53.3649956 ], + // [ 6.3004552, 53.3650073 ], + // [ 6.3003771, 53.365019 ], + // [ 6.3002992, 53.3650307 ], + // [ 6.3002213, 53.3650425 ], + // [ 6.3001433, 53.3650542 ], + // [ 6.3000654, 53.3650659 ], + // [ 6.2999843, 53.3650783 ], + // [ 6.2999033, 53.3650905 ], + // [ 6.2998224, 53.3651029 ], + // [ 6.2997415, 53.3651151 ], + // [ 6.2996603, 53.3651274 ], + // [ 6.2995794, 53.3651398 ], + // [ 6.2994985, 53.365152 ], + // [ 6.2994175, 53.3651644 ], + // [ 6.2993364, 53.3651767 ], + // [ 6.2992623, 53.3651849 ], + // [ 6.2992157, 53.3651857 ], + // [ 6.2991679, 53.365182 ], + // [ 6.2991157, 53.3651694 ], + // [ 6.2990734, 53.3651492 ], + // [ 6.2990423, 53.3651248 ], + // [ 6.299008, 53.3650764 ], + // [ 6.2989616, 53.3649669 ], + // [ 6.2989414, 53.3649136 ], + // [ 6.2989213, 53.3648604 ], + // [ 6.2989011, 53.3648072 ], + // [ 6.298881, 53.3647538 ], + // [ 6.2988608, 53.3647006 ], + // [ 6.2988407, 53.3646474 ], + // [ 6.2988206, 53.3645941 ], + // [ 6.2987995, 53.3645407 ], + // [ 6.2987785, 53.3644873 ], + // [ 6.2987574, 53.3644339 ], + // [ 6.2987364, 53.3643805 ], + // [ 6.2987153, 53.3643271 ], + // [ 6.2986943, 53.3642738 ], + // [ 6.2986731, 53.3642204 ], + // [ 6.298654, 53.364171 ], + // [ 6.2986349, 53.3641217 ], + // [ 6.2986159, 53.3640723 ], + // [ 6.2985968, 53.3640229 ], + // [ 6.2985777, 53.3639736 ], + // [ 6.2985587, 53.3639242 ], + // [ 6.2985396, 53.3638748 ], + // [ 6.2985205, 53.3638254 ], + // [ 6.2985015, 53.3637761 ], + // [ 6.2984826, 53.3637267 ], + // [ 6.2984571, 53.3636525 ], + // [ 6.2984554, 53.3636239 ], + // [ 6.2984678, 53.3635953 ], + // [ 6.2984948, 53.3635675 ], + // [ 6.2985616, 53.3635415 ], + // [ 6.2986411, 53.3635329 ], + // [ 6.2987148, 53.363522 ], + // [ 6.2987885, 53.3635112 ], + // [ 6.2988625, 53.3635003 ], + // [ 6.2989362, 53.3634895 ], + // [ 6.29901, 53.3634786 ], + // [ 6.2990837, 53.3634678 ], + // [ 6.2991574, 53.363457 ], + // [ 6.2992312, 53.3634461 ], + // [ 6.2993049, 53.3634353 ], + // [ 6.2993787, 53.3634244 ], + // [ 6.2994524, 53.3634136 ], + // [ 6.2995262, 53.3634028 ], + // [ 6.2995999, 53.3633919 ], + // [ 6.2996737, 53.3633811 ], + // [ 6.2997474, 53.3633702 ], + // [ 6.2998211, 53.3633594 ], + // [ 6.2998949, 53.3633486 ], + // [ 6.2999686, 53.3633376 ], + // [ 6.3000424, 53.3633268 ], + // [ 6.3001161, 53.3633159 ], + // [ 6.3001899, 53.3633051 ], + // [ 6.3002636, 53.3632943 ], + // [ 6.3003374, 53.3632834 ], + // [ 6.3004111, 53.3632726 ], + // [ 6.3004848, 53.3632617 ], + // [ 6.3005588, 53.3632509 ], + // [ 6.3006325, 53.36324 ], + // [ 6.3007063, 53.3632292 ], + // [ 6.30078, 53.3632184 ], + // [ 6.3008537, 53.3632075 ], + // [ 6.3009275, 53.3631967 ], + // [ 6.3010012, 53.3631858 ], + // [ 6.301075, 53.363175 ], + // [ 6.3011487, 53.3631642 ], + // [ 6.3012225, 53.3631533 ], + // [ 6.3012962, 53.3631425 ], + // [ 6.30137, 53.3631316 ], + // [ 6.3014437, 53.3631208 ], + // [ 6.3015174, 53.36311 ], + // [ 6.3015912, 53.3630991 ], + // [ 6.3016649, 53.3630883 ], + // [ 6.3017387, 53.3630774 ], + // [ 6.3018124, 53.3630666 ], + // [ 6.3018862, 53.3630558 ], + // [ 6.3019599, 53.3630449 ], + // [ 6.3020337, 53.3630341 ], + // [ 6.3021074, 53.3630232 ], + // [ 6.3021811, 53.3630124 ], + // [ 6.3022549, 53.3630016 ], + // [ 6.3023286, 53.3629907 ], + // [ 6.3024026, 53.3629799 ], + // [ 6.3024763, 53.362969 ], + // [ 6.30255, 53.3629582 ], + // [ 6.3026238, 53.3629474 ], + // [ 6.3026975, 53.3629365 ], + // [ 6.3027713, 53.3629256 ], + // [ 6.302845, 53.3629147 ], + // [ 6.3029188, 53.3629039 ], + // [ 6.3029925, 53.362893 ], + // [ 6.3030663, 53.3628822 ], + // [ 6.30314, 53.3628714 ], + // [ 6.3032137, 53.3628605 ], + // [ 6.3032875, 53.3628497 ], + // [ 6.3033612, 53.3628388 ], + // [ 6.303435, 53.362828 ], + // [ 6.3035087, 53.3628172 ], + // [ 6.3035825, 53.3628063 ], + // [ 6.3036562, 53.3627955 ], + // [ 6.30373, 53.3627846 ], + // [ 6.3038037, 53.3627738 ], + // [ 6.3038774, 53.362763 ], + // [ 6.3039512, 53.3627521 ], + // [ 6.3040249, 53.3627413 ], + // [ 6.3040987, 53.3627304 ], + // [ 6.3041724, 53.3627196 ], + // [ 6.3042462, 53.3627088 ], + // [ 6.3043199, 53.3626979 ], + // [ 6.3043937, 53.3626871 ], + // [ 6.3044674, 53.3626762 ], + // [ 6.3045413, 53.3626654 ], + // [ 6.3046151, 53.3626545 ], + // [ 6.3046888, 53.3626436 ], + // [ 6.3047626, 53.3626328 ], + // [ 6.3048363, 53.3626219 ], + // [ 6.30491, 53.3626111 ], + // [ 6.3049838, 53.3626002 ], + // [ 6.3050575, 53.3625894 ], + // [ 6.3051313, 53.3625786 ], + // [ 6.305205, 53.3625677 ], + // [ 6.3052788, 53.3625569 ], + // [ 6.3053525, 53.362546 ], + // [ 6.3054263, 53.3625352 ], + // [ 6.3055, 53.3625244 ], + // [ 6.3055737, 53.3625135 ], + // [ 6.3056475, 53.3625027 ], + // [ 6.3057212, 53.3624918 ], + // [ 6.305795, 53.362481 ], + // [ 6.3058687, 53.3624701 ], + // [ 6.3059425, 53.3624593 ], + // [ 6.3060162, 53.3624485 ], + // [ 6.30609, 53.3624376 ], + // [ 6.3061637, 53.3624268 ], + // [ 6.3062374, 53.3624158 ], + // [ 6.3063112, 53.362405 ], + // [ 6.3063849, 53.3623942 ], + // [ 6.3064587, 53.3623833 ], + // [ 6.3065324, 53.3623725 ], + // [ 6.3066062, 53.3623616 ], + // [ 6.3066799, 53.3623508 ], + // [ 6.3067537, 53.36234 ], + // [ 6.3068274, 53.3623291 ], + // [ 6.3069011, 53.3623183 ], + // [ 6.3069749, 53.3623074 ], + // [ 6.3070314, 53.3623125 ], + // [ 6.3070655, 53.3623397 ] + // ] + // ] + // }, - "GenerateTaskmap": true, - "CellWidth": "3", - "CellHeight": "10", - "Centered": "false", - "StartPoint": { - "type": "Point", - "coordinates": [ 4.61812211075603685, 52.22571430401869463 ] // BBL - }, - "EndPoint": { - "type": "Point", - "coordinates": [ 4.61242270952427802, 52.2287649864655279 ] // BBL - } // if no angle + // "GenerateTaskmap": true, + // "OutputType": "isoxml", // "shape" or "isoxml" if isoxml also add ddiCode + // "DdiCode": "0001", + // "CellWidth": "3", + // "CellHeight": "10", + // "Centered": "false", + // "StartPoint": { + // "type": "Point", + // "coordinates": [ 6.306901145501940, 53.362318269638386 ] + // }, + // "EndPoint": { + // "type": "Point", + // "coordinates": [ 6.298561552458957, 53.363541544243297 ] + // } // if no angle + + // // "Angle": "317.0" // if no endpoint + //} + + { + "File": "PlantingSampleDataLutum.zip", + "OutputFileName": "2021.03.15_vraPoten_SampleData", + "FieldName": "lutum", + "PlantingYear": 2020, + "MeanDensity": "30", + "Variation": "20", + "UseShadow": false, + "CountPerArea": true, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 5.66886041703652044, 52.52929999060298627 ], + [ 5.6716230923214912, 52.52946316399909676 ], + [ 5.67185376229668581, 52.5280565894154563 ], + [ 5.66903207841337231, 52.52790646510525363 ], + [ 5.66886041703652044, 52.52929999060298627 ] + ] + ] + }, + + "GenerateTaskmap": true, + "OutputType": "isoxml", // "shape" or "isoxml" if isoxml also add ddiCode + "DdiCode": "0001", + "CellWidth": "3", + "CellHeight": "10", + "Centered": "true", + "StartPoint": { + "type": "Point", + //"coordinates": [ 5.669032078413372, 52.527906465105254 ] // 1 + //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 + //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 + "coordinates": [ 5.668375834141733, 52.527202407445586 ] // 4 + }, + "EndPoint": { + "type": "Point", + "coordinates": [ 5.668263398897563, 52.529744573890817 ] // 1 + //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 + //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 + //"coordinates": [ 5.671853762296686, 52.528056589415456 ] // 4 + } // if no angle + + //"Angle": "317.0" // if no endpoint + } - //"Angle": "317.0" // if no endpoint - } ] From c7ab62c9119f2c623033b74ab6b05f50e4ca5652 Mon Sep 17 00:00:00 2001 From: Riepma Date: Mon, 12 Apr 2021 14:22:24 +0200 Subject: [PATCH 20/35] Added option to generate ISOXML taskmap, added missing Nbs data --- FarmmapsApi/Constants.cs | 1 + FarmmapsApi/Services/GeneralService.cs | 22 +- FarmmapsNbs/FarmmapsNbs.csproj | 27 ++ FarmmapsNbs/FarmmapsNbs.csproj.user | 10 + FarmmapsNbs/InputData-NBS.json | 41 +++ FarmmapsNbs/Models/NitrogenInput.cs | 30 ++ FarmmapsNbs/Models/Settings.cs | 9 + FarmmapsNbs/Models/TargetNData.cs | 9 + FarmmapsNbs/NbsApplication.cs | 338 ++++++++++++++++++ FarmmapsNbs/NitrogenInput.json | 310 ++++++++++++++++ FarmmapsNbs/NitrogenService.cs | 191 ++++++++++ .../Program.cs | 6 +- FarmmapsPoten/Models/PotenInput.cs | 2 + FarmmapsPoten/PotenApplication.cs | 22 +- FarmmapsPoten/PotenInput.json | 294 +-------------- .../Data/PlantingSampleDataLutum.json | 302 ---------------- .../Data/PlantingSampleDataLutum.zip | Bin 5865 -> 0 bytes .../FarmmapsPoten_AVRapi.csproj | 24 -- FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs | 27 -- FarmmapsPoten_AVRapi/PotenApplicationAVR.cs | 228 ------------ FarmmapsPoten_AVRapi/PotenInputAVR.json | 41 --- FarmmapsPoten_AVRapi/PotenServiceAVR.cs | 120 ------- FarmmapsPoten_AVRapi/appsettings.json | 10 - 23 files changed, 1008 insertions(+), 1056 deletions(-) create mode 100644 FarmmapsNbs/FarmmapsNbs.csproj create mode 100644 FarmmapsNbs/FarmmapsNbs.csproj.user create mode 100644 FarmmapsNbs/InputData-NBS.json create mode 100644 FarmmapsNbs/Models/NitrogenInput.cs create mode 100644 FarmmapsNbs/Models/Settings.cs create mode 100644 FarmmapsNbs/Models/TargetNData.cs create mode 100644 FarmmapsNbs/NbsApplication.cs create mode 100644 FarmmapsNbs/NitrogenInput.json create mode 100644 FarmmapsNbs/NitrogenService.cs rename {FarmmapsPoten_AVRapi => FarmmapsNbs}/Program.cs (79%) delete mode 100644 FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json delete mode 100644 FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.zip delete mode 100644 FarmmapsPoten_AVRapi/FarmmapsPoten_AVRapi.csproj delete mode 100644 FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs delete mode 100644 FarmmapsPoten_AVRapi/PotenApplicationAVR.cs delete mode 100644 FarmmapsPoten_AVRapi/PotenInputAVR.json delete mode 100644 FarmmapsPoten_AVRapi/PotenServiceAVR.cs delete mode 100644 FarmmapsPoten_AVRapi/appsettings.json diff --git a/FarmmapsApi/Constants.cs b/FarmmapsApi/Constants.cs index b061c18..08b271c 100644 --- a/FarmmapsApi/Constants.cs +++ b/FarmmapsApi/Constants.cs @@ -8,6 +8,7 @@ namespace FarmmapsApiSamples public const string GEOTIFF_PROCESSED_ITEMTYPE = "vnd.farmmaps.itemtype.geotiff.processed"; public const string CROPFIELD_ITEMTYPE = "vnd.farmmaps.itemtype.cropfield"; public const string SHAPE_PROCESSED_ITEMTYPE = "vnd.farmmaps.itemtype.shape.processed"; + public const string ISOXML_PROCESSED_ITEMTYPE = "vnd.farmmaps.itemtype.iso11783.taskdata.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"; diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index ce2b0be..5a2e17c 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -110,7 +110,7 @@ namespace FarmmapsApi.Services // 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 ddiCode = "0001", string centered = "false", string endPoint = null, string angle = null, + string startPoint, 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 = null, string unitScale = null, string maximumClasses = null) @@ -126,9 +126,10 @@ namespace FarmmapsApi.Services 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 - //taskmapRequest.attributes["precision"] = precision; + // 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; @@ -155,11 +156,24 @@ namespace FarmmapsApi.Services //the taskmap is a child of the input tiff var itemName = "Taskmap"; - var taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, + 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, itemName); + } + 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 taskmap as a child item under the input"); + _logger.LogError("Could not find the shape/isoxml taskmap as a child item under the input"); return null; } diff --git a/FarmmapsNbs/FarmmapsNbs.csproj b/FarmmapsNbs/FarmmapsNbs.csproj new file mode 100644 index 0000000..6e4a5ab --- /dev/null +++ b/FarmmapsNbs/FarmmapsNbs.csproj @@ -0,0 +1,27 @@ + + + + Exe + netcoreapp3.0 + + + + + Always + + + Always + + + Always + + + Always + + + + + + + + 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/InputData-NBS.json b/FarmmapsNbs/InputData-NBS.json new file mode 100644 index 0000000..6d5fcac --- /dev/null +++ b/FarmmapsNbs/InputData-NBS.json @@ -0,0 +1,41 @@ +[ + { + "UseCreatedCropfield": false, + "storeStatistics": false, + "file": "20210308 WDVI_Harpreet Singh.tif", + "inputVariable": "wdvi", + //"InputLayerName": "Band 1", + "outputFileName": "2021.03.08.Hapreet_Singh_wdvi08_03", + "plantingDate": "2020-04-15", + "measurementDate": "2020-06-27", + "potatoPurposeType": "consumption", + "targetYield": 27, + "fieldName": "Mahindra-Hapreet-Singh", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 75.929090780177305, 31.639314978348551 ], + [ 75.931353489378182, 31.639409065494881 ], + [ 75.931432810729120, 31.638956841735819 ], + [ 75.929072617175663, 31.638879365370279 ], + [ 75.929090780177305, 31.639314978348551 ] + ] + ] + }, + "GenerateTaskmap": true, + "CellWidth": "3", + "CellHeight": "20", + "Centered": false, + "StartPoint": { + "type": "Point", + "coordinates": [ 75.931353489378182, 31.639409065494881 ] + }, + "EndPoint": { + "type": "Point", + "coordinates": [ 75.929090780177305, 31.639314978348551 ] + } // if no angle + + //"Angle": "317.0" // if no endpoint + } + ] \ No newline at end of file diff --git a/FarmmapsNbs/Models/NitrogenInput.cs b/FarmmapsNbs/Models/NitrogenInput.cs new file mode 100644 index 0000000..d82fdc2 --- /dev/null +++ b/FarmmapsNbs/Models/NitrogenInput.cs @@ -0,0 +1,30 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsNbs.Models +{ + public class NitrogenInput + { + public bool UseCreatedCropfield { get; set; } + public string File { get; set; } + public string InputVariable { get; set; } + public string OutputFileName { get; set; } + public DateTime PlantingDate { get; set; } + public DateTime MeasurementDate { get; set; } + public string PotatoPurposeType { get; set; } + public int TargetYield { get; set; } + public JObject GeometryJson { get; set; } + public string InputLayerName { get; set; } + public string fieldName{ get; set; } + public bool storeSatelliteStatistics { get; set; } + public bool GenerateTaskmap { get; set; } + public string CellWidth { get; set; } + public string CellHeight { get; set; } + public bool Centered { get; set; } + public JObject StartPoint { get; set; } + public JObject EndPoint { get; set; } + public string Angle { get; set; } + + + } +} \ No newline at end of file diff --git a/FarmmapsNbs/Models/Settings.cs b/FarmmapsNbs/Models/Settings.cs new file mode 100644 index 0000000..40cd25d --- /dev/null +++ b/FarmmapsNbs/Models/Settings.cs @@ -0,0 +1,9 @@ +namespace FarmmapsNbs { + public class Settings { + public string CropfieldItemCode { get; set; } + public string SatelliteTaskCode { get; set; } + public string VanDerSatTaskCode { get; set; } + public string WatBalTaskCode { get; set; } + + } +} \ No newline at end of file diff --git a/FarmmapsNbs/Models/TargetNData.cs b/FarmmapsNbs/Models/TargetNData.cs new file mode 100644 index 0000000..d4aaea9 --- /dev/null +++ b/FarmmapsNbs/Models/TargetNData.cs @@ -0,0 +1,9 @@ +namespace FarmmapsNbs.Models +{ + public class TargetNData + { + public double TSum { get; set; } + public int TargetYield { get; set; } + public double TargetN { get; set; } + } +} \ No newline at end of file diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs new file mode 100644 index 0000000..d7a5ab2 --- /dev/null +++ b/FarmmapsNbs/NbsApplication.cs @@ -0,0 +1,338 @@ +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 FarmmapsNbs.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsNbs +{ + public class NbsApplication : IApplication + { + private const string DownloadFolder = "Downloads"; + private const string SettingsFile = "settings.json"; + + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly NitrogenService _nitrogenService; + private readonly GeneralService _generalService; + + private Settings _settings; + + public NbsApplication(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, NitrogenService nitrogenService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _nitrogenService = nitrogenService; + } + + public async Task RunAsync() + { + var nitrogenInputJson = File.ReadAllText("InputData-NBS.json"); //NitrogenInput.json + List nitrogenInputs = JsonConvert.DeserializeObject>(nitrogenInputJson); + + 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 nitrogenInputs) + { + try + { + await Process(roots, input); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + + private async Task Process(List roots, NitrogenInput input) + { + + // !!specify if you are using an already created cropfield: + bool useCreatedCropfield = input. UseCreatedCropfield; + var plantingDate = input.PlantingDate; + var FieldName = input.fieldName; + bool StoreStatistics = input.storeSatelliteStatistics; + var measurementDate = input.MeasurementDate; + string settingsfile = $"Settings_{FieldName}.json"; + + LoadSettings(settingsfile); + + var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); + if (uploadedRoot == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive"); + if (myDriveRoot == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + // Use already created cropfield or create new one + Item cropfieldItem; + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) + { + _logger.LogInformation("Creating cropfield"); + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, + $"VRA NBS cropfield {input.OutputFileName}", plantingDate.Year, input.GeometryJson.ToString(Formatting.None)); + _settings.CropfieldItemCode = cropfieldItem.Code; + SaveSettings(settingsfile); + } + else + { + _logger.LogInformation("Cropfield already exists, trying to get it"); + cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); + } + + var geotiffItem = (Item)null; + + // No file input, use most recent satellite image + if (string.IsNullOrEmpty(input.File)) { + _logger.LogInformation("No specific data given, retrieving most recent satellite image"); + + // check if satellite task not yet done, do here and save taskcode + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) { + var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); + _settings.SatelliteTaskCode = satelliteTaskCode; + SaveSettings(settingsfile); + } + + + // Select a particular satellite item from satelliteTask + Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode, FieldName, StoreStatistics); + + var satelliteBand = satalliteItem.Data["layers"][0]["name"]; + var satelliteStatistics = satalliteItem.Data["layers"][0]["renderer"]["band"]["statistics"]; + Console.WriteLine($"Satellite image date: {satalliteItem.DataDate}"); + //Console.WriteLine($"Satellite image statistics for band {satelliteBand}: {satelliteStatistics}"); + + //Store data to csv + if (StoreStatistics==true) { + var SatelliteFile = $"C:\\Akkerweb\\DataSatellite_{FieldName}.csv"; + var NewLineField = $"\"Field\":{FieldName}" + Environment.NewLine; + var NewLineDate = $"\"date\":{satalliteItem.DataDate}" + Environment.NewLine; + var i = 0; + foreach (var item in satelliteStatistics) { + //var NewLines2; + if (i == 0) { + File.AppendAllText(SatelliteFile, NewLineDate); + i++; + } + File.AppendAllText(SatelliteFile, $"{item}" + Environment.NewLine); + + } + } + + + // must be wdvi[1] + var inputType = (satalliteItem.Data["layers"] as JArray)?[1]["name"].ToString(); + if (string.IsNullOrEmpty(inputType)) { + _logger.LogError("Could not get the input type name from the satellite item"); + return; + } + + // download the geotiff + var SatelliteImageDate = (DateTime)satalliteItem.DataDate; + var SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, + Path.Combine(DownloadFolder, $"nbs_inputSatelliteGeotiff_{input.OutputFileName}_{inputType}_{SatelliteDate}.zip")); + + // overwrite measurement date by date of satellite item + measurementDate = satalliteItem.DataDate.Value; + + geotiffItem = satalliteItem; + + + } + + + // (geo)tiff input: + else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { + _logger.LogInformation("input = tiff data"); + var dataPath = Path.Combine("Data", input.File); + geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, + Path.GetFileNameWithoutExtension(input.File)); + + if (geotiffItem == null) { + _logger.LogError("Could not find item for uploaded data"); + return; + } + } + + // json/shape input + 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("Could not find item for uploaded data"); + return; + } + + _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, $"{input.OutputFileName}.input_geotiff.zip")); + } + + + //// check if vandersat task not yet done, do here and save taskcode + //if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.VanDerSatTaskCode)) { + // var VanDerSatTaskCode = await _generalService.RunVanDerSatTask(cropfieldItem); + // _settings.VanDerSatTaskCode = VanDerSatTaskCode; + // SaveSettings(settingsfile); + //} + + //// Select a particular image item from VanDerSat + //Item VanDerSatItem = await _generalService.FindVanDerSatItem(cropfieldItem, _settings.VanDerSatTaskCode, FieldName, StoreStatistics); + + + //// download the geotiff + //_logger.LogInformation("Downloading geotiff file"); + //await _farmmapsApiService.DownloadItemAsync(VanDerSatItem.Code, + // Path.Combine(DownloadFolder, $"nbs_VanDerSatGeotiff_{input.OutputFileName}.zip")); + + _logger.LogInformation($"Calculating targetN with targetYield: {input.TargetYield}"); + var targetNItem = await _nitrogenService.CreateTargetNItem(cropfieldItem); + var targetNData = await _nitrogenService.CalculateTargetN(cropfieldItem, targetNItem, plantingDate, + measurementDate, input.PotatoPurposeType, input.TargetYield); + + if (targetNData == null) { + _logger.LogError("Something went wrong with TargetN calculation"); + return; + } + + _logger.LogInformation($"TargetN: {targetNData.TargetN}"); + ////Option to manually adjust the Target N, for test purposes only! + //targetNData.TargetN = 225; + //_logger.LogInformation($"TargetN adjusted: {targetNData.TargetN}"); + + var targetNDataPath = Path.Combine(DownloadFolder, $"{input.OutputFileName}.targetn.json"); + + await File.WriteAllTextAsync(targetNDataPath, JsonConvert.SerializeObject(targetNData, Formatting.Indented)); + + _logger.LogInformation("Calculating uptake map"); + var uptakeMapItem = + await _nitrogenService.CalculateUptakeMap(cropfieldItem, geotiffItem, plantingDate, + measurementDate, input.InputVariable, input.InputLayerName); + if (uptakeMapItem == null) { + _logger.LogError("Something went wrong with creating the uptakeMap"); + return; + } + + _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 = + await _nitrogenService.CalculateApplicationMap(cropfieldItem, geotiffItem, plantingDate, + measurementDate, input.InputVariable, targetNData.TargetN, input.InputLayerName); + + if (applicationMapItem == null) { + _logger.LogError("Something went wrong with creating the applicationMap"); + return; + } + _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; + //} + + //ApplicationMap (GEOTIFF) To Taskmap + _logger.LogInformation($"Converting geotiff to taskmap"); + var taskmap = await _generalService.CreateTaskmap(cropfieldItem, tiffItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), + input.Centered.ToString(), input.EndPoint.ToString(Formatting.None), input.Angle); + + 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")); + + + } + + + // Functions to save previously created cropfields + private void LoadSettings(string file) + { + if (File.Exists(file)) + { + var jsonText = File.ReadAllText(file); + _settings = JsonConvert.DeserializeObject(jsonText); + } + else + { + _settings = new Settings(); + } + } + + private void SaveSettings(string file) + { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + } + private void SaveInfo(string file) { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + + } + + } + } \ No newline at end of file diff --git a/FarmmapsNbs/NitrogenInput.json b/FarmmapsNbs/NitrogenInput.json new file mode 100644 index 0000000..6e2afe5 --- /dev/null +++ b/FarmmapsNbs/NitrogenInput.json @@ -0,0 +1,310 @@ +[ + { + "file": "Scan_1_20190605.json", + "inputVariable": "irmi", + "outputFileName": "vranbs1", + "plantingDate": "2019-04-18", + "measurementDate": "2019-06-05", + "potatoPurposeType": "consumption", + "targetYield": 45, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.40843828875524, 50.638966444680605 ], + [ 3.408953272886064, 50.639197789621612 ], + [ 3.409242951459603, 50.639469958681836 ], + [ 3.409328782148028, 50.639612846807708 ], + [ 3.409457528180712, 50.639789755314411 ], + [ 3.409639918393741, 50.640014292074966 ], + [ 3.409833037442765, 50.640211611372706 ], + [ 3.410069071836049, 50.640395321698435 ], + [ 3.410380208081761, 50.640572227259661 ], + [ 3.410605513638958, 50.640715112034222 ], + [ 3.411925160474145, 50.641177783561204 ], + [ 3.411935889310142, 50.640728720085136 ], + [ 3.412590348309737, 50.63948356709389 ], + [ 3.413244807309242, 50.638224772339846 ], + [ 3.413400375432099, 50.637901562841307 ], + [ 3.413539850300779, 50.637449065809889 ], + [ 3.413475477284437, 50.637418445552932 ], + [ 3.40999396998362, 50.637449065810451 ], + [ 3.409940325803365, 50.638102293212661 ], + [ 3.409575545377398, 50.638483338338325 ], + [ 3.409060561246574, 50.638707881340494 ], + [ 3.40843828875524, 50.638966444680605 ] + ] + ] + }, + //{ + //"file": "[...].json", + //"inputVariable": "wdvi", + //"outputFileName": "20201211_Mahindra", + //"plantingDate": "2020-11-25", + //"measurementDate": "2020-12-08", + //"potatoPurposeType": "consumption", + //"targetYield": 45, + //"geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 75.4652734163369, 31.26328617861426 ], + // [ 75.46527659380338, 31.26437743805827 ], + // [ 75.46494267602188, 31.26438166160194 ], + // [ 75.46493257618651, 31.26462192072676 ], + // [ 75.46438242791261, 31.26462132638865 ], + // [ 75.46438225728252, 31.26334267015003 ], + // [ 75.46448543502072, 31.26333463263533 ], + // [ 75.46448842093658, 31.26302891147988 ], + // [ 75.46507631089699, 31.26299589154944 ], + // [ 75.46509039016291, 31.26329023051392 ], + // [ 75.4652734163369, 31.26328617861426 ] + // ] + // ] + //} + //} + + //}, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "outputFileName": "vranbs2", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-06-05", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } + //}, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + { + "file": "", // keep emptpy to use satellite image + "inputVariable": "wdvi", + "InputLayerName": "wdvi", + "outputFileName": "rtest1", + "plantingDate": "2020-05-01", + "measurementDate": "2020-06-14", + "potatoPurposeType": "consumption", + "targetYield": 60, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 4.960707146896585, 52.800583669708487 ], + [ 4.960645975538824, 52.800470217610922 ], + [ 4.962140695752897, 52.799177147194797 ], + [ 4.967523821195745, 52.801502400041208 ], + [ 4.966336768950911, 52.802543735879809 ], + [ 4.961711880764330, 52.801009996856429 ], + [ 4.960707146896585, 52.800583669708487 ] + ] + ] + } + } + //, + //{ + // "file": "", // keep emptpy to use satellite image + // "inputVariable": "wdvi", + // "InputLayerName": "wdvi", + // "outputFileName": "mtest1", + // "plantingDate": "2020-04-01", + // "measurementDate": "2020-06-24", + // "potatoPurposeType": "consumption", + // "targetYield": 60, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.575458869234128, 51.308707885669762 ], + // [ 3.575508957999423, 51.30878478562019 ], + // [ 3.576188404403633, 51.309372997559777 ], + // [ 3.576188872410267, 51.309374219701091 ], + // [ 3.576210290749152, 51.309430091473608 ], + // [ 3.57621266537704, 51.309483685674898 ], + // [ 3.576213477455834, 51.309502027981374 ], + // [ 3.577543136860447, 51.310682367527122 ], + // [ 3.57796629328866, 51.31104321405175 ], + // [ 3.578442479292087, 51.311449273042747 ], + // [ 3.57866702353106, 51.311636072786726 ], + // [ 3.57880446997978, 51.31157117599529 ], + // [ 3.579155910205885, 51.311863542729718 ], + // [ 3.579175814007489, 51.311875435159394 ], + // [ 3.579293885246395, 51.311936532835396 ], + // [ 3.579413896180069, 51.311998649478575 ], + // [ 3.579514543462617, 51.312041110734917 ], + // [ 3.579611760655688, 51.312082118352606 ], + // [ 3.579635115371588, 51.312093949652223 ], + // [ 3.579793143414486, 51.312189437140432 ], + // [ 3.579966991648108, 51.312286148850511 ], + // [ 3.580079704980967, 51.312332458349751 ], + // [ 3.580203717638148, 51.312336471368539 ], + // [ 3.580307101018293, 51.312330239539847 ], + // [ 3.580383836270609, 51.312317097185243 ], + // [ 3.580505207977176, 51.312279163554869 ], + // [ 3.580610387713855, 51.312233723091026 ], + // [ 3.5806309754483, 51.312226093729677 ], + // [ 3.580638516049738, 51.312223727082049 ], + // [ 3.58075536599681, 51.312186990706344 ], + // [ 3.580787745633303, 51.312176820129551 ], + // [ 3.580829682241423, 51.312167665428959 ], + // [ 3.58086828456562, 51.312162614898625 ], + // [ 3.580980493636721, 51.312147935609723 ], + // [ 3.581014352632766, 51.312145662592656 ], + // [ 3.581028980583245, 51.312145592849248 ], + // [ 3.581119189823368, 51.312145103215911 ], + // [ 3.581195330198145, 51.312144693075908 ], + // [ 3.581243537809229, 51.312148741603245 ], + // [ 3.58132480221972, 51.312163110548383 ], + // [ 3.581426517039001, 51.312181089466016 ], + // [ 3.581448095953263, 51.312184910295024 ], + // [ 3.581474337475052, 51.312191038736145 ], + // [ 3.581709405982819, 51.312260068944951 ], + // [ 3.581727319558337, 51.312266163697757 ], + // [ 3.581753583356718, 51.312276407881612 ], + // [ 3.581841655772683, 51.312310743468075 ], + // [ 3.581878851795624, 51.312325234405343 ], + // [ 3.581905889860924, 51.312338260798654 ], + // [ 3.581906254501594, 51.312338472113815 ], + // [ 3.582048236499295, 51.312422439022804 ], + // [ 3.58189849928915, 51.312481944577037 ], + // [ 3.583044298383354, 51.312095780444281 ], + // [ 3.582984006671231, 51.312017283925279 ], + // [ 3.582743535862999, 51.311699343434064 ], + // [ 3.582628599916243, 51.311582190756774 ], + // [ 3.581446834435509, 51.310511259982569 ], + // [ 3.580621864908701, 51.309767270412806 ], + // [ 3.579610575760466, 51.308860440593946 ], + // [ 3.579112608916012, 51.308394999612226 ], + // [ 3.578688808506157, 51.307968441218165 ], + // [ 3.578394256007207, 51.307644098092617 ], + // [ 3.578355980318371, 51.307607614964702 ], + // [ 3.578217977585775, 51.307654179547846 ], + // [ 3.577480636332469, 51.307921035607997 ], + // [ 3.575695560441006, 51.308577167973212 ], + // [ 3.575668643609169, 51.30855384769157 ], + // [ 3.575666204524265, 51.308551734020703 ], + // [ 3.575506397192348, 51.308609906947261 ], + // [ 3.575459139533024, 51.308653178431456 ], + // [ 3.575458869234128, 51.308707885669762 ] + // ] + // ] + // } + //} + //, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "InputLayerName": "IRMI", + // "outputFileName": "vranbs3", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-06-20", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } + //}, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + //} + //, + //{ + // "file": "Scan_1_20190605.zip", + // "inputVariable": "irmi", + // "InputLayerName": "", + // "outputFileName": "vranbs4", + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-07-03", + // "potatoPurposeType": "starch", + // "targetYield": 45, + // "geometryJson": { + // "type": "Polygon", + // "coordinates": [ + // [ + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] + // ] + // ] + // } + + } + + //} + +] \ No newline at end of file diff --git a/FarmmapsNbs/NitrogenService.cs b/FarmmapsNbs/NitrogenService.cs new file mode 100644 index 0000000..96416ef --- /dev/null +++ b/FarmmapsNbs/NitrogenService.cs @@ -0,0 +1,191 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmmapsNbs.Models; +using Microsoft.Extensions.Logging; +using static FarmmapsApi.Extensions; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsNbs +{ + public class NitrogenService + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly GeneralService _generalService; + + public NitrogenService(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + } + + public async Task CreateTargetNItem(Item cropfieldItem) + { + var itemRequest = new ItemRequest() + { + ParentCode = cropfieldItem.ParentCode, + ItemType = USERINPUT_ITEMTYPE, + Name = "TargetN" + }; + return await _farmmapsApiService.CreateItemAsync(itemRequest); + } + + /// + /// Calculates TargetN, makes the assumption the cropfield and user.input(targetn) item have the same parent + /// + /// The cropfield to base the calculations on + /// The targetN item to save calculations in + /// The date the crop is planted + /// The date the measurements are taken + /// The crop purpose + /// The target yield input for the TargetN calculation + /// The TargetN + public async Task CalculateTargetN(Item cropfieldItem, Item targetNItem, DateTime plantingDate, + DateTime measurementDate, string purposeType, int targetYield) + { + var nbsTargetNRequest = new TaskRequest {TaskType = VRANBS_TASK}; + nbsTargetNRequest.attributes["operation"] = "targetn"; + nbsTargetNRequest.attributes["inputCode"] = targetNItem.Code; + 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); + + await PollTask(TimeSpan.FromSeconds(3), 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) + { + _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); + return null; + } + + var item = await _farmmapsApiService.GetItemAsync(targetNItem.Code); + return item.Data.ToObject(); + } + + /// + /// Calculates the uptake map based on the given inputs + /// + /// The cropfield to base the calculations on + /// + /// The date the crop is planted + /// The date the measurements are taken + /// Data type, could be yara, ci, irmi or wdvi + /// Column name in which the sensor value is stored + /// + public async Task CalculateUptakeMap(Item cropfieldItem, Item inputItem, DateTime plantingDate, + DateTime measurementDate, string inputType, string inputLayerName) + { + var nbsUptakeMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; + nbsUptakeMapRequest.attributes["operation"] = "uptake"; + nbsUptakeMapRequest.attributes["inputCode"] = inputItem.Code; + nbsUptakeMapRequest.attributes["plantingDate"] = plantingDate.ToString("o"); + nbsUptakeMapRequest.attributes["measurementDate"] = measurementDate.ToString("o"); + nbsUptakeMapRequest.attributes["inputType"] = inputType.ToLower(); + if (!(string.IsNullOrEmpty(inputLayerName))) nbsUptakeMapRequest.attributes["inputLayerName"] = inputLayerName; + //toevoeging FS. Kolom IRMI hernoemd als IMI. Deze wordt niet automatisch herkend. En moet dus gespecificeerd worden. + + + + string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsUptakeMapRequest); + + await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => + { + var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); + _logger.LogInformation($"Calculating uptake map; 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; + } + + var itemName = "VRANbs uptake"; + var uptakeMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, + GEOTIFF_PROCESSED_ITEMTYPE, itemName, + i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && + i.Name.ToLower().Contains(itemName.ToLower())); + + if (uptakeMapItem == null) + { + _logger.LogError("Could not find the uptake geotiff child item under cropfield"); + return null; + } + + return uptakeMapItem; + } + + + /// + /// Creates the nitrogen application map based on given input data + /// + /// The cropfield to base the calculations on + /// The farmmaps item containing the geotiff data + /// The date the crop is planted + /// The date the measurements are taken + /// The inputtype to use, could be yara, ci, irmi or wdvi + /// The target nitrogen to use for the calculations + /// + public async Task CalculateApplicationMap(Item cropfieldItem, Item inputItem, DateTime plantingDate, + DateTime measurementDate, string inputType, double targetN, string inputLayerName) + { + var nbsApplicationMapRequest = new TaskRequest {TaskType = VRANBS_TASK}; + nbsApplicationMapRequest.attributes["operation"] = "application"; + nbsApplicationMapRequest.attributes["inputCode"] = inputItem.Code; + 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); + if (!(string.IsNullOrEmpty(inputLayerName))) nbsApplicationMapRequest.attributes["inputLayerName"] = inputLayerName; + + string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsApplicationMapRequest); + + await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => + { + var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); + _logger.LogInformation($"Calculating application map; 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; + } + + var itemName = $"VRANbs application"; + var applicationMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, + GEOTIFF_PROCESSED_ITEMTYPE, itemName, + i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && + i.Name.ToLower().Contains(itemName.ToLower())); + if (applicationMapItem == null) + { + _logger.LogError("Could not find the application map geotiff child item under cropfield"); + return null; + } + + return applicationMapItem; + } + } +} \ No newline at end of file diff --git a/FarmmapsPoten_AVRapi/Program.cs b/FarmmapsNbs/Program.cs similarity index 79% rename from FarmmapsPoten_AVRapi/Program.cs rename to FarmmapsNbs/Program.cs index 1003080..596637a 100644 --- a/FarmmapsPoten_AVRapi/Program.cs +++ b/FarmmapsNbs/Program.cs @@ -3,9 +3,9 @@ using FarmmapsApi; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace FarmmapsVRApoten +namespace FarmmapsNbs { - class Program : FarmmapsProgram + class Program : FarmmapsProgram { private static async Task Main(string[] args) { @@ -17,7 +17,7 @@ namespace FarmmapsVRApoten serviceCollection.AddLogging(opts => opts .AddConsole() .AddFilter("System.Net.Http", LogLevel.Warning)) - .AddTransient(); + .AddTransient(); } } } \ No newline at end of file diff --git a/FarmmapsPoten/Models/PotenInput.cs b/FarmmapsPoten/Models/PotenInput.cs index 57c71c4..0814ce3 100644 --- a/FarmmapsPoten/Models/PotenInput.cs +++ b/FarmmapsPoten/Models/PotenInput.cs @@ -25,6 +25,8 @@ namespace FarmmapsPoten.Models public JObject StartPoint { get; set; } public JObject EndPoint { get; set; } public string Angle { get; set; } + public string Precision { get; set; } + public string MaximumClasses { get; set; } } } \ No newline at end of file diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index d80c398..2ec8375 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -175,14 +175,7 @@ namespace FarmmapsVRApoten _logger.LogInformation(File.Exists(finalOutput) ? "Download application map completed." : "Something went wrong while downloading."); - - ////GEOTIFF TO SHAPE - //_logger.LogInformation($"Converting geotiff to shape"); - //var geotiffToShapeItem= await _generalService.GeotiffToShape(applianceMapItem); - //if (taskmap == null) { - // _logger.LogError("Something went wrong with geotiff to shape transformation"); - // return; - //} + if(input.GenerateTaskmap) { @@ -192,12 +185,17 @@ namespace FarmmapsVRApoten var taskmap = (Item)null; if (input.OutputType == "isoxml") { - taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.OutputType, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - input.DdiCode, input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle); + + taskmap = await _generalService.CreateTaskmap(cropfieldItem: cropfieldItem, tiffItem: applianceMapItem, outputType: input.OutputType, cellWidth: input.CellWidth, + cellHeight: input.CellHeight, startPoint: input.StartPoint.ToString(Formatting.None), ddiCode: input.DdiCode, centered: input.Centered, + endPoint: input.EndPoint.ToString(Formatting.None), angle: input.Angle, precision: input.Precision, + cropTypeName: null, costumerName: null, ProductGroupName: null, productName : null, resolution: null, unitScale: null, maximumClasses: input.MaximumClasses); + } else { - taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.OutputType, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - null, input.Centered, input.EndPoint.ToString(Formatting.None), input.Angle); + taskmap = await _generalService.CreateTaskmap(cropfieldItem: cropfieldItem, tiffItem: applianceMapItem, outputType: input.OutputType, cellWidth: input.CellWidth, + cellHeight: input.CellHeight, startPoint: input.StartPoint.ToString(Formatting.None), centered: input.Centered, + endPoint: input.EndPoint.ToString(Formatting.None), angle: input.Angle, precision: input.Precision, maximumClasses: input.MaximumClasses); } diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index 9e6bd92..764e62c 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -15,284 +15,6 @@ // [ // [ 6.3070655, 53.3623397 ], // [ 6.3070875, 53.3623898 ], - // [ 6.3071092, 53.3624398 ], - // [ 6.3071312, 53.3624898 ], - // [ 6.3071531, 53.3625399 ], - // [ 6.3071749, 53.3625899 ], - // [ 6.3071968, 53.36264 ], - // [ 6.3072186, 53.36269 ], - // [ 6.3072393, 53.3627389 ], - // [ 6.3072598, 53.3627879 ], - // [ 6.3072803, 53.3628367 ], - // [ 6.3073008, 53.3628856 ], - // [ 6.3073215, 53.3629345 ], - // [ 6.307342, 53.3629834 ], - // [ 6.3073625, 53.3630324 ], - // [ 6.3073832, 53.3630812 ], - // [ 6.3074037, 53.3631301 ], - // [ 6.3074242, 53.363179 ], - // [ 6.3074447, 53.3632279 ], - // [ 6.3074654, 53.3632768 ], - // [ 6.3074859, 53.3633257 ], - // [ 6.3075064, 53.3633746 ], - // [ 6.3075271, 53.3634235 ], - // [ 6.3075476, 53.3634724 ], - // [ 6.3075681, 53.3635213 ], - // [ 6.3075886, 53.3635702 ], - // [ 6.3076093, 53.3636191 ], - // [ 6.3076609, 53.3637669 ], - // [ 6.3076735, 53.3638141 ], - // [ 6.3076717, 53.3638452 ], - // [ 6.3076566, 53.3638765 ], - // [ 6.3076265, 53.3639037 ], - // [ 6.3075848, 53.3639245 ], - // [ 6.3075321, 53.3639382 ], - // [ 6.3074494, 53.3639508 ], - // [ 6.3073665, 53.3639632 ], - // [ 6.3072835, 53.3639758 ], - // [ 6.3072006, 53.3639884 ], - // [ 6.3071179, 53.3640008 ], - // [ 6.307035, 53.3640134 ], - // [ 6.306952, 53.3640258 ], - // [ 6.3068693, 53.3640384 ], - // [ 6.3067864, 53.3640509 ], - // [ 6.3067035, 53.3640634 ], - // [ 6.3066207, 53.3640759 ], - // [ 6.3065378, 53.3640884 ], - // [ 6.3064549, 53.364101 ], - // [ 6.306372, 53.3641135 ], - // [ 6.3062892, 53.364126 ], - // [ 6.3062063, 53.3641385 ], - // [ 6.3061234, 53.364151 ], - // [ 6.305889, 53.3641875 ], - // [ 6.3058018, 53.3642004 ], - // [ 6.3057146, 53.3642134 ], - // [ 6.3056273, 53.3642265 ], - // [ 6.3055401, 53.3642395 ], - // [ 6.3054529, 53.3642525 ], - // [ 6.3053655, 53.3642655 ], - // [ 6.3052782, 53.3642785 ], - // [ 6.3051975, 53.3642908 ], - // [ 6.3051167, 53.3643032 ], - // [ 6.3050358, 53.3643154 ], - // [ 6.304955, 53.3643277 ], - // [ 6.3048741, 53.3643401 ], - // [ 6.3047933, 53.3643523 ], - // [ 6.3047126, 53.3643647 ], - // [ 6.3046316, 53.364377 ], - // [ 6.3045509, 53.3643893 ], - // [ 6.3044701, 53.3644016 ], - // [ 6.3043892, 53.3644139 ], - // [ 6.3043086, 53.3644261 ], - // [ 6.304228, 53.3644382 ], - // [ 6.3041476, 53.3644503 ], - // [ 6.304067, 53.3644624 ], - // [ 6.3039864, 53.3644746 ], - // [ 6.3039059, 53.3644868 ], - // [ 6.3038253, 53.3644989 ], - // [ 6.3037447, 53.3645111 ], - // [ 6.3036641, 53.3645232 ], - // [ 6.3035835, 53.3645353 ], - // [ 6.303503, 53.3645474 ], - // [ 6.3034224, 53.3645596 ], - // [ 6.3033418, 53.3645717 ], - // [ 6.3032344, 53.3645896 ], - // [ 6.3029993, 53.364623 ], - // [ 6.3028959, 53.3646389 ], - // [ 6.3027925, 53.3646549 ], - // [ 6.3026889, 53.3646708 ], - // [ 6.3025855, 53.3646868 ], - // [ 6.3024821, 53.3647026 ], - // [ 6.3024042, 53.3647144 ], - // [ 6.3023261, 53.3647261 ], - // [ 6.3022482, 53.3647378 ], - // [ 6.3021702, 53.3647495 ], - // [ 6.3020923, 53.3647612 ], - // [ 6.3020142, 53.364773 ], - // [ 6.3019363, 53.3647847 ], - // [ 6.3018585, 53.3647964 ], - // [ 6.3017804, 53.3648081 ], - // [ 6.3017025, 53.3648198 ], - // [ 6.3016245, 53.3648315 ], - // [ 6.3015466, 53.3648433 ], - // [ 6.3014687, 53.364855 ], - // [ 6.3013906, 53.3648667 ], - // [ 6.3013128, 53.3648784 ], - // [ 6.3012347, 53.3648901 ], - // [ 6.3011568, 53.3649018 ], - // [ 6.3010788, 53.3649136 ], - // [ 6.3010009, 53.3649253 ], - // [ 6.300923, 53.364937 ], - // [ 6.3008449, 53.3649487 ], - // [ 6.300767, 53.3649604 ], - // [ 6.300689, 53.3649721 ], - // [ 6.3006111, 53.3649839 ], - // [ 6.300533, 53.3649956 ], - // [ 6.3004552, 53.3650073 ], - // [ 6.3003771, 53.365019 ], - // [ 6.3002992, 53.3650307 ], - // [ 6.3002213, 53.3650425 ], - // [ 6.3001433, 53.3650542 ], - // [ 6.3000654, 53.3650659 ], - // [ 6.2999843, 53.3650783 ], - // [ 6.2999033, 53.3650905 ], - // [ 6.2998224, 53.3651029 ], - // [ 6.2997415, 53.3651151 ], - // [ 6.2996603, 53.3651274 ], - // [ 6.2995794, 53.3651398 ], - // [ 6.2994985, 53.365152 ], - // [ 6.2994175, 53.3651644 ], - // [ 6.2993364, 53.3651767 ], - // [ 6.2992623, 53.3651849 ], - // [ 6.2992157, 53.3651857 ], - // [ 6.2991679, 53.365182 ], - // [ 6.2991157, 53.3651694 ], - // [ 6.2990734, 53.3651492 ], - // [ 6.2990423, 53.3651248 ], - // [ 6.299008, 53.3650764 ], - // [ 6.2989616, 53.3649669 ], - // [ 6.2989414, 53.3649136 ], - // [ 6.2989213, 53.3648604 ], - // [ 6.2989011, 53.3648072 ], - // [ 6.298881, 53.3647538 ], - // [ 6.2988608, 53.3647006 ], - // [ 6.2988407, 53.3646474 ], - // [ 6.2988206, 53.3645941 ], - // [ 6.2987995, 53.3645407 ], - // [ 6.2987785, 53.3644873 ], - // [ 6.2987574, 53.3644339 ], - // [ 6.2987364, 53.3643805 ], - // [ 6.2987153, 53.3643271 ], - // [ 6.2986943, 53.3642738 ], - // [ 6.2986731, 53.3642204 ], - // [ 6.298654, 53.364171 ], - // [ 6.2986349, 53.3641217 ], - // [ 6.2986159, 53.3640723 ], - // [ 6.2985968, 53.3640229 ], - // [ 6.2985777, 53.3639736 ], - // [ 6.2985587, 53.3639242 ], - // [ 6.2985396, 53.3638748 ], - // [ 6.2985205, 53.3638254 ], - // [ 6.2985015, 53.3637761 ], - // [ 6.2984826, 53.3637267 ], - // [ 6.2984571, 53.3636525 ], - // [ 6.2984554, 53.3636239 ], - // [ 6.2984678, 53.3635953 ], - // [ 6.2984948, 53.3635675 ], - // [ 6.2985616, 53.3635415 ], - // [ 6.2986411, 53.3635329 ], - // [ 6.2987148, 53.363522 ], - // [ 6.2987885, 53.3635112 ], - // [ 6.2988625, 53.3635003 ], - // [ 6.2989362, 53.3634895 ], - // [ 6.29901, 53.3634786 ], - // [ 6.2990837, 53.3634678 ], - // [ 6.2991574, 53.363457 ], - // [ 6.2992312, 53.3634461 ], - // [ 6.2993049, 53.3634353 ], - // [ 6.2993787, 53.3634244 ], - // [ 6.2994524, 53.3634136 ], - // [ 6.2995262, 53.3634028 ], - // [ 6.2995999, 53.3633919 ], - // [ 6.2996737, 53.3633811 ], - // [ 6.2997474, 53.3633702 ], - // [ 6.2998211, 53.3633594 ], - // [ 6.2998949, 53.3633486 ], - // [ 6.2999686, 53.3633376 ], - // [ 6.3000424, 53.3633268 ], - // [ 6.3001161, 53.3633159 ], - // [ 6.3001899, 53.3633051 ], - // [ 6.3002636, 53.3632943 ], - // [ 6.3003374, 53.3632834 ], - // [ 6.3004111, 53.3632726 ], - // [ 6.3004848, 53.3632617 ], - // [ 6.3005588, 53.3632509 ], - // [ 6.3006325, 53.36324 ], - // [ 6.3007063, 53.3632292 ], - // [ 6.30078, 53.3632184 ], - // [ 6.3008537, 53.3632075 ], - // [ 6.3009275, 53.3631967 ], - // [ 6.3010012, 53.3631858 ], - // [ 6.301075, 53.363175 ], - // [ 6.3011487, 53.3631642 ], - // [ 6.3012225, 53.3631533 ], - // [ 6.3012962, 53.3631425 ], - // [ 6.30137, 53.3631316 ], - // [ 6.3014437, 53.3631208 ], - // [ 6.3015174, 53.36311 ], - // [ 6.3015912, 53.3630991 ], - // [ 6.3016649, 53.3630883 ], - // [ 6.3017387, 53.3630774 ], - // [ 6.3018124, 53.3630666 ], - // [ 6.3018862, 53.3630558 ], - // [ 6.3019599, 53.3630449 ], - // [ 6.3020337, 53.3630341 ], - // [ 6.3021074, 53.3630232 ], - // [ 6.3021811, 53.3630124 ], - // [ 6.3022549, 53.3630016 ], - // [ 6.3023286, 53.3629907 ], - // [ 6.3024026, 53.3629799 ], - // [ 6.3024763, 53.362969 ], - // [ 6.30255, 53.3629582 ], - // [ 6.3026238, 53.3629474 ], - // [ 6.3026975, 53.3629365 ], - // [ 6.3027713, 53.3629256 ], - // [ 6.302845, 53.3629147 ], - // [ 6.3029188, 53.3629039 ], - // [ 6.3029925, 53.362893 ], - // [ 6.3030663, 53.3628822 ], - // [ 6.30314, 53.3628714 ], - // [ 6.3032137, 53.3628605 ], - // [ 6.3032875, 53.3628497 ], - // [ 6.3033612, 53.3628388 ], - // [ 6.303435, 53.362828 ], - // [ 6.3035087, 53.3628172 ], - // [ 6.3035825, 53.3628063 ], - // [ 6.3036562, 53.3627955 ], - // [ 6.30373, 53.3627846 ], - // [ 6.3038037, 53.3627738 ], - // [ 6.3038774, 53.362763 ], - // [ 6.3039512, 53.3627521 ], - // [ 6.3040249, 53.3627413 ], - // [ 6.3040987, 53.3627304 ], - // [ 6.3041724, 53.3627196 ], - // [ 6.3042462, 53.3627088 ], - // [ 6.3043199, 53.3626979 ], - // [ 6.3043937, 53.3626871 ], - // [ 6.3044674, 53.3626762 ], - // [ 6.3045413, 53.3626654 ], - // [ 6.3046151, 53.3626545 ], - // [ 6.3046888, 53.3626436 ], - // [ 6.3047626, 53.3626328 ], - // [ 6.3048363, 53.3626219 ], - // [ 6.30491, 53.3626111 ], - // [ 6.3049838, 53.3626002 ], - // [ 6.3050575, 53.3625894 ], - // [ 6.3051313, 53.3625786 ], - // [ 6.305205, 53.3625677 ], - // [ 6.3052788, 53.3625569 ], - // [ 6.3053525, 53.362546 ], - // [ 6.3054263, 53.3625352 ], - // [ 6.3055, 53.3625244 ], - // [ 6.3055737, 53.3625135 ], - // [ 6.3056475, 53.3625027 ], - // [ 6.3057212, 53.3624918 ], - // [ 6.305795, 53.362481 ], - // [ 6.3058687, 53.3624701 ], - // [ 6.3059425, 53.3624593 ], - // [ 6.3060162, 53.3624485 ], - // [ 6.30609, 53.3624376 ], - // [ 6.3061637, 53.3624268 ], - // [ 6.3062374, 53.3624158 ], - // [ 6.3063112, 53.362405 ], - // [ 6.3063849, 53.3623942 ], - // [ 6.3064587, 53.3623833 ], - // [ 6.3065324, 53.3623725 ], - // [ 6.3066062, 53.3623616 ], - // [ 6.3066799, 53.3623508 ], - // [ 6.3067537, 53.36234 ], - // [ 6.3068274, 53.3623291 ], - // [ 6.3069011, 53.3623183 ], // [ 6.3069749, 53.3623074 ], // [ 6.3070314, 53.3623125 ], // [ 6.3070655, 53.3623397 ] @@ -319,14 +41,14 @@ //} { - "File": "PlantingSampleDataLutum_TEST2.zip", - "OutputFileName": "2021.03.15_vraPoten_SampleData", + "File": "PlantingSampleDataLutum.zip", + "OutputFileName": "2021.04.09_vraPoten_SampleData4", "FieldName": "lutum", "PlantingYear": 2021, "MeanDensity": "30", "Variation": "20", "UseShadow": false, - "CountPerArea": true, + "CountPerArea": false, "geometryJson": { "type": "Polygon", "coordinates": [ @@ -341,8 +63,10 @@ }, "GenerateTaskmap": true, - "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode - "DdiCode": "0001", + "OutputType": "isoxml", // "shape" or "isoxml" if isoxml also add ddiCode + "Precision": "2", + "MaximumClasses": "4", + "DdiCode": "0016", "CellWidth": "3", "CellHeight": "10", "Centered": "true", @@ -351,11 +75,11 @@ //"coordinates": [ 5.669032078413372, 52.527906465105254 ] // 1 //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 - "coordinates": [ 5.668375834141733, 52.527202407445586 ] // 4 + "coordinates": [ 5.671623092321491, 52.529463163999097 ] // 4 }, "EndPoint": { "type": "Point", - "coordinates": [ 5.668263398897563, 52.529744573890817 ] // 1 + "coordinates": [ 5.668860417036520, 52.529299990602986 ] // 1 //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 //"coordinates": [ 5.671853762296686, 52.528056589415456 ] // 4 diff --git a/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json b/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json deleted file mode 100644 index 7414170..0000000 --- a/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.json +++ /dev/null @@ -1,302 +0,0 @@ -{ -"type": "FeatureCollection", -"name": "PlantingSampleDataLutum", -"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, -"features": [ -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669006678383711, 52.529290221274948 ] } }, -{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669006104248043, 52.529200349555097 ] } }, -{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669005530115061, 52.529110477833399 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669004955984766, 52.529020606109889 ] } }, -{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669004381857157, 52.528930734384545 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669003807732236, 52.528840862657354 ] } }, -{ "type": "Feature", "properties": { "lutum": 12 }, "geometry": { "type": "Point", "coordinates": [ 5.66900323361, 52.528750990928309 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669002659490453, 52.528661119197444 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669002085373592, 52.528571247464789 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669001511259415, 52.528481375730252 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669000937147927, 52.528391503993873 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669000363039124, 52.52830163225569 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669154039708354, 52.529289871032709 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669153465272641, 52.529199999313761 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669152890839613, 52.529110127592965 ] } }, -{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669152316409275, 52.529020255870378 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669151741981626, 52.528930384145944 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669151167556664, 52.528840512419642 ] } }, -{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.66915059313439, 52.528750640691563 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669150018714802, 52.528660768961629 ] } }, -{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669149444297904, 52.528570897229841 ] } }, -{ "type": "Feature", "properties": { "lutum": 13 }, "geometry": { "type": "Point", "coordinates": [ 5.669148869883696, 52.528481025496269 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669148295472175, 52.528391153760822 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.66914772106334, 52.528301282023527 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669147146657193, 52.528211410284435 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669146572253736, 52.528121538543502 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669145997852967, 52.528031666800764 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669145423454884, 52.527941795056144 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669301401030309, 52.529289520607428 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.66930082629455, 52.52919964888941 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669300251561478, 52.529109777169573 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669299676831097, 52.529019905447882 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669299102103404, 52.528930033724393 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669298527378404, 52.528840161999021 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669297952656091, 52.528750290271873 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669297377936467, 52.528660418542835 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669296803219534, 52.528570546811963 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669296228505289, 52.528480675079251 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669295653793735, 52.528390803344763 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669295079084868, 52.52830093160842 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669294504378692, 52.528211059870216 ] } }, -{ "type": "Feature", "properties": { "lutum": 14 }, "geometry": { "type": "Point", "coordinates": [ 5.669293929675204, 52.528121188130193 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669293354974407, 52.528031316388351 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669292780276298, 52.527941444644689 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669448762349575, 52.529289169999203 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669448187313771, 52.529199298282116 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669447612280653, 52.529109426563181 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669447037250229, 52.529019554842385 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669446462222496, 52.52892968311982 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669445887197454, 52.528839811395386 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669445312175103, 52.528749939669105 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669444737155444, 52.528660067941011 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669444162138473, 52.528570196211071 ] } }, -{ "type": "Feature", "properties": { "lutum": 15 }, "geometry": { "type": "Point", "coordinates": [ 5.669443587124193, 52.528480324479311 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669443012112604, 52.528390452745711 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669442437103707, 52.528300581010306 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669441862097501, 52.528210709273012 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669441287093983, 52.52812083753394 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669440712093158, 52.528030965793008 ] } }, -{ "type": "Feature", "properties": { "lutum": 16 }, "geometry": { "type": "Point", "coordinates": [ 5.669440137095023, 52.527941094050242 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669596123666151, 52.529288819207963 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.6695955483303, 52.5291989474918 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669594972997138, 52.529109075773768 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669594397666672, 52.529019204053945 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669593822338896, 52.52892933233224 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669593247013813, 52.528839460608751 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669592671691423, 52.528749588883372 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669592096371725, 52.528659717156224 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.66959152105472, 52.528569845427207 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669590945740404, 52.528479973696371 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669590370428786, 52.528390101963666 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669589795119855, 52.528300230229171 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669589219813616, 52.528210358492849 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669588644510072, 52.528120486754638 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669588069209219, 52.52803061501465 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669587493911057, 52.527940743272772 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669743484980033, 52.52928846823373 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.669742909344135, 52.529198596518491 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669742333710931, 52.529108724801389 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669741758080422, 52.529018853082476 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669741182452603, 52.528928981361695 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669740606827482, 52.528839109639122 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669740031205053, 52.528749237914688 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669739455585318, 52.528659366188421 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669738879968274, 52.528569494460349 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669738304353925, 52.528479622730416 ] } }, -{ "type": "Feature", "properties": { "lutum": 17 }, "geometry": { "type": "Point", "coordinates": [ 5.66973772874227, 52.528389750998656 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.66973715313331, 52.528299879265084 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669736577527043, 52.528210007529651 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669736001923469, 52.528120135792392 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669735426322587, 52.528030264053307 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669890846291222, 52.529288117076518 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669890270355278, 52.529198245362174 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.669889694422031, 52.529108373646018 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669889118491477, 52.529018501928007 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.669888542563618, 52.528928630208178 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.669887966638455, 52.528838758486494 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.669887390715987, 52.528748886762969 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.669886814796213, 52.528659015037654 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669886238879136, 52.528569143310477 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669885662964753, 52.528479271581489 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669885087053065, 52.528389399850646 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669884511144072, 52.528299528117998 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.669883935237774, 52.528209656383481 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.66988335933417, 52.528119784647131 ] } }, -{ "type": "Feature", "properties": { "lutum": 18 }, "geometry": { "type": "Point", "coordinates": [ 5.669882783433263, 52.528029912908977 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670038207599717, 52.529287765736285 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670037631363726, 52.529197894022879 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670037055130432, 52.529108022307639 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670036478899838, 52.529018150590574 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670035902671936, 52.528928278871632 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670035326446734, 52.528838407150914 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670034750224229, 52.528748535428306 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670034174004417, 52.528658663703865 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670033597787302, 52.528568791977627 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670033021572887, 52.528478920249526 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670032445361164, 52.528389048519642 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670031869152139, 52.528299176787897 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670031292945811, 52.528209305054318 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670030716742176, 52.528119433318885 ] } }, -{ "type": "Feature", "properties": { "lutum": 19 }, "geometry": { "type": "Point", "coordinates": [ 5.670030140541242, 52.52802956158164 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670186145444245, 52.529377285923772 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670185568905514, 52.52928741421308 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670184992369477, 52.52919754250059 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670184415836139, 52.529107670786267 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670183839305502, 52.529017799070111 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670183262777561, 52.528927927352115 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670182686252317, 52.528838055632299 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670182109729772, 52.528748183910643 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.670181533209922, 52.52865831218714 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670180956692772, 52.528568440461825 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670180380178322, 52.528478568734663 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670179803666567, 52.52838869700566 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670179227157511, 52.528298825274803 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670178650651151, 52.528208953542183 ] } }, -{ "type": "Feature", "properties": { "lutum": 20 }, "geometry": { "type": "Point", "coordinates": [ 5.67017807414749, 52.528119081807688 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670177497646527, 52.528029210071367 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670333507047393, 52.529376934216629 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670332930208612, 52.529287062506896 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670332353372531, 52.529197190795308 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670331776539151, 52.52910731908193 ] } }, -{ "type": "Feature", "properties": { "lutum": 21 }, "geometry": { "type": "Point", "coordinates": [ 5.670331199708469, 52.529017447366684 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670330622880485, 52.528927575649611 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670330046055202, 52.528837703930698 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670329469232617, 52.528747832209973 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670328892412731, 52.528657960487401 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670328315595544, 52.528568088762988 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.67032773878106, 52.528478217036756 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670327161969271, 52.528388345308677 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670326585160184, 52.528298473578765 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670326008353794, 52.52820860184702 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670325431550103, 52.528118730113469 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670324854749113, 52.528028858378079 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.67048086864784, 52.529376582326506 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670480291509012, 52.529286710617704 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670479714372884, 52.529196838907055 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670479137239461, 52.529106967194558 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670478560108735, 52.529017095480263 ] } }, -{ "type": "Feature", "properties": { "lutum": 22 }, "geometry": { "type": "Point", "coordinates": [ 5.670477982980711, 52.5289272237641 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670477405855387, 52.528837352046096 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670476828732764, 52.528747480326295 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.67047625161284, 52.528657608604661 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670475674495618, 52.528567736881193 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670475097381098, 52.528477865155843 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670474520269277, 52.528387993428701 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670473943160156, 52.528298121699713 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670473366053738, 52.52820824996892 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670472788950019, 52.528118378236279 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670472211849, 52.528028506501805 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670628230245586, 52.529376230253412 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670627652806711, 52.529286358545527 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670627075370538, 52.529196486835779 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670626497937069, 52.529106615124242 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670625920506301, 52.52901674341085 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670625343078235, 52.528926871695603 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670624765652872, 52.528836999978552 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670624188230208, 52.528747128259667 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670623610810248, 52.528657256538921 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670623033392991, 52.528567384816377 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670622455978436, 52.528477513091964 ] } }, -{ "type": "Feature", "properties": { "lutum": 23 }, "geometry": { "type": "Point", "coordinates": [ 5.670621878566583, 52.528387641365747 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670621301157429, 52.52829776963771 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.67062072375098, 52.528207897907812 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670620146347232, 52.528118026176088 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670619568946186, 52.528028154442524 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670775591840631, 52.529375877997303 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670775014101707, 52.529286006290334 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670774436365489, 52.529196134581525 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670773858631976, 52.529106262870911 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670773280901165, 52.52901639115845 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670772703173058, 52.52892651944412 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670772125447654, 52.528836647727985 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670771547724952, 52.528746776010017 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670770970004954, 52.528656904290216 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670770392287662, 52.528567032568581 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670769814573072, 52.528477160845142 ] } }, -{ "type": "Feature", "properties": { "lutum": 24 }, "geometry": { "type": "Point", "coordinates": [ 5.670769236861187, 52.528387289119834 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670768659152, 52.528297417392693 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670768081445522, 52.528207545663754 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670767503741745, 52.528117673932925 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670766926040671, 52.528027802200249 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.67092295343297, 52.529375525558216 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.670922375394001, 52.52928565385217 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670921797357738, 52.529195782144292 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670921219324179, 52.529105910434581 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.670920641293326, 52.529016038723007 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670920063265176, 52.528926167009637 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.670919485239732, 52.528836295294447 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670918907216992, 52.52874642357736 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670918329196958, 52.528656551858511 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670917751179629, 52.528566680137807 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.670917173165005, 52.52847680841527 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670916595153083, 52.528386936690872 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.67091601714387, 52.528297064964669 ] } }, -{ "type": "Feature", "properties": { "lutum": 25 }, "geometry": { "type": "Point", "coordinates": [ 5.670915439137358, 52.528207193236646 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.670914861133554, 52.528117321506784 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.670914283132454, 52.528027449775095 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671070315022605, 52.529375172936099 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671069736683591, 52.529285301230992 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671069158347279, 52.529195429524037 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.671068580013677, 52.529105557815264 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671068001682782, 52.529015686104621 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671067423354591, 52.52892581439216 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671066845029107, 52.528835942677908 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.67106626670633, 52.528746070961773 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671065688386258, 52.528656199243834 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671065110068891, 52.528566327524032 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671064531754232, 52.528476455802441 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.67106395344228, 52.528386584078959 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671063375133032, 52.528296712353679 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671062796826493, 52.528206840626574 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671062218522659, 52.528116968897628 ] } }, -{ "type": "Feature", "properties": { "lutum": 26 }, "geometry": { "type": "Point", "coordinates": [ 5.671061640221531, 52.528027097166849 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671217676609535, 52.529374820131018 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671217097970472, 52.52928494842682 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671216519334117, 52.529195076720818 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671215940700469, 52.529105205012932 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67121536206953, 52.529015333303256 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671214783441298, 52.52892546159174 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671214204815774, 52.528835589878348 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67121362619296, 52.528745718163179 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.671213047572849, 52.528655846446135 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671212468955447, 52.528565974727286 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671211890340752, 52.528476103006554 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671211311728769, 52.52838623128406 ] } }, -{ "type": "Feature", "properties": { "lutum": 27 }, "geometry": { "type": "Point", "coordinates": [ 5.67121073311949, 52.528296359559697 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671210154512919, 52.528206487833543 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671209575909057, 52.528116616105528 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671208997307901, 52.528026744375651 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671365038193755, 52.529374467142929 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671364459254646, 52.529284595439655 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671363880318245, 52.529194723734562 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671363301384553, 52.529104852027658 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671362722453573, 52.529014980318877 ] } }, -{ "type": "Feature", "properties": { "lutum": 28 }, "geometry": { "type": "Point", "coordinates": [ 5.671362143525299, 52.528925108608256 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671361564599734, 52.528835236895866 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671360985676879, 52.528745365181578 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671360406756732, 52.528655493465472 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671359827839296, 52.528565621747525 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671359248924568, 52.528475750027788 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671358670012548, 52.528385878306167 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671358091103239, 52.528296006582757 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671357512196637, 52.528206134857442 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671356933292747, 52.528116263130357 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671512399775269, 52.529374113971883 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671511820536112, 52.529284242269519 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671511241299665, 52.529194370565364 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67151066206593, 52.52910449885934 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671510082834905, 52.529014627151504 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.67150950360659, 52.528924755441842 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671508924380984, 52.528834883730326 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671508345158092, 52.528745012016955 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671507765937908, 52.528655140301815 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671507186720435, 52.528565268584806 ] } }, -{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671506607505673, 52.528475396865979 ] } }, -{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671506028293621, 52.528385525145282 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.67150544908428, 52.528295653422774 ] } }, -{ "type": "Feature", "properties": { "lutum": 29 }, "geometry": { "type": "Point", "coordinates": [ 5.671504869877649, 52.52820578169846 ] } }, -{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671504290673729, 52.528115909972264 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671658602278375, 52.52919401721315 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671658022744594, 52.529104145508029 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.671657443213525, 52.529014273801138 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.67165686368517, 52.528924402092414 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671656284159525, 52.52883453038185 ] } }, -{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671655704636593, 52.528744658669403 ] } }, -{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671655125116374, 52.528654786955173 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671654545598865, 52.528564915239095 ] } }, -{ "type": "Feature", "properties": { "lutum": 31 }, "geometry": { "type": "Point", "coordinates": [ 5.671653966084067, 52.528475043521155 ] } }, -{ "type": "Feature", "properties": { "lutum": 30 }, "geometry": { "type": "Point", "coordinates": [ 5.671653386571983, 52.528385171801425 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.67165280706261, 52.528295300079861 ] } }, -{ "type": "Feature", "properties": { "lutum": 32 }, "geometry": { "type": "Point", "coordinates": [ 5.67165222755595, 52.528205428356415 ] } }, -{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671651648051999, 52.528115556631185 ] } }, -{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671800165038228, 52.528294946553906 ] } }, -{ "type": "Feature", "properties": { "lutum": 33 }, "geometry": { "type": "Point", "coordinates": [ 5.671799585231536, 52.528205074831419 ] } }, -{ "type": "Feature", "properties": { "lutum": 34 }, "geometry": { "type": "Point", "coordinates": [ 5.671799005427561, 52.528115203107113 ] } } -] -} diff --git a/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.zip b/FarmmapsPoten_AVRapi/Data/PlantingSampleDataLutum.zip deleted file mode 100644 index 2c83115cb64bab668290cae81db59ccfceafedec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5865 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 L2ZfRr{x$m_igikE diff --git a/FarmmapsPoten_AVRapi/FarmmapsPoten_AVRapi.csproj b/FarmmapsPoten_AVRapi/FarmmapsPoten_AVRapi.csproj deleted file mode 100644 index edb3d3c..0000000 --- a/FarmmapsPoten_AVRapi/FarmmapsPoten_AVRapi.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - netcoreapp3.1 - - - - - Always - - - Always - - - PreserveNewest - - - - - - - - diff --git a/FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs b/FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs deleted file mode 100644 index 0bb2876..0000000 --- a/FarmmapsPoten_AVRapi/Models/PotenInputAVR.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Newtonsoft.Json.Linq; - -namespace FarmmapsPoten_AVRapi.Models -{ - public class PotenInputAVR - { - 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 bool UseShadow { get; set; } - public bool ConvertToCountPerArea { get; set; } - public string Rijbreedte_m { get; set; } - public JObject GeometryJson { get; set; } - - public bool GenerateTaskmap { get; set; } - public string CellWidth { get; set; } - public string CellHeight { get; set; } - public JObject StartPoint { get; set; } - public JObject EndPoint { get; set; } - public string Angle { get; set; } - - } -} \ No newline at end of file diff --git a/FarmmapsPoten_AVRapi/PotenApplicationAVR.cs b/FarmmapsPoten_AVRapi/PotenApplicationAVR.cs deleted file mode 100644 index 295ad21..0000000 --- a/FarmmapsPoten_AVRapi/PotenApplicationAVR.cs +++ /dev/null @@ -1,228 +0,0 @@ -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_AVRapi.Models; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using static FarmmapsApiSamples.Constants; - - -namespace FarmmapsVRApoten -{ - public class PotenApplicationAVR : IApplication - { - private const string DownloadFolder = "Downloads"; - - private readonly ILogger _logger; - private readonly FarmmapsApiService _farmmapsApiService; - private readonly PotenServiceAVR _potenService; - private readonly GeneralService _generalService; - - - public PotenApplicationAVR(ILogger logger, FarmmapsApiService farmmapsApiService, - GeneralService generalService, PotenServiceAVR potenService) - { - _logger = logger; - _farmmapsApiService = farmmapsApiService; - _generalService = generalService; - _potenService = potenService; - } - - public async Task RunAsync() - { - // read field data from separate json file - var VRAPotenInputJson = File.ReadAllText("PotenInputAVR.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, PotenInputAVR input) - { - var meanDensity = input.MeanDensity; - var variation = input.Variation; - var fieldName = input.FieldName; - bool useShadow = input.UseShadow; - bool convertToCountPerArea = input.ConvertToCountPerArea; - string rijBreedte_m = input.Rijbreedte_m; - - 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 if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) - { - _logger.LogInformation("input = tiff data"); - var dataPath = Path.Combine("Data", input.File); - geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, - Path.GetFileNameWithoutExtension(input.File)); - - if (geotiffItem == null) - { - _logger.LogError("Could not find item for uploaded data"); - return; - } - } - 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."); - - - // if convertToCountPerArea == True, than recalculate pootafstand in cm to # of poters/m2 from the geotiffItem with the use of the zoneringsTask - if (convertToCountPerArea) - { - applianceMapItem = - await _potenService.ConvertToCountPerAreaTroughZonering(cropfieldItem, applianceMapItem, input.Rijbreedte_m); - - } - - - //Appliancemap (GEOTIFF) TO Taskmap - if (input.GenerateTaskmap == true) - { - _logger.LogInformation($"Converting geotiff to taskmap"); - var taskmap = await _generalService.CreateTaskmap(cropfieldItem, applianceMapItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - input.EndPoint.ToString(Formatting.None), input.Angle); - 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_AVRapi/PotenInputAVR.json b/FarmmapsPoten_AVRapi/PotenInputAVR.json deleted file mode 100644 index 757844a..0000000 --- a/FarmmapsPoten_AVRapi/PotenInputAVR.json +++ /dev/null @@ -1,41 +0,0 @@ -[ - { - "File": "PlantingSampleDataLutum.zip", - "OutputFileName": "20210216_SampleData_CovertArea", - "FieldName": "lutum", - "PlantingYear": 2021, - "MeanDensity": "30", - "Variation": "20", - "UseShadow": false, - "ConvertToCountPerArea": true, - "Rijbreedte_m": "0.75", //as string - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 5.66886041703652044, 52.52929999060298627 ], - [ 5.6716230923214912, 52.52946316399909676 ], - [ 5.67185376229668581, 52.5280565894154563 ], - [ 5.66903207841337231, 52.52790646510525363 ], - [ 5.66886041703652044, 52.52929999060298627 ] - ] - ] - }, - - // if taskmap is not generated, output is only geotiff - "GenerateTaskmap": true, - "CellWidth": "3", - "CellHeight": "10", - "StartPoint": { - "type": "Point", - "coordinates": [ 5.66886041703652044, 52.52929999060298627 ] - }, - "EndPoint": { - "type": "Point", - "coordinates": [ 5.6716230923214912, 52.52946316399909676 ] - } // if no angle - - //"Angle": "317.0" // if no endpoint - } - -] diff --git a/FarmmapsPoten_AVRapi/PotenServiceAVR.cs b/FarmmapsPoten_AVRapi/PotenServiceAVR.cs deleted file mode 100644 index 42ec93e..0000000 --- a/FarmmapsPoten_AVRapi/PotenServiceAVR.cs +++ /dev/null @@ -1,120 +0,0 @@ -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 PotenServiceAVR - { - private readonly ILogger _logger; - private readonly FarmmapsApiService _farmmapsApiService; - private readonly GeneralService _generalService; - - public PotenServiceAVR(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); - _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; - - } - - - // Extra task making use of the zonering task to convert the planting distance in cm to number of seeds per m2 !!! mind the hardcoded values !!! - public async Task ConvertToCountPerAreaTroughZonering(Item cropfieldItem, Item geotiffItem, string rijBreedte_m) - { - var zoneringTaskRequest = new TaskRequest() { TaskType = VRAZONERING_TASK }; - zoneringTaskRequest.attributes["formula"] = $"((100/[0])/{rijBreedte_m})"; - zoneringTaskRequest.attributes["output"] = "{\"Name\":\"CountPerAreaConversion\",\"Quantity\":\"CountPerArea\",\"Unit\":\"#/m2\"}"; - zoneringTaskRequest.attributes["inputs"] = $"[{{\"ItemCode\":\"{geotiffItem.Code}\",\"LayerName\":\"Appliancemap\"}}]"; - - var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, zoneringTaskRequest); - - await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => - { - var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); - _logger.LogInformation($"Waiting on convertion to Count per area through zoneringTast; 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 applianceMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, - GEOTIFF_PROCESSED_ITEMTYPE, "CountPerAreaConversion", - i => i.Updated >= itemTask.Finished.GetValueOrDefault(DateTime.UtcNow) && - i.Name.ToLower().Contains("CountPerAreaConversion".ToLower())); - - if (applianceMapItem == null) - { - _logger.LogError("Could not find the converted to count per area geotiff child item under cropfield"); - return null; - } - - return applianceMapItem; - } - - - } -} - - diff --git a/FarmmapsPoten_AVRapi/appsettings.json b/FarmmapsPoten_AVRapi/appsettings.json deleted file mode 100644 index 3e75982..0000000 --- a/FarmmapsPoten_AVRapi/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Authority": "https://accounts.test.farmmaps.eu/", - "Endpoint": "https://test.farmmaps.eu/", - "BasePath": "api/v1", - "DiscoveryEndpointUrl": "https://accounts.test.farmmaps.eu/.well-known/openid-configuration", - "RedirectUri": "http://example.nl/api", - "ClientId": "", - "ClientSecret": "", - "Scopes": [ "api" ] -} \ No newline at end of file From 89d2af594411b06ff274086158646e5dc7e10785 Mon Sep 17 00:00:00 2001 From: Riepma Date: Mon, 12 Apr 2021 17:01:17 +0200 Subject: [PATCH 21/35] updated poten&nbs, both work properly again --- FarmmapsApi/Services/GeneralService.cs | 11 +- FarmmapsApiSamples.sln | 6 - .../HaulmkillingApplication.cs | 4 +- FarmmapsHaulmkilling/HaulmkillingService.cs | 33 +- .../Models/HaulmkillingAgent.cs | 25 +- FarmmapsNbs/InputData-NBS.json | 41 -- FarmmapsNbs/Models/NitrogenInput.cs | 6 +- FarmmapsNbs/NbsApplication.cs | 136 ++++--- FarmmapsNbs/NitrogenInput.json | 367 ++++-------------- .../Data/ClaassenVRA_Kruising_Lut.zip | Bin 59959 -> 0 bytes ...assenVRA_Kruising_LutEmptyPointsErased.zip | Bin 59582 -> 0 bytes ...sing_LutEmptyPointsErased_largerExtent.zip | Bin 59796 -> 0 bytes .../Data/ClaassenVRA_Kruising_PG.zip | Bin 4467 -> 0 bytes .../Data/PlantingSampleDataLutum_TEST2.zip | Bin 5991 -> 0 bytes FarmmapsPoten/Data/PotenInput.json | 57 --- FarmmapsPoten/PotenApplication.cs | 15 +- FarmmapsPoten/PotenInput.json | 63 +-- 17 files changed, 220 insertions(+), 544 deletions(-) delete mode 100644 FarmmapsNbs/InputData-NBS.json delete mode 100644 FarmmapsPoten/Data/ClaassenVRA_Kruising_Lut.zip delete mode 100644 FarmmapsPoten/Data/ClaassenVRA_Kruising_LutEmptyPointsErased.zip delete mode 100644 FarmmapsPoten/Data/ClaassenVRA_Kruising_LutEmptyPointsErased_largerExtent.zip delete mode 100644 FarmmapsPoten/Data/ClaassenVRA_Kruising_PG.zip delete mode 100644 FarmmapsPoten/Data/PlantingSampleDataLutum_TEST2.zip delete mode 100644 FarmmapsPoten/Data/PotenInput.json diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 5a2e17c..b122253 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -37,9 +37,10 @@ namespace FarmmapsApi.Services return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest); } - public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName, string fieldGeomJson = null) { + 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, fieldGeomJson, + var result = await _farmmapsApiService.UploadFile(filePath, root.Code, geoJsonString, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); if (result.Progress.Status == UploadStatus.Failed) @@ -50,9 +51,9 @@ namespace FarmmapsApi.Services i.Name.ToLower().Contains(itemName.ToLower())); } - public async Task UploadZipWithShapeAsync(UserRoot root, string filePath, string itemName, string fieldGeomJson = null) { - var startUpload = DateTime.UtcNow; - var result = await _farmmapsApiService.UploadFile(filePath, root.Code, fieldGeomJson, + 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) diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index 62501d5..68171a4 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -23,8 +23,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmMapsBlight", "FarmMapsB EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsZonering", "FarmmapsZonering\FarmmapsZonering.csproj", "{91A58C4A-4A80-4079-B43D-9B851206194F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsPoten_AVRapi", "FarmmapsPoten_AVRapi\FarmmapsPoten_AVRapi.csproj", "{E220AA18-D735-4B07-859B-C0DA11CD1E8B}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,10 +57,6 @@ Global {91A58C4A-4A80-4079-B43D-9B851206194F}.Debug|Any CPU.Build.0 = Debug|Any CPU {91A58C4A-4A80-4079-B43D-9B851206194F}.Release|Any CPU.ActiveCfg = Release|Any CPU {91A58C4A-4A80-4079-B43D-9B851206194F}.Release|Any CPU.Build.0 = Release|Any CPU - {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E220AA18-D735-4B07-859B-C0DA11CD1E8B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FarmmapsHaulmkilling/HaulmkillingApplication.cs b/FarmmapsHaulmkilling/HaulmkillingApplication.cs index 272873d..bf57ac8 100644 --- a/FarmmapsHaulmkilling/HaulmkillingApplication.cs +++ b/FarmmapsHaulmkilling/HaulmkillingApplication.cs @@ -132,11 +132,11 @@ namespace FarmmapsHaulmkilling } var selectedAgent = agents[0]; - var selectedOption = selectedAgent.SupportedOptions[0]; + var selectedOption = selectedAgent.ValidOptions[0]; _logger.LogInformation("Calculating application map"); var applianceMapItem = await _haulmkillingService.CalculateApplicationMapAsync(cropfieldItem, - firstSatelliteItem, inputType, selectedAgent.Name, selectedOption); + firstSatelliteItem, inputType, selectedOption, selectedAgent); if (applianceMapItem == null) { return; diff --git a/FarmmapsHaulmkilling/HaulmkillingService.cs b/FarmmapsHaulmkilling/HaulmkillingService.cs index 98bd4d9..fec48a0 100644 --- a/FarmmapsHaulmkilling/HaulmkillingService.cs +++ b/FarmmapsHaulmkilling/HaulmkillingService.cs @@ -1,3 +1,4 @@ + using System; using System.Collections.Generic; using System.Linq; @@ -6,6 +7,7 @@ using FarmmapsApi.Models; using FarmmapsApi.Services; using FarmmapsHaulmkilling.Models; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using static FarmmapsApi.Extensions; using static FarmmapsApiSamples.Constants; @@ -24,34 +26,31 @@ namespace FarmmapsHaulmkilling _farmmapsApiService = farmmapsApiService; _generalService = generalService; } - + /// /// Gets the list of available haulmkilling agents /// /// List of haulmkilling agents public async Task> GetHaulmkillingAgents() { - var itemType = "vnd.farmmaps.package.vra.haulmkilling"; - var vraHerbicideDataItems = await _farmmapsApiService.GetItemsAsync(string.Empty, itemType); + var itemType = "vnd.farmmaps.itemtype.codelist.fm005"; + var haulmkillingAgentItems = await _farmmapsApiService.GetItemsAsync(string.Empty, itemType); - var item = vraHerbicideDataItems.FirstOrDefault(); - if (item == null) - return null; - - return item.Data.ContainsKey("agents") ? item.Data["agents"].ToObject>() : null; + return haulmkillingAgentItems.Select(item => item.Data.ToObject()) + .ToList(); } - + /// /// Creates an haulmkilling application map /// /// The context cropfield item to use /// The geotiff item to use /// WDVI or NDVI - /// One of the available agents + /// code of one of the available agents /// One of the available options /// Haulmkilling application map item public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem, string inputType, - string agentName, string selectedOption) + string selectedOption, HaulmkillingAgent agent) { var taskRequest = new TaskRequest() { @@ -59,7 +58,7 @@ namespace FarmmapsHaulmkilling }; taskRequest.attributes["inputCode"] = inputItem.Code; taskRequest.attributes["inputType"] = inputType; - taskRequest.attributes["agentName"] = agentName; + taskRequest.attributes["agentCode"] = agent.Code; taskRequest.attributes["selectedOption"] = selectedOption; taskRequest.attributes["minPercentile"] = "0.0"; @@ -71,7 +70,7 @@ namespace FarmmapsHaulmkilling if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); - + var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, taskCode); if (itemTask.State == ItemTaskState.Error) { @@ -79,12 +78,12 @@ namespace FarmmapsHaulmkilling return null; } - var itemName = $"VRAHaulmkilling {agentName}"; + var itemName = $"VRAHaulmkilling {agent.Label}"; var applianceMapItem = await _generalService.FindChildItemAsync(cropfieldItem.Code, - GEOTIFF_PROCESSED_ITEMTYPE, itemName, + GEOTIFF_PROCESSED_ITEMTYPE, itemName, i => i.Updated >= itemTask.Finished && i.Name.ToLower().Contains(itemName.ToLower())); - + if (applianceMapItem == null) { _logger.LogError("Could not find the VRAHaulmkilling geotiff child item under cropfield"); @@ -94,4 +93,4 @@ namespace FarmmapsHaulmkilling return applianceMapItem; } } -} \ No newline at end of file +} diff --git a/FarmmapsHaulmkilling/Models/HaulmkillingAgent.cs b/FarmmapsHaulmkilling/Models/HaulmkillingAgent.cs index 929197a..4f645f6 100644 --- a/FarmmapsHaulmkilling/Models/HaulmkillingAgent.cs +++ b/FarmmapsHaulmkilling/Models/HaulmkillingAgent.cs @@ -5,26 +5,11 @@ namespace FarmmapsHaulmkilling.Models { public class HaulmkillingAgent { - public string Name { get; set; } - public List SupportedOptions { get; set; } - public Dictionary> Values { get; set; } + public string Cl { get; set; } + public string Code { get; set; } + public string Label { get; set; } + public string Options { get; set; } - public HaulmkillingAgentValue? GetAgentValue(string inputName, string optionKey) - { - if (!Values.ContainsKey(inputName)) - return null; - - var agentDataValues = Values[inputName]; - return agentDataValues.FirstOrDefault(v => v.Option.Equals(optionKey)); - } - } - - public struct HaulmkillingAgentValue - { - public string Option { get; set; } - public float Min { get; set; } - public float Max { get; set; } - public float FMul { get; set; } - public float FExp { get; set; } + public List ValidOptions => Options.Split(',').ToList(); } } \ No newline at end of file diff --git a/FarmmapsNbs/InputData-NBS.json b/FarmmapsNbs/InputData-NBS.json deleted file mode 100644 index 6d5fcac..0000000 --- a/FarmmapsNbs/InputData-NBS.json +++ /dev/null @@ -1,41 +0,0 @@ -[ - { - "UseCreatedCropfield": false, - "storeStatistics": false, - "file": "20210308 WDVI_Harpreet Singh.tif", - "inputVariable": "wdvi", - //"InputLayerName": "Band 1", - "outputFileName": "2021.03.08.Hapreet_Singh_wdvi08_03", - "plantingDate": "2020-04-15", - "measurementDate": "2020-06-27", - "potatoPurposeType": "consumption", - "targetYield": 27, - "fieldName": "Mahindra-Hapreet-Singh", - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 75.929090780177305, 31.639314978348551 ], - [ 75.931353489378182, 31.639409065494881 ], - [ 75.931432810729120, 31.638956841735819 ], - [ 75.929072617175663, 31.638879365370279 ], - [ 75.929090780177305, 31.639314978348551 ] - ] - ] - }, - "GenerateTaskmap": true, - "CellWidth": "3", - "CellHeight": "20", - "Centered": false, - "StartPoint": { - "type": "Point", - "coordinates": [ 75.931353489378182, 31.639409065494881 ] - }, - "EndPoint": { - "type": "Point", - "coordinates": [ 75.929090780177305, 31.639314978348551 ] - } // if no angle - - //"Angle": "317.0" // if no endpoint - } - ] \ No newline at end of file diff --git a/FarmmapsNbs/Models/NitrogenInput.cs b/FarmmapsNbs/Models/NitrogenInput.cs index d82fdc2..7441de1 100644 --- a/FarmmapsNbs/Models/NitrogenInput.cs +++ b/FarmmapsNbs/Models/NitrogenInput.cs @@ -18,12 +18,16 @@ namespace FarmmapsNbs.Models public string fieldName{ get; set; } public bool storeSatelliteStatistics { get; set; } public bool GenerateTaskmap { get; set; } + public string OutputType { get; set; } + public string DdiCode { get; set; } public string CellWidth { get; set; } public string CellHeight { get; set; } - public bool Centered { get; set; } + public string Centered { get; set; } public JObject StartPoint { get; set; } public JObject EndPoint { get; set; } public string Angle { get; set; } + public string Precision { get; set; } + public string MaximumClasses { get; set; } } diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index d7a5ab2..196f9f4 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -37,9 +37,9 @@ namespace FarmmapsNbs public async Task RunAsync() { - var nitrogenInputJson = File.ReadAllText("InputData-NBS.json"); //NitrogenInput.json + var nitrogenInputJson = File.ReadAllText("NitrogenInput.json"); List nitrogenInputs = JsonConvert.DeserializeObject>(nitrogenInputJson); - + if (!Directory.Exists(DownloadFolder)) Directory.CreateDirectory(DownloadFolder); @@ -62,9 +62,9 @@ namespace FarmmapsNbs private async Task Process(List roots, NitrogenInput input) { - + // !!specify if you are using an already created cropfield: - bool useCreatedCropfield = input. UseCreatedCropfield; + bool useCreatedCropfield = input.UseCreatedCropfield; var plantingDate = input.PlantingDate; var FieldName = input.fieldName; bool StoreStatistics = input.storeSatelliteStatistics; @@ -86,7 +86,7 @@ namespace FarmmapsNbs _logger.LogError("Could not find a needed root item"); return; } - + // Use already created cropfield or create new one Item cropfieldItem; if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) @@ -105,12 +105,14 @@ namespace FarmmapsNbs var geotiffItem = (Item)null; - // No file input, use most recent satellite image - if (string.IsNullOrEmpty(input.File)) { + // If no input file is specified, use most recent satellite image + if (string.IsNullOrEmpty(input.File)) + { _logger.LogInformation("No specific data given, retrieving most recent satellite image"); // check if satellite task not yet done, do here and save taskcode - if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) { + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) + { var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); _settings.SatelliteTaskCode = satelliteTaskCode; SaveSettings(settingsfile); @@ -126,14 +128,17 @@ namespace FarmmapsNbs //Console.WriteLine($"Satellite image statistics for band {satelliteBand}: {satelliteStatistics}"); //Store data to csv - if (StoreStatistics==true) { - var SatelliteFile = $"C:\\Akkerweb\\DataSatellite_{FieldName}.csv"; + if (StoreStatistics == true) + { + var SatelliteFile = $"\\Downloads\\DataSatellite_{FieldName}.csv"; var NewLineField = $"\"Field\":{FieldName}" + Environment.NewLine; var NewLineDate = $"\"date\":{satalliteItem.DataDate}" + Environment.NewLine; var i = 0; - foreach (var item in satelliteStatistics) { + foreach (var item in satelliteStatistics) + { //var NewLines2; - if (i == 0) { + if (i == 0) + { File.AppendAllText(SatelliteFile, NewLineDate); i++; } @@ -145,7 +150,8 @@ namespace FarmmapsNbs // must be wdvi[1] var inputType = (satalliteItem.Data["layers"] as JArray)?[1]["name"].ToString(); - if (string.IsNullOrEmpty(inputType)) { + if (string.IsNullOrEmpty(inputType)) + { _logger.LogError("Could not get the input type name from the satellite item"); return; } @@ -167,34 +173,39 @@ namespace FarmmapsNbs // (geo)tiff input: - else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) { + else if (input.File.Contains(".tif") || input.File.Contains(".geotiff")) + { _logger.LogInformation("input = tiff data"); var dataPath = Path.Combine("Data", input.File); geotiffItem = await _generalService.UploadDataAsync(uploadedRoot, GEOTIFF_PROCESSED_ITEMTYPE, dataPath, Path.GetFileNameWithoutExtension(input.File)); - if (geotiffItem == null) { + if (geotiffItem == null) + { _logger.LogError("Could not find item for uploaded data"); return; } } // json/shape input - else { + 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)); + await _generalService.UploadDataAsync(uploadedRoot, SHAPE_PROCESSED_ITEMTYPE, dataPath, Path.GetFileNameWithoutExtension(input.File), input.GeometryJson.ToString(Formatting.None)) : + await _generalService.UploadZipWithShapeAsync(uploadedRoot, dataPath, Path.GetFileNameWithoutExtension(input.File), input.GeometryJson.ToString(Formatting.None)); - if (shapeItem == null) { + if (shapeItem == null) + { _logger.LogError("Could not find item for uploaded data"); return; } _logger.LogInformation($"Converting shape to geotiff"); geotiffItem = await _generalService.ShapeToGeotiff(shapeItem); - if (geotiffItem == null) { + if (geotiffItem == null) + { _logger.LogError("Something went wrong with shape to geotiff transformation"); return; } @@ -226,7 +237,8 @@ namespace FarmmapsNbs var targetNData = await _nitrogenService.CalculateTargetN(cropfieldItem, targetNItem, plantingDate, measurementDate, input.PotatoPurposeType, input.TargetYield); - if (targetNData == null) { + if (targetNData == null) + { _logger.LogError("Something went wrong with TargetN calculation"); return; } @@ -237,14 +249,15 @@ namespace FarmmapsNbs //_logger.LogInformation($"TargetN adjusted: {targetNData.TargetN}"); var targetNDataPath = Path.Combine(DownloadFolder, $"{input.OutputFileName}.targetn.json"); - + await File.WriteAllTextAsync(targetNDataPath, JsonConvert.SerializeObject(targetNData, Formatting.Indented)); _logger.LogInformation("Calculating uptake map"); var uptakeMapItem = await _nitrogenService.CalculateUptakeMap(cropfieldItem, geotiffItem, plantingDate, measurementDate, input.InputVariable, input.InputLayerName); - if (uptakeMapItem == null) { + if (uptakeMapItem == null) + { _logger.LogError("Something went wrong with creating the uptakeMap"); return; } @@ -252,57 +265,79 @@ 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("UptakeMap downloaded"); _logger.LogInformation("Calculating application map"); - var applicationMapItem = + var applianceMapItem = await _nitrogenService.CalculateApplicationMap(cropfieldItem, geotiffItem, plantingDate, measurementDate, input.InputVariable, targetNData.TargetN, input.InputLayerName); - if (applicationMapItem == null) { + if (applianceMapItem == null) + { _logger.LogError("Something went wrong with creating the applicationMap"); return; } _logger.LogInformation("Downloading application map"); - await _farmmapsApiService.DownloadItemAsync(applicationMapItem.Code, + await _farmmapsApiService.DownloadItemAsync(applianceMapItem.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; + var tiffItem = applianceMapItem; - if (tiffItem == null) { + 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; - //} - //ApplicationMap (GEOTIFF) To Taskmap - _logger.LogInformation($"Converting geotiff to taskmap"); - var taskmap = await _generalService.CreateTaskmap(cropfieldItem, tiffItem, input.CellWidth, input.CellHeight, input.StartPoint.ToString(Formatting.None), - input.Centered.ToString(), input.EndPoint.ToString(Formatting.None), input.Angle); - - if (taskmap == null) + if (input.GenerateTaskmap) { - _logger.LogError("Something went wrong with geotiff to shape transformation"); - return; + //GEOTIFF TO Taskmap + _logger.LogInformation($"Converting geotiff to taskmap"); + + var taskmap = (Item)null; + if (input.OutputType == "isoxml") + { + + if (input.DdiCode == null) + { + _logger.LogInformation("DDi not given. Using expected identifiers"); + input.DdiCode = input.DdiCode = "0006"; + + } + taskmap = await _generalService.CreateTaskmap(cropfieldItem: cropfieldItem, tiffItem: applianceMapItem, outputType: input.OutputType, cellWidth: input.CellWidth, + cellHeight: input.CellHeight, startPoint: input.StartPoint.ToString(Formatting.None), ddiCode: input.DdiCode, centered: input.Centered, + endPoint: input.EndPoint.ToString(Formatting.None), angle: input.Angle, precision: input.Precision, + cropTypeName: null, costumerName: null, ProductGroupName: null, productName: null, resolution: null, unitScale: null, maximumClasses: input.MaximumClasses); + + } + else + { + taskmap = await _generalService.CreateTaskmap(cropfieldItem: cropfieldItem, tiffItem: applianceMapItem, outputType: input.OutputType, cellWidth: input.CellWidth, + cellHeight: input.CellHeight, startPoint: input.StartPoint.ToString(Formatting.None), centered: input.Centered, + endPoint: input.EndPoint.ToString(Formatting.None), angle: input.Angle, precision: input.Precision, maximumClasses: input.MaximumClasses); + } + + + + if (taskmap == null) + { + _logger.LogError("Something went wrong with geotiff to taskmap transformation"); + return; + } + + _logger.LogInformation("Downloading taskmap"); + await _farmmapsApiService.DownloadItemAsync(taskmap.Code, + Path.Combine(DownloadFolder, $"{input.OutputFileName}.taskmap.zip")); } - _logger.LogInformation("Downloading taskmap"); - await _farmmapsApiService.DownloadItemAsync(taskmap.Code, - Path.Combine(DownloadFolder, $"{input.OutputFileName}.taskmap.zip")); } - // Functions to save previously created cropfields private void LoadSettings(string file) { @@ -325,7 +360,8 @@ namespace FarmmapsNbs var json = JsonConvert.SerializeObject(_settings); File.WriteAllText(file, json); } - private void SaveInfo(string file) { + private void SaveInfo(string file) + { if (_settings == null) return; @@ -334,5 +370,5 @@ namespace FarmmapsNbs } - } - } \ No newline at end of file + } +} diff --git a/FarmmapsNbs/NitrogenInput.json b/FarmmapsNbs/NitrogenInput.json index 6e2afe5..416d05b 100644 --- a/FarmmapsNbs/NitrogenInput.json +++ b/FarmmapsNbs/NitrogenInput.json @@ -1,310 +1,105 @@ [ - { - "file": "Scan_1_20190605.json", - "inputVariable": "irmi", - "outputFileName": "vranbs1", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-05", - "potatoPurposeType": "consumption", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] - }, - //{ - //"file": "[...].json", - //"inputVariable": "wdvi", - //"outputFileName": "20201211_Mahindra", - //"plantingDate": "2020-11-25", - //"measurementDate": "2020-12-08", - //"potatoPurposeType": "consumption", - //"targetYield": 45, - //"geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 75.4652734163369, 31.26328617861426 ], - // [ 75.46527659380338, 31.26437743805827 ], - // [ 75.46494267602188, 31.26438166160194 ], - // [ 75.46493257618651, 31.26462192072676 ], - // [ 75.46438242791261, 31.26462132638865 ], - // [ 75.46438225728252, 31.26334267015003 ], - // [ 75.46448543502072, 31.26333463263533 ], - // [ 75.46448842093658, 31.26302891147988 ], - // [ 75.46507631089699, 31.26299589154944 ], - // [ 75.46509039016291, 31.26329023051392 ], - // [ 75.4652734163369, 31.26328617861426 ] - // ] - // ] - //} - //} - - //}, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - // "outputFileName": "vranbs2", - // "plantingDate": "2019-04-18", - // "measurementDate": "2019-06-05", - // "potatoPurposeType": "starch", - // "targetYield": 45, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 3.40843828875524, 50.638966444680605 ], - // [ 3.408953272886064, 50.639197789621612 ], - // [ 3.409242951459603, 50.639469958681836 ], - // [ 3.409328782148028, 50.639612846807708 ], - // [ 3.409457528180712, 50.639789755314411 ], - // [ 3.409639918393741, 50.640014292074966 ], - // [ 3.409833037442765, 50.640211611372706 ], - // [ 3.410069071836049, 50.640395321698435 ], - // [ 3.410380208081761, 50.640572227259661 ], - // [ 3.410605513638958, 50.640715112034222 ], - // [ 3.411925160474145, 50.641177783561204 ], - // [ 3.411935889310142, 50.640728720085136 ], - // [ 3.412590348309737, 50.63948356709389 ], - // [ 3.413244807309242, 50.638224772339846 ], - // [ 3.413400375432099, 50.637901562841307 ], - // [ 3.413539850300779, 50.637449065809889 ], - // [ 3.413475477284437, 50.637418445552932 ], - // [ 3.40999396998362, 50.637449065810451 ], - // [ 3.409940325803365, 50.638102293212661 ], - // [ 3.409575545377398, 50.638483338338325 ], - // [ 3.409060561246574, 50.638707881340494 ], - // [ 3.40843828875524, 50.638966444680605 ] - // ] - // ] - // } - //}, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - { - "file": "", // keep emptpy to use satellite image - "inputVariable": "wdvi", - "InputLayerName": "wdvi", - "outputFileName": "rtest1", - "plantingDate": "2020-05-01", - "measurementDate": "2020-06-14", + { + "file": "Scan_1_20190605.json", + "inputVariable": "irmi", + "inputLayerName": "", + "outputFileName": "vranbs1", + "plantingDate": "2019-04-18", + "measurementDate": "2019-06-05", "potatoPurposeType": "consumption", - "targetYield": 60, + "targetYield": 45, "geometryJson": { "type": "Polygon", "coordinates": [ [ - [ 4.960707146896585, 52.800583669708487 ], - [ 4.960645975538824, 52.800470217610922 ], - [ 4.962140695752897, 52.799177147194797 ], - [ 4.967523821195745, 52.801502400041208 ], - [ 4.966336768950911, 52.802543735879809 ], - [ 4.961711880764330, 52.801009996856429 ], - [ 4.960707146896585, 52.800583669708487 ] + [ 3.40843828875524, 50.638966444680605 ], + [ 3.408953272886064, 50.639197789621612 ], + [ 3.409242951459603, 50.639469958681836 ], + [ 3.409328782148028, 50.639612846807708 ], + [ 3.409457528180712, 50.639789755314411 ], + [ 3.409639918393741, 50.640014292074966 ], + [ 3.409833037442765, 50.640211611372706 ], + [ 3.410069071836049, 50.640395321698435 ], + [ 3.410380208081761, 50.640572227259661 ], + [ 3.410605513638958, 50.640715112034222 ], + [ 3.411925160474145, 50.641177783561204 ], + [ 3.411935889310142, 50.640728720085136 ], + [ 3.412590348309737, 50.63948356709389 ], + [ 3.413244807309242, 50.638224772339846 ], + [ 3.413400375432099, 50.637901562841307 ], + [ 3.413539850300779, 50.637449065809889 ], + [ 3.413475477284437, 50.637418445552932 ], + [ 3.40999396998362, 50.637449065810451 ], + [ 3.409940325803365, 50.638102293212661 ], + [ 3.409575545377398, 50.638483338338325 ], + [ 3.409060561246574, 50.638707881340494 ], + [ 3.40843828875524, 50.638966444680605 ] ] ] - } - } - //, + }, + + "GenerateTaskmap": true, + "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode + "Precision": "2", + "MaximumClasses": "4", + "DdiCode": "0006", + "CellWidth": "3", + "CellHeight": "10", + "Centered": "true", + "StartPoint": { + "type": "Point", + "coordinates": [ 3.409341600000000, 50.638991900000001 ] + }, + "EndPoint": { // if no angle + "type": "Point", + "coordinates": [ 3.411897000000000, 50.638981500000000 ] + } + //"Angle": "317.0" // if no endpoint + + }, + //{ // "file": "", // keep emptpy to use satellite image // "inputVariable": "wdvi", // "InputLayerName": "wdvi", - // "outputFileName": "mtest1", - // "plantingDate": "2020-04-01", - // "measurementDate": "2020-06-24", + // "outputFileName": "rtest1", + // "plantingDate": "2020-05-01", + // "measurementDate": "2020-06-14", // "potatoPurposeType": "consumption", // "targetYield": 60, // "geometryJson": { // "type": "Polygon", // "coordinates": [ // [ - // [ 3.575458869234128, 51.308707885669762 ], - // [ 3.575508957999423, 51.30878478562019 ], - // [ 3.576188404403633, 51.309372997559777 ], - // [ 3.576188872410267, 51.309374219701091 ], - // [ 3.576210290749152, 51.309430091473608 ], - // [ 3.57621266537704, 51.309483685674898 ], - // [ 3.576213477455834, 51.309502027981374 ], - // [ 3.577543136860447, 51.310682367527122 ], - // [ 3.57796629328866, 51.31104321405175 ], - // [ 3.578442479292087, 51.311449273042747 ], - // [ 3.57866702353106, 51.311636072786726 ], - // [ 3.57880446997978, 51.31157117599529 ], - // [ 3.579155910205885, 51.311863542729718 ], - // [ 3.579175814007489, 51.311875435159394 ], - // [ 3.579293885246395, 51.311936532835396 ], - // [ 3.579413896180069, 51.311998649478575 ], - // [ 3.579514543462617, 51.312041110734917 ], - // [ 3.579611760655688, 51.312082118352606 ], - // [ 3.579635115371588, 51.312093949652223 ], - // [ 3.579793143414486, 51.312189437140432 ], - // [ 3.579966991648108, 51.312286148850511 ], - // [ 3.580079704980967, 51.312332458349751 ], - // [ 3.580203717638148, 51.312336471368539 ], - // [ 3.580307101018293, 51.312330239539847 ], - // [ 3.580383836270609, 51.312317097185243 ], - // [ 3.580505207977176, 51.312279163554869 ], - // [ 3.580610387713855, 51.312233723091026 ], - // [ 3.5806309754483, 51.312226093729677 ], - // [ 3.580638516049738, 51.312223727082049 ], - // [ 3.58075536599681, 51.312186990706344 ], - // [ 3.580787745633303, 51.312176820129551 ], - // [ 3.580829682241423, 51.312167665428959 ], - // [ 3.58086828456562, 51.312162614898625 ], - // [ 3.580980493636721, 51.312147935609723 ], - // [ 3.581014352632766, 51.312145662592656 ], - // [ 3.581028980583245, 51.312145592849248 ], - // [ 3.581119189823368, 51.312145103215911 ], - // [ 3.581195330198145, 51.312144693075908 ], - // [ 3.581243537809229, 51.312148741603245 ], - // [ 3.58132480221972, 51.312163110548383 ], - // [ 3.581426517039001, 51.312181089466016 ], - // [ 3.581448095953263, 51.312184910295024 ], - // [ 3.581474337475052, 51.312191038736145 ], - // [ 3.581709405982819, 51.312260068944951 ], - // [ 3.581727319558337, 51.312266163697757 ], - // [ 3.581753583356718, 51.312276407881612 ], - // [ 3.581841655772683, 51.312310743468075 ], - // [ 3.581878851795624, 51.312325234405343 ], - // [ 3.581905889860924, 51.312338260798654 ], - // [ 3.581906254501594, 51.312338472113815 ], - // [ 3.582048236499295, 51.312422439022804 ], - // [ 3.58189849928915, 51.312481944577037 ], - // [ 3.583044298383354, 51.312095780444281 ], - // [ 3.582984006671231, 51.312017283925279 ], - // [ 3.582743535862999, 51.311699343434064 ], - // [ 3.582628599916243, 51.311582190756774 ], - // [ 3.581446834435509, 51.310511259982569 ], - // [ 3.580621864908701, 51.309767270412806 ], - // [ 3.579610575760466, 51.308860440593946 ], - // [ 3.579112608916012, 51.308394999612226 ], - // [ 3.578688808506157, 51.307968441218165 ], - // [ 3.578394256007207, 51.307644098092617 ], - // [ 3.578355980318371, 51.307607614964702 ], - // [ 3.578217977585775, 51.307654179547846 ], - // [ 3.577480636332469, 51.307921035607997 ], - // [ 3.575695560441006, 51.308577167973212 ], - // [ 3.575668643609169, 51.30855384769157 ], - // [ 3.575666204524265, 51.308551734020703 ], - // [ 3.575506397192348, 51.308609906947261 ], - // [ 3.575459139533024, 51.308653178431456 ], - // [ 3.575458869234128, 51.308707885669762 ] + // [ 4.960707146896585, 52.800583669708487 ], + // [ 4.960645975538824, 52.800470217610922 ], + // [ 4.962140695752897, 52.799177147194797 ], + // [ 4.967523821195745, 52.801502400041208 ], + // [ 4.966336768950911, 52.802543735879809 ], + // [ 4.961711880764330, 52.801009996856429 ], + // [ 4.960707146896585, 52.800583669708487 ] // ] // ] + // }, + + // "GenerateTaskmap": true, + // "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode + // "Precision": "2", + // "MaximumClasses": "4", + // "DdiCode": "0006", + // "CellWidth": "3", + // "CellHeight": "10", + // "Centered": "true", + // "StartPoint": { + // "type": "Point", + // "coordinates": [ 4.960707146896585, 52.800583669708487 ] + // }, + // "EndPoint": { // if no angle + // "type": "Point", + // "coordinates": [ 4.961711880764330, 52.801009996856429 ] // } + // //"Angle": "317.0" // if no endpoint //} - //, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - // "InputLayerName": "IRMI", - // "outputFileName": "vranbs3", - // "plantingDate": "2019-04-18", - // "measurementDate": "2019-06-20", - // "potatoPurposeType": "starch", - // "targetYield": 45, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 3.40843828875524, 50.638966444680605 ], - // [ 3.408953272886064, 50.639197789621612 ], - // [ 3.409242951459603, 50.639469958681836 ], - // [ 3.409328782148028, 50.639612846807708 ], - // [ 3.409457528180712, 50.639789755314411 ], - // [ 3.409639918393741, 50.640014292074966 ], - // [ 3.409833037442765, 50.640211611372706 ], - // [ 3.410069071836049, 50.640395321698435 ], - // [ 3.410380208081761, 50.640572227259661 ], - // [ 3.410605513638958, 50.640715112034222 ], - // [ 3.411925160474145, 50.641177783561204 ], - // [ 3.411935889310142, 50.640728720085136 ], - // [ 3.412590348309737, 50.63948356709389 ], - // [ 3.413244807309242, 50.638224772339846 ], - // [ 3.413400375432099, 50.637901562841307 ], - // [ 3.413539850300779, 50.637449065809889 ], - // [ 3.413475477284437, 50.637418445552932 ], - // [ 3.40999396998362, 50.637449065810451 ], - // [ 3.409940325803365, 50.638102293212661 ], - // [ 3.409575545377398, 50.638483338338325 ], - // [ 3.409060561246574, 50.638707881340494 ], - // [ 3.40843828875524, 50.638966444680605 ] - // ] - // ] - // } - //}, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - //} - //, - //{ - // "file": "Scan_1_20190605.zip", - // "inputVariable": "irmi", - // "InputLayerName": "", - // "outputFileName": "vranbs4", - // "plantingDate": "2019-04-18", - // "measurementDate": "2019-07-03", - // "potatoPurposeType": "starch", - // "targetYield": 45, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 3.40843828875524, 50.638966444680605 ], - // [ 3.408953272886064, 50.639197789621612 ], - // [ 3.409242951459603, 50.639469958681836 ], - // [ 3.409328782148028, 50.639612846807708 ], - // [ 3.409457528180712, 50.639789755314411 ], - // [ 3.409639918393741, 50.640014292074966 ], - // [ 3.409833037442765, 50.640211611372706 ], - // [ 3.410069071836049, 50.640395321698435 ], - // [ 3.410380208081761, 50.640572227259661 ], - // [ 3.410605513638958, 50.640715112034222 ], - // [ 3.411925160474145, 50.641177783561204 ], - // [ 3.411935889310142, 50.640728720085136 ], - // [ 3.412590348309737, 50.63948356709389 ], - // [ 3.413244807309242, 50.638224772339846 ], - // [ 3.413400375432099, 50.637901562841307 ], - // [ 3.413539850300779, 50.637449065809889 ], - // [ 3.413475477284437, 50.637418445552932 ], - // [ 3.40999396998362, 50.637449065810451 ], - // [ 3.409940325803365, 50.638102293212661 ], - // [ 3.409575545377398, 50.638483338338325 ], - // [ 3.409060561246574, 50.638707881340494 ], - // [ 3.40843828875524, 50.638966444680605 ] - // ] - // ] - // } - - } - - //} + ] \ No newline at end of file diff --git a/FarmmapsPoten/Data/ClaassenVRA_Kruising_Lut.zip b/FarmmapsPoten/Data/ClaassenVRA_Kruising_Lut.zip deleted file mode 100644 index 7a34c3d95710c46c803684749509858e27d68820..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59959 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
-JZ5tu1 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 diff --git a/FarmmapsPoten/Data/ClaassenVRA_Kruising_LutEmptyPointsErased.zip b/FarmmapsPoten/Data/ClaassenVRA_Kruising_LutEmptyPointsErased.zip deleted file mode 100644 index cc3b1998c4d89bb156a8b6e38a90b7f0e539be18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59582 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 diff --git a/FarmmapsPoten/Data/PlantingSampleDataLutum_TEST2.zip b/FarmmapsPoten/Data/PlantingSampleDataLutum_TEST2.zip deleted file mode 100644 index 3f6591c6a6aff3581ab2191a22ed3b281f6d92a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5991 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�`ePQp_v7R@%@(xAqL( z`}vDPPSVRLkSR@S7yPeA$h8_Z*@rAlOr>1^ouLpDJRS@$a5#nE#JbN@cIa1>ES1*! z6!H};r5`QjDOe)w6^e!qZwj53wu+Wo`3jvjNE@U=k%Fa`CDIA`NWs$G2&XaDw~2k9 z{^jl-NB2Vo8s)rUoUR;a8gDQ#7%A2FwSRT&<4Wa#=){XzP^b6K<}KKE>{fR<)^vw~ zV+8o~lz|ye9S;LZ@7L!B1j#-cAzQK>w48-izQ8|k=!EI7VEUcs2n};@)yuysQa71V zo4mLQDe2}vV_HLXM8nvoU*=_AGi zr($D&4~WUrjYZk*7N_hxM?3Y0mA->qCwvWpaYX+-VYg}6w52Yyr3n6cd0x|3zND7X zY(-DswSwuwFi#$>ODJ;T80zj`>j>*WWtL>byIg6wF+LZ{;PB37s|PYVZ)e_ zTm_FOR2`|?M=C0B@38Cpq0+fRU)dWOaA0&CgdeHd8pd_TuIws!I-&7M!H(2HpLU%o&G9>J+nOTIG{(m3$(BxxcW;(RY{wn{`)%_>#|F(3G zDF48j9jbqiJ2ZIzj{Cd!_)pyGp>O=}afeRi-*JDp1dnh`e{J^Z;dlM-afb%s-*JEc eA^a1!CGekdG4|GM>_=1B4zJ9^?eq&Dwf+Yu;Dn+8 diff --git a/FarmmapsPoten/Data/PotenInput.json b/FarmmapsPoten/Data/PotenInput.json deleted file mode 100644 index cc195c0..0000000 --- a/FarmmapsPoten/Data/PotenInput.json +++ /dev/null @@ -1,57 +0,0 @@ -[ - { - - "File": "BBL-lutum.tif", - "OutputFileName": "BBL-poten_30_10_Shadow-True_TifInput", - "FieldName": "lutum", - "PlantingYear": 2020, - "MeanDensity": "30", - "Variation": "20", - "UseShadow": true, - "CountPerArea": false, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 4.61098862747705418, 52.22974843124053734 ], - [ 4.61338362824790682, 52.22993593073885421 ], - [ 4.61949010053501397, 52.22667042162688489 ], - [ 4.61954106663577324, 52.22670579437124871 ], - [ 4.61953075937382085, 52.22671128094363269 ], - [ 4.61954202811175296, 52.22670563321562298 ], - [ 4.61901745087585081, 52.22634382962637289 ], - [ 4.61812211075603685, 52.22571430401869463 ], - [ 4.61736058962494678, 52.22611942369080396 ], - [ 4.61464150409827134, 52.2275669988328346 ], - [ 4.61407137489250019, 52.22787370291415243 ], - [ 4.61242270952427802, 52.2287649864655279 ], - [ 4.61261020576063618, 52.22887063061121182 ], - [ 4.61256645484349015, 52.22889713517798072 ], - [ 4.61229657126373027, 52.22904994233584119 ], - [ 4.61207777596369262, 52.2291602791168188 ], - [ 4.61174554282434013, 52.22933965721241378 ], - [ 4.61169310584479941, 52.22934596082312453 ], - [ 4.61143525446537428, 52.22949107699816551 ], - [ 4.61100843013417183, 52.22973689585098356 ], - [ 4.61098862747705418, 52.22974843124053734 ] - ] - ] - }, - - "GenerateTaskmap": true, - "CellWidth": "3", - "CellHeight": "10", - "Centered": "false", - "StartPoint": { - "type": "Point", - "coordinates": [ 4.61812211075603685, 52.22571430401869463 ] // BBL - }, - "EndPoint": { - "type": "Point", - "coordinates": [ 4.61242270952427802, 52.2287649864655279 ] // BBL - } // if no angle - - //"Angle": "317.0" // if no endpoint - } - -] diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 2ec8375..7141f11 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -78,11 +78,7 @@ namespace FarmmapsVRApoten $"VRA Poten cropfield {input.OutputFileName}", input.PlantingYear, input.GeometryJson.ToString(Formatting.None)); - //Item cropfieldItem; - // _logger.LogInformation("Cropfield already exists, trying to get it"); - //cropfieldItem = await _farmmapsApiService.GetItemAsync("2927b80f63b946afb36821470b9c5c23"); - - //Calculating shadow map + //Downloading shadowMap for own interpretation if (useShadow) { _logger.LogInformation("Calculate shadow map for field"); var shadowItem = await _generalService.RunShadowTask(cropfieldItem); @@ -96,7 +92,6 @@ namespace FarmmapsVRApoten Path.Combine(DownloadFolder, $"{input.OutputFileName}.shadow.zip")); } - _logger.LogInformation("Looking for local data to use"); var localDataAvailable = input.File; var geotiffItem = (Item)null; @@ -185,7 +180,13 @@ namespace FarmmapsVRApoten var taskmap = (Item)null; if (input.OutputType == "isoxml") { - + + if (input.DdiCode == null) + { + _logger.LogInformation("DDi not given. Using expected identifiers"); + if (countPerArea == true) {input.DdiCode = input.DdiCode = "0011";} + else { input.DdiCode = "0016"; }; + } taskmap = await _generalService.CreateTaskmap(cropfieldItem: cropfieldItem, tiffItem: applianceMapItem, outputType: input.OutputType, cellWidth: input.CellWidth, cellHeight: input.CellHeight, startPoint: input.StartPoint.ToString(Formatting.None), ddiCode: input.DdiCode, centered: input.Centered, endPoint: input.EndPoint.ToString(Formatting.None), angle: input.Angle, precision: input.Precision, diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index 764e62c..31ed4e8 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -1,54 +1,13 @@ [ - //{ - - // "File": "ClaassenVRA_Kruising_LutEmptyPointsErased_largerExtent.zip", - // "OutputFileName": "2021.03.31.ClaassenVRA_Kruising_Lut_EmptyPointsErased_largeExtent", - // "FieldName": "Lutum", - // "PlantingYear": 2021, - // "MeanDensity": "15", - // "Variation": "20", - // "UseShadow": true, - // "CountPerArea": false, - // "geometryJson": { - // "type": "Polygon", - // "coordinates": [ - // [ - // [ 6.3070655, 53.3623397 ], - // [ 6.3070875, 53.3623898 ], - // [ 6.3069749, 53.3623074 ], - // [ 6.3070314, 53.3623125 ], - // [ 6.3070655, 53.3623397 ] - // ] - // ] - // }, - - // "GenerateTaskmap": true, - // "OutputType": "isoxml", // "shape" or "isoxml" if isoxml also add ddiCode - // "DdiCode": "0001", - // "CellWidth": "3", - // "CellHeight": "10", - // "Centered": "false", - // "StartPoint": { - // "type": "Point", - // "coordinates": [ 6.306901145501940, 53.362318269638386 ] - // }, - // "EndPoint": { - // "type": "Point", - // "coordinates": [ 6.298561552458957, 53.363541544243297 ] - // } // if no angle - - // // "Angle": "317.0" // if no endpoint - //} - { "File": "PlantingSampleDataLutum.zip", - "OutputFileName": "2021.04.09_vraPoten_SampleData4", + "OutputFileName": "2021.04.12_vraPoten_SampleData", "FieldName": "lutum", "PlantingYear": 2021, "MeanDensity": "30", "Variation": "20", - "UseShadow": false, - "CountPerArea": false, + "UseShadow": true, + "CountPerArea": false, // don't forget to change ddi if isoxml is created "geometryJson": { "type": "Polygon", "coordinates": [ @@ -72,17 +31,17 @@ "Centered": "true", "StartPoint": { "type": "Point", - //"coordinates": [ 5.669032078413372, 52.527906465105254 ] // 1 - //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 - //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 - "coordinates": [ 5.671623092321491, 52.529463163999097 ] // 4 + //"coordinates": [ 5.66886041703652044, 52.52929999060298627 ] // 1 + //"coordinates": [ 5.6716230923214912, 52.52946316399909676 ] // 2 + //"coordinates": [ 5.67185376229668581, 52.5280565894154563 ] // 3 + "coordinates": [ 5.66903207841337231, 52.52790646510525363 ] // 4 }, "EndPoint": { "type": "Point", - "coordinates": [ 5.668860417036520, 52.529299990602986 ] // 1 - //"coordinates": [ 5.668860417036520, 52.529299990602986 ] // 2 - //"coordinates": [ 5.671623092321491, 52.529463163999097 ] // 3 - //"coordinates": [ 5.671853762296686, 52.528056589415456 ] // 4 + "coordinates": [ 5.66886041703652044, 52.52929999060298627 ] // 1 + //"coordinates": [ 5.6716230923214912, 52.52946316399909676 ] // 2 + //"coordinates": [ 5.67185376229668581, 52.5280565894154563 ] // 3 + //"coordinates": [ 5.66903207841337231, 52.52790646510525363 ] // 4 } // if no angle //"Angle": "317.0" // if no endpoint From 7baf16db74f901d75651a97147d1d49d446b4a48 Mon Sep 17 00:00:00 2001 From: Riepma Date: Tue, 13 Apr 2021 10:54:04 +0200 Subject: [PATCH 22/35] fixed to store statistics of satellite images --- FarmmapsApi/Services/GeneralService.cs | 66 +++++------ FarmmapsNbs/NbsApplication.cs | 41 ++----- FarmmapsNbs/NitrogenInput.json | 158 +++++++++++++------------ 3 files changed, 122 insertions(+), 143 deletions(-) diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index b122253..ac0574a 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -345,7 +345,7 @@ namespace FarmmapsApi.Services } - public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode, string FieldName, bool StoreStatistics) { + public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); @@ -384,11 +384,6 @@ namespace FarmmapsApi.Services } return selectedSatelliteItem; - - - - - } //VanDerSat @@ -416,32 +411,6 @@ namespace FarmmapsApi.Services return itemTask.Code; } - - 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 FindVanDerSatItem(Item cropfieldItem, string VanDerSatTaskCode, string FieldName, bool StoreStatistics) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, VanDerSatTaskCode); @@ -478,7 +447,6 @@ namespace FarmmapsApi.Services 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"); @@ -512,6 +480,32 @@ namespace FarmmapsApi.Services } + 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) { @@ -519,10 +513,8 @@ namespace FarmmapsApi.Services // find WatBal data temporal - var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "WatBal");//, item => item.SourceTask == VANDERSAT_TASK && - // taskStatus.Finished >= item.Created && - // taskStatus.Finished <= item.Created.Value.AddHours(1)); - + 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"); diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index 196f9f4..1000354 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -120,7 +120,7 @@ namespace FarmmapsNbs // Select a particular satellite item from satelliteTask - Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode, FieldName, StoreStatistics); + Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode); var satelliteBand = satalliteItem.Data["layers"][0]["name"]; var satelliteStatistics = satalliteItem.Data["layers"][0]["renderer"]["band"]["statistics"]; @@ -130,21 +130,18 @@ namespace FarmmapsNbs //Store data to csv if (StoreStatistics == true) { - var SatelliteFile = $"\\Downloads\\DataSatellite_{FieldName}.csv"; - var NewLineField = $"\"Field\":{FieldName}" + Environment.NewLine; - var NewLineDate = $"\"date\":{satalliteItem.DataDate}" + Environment.NewLine; - var i = 0; - foreach (var item in satelliteStatistics) - { - //var NewLines2; - if (i == 0) - { - File.AppendAllText(SatelliteFile, NewLineDate); - i++; - } - File.AppendAllText(SatelliteFile, $"{item}" + Environment.NewLine); + var SatelliteStatsFile = $"{DownloadFolder}/SatelliteDataStatistics_{FieldName}_{satalliteItem.DataDate.Value:d}.csv"; + using var w = new StreamWriter(SatelliteStatsFile); + { + foreach (var item in satelliteStatistics) + { + var line = string.Format("{0}", item); + w.WriteLine(line); + w.Flush(); + } } + } @@ -216,22 +213,6 @@ namespace FarmmapsNbs } - //// check if vandersat task not yet done, do here and save taskcode - //if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.VanDerSatTaskCode)) { - // var VanDerSatTaskCode = await _generalService.RunVanDerSatTask(cropfieldItem); - // _settings.VanDerSatTaskCode = VanDerSatTaskCode; - // SaveSettings(settingsfile); - //} - - //// Select a particular image item from VanDerSat - //Item VanDerSatItem = await _generalService.FindVanDerSatItem(cropfieldItem, _settings.VanDerSatTaskCode, FieldName, StoreStatistics); - - - //// download the geotiff - //_logger.LogInformation("Downloading geotiff file"); - //await _farmmapsApiService.DownloadItemAsync(VanDerSatItem.Code, - // Path.Combine(DownloadFolder, $"nbs_VanDerSatGeotiff_{input.OutputFileName}.zip")); - _logger.LogInformation($"Calculating targetN with targetYield: {input.TargetYield}"); var targetNItem = await _nitrogenService.CreateTargetNItem(cropfieldItem); var targetNData = await _nitrogenService.CalculateTargetN(cropfieldItem, targetNItem, plantingDate, diff --git a/FarmmapsNbs/NitrogenInput.json b/FarmmapsNbs/NitrogenInput.json index 416d05b..064956d 100644 --- a/FarmmapsNbs/NitrogenInput.json +++ b/FarmmapsNbs/NitrogenInput.json @@ -1,83 +1,42 @@ [ - { - "file": "Scan_1_20190605.json", - "inputVariable": "irmi", - "inputLayerName": "", - "outputFileName": "vranbs1", - "plantingDate": "2019-04-18", - "measurementDate": "2019-06-05", - "potatoPurposeType": "consumption", - "targetYield": 45, - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.40843828875524, 50.638966444680605 ], - [ 3.408953272886064, 50.639197789621612 ], - [ 3.409242951459603, 50.639469958681836 ], - [ 3.409328782148028, 50.639612846807708 ], - [ 3.409457528180712, 50.639789755314411 ], - [ 3.409639918393741, 50.640014292074966 ], - [ 3.409833037442765, 50.640211611372706 ], - [ 3.410069071836049, 50.640395321698435 ], - [ 3.410380208081761, 50.640572227259661 ], - [ 3.410605513638958, 50.640715112034222 ], - [ 3.411925160474145, 50.641177783561204 ], - [ 3.411935889310142, 50.640728720085136 ], - [ 3.412590348309737, 50.63948356709389 ], - [ 3.413244807309242, 50.638224772339846 ], - [ 3.413400375432099, 50.637901562841307 ], - [ 3.413539850300779, 50.637449065809889 ], - [ 3.413475477284437, 50.637418445552932 ], - [ 3.40999396998362, 50.637449065810451 ], - [ 3.409940325803365, 50.638102293212661 ], - [ 3.409575545377398, 50.638483338338325 ], - [ 3.409060561246574, 50.638707881340494 ], - [ 3.40843828875524, 50.638966444680605 ] - ] - ] - }, - - "GenerateTaskmap": true, - "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode - "Precision": "2", - "MaximumClasses": "4", - "DdiCode": "0006", - "CellWidth": "3", - "CellHeight": "10", - "Centered": "true", - "StartPoint": { - "type": "Point", - "coordinates": [ 3.409341600000000, 50.638991900000001 ] - }, - "EndPoint": { // if no angle - "type": "Point", - "coordinates": [ 3.411897000000000, 50.638981500000000 ] - } - //"Angle": "317.0" // if no endpoint - - }, - //{ - // "file": "", // keep emptpy to use satellite image - // "inputVariable": "wdvi", - // "InputLayerName": "wdvi", - // "outputFileName": "rtest1", - // "plantingDate": "2020-05-01", - // "measurementDate": "2020-06-14", + // "UseCreatedCropfield": true, + // "file": "Scan_1_20190605.json", + // "inputVariable": "irmi", + // "inputLayerName": "", + // "outputFileName": "vranbs1", + // "fieldName": "testJson_irmi", + // "storeSatelliteStatistics": true, + // "plantingDate": "2019-04-18", + // "measurementDate": "2019-06-05", // "potatoPurposeType": "consumption", - // "targetYield": 60, + // "targetYield": 45, // "geometryJson": { // "type": "Polygon", // "coordinates": [ // [ - // [ 4.960707146896585, 52.800583669708487 ], - // [ 4.960645975538824, 52.800470217610922 ], - // [ 4.962140695752897, 52.799177147194797 ], - // [ 4.967523821195745, 52.801502400041208 ], - // [ 4.966336768950911, 52.802543735879809 ], - // [ 4.961711880764330, 52.801009996856429 ], - // [ 4.960707146896585, 52.800583669708487 ] + // [ 3.40843828875524, 50.638966444680605 ], + // [ 3.408953272886064, 50.639197789621612 ], + // [ 3.409242951459603, 50.639469958681836 ], + // [ 3.409328782148028, 50.639612846807708 ], + // [ 3.409457528180712, 50.639789755314411 ], + // [ 3.409639918393741, 50.640014292074966 ], + // [ 3.409833037442765, 50.640211611372706 ], + // [ 3.410069071836049, 50.640395321698435 ], + // [ 3.410380208081761, 50.640572227259661 ], + // [ 3.410605513638958, 50.640715112034222 ], + // [ 3.411925160474145, 50.641177783561204 ], + // [ 3.411935889310142, 50.640728720085136 ], + // [ 3.412590348309737, 50.63948356709389 ], + // [ 3.413244807309242, 50.638224772339846 ], + // [ 3.413400375432099, 50.637901562841307 ], + // [ 3.413539850300779, 50.637449065809889 ], + // [ 3.413475477284437, 50.637418445552932 ], + // [ 3.40999396998362, 50.637449065810451 ], + // [ 3.409940325803365, 50.638102293212661 ], + // [ 3.409575545377398, 50.638483338338325 ], + // [ 3.409060561246574, 50.638707881340494 ], + // [ 3.40843828875524, 50.638966444680605 ] // ] // ] // }, @@ -92,14 +51,61 @@ // "Centered": "true", // "StartPoint": { // "type": "Point", - // "coordinates": [ 4.960707146896585, 52.800583669708487 ] + // "coordinates": [ 3.409341600000000, 50.638991900000001 ] // }, // "EndPoint": { // if no angle // "type": "Point", - // "coordinates": [ 4.961711880764330, 52.801009996856429 ] + // "coordinates": [ 3.411897000000000, 50.638981500000000 ] // } // //"Angle": "317.0" // if no endpoint - //} + + //}, + + { + "UseCreatedCropfield": true, + "file": "", // keep emptpy to use satellite image + "inputVariable": "wdvi", + "InputLayerName": "wdvi", + "outputFileName": "rtest1", + "fieldName": "test_satelliteWDVI", + "storeSatelliteStatistics": true, + "plantingDate": "2020-05-01", + "measurementDate": "2020-06-14", + "potatoPurposeType": "consumption", + "targetYield": 60, + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 4.960707146896585, 52.800583669708487 ], + [ 4.960645975538824, 52.800470217610922 ], + [ 4.962140695752897, 52.799177147194797 ], + [ 4.967523821195745, 52.801502400041208 ], + [ 4.966336768950911, 52.802543735879809 ], + [ 4.961711880764330, 52.801009996856429 ], + [ 4.960707146896585, 52.800583669708487 ] + ] + ] + }, + + "GenerateTaskmap": true, + "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode + "Precision": "2", + "MaximumClasses": "4", + "DdiCode": "0006", + "CellWidth": "3", + "CellHeight": "10", + "Centered": "true", + "StartPoint": { + "type": "Point", + "coordinates": [ 4.960707146896585, 52.800583669708487 ] + }, + "EndPoint": { // if no angle + "type": "Point", + "coordinates": [ 4.961711880764330, 52.801009996856429 ] + } + //"Angle": "317.0" // if no endpoint + } ] \ No newline at end of file From 1ddafc6d8781409280fcd59df3f8b44d994bf6d1 Mon Sep 17 00:00:00 2001 From: Riepma Date: Wed, 14 Apr 2021 20:53:26 +0200 Subject: [PATCH 23/35] added layer specification VRApoten --- FarmmapsPoten/Models/PotenInput.cs | 1 + FarmmapsPoten/PotenApplication.cs | 5 ++++- FarmmapsPoten/PotenInput.json | 12 +++++++----- FarmmapsPoten/PotenService.cs | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/FarmmapsPoten/Models/PotenInput.cs b/FarmmapsPoten/Models/PotenInput.cs index 0814ce3..bcf5932 100644 --- a/FarmmapsPoten/Models/PotenInput.cs +++ b/FarmmapsPoten/Models/PotenInput.cs @@ -7,6 +7,7 @@ namespace FarmmapsPoten.Models { public string File { get; set; } + public string InputLayerName { get; set; } public string OutputFileName { get; set; } public string FieldName { get; set; } public int PlantingYear { get; set; } diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 7141f11..8982a9c 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -59,6 +59,7 @@ namespace FarmmapsVRApoten var fieldName = input.FieldName; bool useShadow = input.UseShadow; bool countPerArea = input.CountPerArea; + var inputLayerName = input.InputLayerName; var myDrive = roots.SingleOrDefault(r => r.Name == "My drive"); if (myDrive == null) { @@ -78,6 +79,8 @@ namespace FarmmapsVRApoten $"VRA Poten cropfield {input.OutputFileName}", input.PlantingYear, input.GeometryJson.ToString(Formatting.None)); + _logger.LogInformation($"CropfielditemCode: {cropfieldItem.Code}"); + //Downloading shadowMap for own interpretation if (useShadow) { _logger.LogInformation("Calculate shadow map for field"); @@ -156,7 +159,7 @@ namespace FarmmapsVRApoten // INPUT IS NEEDED as GEOTIFF var applianceMapItem = - await _potenService.CalculateApplicationMapAsync(cropfieldItem, geotiffItem, meanDensity, variation, countPerArea, useShadow); + await _potenService.CalculateApplicationMapAsync(cropfieldItem, geotiffItem, meanDensity, variation, countPerArea, useShadow, inputLayerName); if (applianceMapItem == null) { return; diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index 31ed4e8..3b7a3c1 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -1,12 +1,14 @@ [ + { - "File": "PlantingSampleDataLutum.zip", - "OutputFileName": "2021.04.12_vraPoten_SampleData", + "File": "PlantingSampleDataLutumANDec.zip", + "InputLayerName": "EC0-60", + "OutputFileName": "2021.04.14_vraPoten_SampleDataMultipleLayers", "FieldName": "lutum", "PlantingYear": 2021, "MeanDensity": "30", "Variation": "20", - "UseShadow": true, + "UseShadow": false, "CountPerArea": false, // don't forget to change ddi if isoxml is created "geometryJson": { "type": "Polygon", @@ -22,9 +24,9 @@ }, "GenerateTaskmap": true, - "OutputType": "isoxml", // "shape" or "isoxml" if isoxml also add ddiCode + "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode "Precision": "2", - "MaximumClasses": "4", + "MaximumClasses": "5", "DdiCode": "0016", "CellWidth": "3", "CellHeight": "10", diff --git a/FarmmapsPoten/PotenService.cs b/FarmmapsPoten/PotenService.cs index b3a6fe0..297f9ea 100644 --- a/FarmmapsPoten/PotenService.cs +++ b/FarmmapsPoten/PotenService.cs @@ -25,7 +25,7 @@ namespace FarmmapsVRApoten _generalService = generalService; } - public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem, string meanDensity, string variation, bool countPerArea, bool useShadow) + public async Task CalculateApplicationMapAsync(Item cropfieldItem, Item inputItem, string meanDensity, string variation, bool countPerArea, bool useShadow, string inputLayerName = null) { var potenApplicationMapRequest = new TaskRequest() { TaskType = VRAPLANTING_TASK }; if (inputItem != null) {potenApplicationMapRequest.attributes["inputCode"] = inputItem.Code; } @@ -34,6 +34,7 @@ namespace FarmmapsVRApoten potenApplicationMapRequest.attributes["variation"] = variation; potenApplicationMapRequest.attributes["countPerArea"] = countPerArea.ToString(); potenApplicationMapRequest.attributes["useShadow"] = useShadow.ToString(); + potenApplicationMapRequest.attributes["inputLayerName"] = inputLayerName; var taskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, potenApplicationMapRequest); _logger.LogInformation($"itemTaskCode: {taskCode}"); From bd846e9f0de8a3a4a0d613eb84bd7fc316fab582 Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Wed, 21 Apr 2021 08:59:54 +0200 Subject: [PATCH 24/35] update NBS --- FarmmapsNbs/appsettings.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 FarmmapsNbs/appsettings.json diff --git a/FarmmapsNbs/appsettings.json b/FarmmapsNbs/appsettings.json new file mode 100644 index 0000000..7b11e9d --- /dev/null +++ b/FarmmapsNbs/appsettings.json @@ -0,0 +1,10 @@ +{ + "Authority": "https://accounts.test.farmmaps.eu/", + "Endpoint": "https://test.farmmaps.eu/", + "BasePath": "api/v1", + "DiscoveryEndpointUrl": "https://accounts.test.farmmaps.eu/.well-known/openid-configuration", + "RedirectUri": "http://example.nl/api", + "ClientId": "", + "ClientSecret": "", + "Scopes": [ "api" ] +} From e0aae4afaf0f9463803248f471e7c33f11509fb2 Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Wed, 21 Apr 2021 09:03:25 +0200 Subject: [PATCH 25/35] small change of date notation in a filename of a csv --- FarmmapsNbs/NbsApplication.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FarmmapsNbs/NbsApplication.cs b/FarmmapsNbs/NbsApplication.cs index 1000354..c98cec6 100644 --- a/FarmmapsNbs/NbsApplication.cs +++ b/FarmmapsNbs/NbsApplication.cs @@ -131,7 +131,7 @@ namespace FarmmapsNbs if (StoreStatistics == true) { - var SatelliteStatsFile = $"{DownloadFolder}/SatelliteDataStatistics_{FieldName}_{satalliteItem.DataDate.Value:d}.csv"; + var SatelliteStatsFile = $"{DownloadFolder}/SatelliteDataStatistics_{FieldName}_{satalliteItem.DataDate.Value:yyyy-MM-dd}.csv"; using var w = new StreamWriter(SatelliteStatsFile); { foreach (var item in satelliteStatistics) From bea4d207d04ddab0949fc299e7a334841869f349 Mon Sep 17 00:00:00 2001 From: Riepma Date: Tue, 11 May 2021 15:03:08 +0200 Subject: [PATCH 26/35] added project to only download some data --- .../DataDownloadApplication.cs | 235 ++++++++++++++++++ FarmmapsDataDownload/DataDownloadInput.json | 31 +++ FarmmapsDataDownload/DataDownloadService.cs | 29 +++ .../FarmmapsDataDownload.csproj | 27 ++ .../Models/DataDownloadInput.cs | 26 ++ FarmmapsDataDownload/Models/Settings.cs | 11 + FarmmapsDataDownload/Models/TargetNData.cs | 9 + FarmmapsDataDownload/Program.cs | 21 ++ FarmmapsPoten/PotenApplication.cs | 11 +- FarmmapsPoten/PotenInput.json | 6 +- 10 files changed, 401 insertions(+), 5 deletions(-) create mode 100644 FarmmapsDataDownload/DataDownloadApplication.cs create mode 100644 FarmmapsDataDownload/DataDownloadInput.json create mode 100644 FarmmapsDataDownload/DataDownloadService.cs create mode 100644 FarmmapsDataDownload/FarmmapsDataDownload.csproj create mode 100644 FarmmapsDataDownload/Models/DataDownloadInput.cs create mode 100644 FarmmapsDataDownload/Models/Settings.cs create mode 100644 FarmmapsDataDownload/Models/TargetNData.cs create mode 100644 FarmmapsDataDownload/Program.cs diff --git a/FarmmapsDataDownload/DataDownloadApplication.cs b/FarmmapsDataDownload/DataDownloadApplication.cs new file mode 100644 index 0000000..9aec1d2 --- /dev/null +++ b/FarmmapsDataDownload/DataDownloadApplication.cs @@ -0,0 +1,235 @@ +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 FarmmapsDataDownload.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsDataDownload +{ + public class DataDownloadApplication : IApplication + { + private const string DownloadFolder = "Downloads"; + private const string SettingsFile = "settings.json"; + + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly DataDownloadService _dataDownloadService; + private readonly GeneralService _generalService; + + private Settings _settings; + + public DataDownloadApplication(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, DataDownloadService dataDownloadService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _dataDownloadService = dataDownloadService; + } + + public async Task RunAsync() + { + var fieldsInputJson = File.ReadAllText("DataDownloadInput.json"); + List fieldsInputs = JsonConvert.DeserializeObject>(fieldsInputJson); + + 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 fieldsInputs) + { + try + { + await Process(roots, input); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + + private async Task Process(List roots, DataDownloadInput input) + { + // !!specify if you are using an already created cropfield: + bool useCreatedCropfield = input.UseCreatedCropfield; + var cropYear = input.CropYear; + var fieldName = input.fieldName; + bool storeSatelliteStatistics = input.StoreSatelliteStatistics; + string settingsfile = $"Settings_{fieldName}.json"; + + LoadSettings(settingsfile); + + var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); + if (uploadedRoot == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive"); + if (myDriveRoot == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + // Use already created cropfield or create new one + Item cropfieldItem; + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) + { + _logger.LogInformation("Creating cropfield"); + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, + $"DataCropfield {input.OutputFileName}", cropYear.Year, input.GeometryJson.ToString(Formatting.None)); + _settings.CropfieldItemCode = cropfieldItem.Code; + SaveSettings(settingsfile); + } + else + { + _logger.LogInformation("Cropfield already exists, trying to get it"); + cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); + } + + + // Get shadow data + + if (input.GetShadowData) + { + _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")); + } + + + + + // Get satellite data + if (input.GetSatelliteData) + { + // check if satellite task not yet done, do here and save taskcode + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) + { + var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); + _settings.SatelliteTaskCode = satelliteTaskCode; + SaveSettings(settingsfile); + } + + // Select a particular satellite item from satelliteTask + Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode); + int selectedLayer= 2; + + if (input.SatelliteBand == "ndvi") selectedLayer = 0; + if (input.SatelliteBand == "wdvi") selectedLayer = 1; + if (input.SatelliteBand == "natural") selectedLayer = 2; + + var satelliteBand = satalliteItem.Data["layers"][selectedLayer]["name"]; + + //Store satellite data to csv + if (storeSatelliteStatistics == true && (selectedLayer == 0 || selectedLayer ==1)) + { + var satelliteStatistics = satalliteItem.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; + Console.WriteLine($"Satellite image date: {satalliteItem.DataDate}"); + + var SatelliteStatsFile = $"{DownloadFolder}/SatelliteDataStatistics_{fieldName}_{input.SatelliteBand}_{satalliteItem.DataDate.Value:yyyy-MM-dd}.csv"; + using var w = new StreamWriter(SatelliteStatsFile); + { + foreach (var item in satelliteStatistics) + { + var line = string.Format("{0}", item); + w.WriteLine(line); + w.Flush(); + } + } + + } + + var inputType = (satalliteItem.Data["layers"] as JArray)?[selectedLayer]["name"].ToString(); + if (string.IsNullOrEmpty(inputType)) + { + _logger.LogError("Could not get the input type name from the satellite item"); + return; + } + + // download the geotiff of needed inputtype + var SatelliteImageDate = (DateTime)satalliteItem.DataDate; + var SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, + Path.Combine(DownloadFolder, $"satelliteGeotiff_{input.OutputFileName}_{inputType}_{SatelliteDate}.zip")); + } + + + // Get vanDerSat data + if (input.GetVanDerSatData) + { + // check if satellite task not yet done, do here and save taskcode + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.VanDerSatTaskCode)) + { + var vanDerSatTaskCode = await _generalService.RunVanDerSatTask(cropfieldItem); + _settings.VanDerSatTaskCode = vanDerSatTaskCode; + SaveSettings(settingsfile); + } + + // Select a particular satellite item from satelliteTask + Item vanDerSatItem = await _generalService.FindVanDerSatItem(cropfieldItem, _settings.VanDerSatTaskCode, fieldName, input.StoreVanDerSatStatistics); + + + + } + + + } + + // Functions to save previously created cropfields + private void LoadSettings(string file) + { + if (File.Exists(file)) + { + var jsonText = File.ReadAllText(file); + _settings = JsonConvert.DeserializeObject(jsonText); + } + else + { + _settings = new Settings(); + } + } + + private void SaveSettings(string file) + { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + } + private void SaveInfo(string file) + { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + + } + + } +} diff --git a/FarmmapsDataDownload/DataDownloadInput.json b/FarmmapsDataDownload/DataDownloadInput.json new file mode 100644 index 0000000..15678ae --- /dev/null +++ b/FarmmapsDataDownload/DataDownloadInput.json @@ -0,0 +1,31 @@ +[ + { + "UseCreatedCropfield": true, + "outputFileName": "testSatData", + "fieldName": "test_satData", + "GetShadowData": true, + "GetSatelliteData": true, + "SatelliteBand": "natural", // "ndvi" or "wdvi" + "StoreSatelliteStatistics": true, + + "GetVanDerSatData": false, + "StoreVanDerSatStatistics": false, + "CropYear": "2020-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 4.960707146896585, 52.800583669708487 ], + [ 4.960645975538824, 52.800470217610922 ], + [ 4.962140695752897, 52.799177147194797 ], + [ 4.967523821195745, 52.801502400041208 ], + [ 4.966336768950911, 52.802543735879809 ], + [ 4.961711880764330, 52.801009996856429 ], + [ 4.960707146896585, 52.800583669708487 ] + ] + ] + } + } + + +] \ No newline at end of file diff --git a/FarmmapsDataDownload/DataDownloadService.cs b/FarmmapsDataDownload/DataDownloadService.cs new file mode 100644 index 0000000..81904bf --- /dev/null +++ b/FarmmapsDataDownload/DataDownloadService.cs @@ -0,0 +1,29 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmmapsDataDownload.Models; +using Microsoft.Extensions.Logging; +using static FarmmapsApi.Extensions; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsDataDownload +{ + public class DataDownloadService + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly GeneralService _generalService; + + public DataDownloadService(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + } + + + } +} \ No newline at end of file diff --git a/FarmmapsDataDownload/FarmmapsDataDownload.csproj b/FarmmapsDataDownload/FarmmapsDataDownload.csproj new file mode 100644 index 0000000..f1a5343 --- /dev/null +++ b/FarmmapsDataDownload/FarmmapsDataDownload.csproj @@ -0,0 +1,27 @@ + + + + Exe + netcoreapp3.1 + + + + + Always + + + Always + + + Always + + + Always + + + + + + + + diff --git a/FarmmapsDataDownload/Models/DataDownloadInput.cs b/FarmmapsDataDownload/Models/DataDownloadInput.cs new file mode 100644 index 0000000..57cc6e8 --- /dev/null +++ b/FarmmapsDataDownload/Models/DataDownloadInput.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsDataDownload.Models +{ + public class DataDownloadInput + { + public bool UseCreatedCropfield { get; set; } + public string File { get; set; } + public string InputVariable { get; set; } + public string OutputFileName { get; set; } + public DateTime CropYear { get; set; } + public JObject GeometryJson { get; set; } + public string InputLayerName { get; set; } + public string fieldName { get; set; } + public bool GetSatelliteData { get; set; } + public bool GetVanDerSatData { get; set; } + public string SatelliteBand { get; set; } + public bool StoreSatelliteStatistics { get; set; } + public bool StoreVanDerSatStatistics { get; set; } + public bool GetShadowData { get; set; } + + + + } +} \ No newline at end of file diff --git a/FarmmapsDataDownload/Models/Settings.cs b/FarmmapsDataDownload/Models/Settings.cs new file mode 100644 index 0000000..21506b4 --- /dev/null +++ b/FarmmapsDataDownload/Models/Settings.cs @@ -0,0 +1,11 @@ +namespace FarmmapsDataDownload +{ + public class Settings + { + public string CropfieldItemCode { get; set; } + public string SatelliteTaskCode { get; set; } + public string VanDerSatTaskCode { get; set; } + public string WatBalTaskCode { get; set; } + + } +} \ No newline at end of file diff --git a/FarmmapsDataDownload/Models/TargetNData.cs b/FarmmapsDataDownload/Models/TargetNData.cs new file mode 100644 index 0000000..b829a1a --- /dev/null +++ b/FarmmapsDataDownload/Models/TargetNData.cs @@ -0,0 +1,9 @@ +namespace FarmmapsDataDownload.Models +{ + public class TargetNData + { + public double TSum { get; set; } + public int TargetYield { get; set; } + public double TargetN { get; set; } + } +} \ No newline at end of file diff --git a/FarmmapsDataDownload/Program.cs b/FarmmapsDataDownload/Program.cs new file mode 100644 index 0000000..0c1ad78 --- /dev/null +++ b/FarmmapsDataDownload/Program.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using FarmmapsApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace FarmmapsDataDownload +{ + class Program : FarmmapsProgram + { + private static async Task Main(string[] args) + { + await new Program().Start(args); + } + + protected override void Configure(IServiceCollection serviceCollection) + { + serviceCollection.AddLogging() + .AddTransient(); + } + } +} \ No newline at end of file diff --git a/FarmmapsPoten/PotenApplication.cs b/FarmmapsPoten/PotenApplication.cs index 8982a9c..7479bfb 100644 --- a/FarmmapsPoten/PotenApplication.cs +++ b/FarmmapsPoten/PotenApplication.cs @@ -81,6 +81,7 @@ namespace FarmmapsVRApoten _logger.LogInformation($"CropfielditemCode: {cropfieldItem.Code}"); + //Downloading shadowMap for own interpretation if (useShadow) { _logger.LogInformation("Calculate shadow map for field"); @@ -142,6 +143,9 @@ namespace FarmmapsVRApoten _logger.LogInformation($"Converting shape to geotiff"); geotiffItem = await _generalService.ShapeToGeotiff(shapeItem); + _logger.LogInformation($"ShapeToGeotiff_GeotiffItemcode: {geotiffItem.Code}"); + + if (geotiffItem == null) { _logger.LogError("Something went wrong with shape to geotiff transformation"); return; @@ -193,7 +197,7 @@ namespace FarmmapsVRApoten taskmap = await _generalService.CreateTaskmap(cropfieldItem: cropfieldItem, tiffItem: applianceMapItem, outputType: input.OutputType, cellWidth: input.CellWidth, cellHeight: input.CellHeight, startPoint: input.StartPoint.ToString(Formatting.None), ddiCode: input.DdiCode, centered: input.Centered, endPoint: input.EndPoint.ToString(Formatting.None), angle: input.Angle, precision: input.Precision, - cropTypeName: null, costumerName: null, ProductGroupName: null, productName : null, resolution: null, unitScale: null, maximumClasses: input.MaximumClasses); + cropTypeName: null, costumerName: null, ProductGroupName: null, productName : null, resolution: "3", unitScale: null, maximumClasses: input.MaximumClasses); } else { @@ -212,7 +216,10 @@ namespace FarmmapsVRApoten _logger.LogInformation("Downloading taskmap"); await _farmmapsApiService.DownloadItemAsync(taskmap.Code, - Path.Combine(DownloadFolder, $"VRApoten_taskmap_{input.OutputFileName}.zip")); + Path.Combine(DownloadFolder, $"VRApoten_taskmap_{input.OutputFileName}_isoxml.zip")); + + + } } } diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index 3b7a3c1..6753abd 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -3,13 +3,13 @@ { "File": "PlantingSampleDataLutumANDec.zip", "InputLayerName": "EC0-60", - "OutputFileName": "2021.04.14_vraPoten_SampleDataMultipleLayers", + "OutputFileName": "2021.05.11_vraPoten_SampleDataMultipleLayers", "FieldName": "lutum", "PlantingYear": 2021, "MeanDensity": "30", "Variation": "20", "UseShadow": false, - "CountPerArea": false, // don't forget to change ddi if isoxml is created + "CountPerArea": true, // don't forget to change ddi if isoxml is created "geometryJson": { "type": "Polygon", "coordinates": [ @@ -24,7 +24,7 @@ }, "GenerateTaskmap": true, - "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode + "OutputType": "isoxml", // "shape" or "isoxml" if isoxml also add ddiCode "Precision": "2", "MaximumClasses": "5", "DdiCode": "0016", From 245e82adbc5f0b7da810c27e2168b1cd2a0b748f Mon Sep 17 00:00:00 2001 From: Riepma Date: Wed, 12 May 2021 08:06:30 +0200 Subject: [PATCH 27/35] Added rest of the modified files --- FarmmapsApi/Services/GeneralService.cs | 7 +++---- FarmmapsApiSamples.sln | 6 ++++++ FarmmapsNbs/FarmmapsNbs.csproj | 2 +- FarmmapsNbs/Program.cs | 4 +--- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 61583e2..e934f85 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -115,7 +115,7 @@ namespace FarmmapsApi.Services public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string outputType, string cellWidth, string cellHeight, string startPoint, 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 = null, string unitScale = null, string maximumClasses = null) + string resolution = "3", string unitScale = null, string maximumClasses = null) { @@ -377,9 +377,8 @@ namespace FarmmapsApi.Services } _logger.LogInformation("Enter satellite image number for NBS application"); - int elment = Int32.Parse(Console.ReadLine()); - - var selectedSatelliteItem = satelliteTiffs[elment]; + int element = Int32.Parse(Console.ReadLine()); + var selectedSatelliteItem = satelliteTiffs[element]; if (selectedSatelliteItem == null) { _logger.LogError("Satellite item not found"); diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index 68171a4..3fc9bd7 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmMapsBlight", "FarmMapsB EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsZonering", "FarmmapsZonering\FarmmapsZonering.csproj", "{91A58C4A-4A80-4079-B43D-9B851206194F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsDataDownload", "FarmmapsDataDownload\FarmmapsDataDownload.csproj", "{32ED9500-AAAB-4030-9C7A-F611A85DF890}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,6 +59,10 @@ Global {91A58C4A-4A80-4079-B43D-9B851206194F}.Debug|Any CPU.Build.0 = Debug|Any CPU {91A58C4A-4A80-4079-B43D-9B851206194F}.Release|Any CPU.ActiveCfg = Release|Any CPU {91A58C4A-4A80-4079-B43D-9B851206194F}.Release|Any CPU.Build.0 = Release|Any CPU + {32ED9500-AAAB-4030-9C7A-F611A85DF890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32ED9500-AAAB-4030-9C7A-F611A85DF890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32ED9500-AAAB-4030-9C7A-F611A85DF890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32ED9500-AAAB-4030-9C7A-F611A85DF890}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FarmmapsNbs/FarmmapsNbs.csproj b/FarmmapsNbs/FarmmapsNbs.csproj index 6e4a5ab..f43ac99 100644 --- a/FarmmapsNbs/FarmmapsNbs.csproj +++ b/FarmmapsNbs/FarmmapsNbs.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + netcoreapp3.1 diff --git a/FarmmapsNbs/Program.cs b/FarmmapsNbs/Program.cs index 596637a..395efb9 100644 --- a/FarmmapsNbs/Program.cs +++ b/FarmmapsNbs/Program.cs @@ -14,9 +14,7 @@ namespace FarmmapsNbs protected override void Configure(IServiceCollection serviceCollection) { - serviceCollection.AddLogging(opts => opts - .AddConsole() - .AddFilter("System.Net.Http", LogLevel.Warning)) + serviceCollection.AddLogging() .AddTransient(); } } From 5da74c2db097d796cab7ccf49e156ea5b9248a3a Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Wed, 12 May 2021 13:05:17 +0200 Subject: [PATCH 28/35] small update on DataDownload and GenerelServices --- FarmmapsApi/Services/GeneralService.cs | 81 +++++++++++++++---- .../DataDownloadApplication.cs | 36 +++++---- FarmmapsDataDownload/DataDownloadInput.json | 51 ++++++------ .../FarmmapsDataDownload.csproj | 2 +- .../Models/DataDownloadInput.cs | 1 + 5 files changed, 114 insertions(+), 57 deletions(-) diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index e934f85..b6ee298 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -348,7 +348,7 @@ namespace FarmmapsApi.Services } - public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode) { + public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode, string FieldName = null, int selectedLayer = 0, bool allItemsStatistics = false, string DownloadFolder = null) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); @@ -366,30 +366,81 @@ namespace FarmmapsApi.Services } var satelliteTiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); + + if (allItemsStatistics == false) { + _logger.LogInformation("Available satellite images:"); + var count = 0; + TimeSpan.FromSeconds(0.5); + foreach (var item in satelliteTiffs) { - _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++; + } - 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; + } } - _logger.LogInformation("Enter satellite image number for NBS application"); - int element = Int32.Parse(Console.ReadLine()); - var selectedSatelliteItem = satelliteTiffs[element]; + if (allItemsStatistics == true ) { + var count = 0; + foreach (var item in satelliteTiffs) { + var satellitetBand = item.Data["layers"][selectedLayer]["name"]; + var SatelliteImageYear = (DateTime)item.DataDate; + var SatelliteYear = SatelliteImageYear.ToString("yyyy"); + var SatelliteFile = $"{DownloadFolder}/SatelliteDataStatistics_{SatelliteYear}_{FieldName}_{satellitetBand}.csv"; + var NewLineField = $"Field,Date,Mean,Min,Max,Standard deviation, ConfidenceInterval low, ConfidenceInterval high" + Environment.NewLine; + if (count == 0) { + File.AppendAllText(SatelliteFile, NewLineField); + var numbervandersat = satelliteTiffs.Count; + Console.WriteLine($"{numbervandersat} Satellite images found"); + } - if (selectedSatelliteItem == null) { + var SatelliteStatistics = item.Data["layers"][0]["renderer"]["band"]["statistics"]; + var SatelliteImageDate = (DateTime)item.DataDate; + var satelliteDate = SatelliteImageDate.ToString("yyyy-MM-dd"); + var NewLineDate = $"\"date\":{satelliteDate}" + Environment.NewLine; + if (SatelliteStatistics == null) { + Console.WriteLine($"{satelliteDate} no statistics found"); + //Console.WriteLine($"Available data: {item.Data}"); + } else { + File.AppendAllText(SatelliteFile, $"{FieldName},{satelliteDate},{SatelliteStatistics["mean"]},{SatelliteStatistics["min"]},{SatelliteStatistics["max"]},{SatelliteStatistics["stddev"]},{SatelliteStatistics["confidenceIntervalLow"]},{SatelliteStatistics["confidenceIntervalHigh"]}" + Environment.NewLine); + } + + if (true) { + // download the geotiff of needed inputtype + var selectedSatelliteItemDate = (DateTime)item.DataDate; + var SatelliteDate = selectedSatelliteItemDate.ToString("yyyyMMdd"); + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(item.Code, + Path.Combine(DownloadFolder, $"satelliteGeotiff_{FieldName}_{satellitetBand}_{SatelliteDate}.zip")); + } + + count++; + } + } + + var selectedSatelliteItemTemp = satelliteTiffs[0]; + + if (selectedSatelliteItemTemp == null) { _logger.LogError("Satellite item not found"); + return selectedSatelliteItemTemp; } - return selectedSatelliteItem; - } + return selectedSatelliteItemTemp; - //VanDerSat - public async Task RunVanDerSatTask(Item cropfieldItem) { + } + + + //VanDerSat + public async Task RunVanDerSatTask(Item cropfieldItem) { _logger.LogInformation("Gathering VanDerSat information for cropfield, this might take a while!"); diff --git a/FarmmapsDataDownload/DataDownloadApplication.cs b/FarmmapsDataDownload/DataDownloadApplication.cs index 9aec1d2..6001596 100644 --- a/FarmmapsDataDownload/DataDownloadApplication.cs +++ b/FarmmapsDataDownload/DataDownloadApplication.cs @@ -67,6 +67,7 @@ namespace FarmmapsDataDownload var cropYear = input.CropYear; var fieldName = input.fieldName; bool storeSatelliteStatistics = input.StoreSatelliteStatistics; + bool storeSatelliteStatisticsCropYear = input.StoreSatelliteStatisticsCropYear; string settingsfile = $"Settings_{fieldName}.json"; LoadSettings(settingsfile); @@ -132,20 +133,23 @@ namespace FarmmapsDataDownload _settings.SatelliteTaskCode = satelliteTaskCode; SaveSettings(settingsfile); } - - // Select a particular satellite item from satelliteTask - Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode); + int selectedLayer= 2; if (input.SatelliteBand == "ndvi") selectedLayer = 0; if (input.SatelliteBand == "wdvi") selectedLayer = 1; if (input.SatelliteBand == "natural") selectedLayer = 2; + // Select a particular satellite item from satelliteTask + Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode, fieldName, selectedLayer, storeSatelliteStatisticsCropYear, DownloadFolder); + var satelliteBand = satalliteItem.Data["layers"][selectedLayer]["name"]; //Store satellite data to csv if (storeSatelliteStatistics == true && (selectedLayer == 0 || selectedLayer ==1)) { + + var satelliteStatistics = satalliteItem.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; Console.WriteLine($"Satellite image date: {satalliteItem.DataDate}"); @@ -160,24 +164,24 @@ namespace FarmmapsDataDownload } } - } + var inputType = (satalliteItem.Data["layers"] as JArray)?[selectedLayer]["name"].ToString(); + if (string.IsNullOrEmpty(inputType)) { + _logger.LogError("Could not get the input type name from the satellite item"); + return; + } - var inputType = (satalliteItem.Data["layers"] as JArray)?[selectedLayer]["name"].ToString(); - if (string.IsNullOrEmpty(inputType)) - { - _logger.LogError("Could not get the input type name from the satellite item"); - return; + // download the geotiff of needed inputtype + var SatelliteImageDate = (DateTime)satalliteItem.DataDate; + var SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); + _logger.LogInformation("Downloading geotiff file"); + await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, + Path.Combine(DownloadFolder, $"satelliteGeotiff_{input.OutputFileName}_{inputType}_{SatelliteDate}.zip")); } - - // download the geotiff of needed inputtype - var SatelliteImageDate = (DateTime)satalliteItem.DataDate; - var SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); - _logger.LogInformation("Downloading geotiff file"); - await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, - Path.Combine(DownloadFolder, $"satelliteGeotiff_{input.OutputFileName}_{inputType}_{SatelliteDate}.zip")); } + + // Get vanDerSat data if (input.GetVanDerSatData) { diff --git a/FarmmapsDataDownload/DataDownloadInput.json b/FarmmapsDataDownload/DataDownloadInput.json index 15678ae..11d80ac 100644 --- a/FarmmapsDataDownload/DataDownloadInput.json +++ b/FarmmapsDataDownload/DataDownloadInput.json @@ -1,31 +1,32 @@ [ - { - "UseCreatedCropfield": true, - "outputFileName": "testSatData", - "fieldName": "test_satData", - "GetShadowData": true, - "GetSatelliteData": true, - "SatelliteBand": "natural", // "ndvi" or "wdvi" - "StoreSatelliteStatistics": true, + { + "UseCreatedCropfield": true, + "outputFileName": "testSatData2", + "fieldName": "test_satData2", + "GetShadowData": false, + "GetSatelliteData": true, + "SatelliteBand": "wdvi", // "natural", "ndvi" or "wdvi" + "StoreSatelliteStatistics": false, + "StoreSatelliteStatisticsCropYear": true, - "GetVanDerSatData": false, - "StoreVanDerSatStatistics": false, - "CropYear": "2020-01-01", - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 4.960707146896585, 52.800583669708487 ], - [ 4.960645975538824, 52.800470217610922 ], - [ 4.962140695752897, 52.799177147194797 ], - [ 4.967523821195745, 52.801502400041208 ], - [ 4.966336768950911, 52.802543735879809 ], - [ 4.961711880764330, 52.801009996856429 ], - [ 4.960707146896585, 52.800583669708487 ] - ] - ] - } + "GetVanDerSatData": false, + "StoreVanDerSatStatistics": false, + "CropYear": "2020-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 4.960707146896585, 52.800583669708487 ], + [ 4.960645975538824, 52.800470217610922 ], + [ 4.962140695752897, 52.799177147194797 ], + [ 4.967523821195745, 52.801502400041208 ], + [ 4.966336768950911, 52.802543735879809 ], + [ 4.961711880764330, 52.801009996856429 ], + [ 4.960707146896585, 52.800583669708487 ] + ] + ] } + } ] \ No newline at end of file diff --git a/FarmmapsDataDownload/FarmmapsDataDownload.csproj b/FarmmapsDataDownload/FarmmapsDataDownload.csproj index f1a5343..7f0e2c6 100644 --- a/FarmmapsDataDownload/FarmmapsDataDownload.csproj +++ b/FarmmapsDataDownload/FarmmapsDataDownload.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + netcoreapp3.0 diff --git a/FarmmapsDataDownload/Models/DataDownloadInput.cs b/FarmmapsDataDownload/Models/DataDownloadInput.cs index 57cc6e8..348f95e 100644 --- a/FarmmapsDataDownload/Models/DataDownloadInput.cs +++ b/FarmmapsDataDownload/Models/DataDownloadInput.cs @@ -17,6 +17,7 @@ namespace FarmmapsDataDownload.Models public bool GetVanDerSatData { get; set; } public string SatelliteBand { get; set; } public bool StoreSatelliteStatistics { get; set; } + public bool StoreSatelliteStatisticsCropYear { get; set; } public bool StoreVanDerSatStatistics { get; set; } public bool GetShadowData { get; set; } From a8efaf93c2c37be0176771edca19621918a56dd8 Mon Sep 17 00:00:00 2001 From: Riepma Date: Wed, 12 May 2021 13:29:53 +0200 Subject: [PATCH 29/35] added appsettings.json --- FarmmapsDataDownload/appsettings.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 FarmmapsDataDownload/appsettings.json diff --git a/FarmmapsDataDownload/appsettings.json b/FarmmapsDataDownload/appsettings.json new file mode 100644 index 0000000..924ba52 --- /dev/null +++ b/FarmmapsDataDownload/appsettings.json @@ -0,0 +1,10 @@ +{ + "Authority": "https://accounts.test.farmmaps.eu/", + "Endpoint": "https://test.farmmaps.eu/", + "BasePath": "api/v1", + "DiscoveryEndpointUrl": "https://accounts.test.farmmaps.eu/.well-known/openid-configuration", + "RedirectUri": "http://example.nl/api", + "ClientId": "", + "ClientSecret": "", + "Scopes": [ "api" ] +} From 250020be7890dd5a85a0204621d036ed3366f8ed Mon Sep 17 00:00:00 2001 From: pepijn van oort Date: Tue, 18 May 2021 18:19:28 +0200 Subject: [PATCH 30/35] Added project FarmmapsBulkSatDownload --- FarmmapsApi/Services/GeneralService.cs | 23 ++ FarmmapsApiSamples.sln | 8 +- .../BulkSatDownloadApplication.cs | 242 +++++++++++++++ .../BulkSatDownloadInput.json | 293 ++++++++++++++++++ .../BulkSatDownloadService.cs | 29 ++ FarmmapsBulkSatDownload/DBsettings.json | 6 + .../FarmmapsBulkSatDownload.csproj | 31 ++ .../Models/BulkSatDownloadInput.cs | 18 ++ FarmmapsBulkSatDownload/Models/DB.cs | 47 +++ .../Models/GroenmonitorTable.cs | 28 ++ FarmmapsBulkSatDownload/Models/Settings.cs | 11 + FarmmapsBulkSatDownload/Program.cs | 21 ++ FarmmapsBulkSatDownload/ShowGeotiff.r | 152 +++++++++ FarmmapsBulkSatDownload/appsettings.json | 10 + 14 files changed, 918 insertions(+), 1 deletion(-) create mode 100644 FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs create mode 100644 FarmmapsBulkSatDownload/BulkSatDownloadInput.json create mode 100644 FarmmapsBulkSatDownload/BulkSatDownloadService.cs create mode 100644 FarmmapsBulkSatDownload/DBsettings.json create mode 100644 FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj create mode 100644 FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs create mode 100644 FarmmapsBulkSatDownload/Models/DB.cs create mode 100644 FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs create mode 100644 FarmmapsBulkSatDownload/Models/Settings.cs create mode 100644 FarmmapsBulkSatDownload/Program.cs create mode 100644 FarmmapsBulkSatDownload/ShowGeotiff.r create mode 100644 FarmmapsBulkSatDownload/appsettings.json diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index e934f85..9cd3015 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -388,6 +388,29 @@ namespace FarmmapsApi.Services return selectedSatelliteItem; } + public async Task> FindSatelliteItemsAll(Item cropfieldItem, string satelliteTaskCode) + { + + var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); + + + // find ndvi or wdvi 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.LogError("Temporal item not found"); + + } + + var satelliteTiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); + + return satelliteTiffs; + } //VanDerSat public async Task RunVanDerSatTask(Item cropfieldItem) { diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index 3fc9bd7..42962b5 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -23,7 +23,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmMapsBlight", "FarmMapsB EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsZonering", "FarmmapsZonering\FarmmapsZonering.csproj", "{91A58C4A-4A80-4079-B43D-9B851206194F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FarmmapsDataDownload", "FarmmapsDataDownload\FarmmapsDataDownload.csproj", "{32ED9500-AAAB-4030-9C7A-F611A85DF890}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsDataDownload", "FarmmapsDataDownload\FarmmapsDataDownload.csproj", "{32ED9500-AAAB-4030-9C7A-F611A85DF890}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsBulkSatDownload", "FarmmapsBulkSatDownload\FarmmapsBulkSatDownload.csproj", "{772DBDCD-9FAA-40A7-8551-2C1620C4AB67}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -63,6 +65,10 @@ Global {32ED9500-AAAB-4030-9C7A-F611A85DF890}.Debug|Any CPU.Build.0 = Debug|Any CPU {32ED9500-AAAB-4030-9C7A-F611A85DF890}.Release|Any CPU.ActiveCfg = Release|Any CPU {32ED9500-AAAB-4030-9C7A-F611A85DF890}.Release|Any CPU.Build.0 = Release|Any CPU + {772DBDCD-9FAA-40A7-8551-2C1620C4AB67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {772DBDCD-9FAA-40A7-8551-2C1620C4AB67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {772DBDCD-9FAA-40A7-8551-2C1620C4AB67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {772DBDCD-9FAA-40A7-8551-2C1620C4AB67}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs new file mode 100644 index 0000000..57dd71a --- /dev/null +++ b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Threading.Tasks; +using FarmmapsApi; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmmapsBulkSatDownload.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Npgsql; +using Newtonsoft.Json.Linq; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsBulkSatDownload +{ + public class BulkSatDownloadApplication : IApplication + { + private const string SettingsFile = "settings.json"; + + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly BulkSatDownloadService _dataDownloadService; + private readonly GeneralService _generalService; + + private Settings _settings; + static DB dbparcels; + + public BulkSatDownloadApplication(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, BulkSatDownloadService dataDownloadService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _dataDownloadService = dataDownloadService; + } + public async Task RunAsync() + { + // Check if we have permission + // !! 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(); + // Initialize databases. Username, password etc stored in file "DBsettings.json" + dbparcels = JsonConvert.DeserializeObject(File.ReadAllText("DBsettings.json")); + + //string date; + //string asterices = "*****************************************************************************"; + //string bboxtiffWithTempPath; + //string parcelshp = "parcel.shp"; + //string parcelshpWithTempPath; + //string gwoptions; //= String.Format("-overwrite -s_srs EPSG:28992 -t_srs EPSG:28992 -dstnodata 0 -cutline {0} -crop_to_cutline", parcelshp); + //string parceltiff = "parcel.tiff"; + //string parceltiffWithTempPath; + //string[] vegetationindices = { "WDVI", "NDVI" }; + //string[] sources = { "groenmonitor" }; // { "groenmonitor", "akkerwebwenr", "akkerwebneo" }; { "groenmonitor" }; + string schemaname = "bigdata"; + string parceltablename = "parcel_flowerbulbs";//"parcelsijbrandij" "parcel"; "parcel_flowerbulbs" + //string groenmonitortablename = "groenmonitor_flowerbulbs";//"groenmonitorsijbrandij" "groenmonitor" "groenmonitor_flowerbulbs" //PO20190605: "groenmonitormpt" contains groenmonitor data from multiple parcel tables (mpt) + // The view 'groenmonitorlatestviewname' contains per parcelid (arbid) the year in which it "exists" and the date of the latest image downloaded. It is used to prevent downloading images already downloaded + string groenmonitorlatestviewname = "groenmonitorlatest_flowerbulbs"; //"groenmonitorsijbrandijlatest" "groenmonitorlatest" "groenmonitorlatest_flowerbulbs" //PO20190605: "v_groenmonitorlatest" contains latest available dates from groenmonitortablename = "groenmonitormpt" + //GroenmonitorTable gmtb; + //DateTime dt; + //DateTime dtfirst; + //DateTime dtlast; + //double scalingfactor; + BulkSatDownloadInput dataDownloadInput; + List fieldsInputs = new List(); + + // Database query and connection. Geometry must be in WGS84 coordinate system, EPSG 4326 + // Apparently the FarmmapsApi cannot handle MultiPolygon, so we need to convert to single Polygon + // In case database returns a MultiPolygon use ST_NumGeometries(pt.geom) to count the number of polygons + // If necessary use WHERE T_NumGeometries(pt.geom) = 1 to use only single polygons + string connectionString = dbparcels.GetConnectionString(); + string readSql = string.Format( +@" +SELECT pt.arbid, pt.crop, pt.year, gml.lastwenrdate, ST_AsGeoJSON(ST_Transform((ST_DUMP(pt.geom)).geom::geometry(Polygon),4326)) AS geojson_polygon_wgs84 +FROM {0}.{1} pt, {0}.{2} gml +WHERE + pt.arbid = gml.arbid AND + pt.crop NOT IN('Tulp', 'Lelie') AND + pt.year = 2018 +LIMIT 10;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT 10 for testing + + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + + // Read data (run query) = build a list of fields for which to download images + NpgsqlCommand command = connection.CreateCommand(); + command.CommandText = readSql; + NpgsqlDataReader dr = command.ExecuteReader(); + while (dr.Read()) + { + dataDownloadInput = new BulkSatDownloadInput(); + dataDownloadInput.UseCreatedCropfield = false; + dataDownloadInput.DownloadFolder = "C:\\workdir\\groenmonitor\\"; + dataDownloadInput.fieldID = dr.GetInt16(0); + dataDownloadInput.fieldName = string.Format($"{parceltablename}_fld{dataDownloadInput.fieldID}"); + dataDownloadInput.cropName = dr.GetString(1); + dataDownloadInput.cropYear = dr.GetInt16(2); + dataDownloadInput.lastdownloadedimagedate = dr.GetDateTime(3); + dataDownloadInput.GeometryJson = JObject.Parse(dr.GetString(4)); + fieldsInputs.Add(dataDownloadInput); + } + connection.Close(); + } + + // For testing using json input instead of database input + var fieldsInputJson = File.ReadAllText("BulkSatDownloadInput.json"); + List fieldsInputs2 = JsonConvert.DeserializeObject>(fieldsInputJson); + + // Now for each input download all images. Use fieldsInputs or fieldsInputs2 for testing + foreach (var input in fieldsInputs2) + { + try + { + await Process(roots, input); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + } + + private async Task Process(List roots, BulkSatDownloadInput input) + { + string DownloadFolder = input.DownloadFolder; + if (!Directory.Exists(DownloadFolder)) + Directory.CreateDirectory(DownloadFolder); + + // !!specify if you are using an already created cropfield: + bool useCreatedCropfield = input.UseCreatedCropfield; + string settingsfile = $"Settings_BulkSatDownloadApplication.json"; + + LoadSettings(settingsfile); + + var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded"); + if (uploadedRoot == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive"); + if (myDriveRoot == null) + { + _logger.LogError("Could not find a needed root item"); + return; + } + + // Use already created cropfield or create new one + Item cropfieldItem; + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) + { + _logger.LogInformation(string.Format($"Creating cropfield for a field in the year {input.cropYear}")); + //string geomjson = input.GeometryJson.ToString(Formatting.None); //"{\"type\":\"Polygon\",\"coordinates\":[[[4.960707146896585,52.80058366970849],[4.960645975538824,52.80047021761092],[4.962140695752897,52.7991771471948],[4.967523821195745,52.80150240004121],[4.966336768950911,52.80254373587981],[4.96171188076433,52.80100999685643],[4.960707146896585,52.80058366970849]]]}" + string geomjson = input.GeometryJson.ToString(Formatting.None); + cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, + $"DataCropfield {input.cropName}", input.cropYear, input.GeometryJson.ToString(Formatting.None)); + _settings.CropfieldItemCode = cropfieldItem.Code; + SaveSettings(settingsfile); + } + else + { + _logger.LogInformation("Cropfield already exists, trying to get it"); + cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); + } + + // check if satellite task not yet done, do here and save taskcode + if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) + { + var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); + _settings.SatelliteTaskCode = satelliteTaskCode; + SaveSettings(settingsfile); + } + + // Now get the tiffs + List satelliteTiffs = await _generalService.FindSatelliteItemsAll(cropfieldItem, _settings.SatelliteTaskCode); + _logger.LogInformation(string.Format($"Downloading {satelliteTiffs.Count} geotiffs for a field in year {input.cropYear} ...")); + foreach (Item satalliteItem in satelliteTiffs) { + // download the geotiff. Returns a zip file with always these three files: + // data.dat.aux.xml + // thumbnail.jpg + // wenr.tif. Contains 5 layers: (1) ndvi, (2) wdvi, (3) Red, (4) Green and (5) Blue + DateTime SatelliteImageDate = (DateTime)satalliteItem.DataDate; + string SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); + string fileName = string.Format($"{input.cropName}_{input.fieldName}_{SatelliteDate}"); // assuming the fieldName is good enough for an ID. One might add here _{input.fieldID} or _{input.cropName} + string fileNameZip = string.Format($"{fileName}.zip"); + string fileNameGeotiff = string.Format($"{fileName}.tif"); + await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, Path.Combine(DownloadFolder, fileNameZip)); + + // Extract the file "wenr.tif" from zip, rename it, delete "wenr.tif". overwriteFiles = true + ZipFile.ExtractToDirectory(Path.Combine(DownloadFolder, fileNameZip), DownloadFolder, true); + File.Delete(Path.Combine(DownloadFolder, fileNameGeotiff)); // Delete the fileNameGeotiff file if exists + File.Move(Path.Combine(DownloadFolder, "wenr.tif"), Path.Combine(DownloadFolder, fileNameGeotiff)); // Rename the oldFileName into newFileName + + // Cleanup + string[] filesToDelete = new string[] { fileNameZip, "wenr.tif", "thumbnail.jpg", "data.dat.aux.xml" }; + foreach (string f in filesToDelete) + { + File.Delete(Path.Combine(DownloadFolder, f)); + } + } + } + + // Functions to save previously created cropfields + private void LoadSettings(string file) + { + if (File.Exists(file)) + { + var jsonText = File.ReadAllText(file); + _settings = JsonConvert.DeserializeObject(jsonText); + } + else + { + _settings = new Settings(); + } + } + + private void SaveSettings(string file) + { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + } + private void SaveInfo(string file) + { + if (_settings == null) + return; + + var json = JsonConvert.SerializeObject(_settings); + File.WriteAllText(file, json); + + } + + } +} diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadInput.json b/FarmmapsBulkSatDownload/BulkSatDownloadInput.json new file mode 100644 index 0000000..3463e41 --- /dev/null +++ b/FarmmapsBulkSatDownload/BulkSatDownloadInput.json @@ -0,0 +1,293 @@ +[ + { + "UseCreatedCropfield": false, + "fieldName": "fld5641", // FarmMaps won't do without a fieldName + "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ + "cropName": "wheat", + "cropYear": 2021, + "fieldID": 5641, + "lastdownloadedimagedate": "2021-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + } + }, + { + "UseCreatedCropfield": false, + "fieldName": "fld5641", // FarmMaps won't do without a fieldName + "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ + "cropName": "wheat", + "cropYear": 2020, + "fieldID": 5641, + "lastdownloadedimagedate": "2020-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + + ] + ] + } + }, + { + "UseCreatedCropfield": false, + "fieldName": "fld5641", // FarmMaps won't do without a fieldName + "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ + "cropName": "wheat", + "cropYear": 2019, + "fieldID": 5641, + "lastdownloadedimagedate": "2019-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + } + }, + { + "UseCreatedCropfield": false, + "fieldName": "fld5641", // FarmMaps won't do without a fieldName + "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ + "cropName": "wheat", + "cropYear": 2018, + "fieldID": 5641, + "lastdownloadedimagedate": "2018-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + } + }, + { + "UseCreatedCropfield": false, + "fieldName": "fld5641", // FarmMaps won't do without a fieldName + "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ + "cropName": "wheat", + "cropYear": 2017, + "fieldID": 5641, + "lastdownloadedimagedate": "2017-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + } + }, + { + "UseCreatedCropfield": false, + "fieldName": "fld5641", // FarmMaps won't do without a fieldName + "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ + "cropName": "wheat", + "cropYear": 2016, + "fieldID": 5641, + "lastdownloadedimagedate": "2016-01-01", + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + } + } + + +] \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadService.cs b/FarmmapsBulkSatDownload/BulkSatDownloadService.cs new file mode 100644 index 0000000..5be9864 --- /dev/null +++ b/FarmmapsBulkSatDownload/BulkSatDownloadService.cs @@ -0,0 +1,29 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmmapsBulkSatDownload.Models; +using Microsoft.Extensions.Logging; +using static FarmmapsApi.Extensions; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsBulkSatDownload +{ + public class BulkSatDownloadService + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly GeneralService _generalService; + + public BulkSatDownloadService(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + } + + + } +} \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/DBsettings.json b/FarmmapsBulkSatDownload/DBsettings.json new file mode 100644 index 0000000..2f84ad7 --- /dev/null +++ b/FarmmapsBulkSatDownload/DBsettings.json @@ -0,0 +1,6 @@ +{ + "User": "", + "Password": "", + "Database": "", + "Host": "" +} \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj b/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj new file mode 100644 index 0000000..e3efb57 --- /dev/null +++ b/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj @@ -0,0 +1,31 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + Always + + + Always + + + Always + + + Always + + + + + + + + diff --git a/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs b/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs new file mode 100644 index 0000000..f89833f --- /dev/null +++ b/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs @@ -0,0 +1,18 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace FarmmapsBulkSatDownload.Models +{ + public class BulkSatDownloadInput + { + public bool UseCreatedCropfield { get; set; } + public string fieldName { get; set; } + public string DownloadFolder { get; set; } + public string cropName { get; set; } + public int cropYear { get; set; } + public int fieldID { get; set; } + public DateTime lastdownloadedimagedate { get; set; } + public JObject GeometryJson { get; set; } + //public string fieldName { get { return string.Format($"{cropName}_fld{fieldID}"); } } + } +} \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/Models/DB.cs b/FarmmapsBulkSatDownload/Models/DB.cs new file mode 100644 index 0000000..2ff705f --- /dev/null +++ b/FarmmapsBulkSatDownload/Models/DB.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Npgsql; + +namespace FarmmapsApi.Services +{ + public class DB + { + public string Database; + public string User; + public string Password; + public string Host; + + public string GetConnectionString() + { + NpgsqlConnectionStringBuilder sb = new NpgsqlConnectionStringBuilder(); + sb.Database = Database; + sb.Host = Host; + sb.Username = User; + sb.Password = Password; + sb.Port = 5432; + + return sb.ConnectionString; + } + + public int ExecuteNonQuery(string sql) + { + using (NpgsqlConnection conn = new NpgsqlConnection(GetConnectionString())) + { + conn.Open(); + NpgsqlCommand command = conn.CreateCommand(); + command.CommandText = sql; + int r = command.ExecuteNonQuery(); + return r; + } + } + + public NpgsqlConnection CreateConnection() + { + NpgsqlConnection conn = new NpgsqlConnection(GetConnectionString()); + conn.Open(); + return conn; + } + + } +} diff --git a/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs b/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs new file mode 100644 index 0000000..8b48028 --- /dev/null +++ b/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FarmmapsBulkSatDownload.Models +{ + public class GroenmonitorTable + { + //public string parceltablename; //PO20190605: parceltablename added as a field (=column) in the GroenmonitorTable. The GroenmonitorTable contains data from multiple parceltables + public int parcelid; + public string date; + public string source; + public int wdvi_pixelcount; //count of pixels with data + public double wdvi_max; + public double wdvi_mean; + public double wdvi_min; + public double wdvi_stdev; + public double wdvi_median; + public double wdvi_p90; + public int ndvi_pixelcount; //count of pixels with data + public double ndvi_max; + public double ndvi_mean; + public double ndvi_min; + public double ndvi_stdev; + public double ndvi_median; + public double ndvi_p90; + } +} diff --git a/FarmmapsBulkSatDownload/Models/Settings.cs b/FarmmapsBulkSatDownload/Models/Settings.cs new file mode 100644 index 0000000..2d70cba --- /dev/null +++ b/FarmmapsBulkSatDownload/Models/Settings.cs @@ -0,0 +1,11 @@ +namespace FarmmapsBulkSatDownload +{ + public class Settings + { + public string CropfieldItemCode { get; set; } + public string SatelliteTaskCode { get; set; } + public string VanDerSatTaskCode { get; set; } + public string WatBalTaskCode { get; set; } + + } +} \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/Program.cs b/FarmmapsBulkSatDownload/Program.cs new file mode 100644 index 0000000..b9ca73d --- /dev/null +++ b/FarmmapsBulkSatDownload/Program.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using FarmmapsApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace FarmmapsBulkSatDownload +{ + class Program : FarmmapsProgram + { + private static async Task Main(string[] args) + { + await new Program().Start(args); + } + + protected override void Configure(IServiceCollection serviceCollection) + { + serviceCollection.AddLogging() + .AddTransient(); + } + } +} \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/ShowGeotiff.r b/FarmmapsBulkSatDownload/ShowGeotiff.r new file mode 100644 index 0000000..68e010c --- /dev/null +++ b/FarmmapsBulkSatDownload/ShowGeotiff.r @@ -0,0 +1,152 @@ +# ShowGeotiff.r +# I downloaded and calculated the stats for the polygon defined in C:\git\FarmMapsApiClient_WURtest\FarmmapsDataDownload\DataDownloadInput.json +# in which I set "SatelliteBand": "wdvi" and in which in the console I requested image 8 for date '2020-06-14' + +library(raster) +library(sf) +library(rgdal) +setwd("C:/workdir/groenmonitor/") +# FarmmapsDataDownload generates two files: +fileGeotiff <- "wenr.tif" +# fileJpg <- "thumbnail.jpg" +# FarmmapsBulkSatDownload generates many files. Here is what I tried when inputing the same field for a number of years using BulkSatDownloadInput.json +# fileGeotiff <- "wheat_fld5641_20210224.tif" # 2 files for year 2021. This file is a nice example having in upperleft corner no data +# fileGeotiff <- "wheat_fld5641_20210331.tif" # 2 files for year 2021 +# fileGeotiff <- "wheat_fld5641_20200321.tif" # 14 files for year 2020, earliest +# fileGeotiff <- "wheat_fld5641_20200922.tif" # 14 files for year 2020, latest +# fileGeotiff <- "wheat_fld5641_20190121.tif" # 9 files for year 2019, earliest +# fileGeotiff <- "wheat_fld5641_20191117.tif" # 9 files for year 2019, latest +# 1 file for year 2018, with error message 'End of Central Directory record could not be found' and invalid wheat_fld5641_20180630.zip +# fileGeotiff <- "wheat_fld5641_20170526.tif" # 1 file for year 2017 +# Zero files for 2016 +lenfilename <- nchar(fileGeotiff) +year <- substr(fileGeotiff,lenfilename-11,lenfilename-8) +imgdate <- substr(fileGeotiff,lenfilename-11,lenfilename-4) + +# The thumbnail has the polygon clipped out, has 1 layer, no crs and the mean value is not the mean wdvi we are looking for +# r.thumbnail <- raster(fileJpg) +# plot(r.thumbnail) +# crs(r.thumbnail) +# CRS arguments: NA +# cellStats(r.thumbnail,'mean') #59.91667 + +stk.wenr <- stack(x=fileGeotiff) +# plot(stk.wenr) shows 5 plots (5 bands) +# I think these are: +# 1. ndvi (since it runs from 0 to 1) +# 2. wdvi (since it runs from 0 to 0.5) +# 3-5: RGB (since they run from 0 to 255) +plot(stk.wenr) +# CRS arguments: +# +proj=sterea +lat_0=52.1561605555556 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +units=m +no_defs +# Or use st_crs(stk.wenr) to get more info, but no EPSG code in there. +# Likely it is epsg:28992 (Amersfoort) +crs(stk.wenr) +stk.wenr <- projectRaster(stk.wenr, crs = CRS('+init=EPSG:28992')) +crs(stk.wenr) +# Looks the same but strangely, if we don't do projectRaster(stk.wenr, crs = CRS('+init=EPSG:28992')), we find below the bottom left corner of the polygon missing + +r.wenr.rd.wdvi <- subset(stk.wenr,2) +dev.off() +plot(r.wenr.rd.wdvi,main=paste("wdvi",imgdate),xlab="RDX",ylab="RDY") +cellStats(r.wenr.rd.wdvi,'mean') #0.1654627 +# Same as in downloaded file SatelliteDataStatistics_test_satData_wdvi_2020-06-14.csv +# "mean": 0.16564258790046743 +# So FindSatelliteItem in C:\git\FarmMapsApiClient_WURtest\FarmmapsApi\Services\GeneralService.cs returns a a stacked geotiff in +# i.e. the cellstats are calculated from the rectangle + +# Furthermore we can see +# shows coordinates in RD +# returns a rectangle, thus the shape of the polygon submitted is not clipped. +# The polygon was provided in WGS84. Let's draw it on top. +# First convert the raster to WGS84 +r.wenr.wgs84.wdvi <- projectRaster(r.wenr.rd.wdvi, crs = CRS('+init=EPSG:4326')) + +# Draw a polygon on top of the raster +# Example polygon p1 +# p1 <- data.frame(id = 1, wkt = 'POLYGON((4.963 52.801, 4.966 52.801, 4.966 52.803, 4.963 52.803, 4.963 52.801))') +# p1 <- st_as_sf(p1, wkt = 'wkt', crs = targetcrs) +# plot(p1,add=TRUE, col="transparent",border="black") +# Draw the polygon on top of the raster +# Polygon p2 from C:\git\FarmMapsApiClient_WURtest\FarmmapsDataDownload\DataDownloadInput.json +p2 <- data.frame(id = 1, wkt = gsub("\n","",'POLYGON(( + 3.37837807779104 51.3231095796538, + 3.38065689232502 51.3212527499355, + 3.38022924592256 51.3210683536359, + 3.37980548452565 51.3208801127141, + 3.37959556105776 51.3207540143696, + 3.3793691292654 51.3205959677371, + 3.37822219207335 51.3215667913007, + 3.37816999925795 51.3216109809456, + 3.37646704574705 51.3208025481261, + 3.37646695791282 51.3208025061493, + 3.37608401443192 51.3206231652693, + 3.37607169507628 51.3206173959751, + 3.37606021048754 51.320612017601, + 3.37582728410659 51.3205029306946, + 3.37580409779263 51.3206502985963, + 3.37575872019649 51.3207993094705, + 3.37575476634361 51.3208122883487, + 3.37571181656268 51.3208797459348, + 3.3756624532907 51.3209415238446, + 3.37557609963811 51.3210110142077, + 3.37541089899821 51.3211055871218, + 3.37477516102591 51.3214102985009, + 3.37473173914127 51.3214311108204, + 3.37455904622072 51.3215138815012, + 3.37415098054777 51.3217199232877, + 3.37313700916272 51.3222422862785, + 3.37748824689601 51.3242852920348, + 3.37749760805371 51.3242713084009, + 3.37811903757028 51.3233437635596, + 3.37818758851947 51.3232647797363, + 3.37823803668144 51.3232236798646, + 3.37837807779104 51.3231095796538))')) +p2.wgs84 <- st_as_sf(p2, wkt = 'wkt', crs = CRS('+init=EPSG:4326')) +# Or other way round, in RD Amersfoort. That looks ok +p2.rd <- st_transform(p2.wgs84, "+init=epsg:28992") + +# Have a look at both +# wg84 +dev.off() +plot(r.wenr.wgs84.wdvi,main=paste("wdvi",imgdate),xlab="LON",ylab="LAT") +plot(p2.wgs84,add=TRUE, col="transparent",border="red") +# RD +dev.off() +plot(r.wenr.rd.wdvi,main=paste("wdvi",imgdate),xlab="RDX",ylab="RDY") +plot(p2.rd,add=TRUE, col="transparent",border="red") + +#Let's clip the polygon +r.wenr.rd.wdvi.pol <- mask(r.wenr.rd.wdvi,p2.rd) +r.wenr.wgs84.wdvi.pol <- mask(r.wenr.wgs84.wdvi,p2.wgs84) +dev.off() +plot(r.wenr.wgs84.wdvi.pol,main=paste("wdvi",imgdate),xlab="LON",ylab="LAT") +plot(p2.wgs84,add=TRUE, col="transparent",border="red") +#That's what we want! Now compare the stats +cellStats(r.wenr.rd.wdvi,'mean') #0.1654627 # Stats from rectangle, RD +cellStats(r.wenr.wgs84.wdvi,'mean') #0.1656399 # Stats from rectangle, WGS84 +cellStats(r.wenr.rd.wdvi.pol,'mean') #0.1658702 # Stats from raster clipped by polygon, WGS84 +cellStats(r.wenr.wgs84.wdvi.pol,'mean') #0.1658611 # Stats from raster clipped by polygon, WGS84 +# Conclusion: in this example little difference, but can be quite different! + +# The project FarmmapsNbs can generate a wenr.tif file, application.tif, uptake.tif (in rtest1.uptake.zip)and shape.shp (in rtest1.taskmap.zip) +r.application <- raster("C:/git/FarmMapsApiClient_WURtest/FarmmapsNbs/bin/Debug/netcoreapp3.1/Downloads/application.tif") +dev.off() +plot(r.application) +plot(p2.rd,add=TRUE, col="transparent",border="red") +# The application.tif file is a rectangle (polygon not yet clipped), in projection Amersfoort RD New (EPSG:28992) + +r.uptake <- raster("C:/git/FarmMapsApiClient_WURtest/FarmmapsNbs/bin/Debug/netcoreapp3.1/Downloads/uptake.tif") +dev.off() +plot(r.uptake) +plot(p2.rd,add=TRUE, col="transparent",border="red") +# The uptake.tif file is a rectangle (polygon not yet clipped), in projection Amersfoort RD New (EPSG:28992) + +shp.wgs84 <- readOGR(dsn="C:/git/FarmMapsApiClient_WURtest/FarmmapsNbs/bin/Debug/netcoreapp3.1/Downloads", layer="shape") +crs(shp.wgs84) +# CRS arguments: +proj=longlat +datum=WGS84 +no_defs +dev.off() +plot(r.wenr.wgs84.wdvi,main="wdvi",xlab="LON",ylab="LAT") +plot(shp.wgs84,add=TRUE, col="transparent",border="black") +plot(p2.wgs84,add=TRUE, col="transparent",border="red") +# The shape file is in WGS84 diff --git a/FarmmapsBulkSatDownload/appsettings.json b/FarmmapsBulkSatDownload/appsettings.json new file mode 100644 index 0000000..c5d440a --- /dev/null +++ b/FarmmapsBulkSatDownload/appsettings.json @@ -0,0 +1,10 @@ +{ + "Authority": "https://accounts.test.farmmaps.eu/", + "Endpoint": "https://test.farmmaps.eu/", + "BasePath": "api/v1", + "DiscoveryEndpointUrl": "https://accounts.test.farmmaps.eu/.well-known/openid-configuration", + "RedirectUri": "http://example.nl/api", + "ClientId": "", + "ClientSecret": "", + "Scopes": [ "api" ] +} \ No newline at end of file From 1bc326cfd2b606c21e82b0ff3b7e970d1ceedabe Mon Sep 17 00:00:00 2001 From: pepijn van oort Date: Wed, 26 May 2021 10:16:29 +0200 Subject: [PATCH 31/35] Projects FarmmapsBulkSatDownload and FarmmapsDataDownload fully updated and tested Added some extra Tasks to /FarmmapsApi/Services/GeneralService.cs Added class SatelliteStatistics.cs to /FarmmapsApi/Models --- .gitignore | 3 +- FarmmapsApi/Models/SatelliteStatistics.cs | 29 ++ FarmmapsApi/Services/GeneralService.cs | 211 ++++---- .../BulkSatDownloadApplication.cs | 463 ++++++++++++++---- .../BulkSatDownloadInput.json | 285 +++-------- .../FarmmapsBulkSatDownload.csproj | 2 +- .../Models/BulkSatDownloadInput.cs | 14 +- FarmmapsBulkSatDownload/Models/DB.cs | 2 +- .../Models/GroenmonitorTable.cs | 2 +- FarmmapsBulkSatDownload/Models/Settings.cs | 13 +- FarmmapsBulkSatDownload/ShowGeotiff.r | 66 ++- .../DataDownloadApplication.cs | 116 +++-- FarmmapsDataDownload/DataDownloadInput.json | 6 +- .../Models/DataDownloadInput.cs | 5 +- 14 files changed, 738 insertions(+), 479 deletions(-) create mode 100644 FarmmapsApi/Models/SatelliteStatistics.cs diff --git a/.gitignore b/.gitignore index 6c59346..b788684 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .vs/ bin/ obj/ -appsettings.secrets.json \ No newline at end of file +appsettings.secrets.json +FarmmapsBulkSatDownload/DBsettings.secrets.json \ No newline at end of file diff --git a/FarmmapsApi/Models/SatelliteStatistics.cs b/FarmmapsApi/Models/SatelliteStatistics.cs new file mode 100644 index 0000000..c5b160e --- /dev/null +++ b/FarmmapsApi/Models/SatelliteStatistics.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FarmmapsApi.Models +{ + public class SatelliteStatistics + { + public string fieldName; + public DateTime satelliteDate; + public string satelliteBand; + public double max; + public double min; + public double mean; + public double mode; + public double median; + public double stddev; + public double minPlus; + public double curtosis; + public double maxMinus; + public double skewness; + public double variance; + public int populationCount; + public double variationCoefficient; + public double confidenceIntervalLow; + public double confidenceIntervalHigh; + public double confidenceIntervalErrorMargin; + } +} diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 8cefac3..83f9061 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using FarmmapsApi.Models; using Google.Apis.Upload; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using static FarmmapsApi.Extensions; using static FarmmapsApiSamples.Constants; @@ -348,12 +349,10 @@ namespace FarmmapsApi.Services } - public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode, string FieldName = null, int selectedLayer = 0, bool allItemsStatistics = false, string DownloadFolder = null) { + public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); - - // find ndvi or wdvi satellite data geotiffs var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Cropfield Satellite items", item => item.SourceTask == SATELLITE_TASK && taskStatus.Finished >= item.Created && @@ -367,101 +366,149 @@ namespace FarmmapsApi.Services var satelliteTiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); - if (allItemsStatistics == false) { - _logger.LogInformation("Available satellite images:"); - var count = 0; - TimeSpan.FromSeconds(0.5); - foreach (var item in satelliteTiffs) { + _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; - } + Console.WriteLine($"Satellite image #{count}: {item.DataDate}"); + count++; } - if (allItemsStatistics == true ) { - var count = 0; - foreach (var item in satelliteTiffs) { - var satellitetBand = item.Data["layers"][selectedLayer]["name"]; - var SatelliteImageYear = (DateTime)item.DataDate; - var SatelliteYear = SatelliteImageYear.ToString("yyyy"); - var SatelliteFile = $"{DownloadFolder}/SatelliteDataStatistics_{SatelliteYear}_{FieldName}_{satellitetBand}.csv"; - var NewLineField = $"Field,Date,Mean,Min,Max,Standard deviation, ConfidenceInterval low, ConfidenceInterval high" + Environment.NewLine; - if (count == 0) { - File.AppendAllText(SatelliteFile, NewLineField); - var numbervandersat = satelliteTiffs.Count; - Console.WriteLine($"{numbervandersat} Satellite images found"); - } + _logger.LogInformation("Enter satellite image number for NBS application"); + int element = Int32.Parse(Console.ReadLine()); + var selectedSatelliteItem = satelliteTiffs[element]; - var SatelliteStatistics = item.Data["layers"][0]["renderer"]["band"]["statistics"]; - var SatelliteImageDate = (DateTime)item.DataDate; - var satelliteDate = SatelliteImageDate.ToString("yyyy-MM-dd"); - var NewLineDate = $"\"date\":{satelliteDate}" + Environment.NewLine; - if (SatelliteStatistics == null) { - Console.WriteLine($"{satelliteDate} no statistics found"); - //Console.WriteLine($"Available data: {item.Data}"); - } else { - File.AppendAllText(SatelliteFile, $"{FieldName},{satelliteDate},{SatelliteStatistics["mean"]},{SatelliteStatistics["min"]},{SatelliteStatistics["max"]},{SatelliteStatistics["stddev"]},{SatelliteStatistics["confidenceIntervalLow"]},{SatelliteStatistics["confidenceIntervalHigh"]}" + Environment.NewLine); - } - - if (true) { - // download the geotiff of needed inputtype - var selectedSatelliteItemDate = (DateTime)item.DataDate; - var SatelliteDate = selectedSatelliteItemDate.ToString("yyyyMMdd"); - _logger.LogInformation("Downloading geotiff file"); - await _farmmapsApiService.DownloadItemAsync(item.Code, - Path.Combine(DownloadFolder, $"satelliteGeotiff_{FieldName}_{satellitetBand}_{SatelliteDate}.zip")); - } - - count++; - } - } - - var selectedSatelliteItemTemp = satelliteTiffs[0]; - - if (selectedSatelliteItemTemp == null) { + if (selectedSatelliteItem == null) + { _logger.LogError("Satellite item not found"); - - return selectedSatelliteItemTemp; } - - return selectedSatelliteItemTemp; - + + return selectedSatelliteItem; } - - public async Task> FindSatelliteItemsAll(Item cropfieldItem, string satelliteTaskCode) + public async Task> FindSatelliteItems(Item cropfieldItem, string satelliteTaskCode) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); - - // find ndvi or wdvi 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) + if (taskStatus.State == ItemTaskState.Error) { - _logger.LogError("Temporal item not found"); + _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; + } } - - 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 + List selectedLayers = new List(); + foreach (string satelliteBand in satelliteBands) + { + if (satelliteBand == "ndvi") selectedLayers.Add(0); + if (satelliteBand == "wdvi") selectedLayers.Add(1); + }; + 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 item in satelliteTiffs) + foreach (int selectedLayer in selectedLayers) + { + { + var satelliteBand = item.Data["layers"][selectedLayer]["name"]; + var satelliteImageDate = (DateTime)item.DataDate; + var satelliteStatisticsJtoken = item.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; + if (satelliteStatisticsJtoken == null) + { + _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found"); + //Console.WriteLine($"Available data: {item.Data}"); + } + else + { + SatelliteStatistics satelliteStatistics = satelliteStatisticsJtoken.ToObject(); + satelliteStatistics.fieldName = fieldName; + satelliteStatistics.satelliteDate = satelliteImageDate; + satelliteStatistics.satelliteBand = satelliteBand.ToString(); + 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 selectedLayers = new List(); + foreach (string satelliteBand in satelliteBands) + { + if (satelliteBand == "ndvi") selectedLayers.Add(0); + if (satelliteBand == "wdvi") selectedLayers.Add(1); + }; + + foreach (int selectedLayer in selectedLayers) + { + var satelliteBand = satelliteTiff.Data["layers"][selectedLayer]["name"]; + var satelliteImageDate = (DateTime)satelliteTiff.DataDate; + var satelliteStatisticsJtoken = satelliteTiff.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; + if (satelliteStatisticsJtoken == null) + { + _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found"); + //Console.WriteLine($"Available data: {item.Data}"); + } + else + { + satelliteStatistics = satelliteStatisticsJtoken.ToObject(); + satelliteStatistics.fieldName = fieldName.ToString(); + satelliteStatistics.satelliteDate = satelliteImageDate; + satelliteStatistics.satelliteBand = satelliteBand.ToString(); + listSatelliteStatistics.Add(satelliteStatistics); + } + } + + return listSatelliteStatistics; + } + //VanDerSat public async Task RunVanDerSatTask(Item cropfieldItem) { _logger.LogInformation("Gathering VanDerSat information for cropfield, this might take a while!"); diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs index 57dd71a..c6ea1a3 100644 --- a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs +++ b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs @@ -13,28 +13,27 @@ using Newtonsoft.Json; using Npgsql; using Newtonsoft.Json.Linq; using static FarmmapsApiSamples.Constants; +using System.Text; namespace FarmmapsBulkSatDownload { public class BulkSatDownloadApplication : IApplication { - private const string SettingsFile = "settings.json"; - private readonly ILogger _logger; private readonly FarmmapsApiService _farmmapsApiService; - private readonly BulkSatDownloadService _dataDownloadService; + private readonly BulkSatDownloadService _bulkSatDownloadService; private readonly GeneralService _generalService; + public const string settingsfile = "Settings.json"; private Settings _settings; - static DB dbparcels; public BulkSatDownloadApplication(ILogger logger, FarmmapsApiService farmmapsApiService, - GeneralService generalService, BulkSatDownloadService dataDownloadService) + GeneralService generalService, BulkSatDownloadService bulkSatDownloadService) { _logger = logger; _farmmapsApiService = farmmapsApiService; _generalService = generalService; - _dataDownloadService = dataDownloadService; + _bulkSatDownloadService = bulkSatDownloadService; } public async Task RunAsync() { @@ -42,46 +41,47 @@ namespace FarmmapsBulkSatDownload // !! 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(); - // Initialize databases. Username, password etc stored in file "DBsettings.json" - dbparcels = JsonConvert.DeserializeObject(File.ReadAllText("DBsettings.json")); - //string date; - //string asterices = "*****************************************************************************"; - //string bboxtiffWithTempPath; - //string parcelshp = "parcel.shp"; - //string parcelshpWithTempPath; - //string gwoptions; //= String.Format("-overwrite -s_srs EPSG:28992 -t_srs EPSG:28992 -dstnodata 0 -cutline {0} -crop_to_cutline", parcelshp); - //string parceltiff = "parcel.tiff"; - //string parceltiffWithTempPath; - //string[] vegetationindices = { "WDVI", "NDVI" }; - //string[] sources = { "groenmonitor" }; // { "groenmonitor", "akkerwebwenr", "akkerwebneo" }; { "groenmonitor" }; + BulkSatDownloadInput bulkSatDownloadInput; + List bulkSatDownloadInputList; + // Below are two options to for bulk download: (1) from and to database or (2) inputs from json, output to csv + // For illustration we make two lists bulkSatDownloadInputListDB and bulkSatDownloadInputListCsv and then choose which one we will use + List bulkSatDownloadInputListDB; + List bulkSatDownloadInputListCsv; + DateTime lastdownloadedimagedate; + int cropYear; + + // Option 1: When using database need to (1) fill in database data in DBsettings.secrets.json; (2) write tailor made SELECT query for fieldinputs in following lines; + // (3) Write tailor made INSERT INTO query in Task Process() below; + // Initialize databases. Username, password etc stored in file "DBsettings.secrets.json". + // Crashes if "DBsettings.secrets.json" is absent or empty + DB dbparcels = JsonConvert.DeserializeObject(File.ReadAllText("DBsettings.secrets.json")); string schemaname = "bigdata"; - string parceltablename = "parcel_flowerbulbs";//"parcelsijbrandij" "parcel"; "parcel_flowerbulbs" - //string groenmonitortablename = "groenmonitor_flowerbulbs";//"groenmonitorsijbrandij" "groenmonitor" "groenmonitor_flowerbulbs" //PO20190605: "groenmonitormpt" contains groenmonitor data from multiple parcel tables (mpt) - // The view 'groenmonitorlatestviewname' contains per parcelid (arbid) the year in which it "exists" and the date of the latest image downloaded. It is used to prevent downloading images already downloaded - string groenmonitorlatestviewname = "groenmonitorlatest_flowerbulbs"; //"groenmonitorsijbrandijlatest" "groenmonitorlatest" "groenmonitorlatest_flowerbulbs" //PO20190605: "v_groenmonitorlatest" contains latest available dates from groenmonitortablename = "groenmonitormpt" + string parceltablename = "parcel_disac";//"parcelsijbrandij" "parcel"; "parcel_flowerbulbs"; "parcel_disac" + string groenmonitortablename = "groenmonitor_disac";//"groenmonitorsijbrandij" "groenmonitor" "groenmonitor_flowerbulbs" "groenmonitor_disac" + // The view 'groenmonitorlatestviewname' contains per parcelid (arbid) the year in which it "exists" and the date of the latest image downloaded. It is used to prevent unneccessary downloading of image statistics already in the database + string groenmonitorlatestviewname = "groenmonitorlatest_disac"; //"groenmonitorsijbrandijlatest" "groenmonitorlatest" "groenmonitorlatest_flowerbulbs" "groenmonitorlatest_disac" //GroenmonitorTable gmtb; - //DateTime dt; - //DateTime dtfirst; - //DateTime dtlast; - //double scalingfactor; - BulkSatDownloadInput dataDownloadInput; - List fieldsInputs = new List(); // Database query and connection. Geometry must be in WGS84 coordinate system, EPSG 4326 // Apparently the FarmmapsApi cannot handle MultiPolygon, so we need to convert to single Polygon // In case database returns a MultiPolygon use ST_NumGeometries(pt.geom) to count the number of polygons - // If necessary use WHERE T_NumGeometries(pt.geom) = 1 to use only single polygons + // If necessary use WHERE T_NumGeometries(pt.geom) = 1 to select only single polygons + // + // FarmMaps get's its satellite images from www.groenmonitor.nl through the https://agrodatacube.wur.nl/. + // Many images are available at www.groenmonitor.nl, the https://agrodatacube.wur.nl/ serves only the clean images, 10-30 per year, 2019 onwards. Possibly more images will be added for earlier years + // For other images contact www.groenmonitor.nl, gerbert.roerink@wur.nl + bulkSatDownloadInputListDB = new List(); + List satelliteBands = new List { "wdvi", "ndvi" }; string connectionString = dbparcels.GetConnectionString(); string readSql = string.Format( @" -SELECT pt.arbid, pt.crop, pt.year, gml.lastwenrdate, ST_AsGeoJSON(ST_Transform((ST_DUMP(pt.geom)).geom::geometry(Polygon),4326)) AS geojson_polygon_wgs84 +SELECT pt.arbid, pt.year, gml.lastwenrdate, ST_AsGeoJSON(ST_Transform((ST_DUMP(pt.geom)).geom::geometry(Polygon),4326)) AS geojson_polygon_wgs84, COALESCE(pt.cropfielditemcode,'') AS cropfielditemcode FROM {0}.{1} pt, {0}.{2} gml WHERE - pt.arbid = gml.arbid AND - pt.crop NOT IN('Tulp', 'Lelie') AND - pt.year = 2018 -LIMIT 10;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT 10 for testing + pt.arbid = gml.arbid +ORDER BY pt.arbid +LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT x for testing using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) { @@ -93,47 +93,113 @@ LIMIT 10;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT NpgsqlDataReader dr = command.ExecuteReader(); while (dr.Read()) { - dataDownloadInput = new BulkSatDownloadInput(); - dataDownloadInput.UseCreatedCropfield = false; - dataDownloadInput.DownloadFolder = "C:\\workdir\\groenmonitor\\"; - dataDownloadInput.fieldID = dr.GetInt16(0); - dataDownloadInput.fieldName = string.Format($"{parceltablename}_fld{dataDownloadInput.fieldID}"); - dataDownloadInput.cropName = dr.GetString(1); - dataDownloadInput.cropYear = dr.GetInt16(2); - dataDownloadInput.lastdownloadedimagedate = dr.GetDateTime(3); - dataDownloadInput.GeometryJson = JObject.Parse(dr.GetString(4)); - fieldsInputs.Add(dataDownloadInput); + bulkSatDownloadInput = new BulkSatDownloadInput(); + bulkSatDownloadInput.fieldID = dr.GetInt16(0); + bulkSatDownloadInput.fieldName = string.Format($"{parceltablename}_{bulkSatDownloadInput.fieldID}"); + bulkSatDownloadInput.cropYear = dr.GetInt16(1); ; + bulkSatDownloadInput.lastdownloadedimagedate = dr.GetDateTime(2); + bulkSatDownloadInput.GeometryJson = JObject.Parse(dr.GetString(3)); + bulkSatDownloadInput.SatelliteBands = satelliteBands; + bulkSatDownloadInput.cropfielditemcode = dr.GetString(4); + bulkSatDownloadInput.database = dbparcels; + bulkSatDownloadInput.schemaname = schemaname; + bulkSatDownloadInput.cropfieldtable = parceltablename; + bulkSatDownloadInput.satelllitetable = groenmonitortablename; + bulkSatDownloadInputListDB.Add(bulkSatDownloadInput); } connection.Close(); } - // For testing using json input instead of database input + // Option 2: Example without database. Comment out this part if you want to use database + // Read cropfields "BulkSatDownloadInput.json" and write all stats to a single csv file + // Write all stats for multiple fields will be written to a single csv file + string downloadFolder; + string fileNameStats; + string headerLineStats = $"FieldName,satelliteDate,satelliteBand,max,min,mean,mode,median,stddev,minPlus,curtosis,maxMinus,skewness,variance,populationCount,variationCoefficient,confidenceIntervalLow, confidenceIntervalHigh,confidenceIntervalErrorMargin" + Environment.NewLine; var fieldsInputJson = File.ReadAllText("BulkSatDownloadInput.json"); - List fieldsInputs2 = JsonConvert.DeserializeObject>(fieldsInputJson); - - // Now for each input download all images. Use fieldsInputs or fieldsInputs2 for testing - foreach (var input in fieldsInputs2) + bulkSatDownloadInputListCsv = JsonConvert.DeserializeObject>(fieldsInputJson); + for (int i = 0; i < bulkSatDownloadInputListCsv.Count; i++) { + downloadFolder = bulkSatDownloadInputListCsv[i].downloadFolder; + fileNameStats = Path.Combine(downloadFolder, bulkSatDownloadInputListCsv[i].fileNameStats); + if (!Directory.Exists(downloadFolder)) + Directory.CreateDirectory(downloadFolder); + bulkSatDownloadInputListCsv[i].fileNameStats = fileNameStats; + // Header same as in GeneralService.DownloadSatelliteStats + // Delete fileNameStats if existing. Create a new file. Add a header to csv file + File.Delete(fileNameStats); + File.AppendAllText(fileNameStats, headerLineStats); + } + + // Now choose which list you want to use + bulkSatDownloadInputList = bulkSatDownloadInputListDB; //bulkSatDownloadInputListDB; //bulkSatDownloadInputListCsv; + + // Whichever option (database or json/csv), continue here + // Delete the settingsfile + File.Delete(settingsfile); + + // For each input download all images. Keep track to time, important when doing bulk downloads + var watch = System.Diagnostics.Stopwatch.StartNew(); + TimeSpan tsSofar = new TimeSpan(); + TimeSpan tsRemaining; + TimeSpan tsTotalEstimated; + + for (int i = 0; i < bulkSatDownloadInputList.Count; i++) + { + watch.Restart(); + bulkSatDownloadInput = bulkSatDownloadInputList[i]; + if (string.IsNullOrEmpty(bulkSatDownloadInput.fileNameStats) == false) _logger.LogInformation(string.Format($"// FarmmapsBulkSatDownload: Downloading stats for field {i+1} out of {bulkSatDownloadInputList.Count} to single csv file {bulkSatDownloadInput.fileNameStats}")); + if (bulkSatDownloadInput.database != null) _logger.LogInformation(string.Format($"// FarmmapsBulkSatDownload: Downloading stats for field {i+1} out of {bulkSatDownloadInputList.Count} to database {bulkSatDownloadInput.schemaname}.{bulkSatDownloadInput.satelllitetable}")); try { - await Process(roots, input); + await Process(roots, bulkSatDownloadInput); + } catch (Exception ex) { _logger.LogError(ex.Message); } + watch.Stop(); + tsSofar = tsSofar + watch.Elapsed; + tsTotalEstimated = tsSofar / (i + 1) * bulkSatDownloadInputList.Count; + tsRemaining = tsTotalEstimated - tsSofar; + _logger.LogInformation(string.Format($"// Time (hh:mm:ss): this field: {strTime(watch.Elapsed)}. Sofar: {strTime(tsSofar)}. Total: {strTime(tsTotalEstimated)}. Remaining: {strTime(tsRemaining)}")); } + string strExeFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location; + string strWorkPath = Path.GetDirectoryName(strExeFilePath); + _logger.LogInformation(string.Format($"// FarmmapsBulkSatDownload:")); + _logger.LogInformation(string.Format($"// FarmmapsBulkSatDownload: Done! List of all downloaded cropfieldItems stored in {Path.Combine(strWorkPath,settingsfile)}")); + _logger.LogInformation(string.Format($"// FarmmapsBulkSatDownload: If you plan to rerun certain fields then adding cropfielditemcode to your input can greatly speed up your application!")); + } private async Task Process(List roots, BulkSatDownloadInput input) { - string DownloadFolder = input.DownloadFolder; - if (!Directory.Exists(DownloadFolder)) - Directory.CreateDirectory(DownloadFolder); + string cropfielditemcode; + Item cropfieldItem; + bool satelliteItemsAvailable; + bool statsAvailable; + DateTime dtSatelliteDate; + string strSatelliteDate; + List satelliteItemsCropYear; + GroenmonitorTable groenmonitorTable = new GroenmonitorTable(); + List listSatelliteStatistics; + SatelliteStatistics satelliteStatistics_wdvi; + SatelliteStatistics satelliteStatistics_ndvi; + int cntDatesDownloaded; - // !!specify if you are using an already created cropfield: - bool useCreatedCropfield = input.UseCreatedCropfield; - string settingsfile = $"Settings_BulkSatDownloadApplication.json"; + string fieldName = input.fieldName; + int cropYear = input.cropYear; + List satelliteBands = input.SatelliteBands; + string downloadFolder = input.downloadFolder; + string fileNameStats = input.fileNameStats; + DB database = input.database; + string schemaname = input.schemaname; + string cropfieldtable = input.cropfieldtable; + string satelllitetable = input.satelllitetable; + DateTime lastDownloadedSatelliteDate = input.lastdownloadedimagedate; + cropfielditemcode = input.cropfielditemcode; + string insertSql = InsertSQLfromClass(schemaname, satelllitetable); LoadSettings(settingsfile); @@ -151,61 +217,195 @@ LIMIT 10;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT return; } - // Use already created cropfield or create new one - Item cropfieldItem; - if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.CropfieldItemCode)) + if (string.IsNullOrEmpty(cropfielditemcode)) { - _logger.LogInformation(string.Format($"Creating cropfield for a field in the year {input.cropYear}")); - //string geomjson = input.GeometryJson.ToString(Formatting.None); //"{\"type\":\"Polygon\",\"coordinates\":[[[4.960707146896585,52.80058366970849],[4.960645975538824,52.80047021761092],[4.962140695752897,52.7991771471948],[4.967523821195745,52.80150240004121],[4.966336768950911,52.80254373587981],[4.96171188076433,52.80100999685643],[4.960707146896585,52.80058366970849]]]}" - string geomjson = input.GeometryJson.ToString(Formatting.None); + _logger.LogInformation(string.Format($"Creating cropfield '{fieldName}' in the year {cropYear}")); cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, - $"DataCropfield {input.cropName}", input.cropYear, input.GeometryJson.ToString(Formatting.None)); - _settings.CropfieldItemCode = cropfieldItem.Code; - SaveSettings(settingsfile); + $"DataCropfield {fieldName}", cropYear, input.GeometryJson.ToString(Formatting.None)); + cropfielditemcode = cropfieldItem.Code; + // If working with a database, add this cropfieldItem.Code to the database so that next case same cropField is requested, will be faster + if (database != null) + { + // add this CropfieldItemCode to the parceltable + using (NpgsqlConnection connection = new NpgsqlConnection(database.GetConnectionString())) + { + connection.Open(); + NpgsqlCommand updateCmd = connection.CreateCommand(); + string updateSql = string.Format($"UPDATE {schemaname}.{cropfieldtable} SET cropfielditemcode = '{cropfieldItem.Code}' WHERE arbid = {input.fieldID};"); + updateCmd.CommandText = updateSql; + //Console.WriteLine(insertCmd.CommandText); + int r = updateCmd.ExecuteNonQuery(); + if (r != 1) + throw new Exception("// FarmmapsBulkSatDownload: Insert Failed"); + connection.Close(); + } + _logger.LogInformation($"// FarmmapsBulkSatDownload: Added cropfieldItem.Code '{cropfieldItem.Code}' for parcelid {input.fieldID} to {schemaname}.{cropfieldtable} "); + } } else { - _logger.LogInformation("Cropfield already exists, trying to get it"); - cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode); + // WOULD IT BE POSSIBLE TO GET AVAILABLE Item MEMBER VALUES FOR A GIVEN cropfielditemcode? + cropfieldItem = new Item(); + cropfieldItem.Code = cropfielditemcode; + cropfieldItem.Name = "DataCropfield " + fieldName; + _logger.LogInformation($"// FarmmapsBulkSatDownload: CropfieldItem.Code for parcelid {input.fieldID} already there in {schemaname}.{cropfieldtable}: '{cropfieldItem.Code}'"); } + _settings.cropFieldItems.Add(cropfieldItem); + SaveSettings(settingsfile); - // check if satellite task not yet done, do here and save taskcode - if (useCreatedCropfield == false || string.IsNullOrEmpty(_settings.SatelliteTaskCode)) + //Create satelliteTaskCode & save satelliteTaskCode.Code to settingsfile for retracing last call (can be useful if failed) + _logger.LogInformation(string.Format($"Running RunSatelliteTask for cropfieldItem '{cropfielditemcode}' and saving settings to {settingsfile}")); + var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); + // POSSIBLE & DESIRABLE TO ALSO LOG satelliteTaskCode? + // SaveSettings(settingsfile); + + // Getting satellite items + _logger.LogInformation(string.Format($"Running FindSatelliteItemsCropYear for cropfieldItem.Code '{cropfieldItem.Code}', SatelliteTaskCode '{satelliteTaskCode}'")); + satelliteItemsCropYear = await _generalService.FindSatelliteItems(cropfieldItem, satelliteTaskCode); + + // Checking if satellite items found + satelliteItemsAvailable = true; + if (satelliteItemsCropYear == null) { - var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); - _settings.SatelliteTaskCode = satelliteTaskCode; - SaveSettings(settingsfile); + satelliteItemsAvailable = false; + _logger.LogInformation($"No satellite tiffs found for fieldName '{fieldName}', cropYear {cropYear}"); + } + else + { + if (satelliteItemsCropYear.Count == 0) + { + satelliteItemsAvailable = false; + _logger.LogInformation($"No satellite tiffs found for fieldName '{fieldName}', cropYear {cropYear}"); + } } - // Now get the tiffs - List satelliteTiffs = await _generalService.FindSatelliteItemsAll(cropfieldItem, _settings.SatelliteTaskCode); - _logger.LogInformation(string.Format($"Downloading {satelliteTiffs.Count} geotiffs for a field in year {input.cropYear} ...")); - foreach (Item satalliteItem in satelliteTiffs) { - // download the geotiff. Returns a zip file with always these three files: - // data.dat.aux.xml - // thumbnail.jpg - // wenr.tif. Contains 5 layers: (1) ndvi, (2) wdvi, (3) Red, (4) Green and (5) Blue - DateTime SatelliteImageDate = (DateTime)satalliteItem.DataDate; - string SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); - string fileName = string.Format($"{input.cropName}_{input.fieldName}_{SatelliteDate}"); // assuming the fieldName is good enough for an ID. One might add here _{input.fieldID} or _{input.cropName} - string fileNameZip = string.Format($"{fileName}.zip"); - string fileNameGeotiff = string.Format($"{fileName}.tif"); - await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, Path.Combine(DownloadFolder, fileNameZip)); + // Sort the list by date + if (satelliteItemsAvailable) satelliteItemsCropYear = satelliteItemsCropYear.OrderBy(x => x.DataDate).ToList(); - // Extract the file "wenr.tif" from zip, rename it, delete "wenr.tif". overwriteFiles = true - ZipFile.ExtractToDirectory(Path.Combine(DownloadFolder, fileNameZip), DownloadFolder, true); - File.Delete(Path.Combine(DownloadFolder, fileNameGeotiff)); // Delete the fileNameGeotiff file if exists - File.Move(Path.Combine(DownloadFolder, "wenr.tif"), Path.Combine(DownloadFolder, fileNameGeotiff)); // Rename the oldFileName into newFileName - - // Cleanup - string[] filesToDelete = new string[] { fileNameZip, "wenr.tif", "thumbnail.jpg", "data.dat.aux.xml" }; - foreach (string f in filesToDelete) + // Download statistics to a single csv file + if (satelliteItemsAvailable && downloadFolder != null && fileNameStats != null) + { + // Write statistics for all images for all fieldNane and cropYear to a single csv file, fileNameStats + _logger.LogInformation($"Downloading stats for field '{fieldName}' in cropyear {cropYear} to {fileNameStats}"); + string downloadedStats = await _generalService.DownloadSatelliteStats(satelliteItemsCropYear, fieldName, satelliteBands, downloadFolder); + // Add contents of this csv file to thee single large csv file + var retainedLines = File.ReadAllLines(downloadedStats).Skip(1); + File.AppendAllLines(fileNameStats, retainedLines); + File.Delete(downloadedStats); + // Optionally, also download the zipped tiffs. This can be a lot of files and increase runtime + if (false) { - File.Delete(Path.Combine(DownloadFolder, f)); + foreach (Item selectedSatelliteItem in satelliteItemsCropYear) + { + // download the geotiffs. Returns a zip file with always these three files: + // data.dat.aux.xml + // thumbnail.jpg + // wenr.tif. Contains 5 layers: (1) ndvi, (2) wdvi, (3) Red, (4) Green and (5) Blue + var SatelliteDate = selectedSatelliteItem.DataDate.Value.ToString("yyyyMMdd"); + _logger.LogInformation($"Downloading geotiff file for field {fieldName}, date {SatelliteDate}"); + string fileName = string.Format($"satelliteGeotiff_{fieldName}_{SatelliteDate}"); // no need to add satelliteBand in the name because the tif contains all bands + string fileNameZip = string.Format($"{fileName}.zip"); + string fileNameGeotiff = string.Format($"{fileName}.tif"); + await _farmmapsApiService.DownloadItemAsync(selectedSatelliteItem.Code, Path.Combine(downloadFolder, fileNameZip)); + if (false) + { + // Extract the file "wenr.tif" from zip, rename it to fileNameGeotiff + ZipFile.ExtractToDirectory(Path.Combine(downloadFolder, fileNameZip), downloadFolder, true); + File.Delete(Path.Combine(downloadFolder, fileNameGeotiff)); // Delete the fileNameGeotiff file if exists + File.Move(Path.Combine(downloadFolder, "wenr.tif"), Path.Combine(downloadFolder, fileNameGeotiff)); // Rename the oldFileName into newFileName + + // Cleanup + string[] filesToDelete = new string[] { fileNameZip, "wenr.tif", "thumbnail.jpg", "data.dat.aux.xml" }; + foreach (string f in filesToDelete) + { + File.Delete(Path.Combine(downloadFolder, f)); + } + } + } + } + } + // Download statistics to database + if (satelliteItemsAvailable && database != null) + { + // Tailormade code for writing to database + // No unnecessary intermediate step here of writing to csv and getting stats for all images in the crop year. + // Efficient is to check if there is any image for which stats are to be added to database and add only add these new stats for not yet archived dates directly to database + // A full check of downloaded dates versus available dates in the database is not made here. + // We assume only new images will be added (i.e. for later dates), assuming no historical images are added in groenmonitor (check!). + // And we assume farmMaps always nicely generates statistics, no hick-ups + // Under this assumptions, we only need to compare with the lastDownloadedSatelliteDate from the database + cntDatesDownloaded = 0; + foreach (Item satelliteItem in satelliteItemsCropYear) + { + dtSatelliteDate = satelliteItem.DataDate.Value; + strSatelliteDate = dtSatelliteDate.ToString("yyyy-MM-dd"); + listSatelliteStatistics = await _generalService.ListSatelliteStatistics(satelliteItem, satelliteBands, fieldName); + statsAvailable = true; + if (listSatelliteStatistics == null) + { + statsAvailable = false; + _logger.LogWarning($"No stats found for satellite, fielName '{fieldName}', date '{strSatelliteDate}'"); + } + else + { + if (listSatelliteStatistics.Count == 0) + { + statsAvailable = false; + _logger.LogWarning($"No stats found for satellite, fielName '{fieldName}', date '{strSatelliteDate}'"); + } + } + if (statsAvailable) + { + if (dtSatelliteDate <= input.lastdownloadedimagedate) + { + _logger.LogInformation($"// Stats for parcelid {input.fieldID}, date '{strSatelliteDate}' already there in {schemaname}.{satelllitetable}"); + } + else + { + cntDatesDownloaded++; + // Map satelliteStatistics to groenmonitorTable + satelliteStatistics_wdvi = listSatelliteStatistics.SingleOrDefault(p => p.satelliteBand == "wdvi"); + satelliteStatistics_ndvi = listSatelliteStatistics.SingleOrDefault(p => p.satelliteBand == "ndvi"); + groenmonitorTable.parcelid = input.fieldID; + groenmonitorTable.date = strSatelliteDate; + groenmonitorTable.source = "akkerwebwenr"; // Like this so SQL in bigdata.groenmonitorlatest_flowerbulbs works properly + groenmonitorTable.wdvi_pixelcount = satelliteStatistics_wdvi.populationCount; //count of pixels with data + groenmonitorTable.wdvi_max = satelliteStatistics_wdvi.max; + groenmonitorTable.wdvi_mean = satelliteStatistics_wdvi.mean; + groenmonitorTable.wdvi_min = satelliteStatistics_wdvi.min; + groenmonitorTable.wdvi_stdev = satelliteStatistics_wdvi.stddev; + groenmonitorTable.wdvi_median = satelliteStatistics_wdvi.median; + groenmonitorTable.wdvi_p90 = -99; // Example of a statistics (90% not included in satelliteStatistics + groenmonitorTable.ndvi_pixelcount = satelliteStatistics_ndvi.populationCount; //count of pixels with data + groenmonitorTable.ndvi_max = satelliteStatistics_ndvi.max; + groenmonitorTable.ndvi_mean = satelliteStatistics_ndvi.mean; + groenmonitorTable.ndvi_min = satelliteStatistics_ndvi.min; + groenmonitorTable.ndvi_stdev = satelliteStatistics_ndvi.stddev; + groenmonitorTable.ndvi_median = satelliteStatistics_ndvi.median; + groenmonitorTable.ndvi_p90 = -99; // Example of a statistics (90% not included in satelliteStatistics + + // fill the insertSql query with fieldValues from the groenmonitorTable, then run the query + using (NpgsqlConnection connection = new NpgsqlConnection(database.GetConnectionString())) + { + connection.Open(); + NpgsqlCommand insertCmd = connection.CreateCommand(); + object[] fieldValues = groenmonitorTable.GetType() + .GetFields() + .Select(field => field.GetValue(groenmonitorTable)) + .ToArray(); + insertCmd.CommandText = string.Format(insertSql, fieldValues); + //Console.WriteLine(insertCmd.CommandText); + int r = insertCmd.ExecuteNonQuery(); + if (r != 1) + throw new Exception("// FarmmapsBulkSatDownload: Insert Failed"); + connection.Close(); + } + _logger.LogInformation($"// Added stats to {schemaname}.{satelllitetable} for parcelid {input.fieldID}, date '{strSatelliteDate}'. cntDatesDownloaded: {cntDatesDownloaded}"); + } + } } } } - // Functions to save previously created cropfields private void LoadSettings(string file) { @@ -216,7 +416,9 @@ LIMIT 10;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT } else { - _settings = new Settings(); + Settings settings = new Settings(); + settings.cropFieldItems = new List(); + _settings = settings; } } @@ -228,14 +430,67 @@ LIMIT 10;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT var json = JsonConvert.SerializeObject(_settings); File.WriteAllText(file, json); } - private void SaveInfo(string file) + private string strTime(TimeSpan ts) + { + return String.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds); + } + private string InsertSQLfromClass(string schemaname, string groenmonitortablename) { - if (_settings == null) - return; - - var json = JsonConvert.SerializeObject(_settings); - File.WriteAllText(file, json); - + // Generates an INSERT query for GroenmonitorTable. + // When writing to a different table structure, just make a new class for the statellite statistics table, similar to GroenmonitorTable.cs + // @" + //INSERT INTO bigdata.groenmonitor (parcelid,date,wdvi_pixelcount,wdvi_max,wdvi_mean,wdvi_min,wdvi_stdev,wdvi_median,wdvi_p90,ndvi_pixelcount,ndvi_max,ndvi_mean,ndvi_min,ndvi_stdev,ndvi_median,ndvi_p90) + //VALUES({0},'{1}',{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}) + //ON CONFLICT (parcelid, date) + //DO UPDATE SET parcelid={0},date='{1}',wdvi_pixelcount={2},wdvi_max={3},wdvi_mean={4},wdvi_min={5},wdvi_stdev={6},wdvi_median={7},wdvi_p90={8},ndvi_pixelcount={9},ndvi_max={10},ndvi_mean={11},ndvi_min={12},ndvi_stdev={13},ndvi_median={14},ndvi_p90={15} + //"; + string fieldName; + var fieldNames = typeof(GroenmonitorTable).GetFields() + .Select(field => field.Name) + .ToList(); + StringBuilder sbInsertSql = new StringBuilder(); + string insertSql; + StringBuilder insertSql1 = new StringBuilder(); + insertSql1.Append(@"INSERT INTO " + schemaname + "." + groenmonitortablename + " ("); + StringBuilder insertSql2 = new StringBuilder(); + insertSql2.Append("VALUES("); + StringBuilder insertSql3 = new StringBuilder(); + insertSql3.Append("ON CONFLICT(parcelid, date, source)"); + StringBuilder insertSql4 = new StringBuilder(); + insertSql4.Append("DO UPDATE SET "); + for (int i = 0; i < fieldNames.Count; i++) + { + fieldName = fieldNames[i]; + insertSql1.Append(fieldName); + if (fieldName == "date" | fieldName == "source") + { + //add extra quotes ' for fields in the Postgress table for which we know they are date or varchar + insertSql2.Append("'{" + i + "}'"); + insertSql4.Append(fieldName + "='{" + i + "}'"); + } + else + { + insertSql2.Append("{" + i + "}"); + insertSql4.Append(fieldName + "={" + i + "}"); + } + if (i < fieldNames.Count - 1) + { + insertSql1.Append(","); + insertSql2.Append(","); + insertSql4.Append(","); + } + else + { + insertSql1.Append(")"); + insertSql2.Append(")"); + } + } + sbInsertSql.AppendLine(insertSql1.ToString()); + sbInsertSql.AppendLine(insertSql2.ToString()); + sbInsertSql.AppendLine(insertSql3.ToString()); + sbInsertSql.AppendLine(insertSql4.ToString()); + insertSql = sbInsertSql.ToString(); + return insertSql; } } diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadInput.json b/FarmmapsBulkSatDownload/BulkSatDownloadInput.json index 3463e41..d045560 100644 --- a/FarmmapsBulkSatDownload/BulkSatDownloadInput.json +++ b/FarmmapsBulkSatDownload/BulkSatDownloadInput.json @@ -1,12 +1,63 @@ [ { - "UseCreatedCropfield": false, - "fieldName": "fld5641", // FarmMaps won't do without a fieldName - "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ - "cropName": "wheat", + "fieldName": "5641", // FarmMaps minimum needs are: fieldName, cropYear & geometryJson + "cropYear": 1999, //For testing a year for which we know no satellite data available, program shouldn't crash + "fieldID": 5641, + "SatelliteBands": [ "wdvi", "ndvi" ], // ["ndvi"] or ["wdvi"] or both: [ "wdvi", "ndvi" ] + "lastdownloadedimagedate": "1999-01-01", //downloads images from this date till end of the year + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + }, + "cropfielditemcode": null, //BulkSatDownloadApplication returns a Settings.json file with cropfielditems.Code. After first run, you can copy-paste that here to speed up + "downloadFolder": "C:\\workdir\\groenmonitor\\", + "fileNameStats": "BulkSatDownload.csv", + "database": null, + "schemaname": null, + "cropfieldtable": null, + "satelllitetable": null + }, + { + "fieldName": "5641", // FarmMaps minimum needs are: fieldName, cropYear & geometryJson "cropYear": 2021, "fieldID": 5641, - "lastdownloadedimagedate": "2021-01-01", + "SatelliteBands": [ "wdvi", "ndvi" ], // ["ndvi"] or ["wdvi"] or both: [ "wdvi", "ndvi" ] + "lastdownloadedimagedate": "2021-01-01", //downloads images from this date till end of the year "geometryJson": { "type": "Polygon", "coordinates": [ @@ -45,65 +96,22 @@ [ 3.37837807779104, 51.3231095796538 ] ] ] - } - }, + }, + "cropfielditemcode": null, //BulkSatDownloadApplication returns a Settings.json file with cropfielditems.Code. After first run, you can copy-paste that here to speed up + "downloadFolder": "C:\\workdir\\groenmonitor\\", + "fileNameStats": "BulkSatDownload.csv", + "database": null, + "schemaname": null, + "cropfieldtable": null, + "satelllitetable": null + } + , { - "UseCreatedCropfield": false, - "fieldName": "fld5641", // FarmMaps won't do without a fieldName - "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ - "cropName": "wheat", + "fieldName": "5641", // FarmMaps minimum needs are: fieldName, cropYear & geometryJson "cropYear": 2020, "fieldID": 5641, - "lastdownloadedimagedate": "2020-01-01", - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.37837807779104, 51.3231095796538 ], - [ 3.38065689232502, 51.3212527499355 ], - [ 3.38022924592256, 51.3210683536359 ], - [ 3.37980548452565, 51.3208801127141 ], - [ 3.37959556105776, 51.3207540143696 ], - [ 3.3793691292654, 51.3205959677371 ], - [ 3.37822219207335, 51.3215667913007 ], - [ 3.37816999925795, 51.3216109809456 ], - [ 3.37646704574705, 51.3208025481261 ], - [ 3.37646695791282, 51.3208025061493 ], - [ 3.37608401443192, 51.3206231652693 ], - [ 3.37607169507628, 51.3206173959751 ], - [ 3.37606021048754, 51.320612017601 ], - [ 3.37582728410659, 51.3205029306946 ], - [ 3.37580409779263, 51.3206502985963 ], - [ 3.37575872019649, 51.3207993094705 ], - [ 3.37575476634361, 51.3208122883487 ], - [ 3.37571181656268, 51.3208797459348 ], - [ 3.3756624532907, 51.3209415238446 ], - [ 3.37557609963811, 51.3210110142077 ], - [ 3.37541089899821, 51.3211055871218 ], - [ 3.37477516102591, 51.3214102985009 ], - [ 3.37473173914127, 51.3214311108204 ], - [ 3.37455904622072, 51.3215138815012 ], - [ 3.37415098054777, 51.3217199232877 ], - [ 3.37313700916272, 51.3222422862785 ], - [ 3.37748824689601, 51.3242852920348 ], - [ 3.37749760805371, 51.3242713084009 ], - [ 3.37811903757028, 51.3233437635596 ], - [ 3.37818758851947, 51.3232647797363 ], - [ 3.37823803668144, 51.3232236798646 ], - [ 3.37837807779104, 51.3231095796538 ] - - ] - ] - } - }, - { - "UseCreatedCropfield": false, - "fieldName": "fld5641", // FarmMaps won't do without a fieldName - "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ - "cropName": "wheat", - "cropYear": 2019, - "fieldID": 5641, - "lastdownloadedimagedate": "2019-01-01", + "SatelliteBands": [ "wdvi", "ndvi" ], // ["ndvi"] or ["wdvi"] or both: [ "wdvi", "ndvi" ] + "lastdownloadedimagedate": "2020-01-01", //downloads images from this date till end of the year "geometryJson": { "type": "Polygon", "coordinates": [ @@ -142,152 +150,13 @@ [ 3.37837807779104, 51.3231095796538 ] ] ] - } - }, - { - "UseCreatedCropfield": false, - "fieldName": "fld5641", // FarmMaps won't do without a fieldName - "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ - "cropName": "wheat", - "cropYear": 2018, - "fieldID": 5641, - "lastdownloadedimagedate": "2018-01-01", - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.37837807779104, 51.3231095796538 ], - [ 3.38065689232502, 51.3212527499355 ], - [ 3.38022924592256, 51.3210683536359 ], - [ 3.37980548452565, 51.3208801127141 ], - [ 3.37959556105776, 51.3207540143696 ], - [ 3.3793691292654, 51.3205959677371 ], - [ 3.37822219207335, 51.3215667913007 ], - [ 3.37816999925795, 51.3216109809456 ], - [ 3.37646704574705, 51.3208025481261 ], - [ 3.37646695791282, 51.3208025061493 ], - [ 3.37608401443192, 51.3206231652693 ], - [ 3.37607169507628, 51.3206173959751 ], - [ 3.37606021048754, 51.320612017601 ], - [ 3.37582728410659, 51.3205029306946 ], - [ 3.37580409779263, 51.3206502985963 ], - [ 3.37575872019649, 51.3207993094705 ], - [ 3.37575476634361, 51.3208122883487 ], - [ 3.37571181656268, 51.3208797459348 ], - [ 3.3756624532907, 51.3209415238446 ], - [ 3.37557609963811, 51.3210110142077 ], - [ 3.37541089899821, 51.3211055871218 ], - [ 3.37477516102591, 51.3214102985009 ], - [ 3.37473173914127, 51.3214311108204 ], - [ 3.37455904622072, 51.3215138815012 ], - [ 3.37415098054777, 51.3217199232877 ], - [ 3.37313700916272, 51.3222422862785 ], - [ 3.37748824689601, 51.3242852920348 ], - [ 3.37749760805371, 51.3242713084009 ], - [ 3.37811903757028, 51.3233437635596 ], - [ 3.37818758851947, 51.3232647797363 ], - [ 3.37823803668144, 51.3232236798646 ], - [ 3.37837807779104, 51.3231095796538 ] - ] - ] - } - }, - { - "UseCreatedCropfield": false, - "fieldName": "fld5641", // FarmMaps won't do without a fieldName - "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ - "cropName": "wheat", - "cropYear": 2017, - "fieldID": 5641, - "lastdownloadedimagedate": "2017-01-01", - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.37837807779104, 51.3231095796538 ], - [ 3.38065689232502, 51.3212527499355 ], - [ 3.38022924592256, 51.3210683536359 ], - [ 3.37980548452565, 51.3208801127141 ], - [ 3.37959556105776, 51.3207540143696 ], - [ 3.3793691292654, 51.3205959677371 ], - [ 3.37822219207335, 51.3215667913007 ], - [ 3.37816999925795, 51.3216109809456 ], - [ 3.37646704574705, 51.3208025481261 ], - [ 3.37646695791282, 51.3208025061493 ], - [ 3.37608401443192, 51.3206231652693 ], - [ 3.37607169507628, 51.3206173959751 ], - [ 3.37606021048754, 51.320612017601 ], - [ 3.37582728410659, 51.3205029306946 ], - [ 3.37580409779263, 51.3206502985963 ], - [ 3.37575872019649, 51.3207993094705 ], - [ 3.37575476634361, 51.3208122883487 ], - [ 3.37571181656268, 51.3208797459348 ], - [ 3.3756624532907, 51.3209415238446 ], - [ 3.37557609963811, 51.3210110142077 ], - [ 3.37541089899821, 51.3211055871218 ], - [ 3.37477516102591, 51.3214102985009 ], - [ 3.37473173914127, 51.3214311108204 ], - [ 3.37455904622072, 51.3215138815012 ], - [ 3.37415098054777, 51.3217199232877 ], - [ 3.37313700916272, 51.3222422862785 ], - [ 3.37748824689601, 51.3242852920348 ], - [ 3.37749760805371, 51.3242713084009 ], - [ 3.37811903757028, 51.3233437635596 ], - [ 3.37818758851947, 51.3232647797363 ], - [ 3.37823803668144, 51.3232236798646 ], - [ 3.37837807779104, 51.3231095796538 ] - ] - ] - } - }, - { - "UseCreatedCropfield": false, - "fieldName": "fld5641", // FarmMaps won't do without a fieldName - "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\"; // "Downloads" -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ - "cropName": "wheat", - "cropYear": 2016, - "fieldID": 5641, - "lastdownloadedimagedate": "2016-01-01", - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 3.37837807779104, 51.3231095796538 ], - [ 3.38065689232502, 51.3212527499355 ], - [ 3.38022924592256, 51.3210683536359 ], - [ 3.37980548452565, 51.3208801127141 ], - [ 3.37959556105776, 51.3207540143696 ], - [ 3.3793691292654, 51.3205959677371 ], - [ 3.37822219207335, 51.3215667913007 ], - [ 3.37816999925795, 51.3216109809456 ], - [ 3.37646704574705, 51.3208025481261 ], - [ 3.37646695791282, 51.3208025061493 ], - [ 3.37608401443192, 51.3206231652693 ], - [ 3.37607169507628, 51.3206173959751 ], - [ 3.37606021048754, 51.320612017601 ], - [ 3.37582728410659, 51.3205029306946 ], - [ 3.37580409779263, 51.3206502985963 ], - [ 3.37575872019649, 51.3207993094705 ], - [ 3.37575476634361, 51.3208122883487 ], - [ 3.37571181656268, 51.3208797459348 ], - [ 3.3756624532907, 51.3209415238446 ], - [ 3.37557609963811, 51.3210110142077 ], - [ 3.37541089899821, 51.3211055871218 ], - [ 3.37477516102591, 51.3214102985009 ], - [ 3.37473173914127, 51.3214311108204 ], - [ 3.37455904622072, 51.3215138815012 ], - [ 3.37415098054777, 51.3217199232877 ], - [ 3.37313700916272, 51.3222422862785 ], - [ 3.37748824689601, 51.3242852920348 ], - [ 3.37749760805371, 51.3242713084009 ], - [ 3.37811903757028, 51.3233437635596 ], - [ 3.37818758851947, 51.3232647797363 ], - [ 3.37823803668144, 51.3232236798646 ], - [ 3.37837807779104, 51.3231095796538 ] - ] - ] - } + }, + "cropfielditemcode": null, //BulkSatDownloadApplication returns a Settings.json file with cropfielditems.Code. After first run, you can copy-paste that here to speed up + "downloadFolder": "C:\\workdir\\groenmonitor\\", + "fileNameStats": "BulkSatDownload.csv", + "database": null, + "schemaname": null, + "cropfieldtable": null, + "satelllitetable": null } - - ] \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj b/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj index f8e8b08..01e90bd 100644 --- a/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj +++ b/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj @@ -16,7 +16,7 @@ Always - + Always diff --git a/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs b/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs index f89833f..e23b914 100644 --- a/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs +++ b/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs @@ -1,18 +1,24 @@ using System; +using System.Collections.Generic; using Newtonsoft.Json.Linq; namespace FarmmapsBulkSatDownload.Models { public class BulkSatDownloadInput { - public bool UseCreatedCropfield { get; set; } public string fieldName { get; set; } - public string DownloadFolder { get; set; } - public string cropName { get; set; } public int cropYear { get; set; } public int fieldID { get; set; } + public List SatelliteBands { get; set; } public DateTime lastdownloadedimagedate { get; set; } public JObject GeometryJson { get; set; } - //public string fieldName { get { return string.Format($"{cropName}_fld{fieldID}"); } } + public string cropfielditemcode { get; set; } + public string downloadFolder { get; set; } + public string fileNameStats { get; set; } + public DB database { get; set; } + public string schemaname { get; set; } + public string cropfieldtable { get; set; } + public string satelllitetable { get; set; } + } } \ No newline at end of file diff --git a/FarmmapsBulkSatDownload/Models/DB.cs b/FarmmapsBulkSatDownload/Models/DB.cs index 2ff705f..0cd2204 100644 --- a/FarmmapsBulkSatDownload/Models/DB.cs +++ b/FarmmapsBulkSatDownload/Models/DB.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text; using Npgsql; -namespace FarmmapsApi.Services +namespace FarmmapsBulkSatDownload.Models { public class DB { diff --git a/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs b/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs index 8b48028..9437e62 100644 --- a/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs +++ b/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs @@ -6,7 +6,7 @@ namespace FarmmapsBulkSatDownload.Models { public class GroenmonitorTable { - //public string parceltablename; //PO20190605: parceltablename added as a field (=column) in the GroenmonitorTable. The GroenmonitorTable contains data from multiple parceltables + //double? makes a member nullable public int parcelid; public string date; public string source; diff --git a/FarmmapsBulkSatDownload/Models/Settings.cs b/FarmmapsBulkSatDownload/Models/Settings.cs index 2d70cba..51d4ebb 100644 --- a/FarmmapsBulkSatDownload/Models/Settings.cs +++ b/FarmmapsBulkSatDownload/Models/Settings.cs @@ -1,11 +1,10 @@ -namespace FarmmapsBulkSatDownload +using System.Collections.Generic; +using FarmmapsApi.Models; + +namespace FarmmapsBulkSatDownload { public class Settings { - public string CropfieldItemCode { get; set; } - public string SatelliteTaskCode { get; set; } - public string VanDerSatTaskCode { get; set; } - public string WatBalTaskCode { get; set; } - + public List cropFieldItems { get; set; } } -} \ No newline at end of file +} diff --git a/FarmmapsBulkSatDownload/ShowGeotiff.r b/FarmmapsBulkSatDownload/ShowGeotiff.r index 68e010c..bd28e92 100644 --- a/FarmmapsBulkSatDownload/ShowGeotiff.r +++ b/FarmmapsBulkSatDownload/ShowGeotiff.r @@ -1,15 +1,18 @@ # ShowGeotiff.r # I downloaded and calculated the stats for the polygon defined in C:\git\FarmMapsApiClient_WURtest\FarmmapsDataDownload\DataDownloadInput.json -# in which I set "SatelliteBand": "wdvi" and in which in the console I requested image 8 for date '2020-06-14' +# in which I set "SatelliteBand": "wdvi" and in which in the console I requested the image for date '2020-09-22' +# FarmmapsBulkSatDownload generates many files. Here is what I tried when inputing the same field for a number of years using BulkSatDownloadInput.json +# see list below library(raster) library(sf) library(rgdal) -setwd("C:/workdir/groenmonitor/") -# FarmmapsDataDownload generates two files: +setwd("C:/workdir/groenmonitor/DataDownload/") + +# FarmmapsDataDownload and BulkSatDownload can be used to download zip files with inside two files: fileGeotiff <- "wenr.tif" -# fileJpg <- "thumbnail.jpg" -# FarmmapsBulkSatDownload generates many files. Here is what I tried when inputing the same field for a number of years using BulkSatDownloadInput.json +fileJpg <- "thumbnail.jpg" +# Here is what I tried when inputing the same field for a number of years: # fileGeotiff <- "wheat_fld5641_20210224.tif" # 2 files for year 2021. This file is a nice example having in upperleft corner no data # fileGeotiff <- "wheat_fld5641_20210331.tif" # 2 files for year 2021 # fileGeotiff <- "wheat_fld5641_20200321.tif" # 14 files for year 2020, earliest @@ -24,11 +27,11 @@ year <- substr(fileGeotiff,lenfilename-11,lenfilename-8) imgdate <- substr(fileGeotiff,lenfilename-11,lenfilename-4) # The thumbnail has the polygon clipped out, has 1 layer, no crs and the mean value is not the mean wdvi we are looking for -# r.thumbnail <- raster(fileJpg) -# plot(r.thumbnail) -# crs(r.thumbnail) -# CRS arguments: NA -# cellStats(r.thumbnail,'mean') #59.91667 +r.thumbnail <- raster(fileJpg) +plot(r.thumbnail) +crs(r.thumbnail) +#CRS arguments: NA +cellStats(r.thumbnail,'mean') #87.5128 # nonsense stk.wenr <- stack(x=fileGeotiff) # plot(stk.wenr) shows 5 plots (5 bands) @@ -49,11 +52,7 @@ crs(stk.wenr) r.wenr.rd.wdvi <- subset(stk.wenr,2) dev.off() plot(r.wenr.rd.wdvi,main=paste("wdvi",imgdate),xlab="RDX",ylab="RDY") -cellStats(r.wenr.rd.wdvi,'mean') #0.1654627 -# Same as in downloaded file SatelliteDataStatistics_test_satData_wdvi_2020-06-14.csv -# "mean": 0.16564258790046743 -# So FindSatelliteItem in C:\git\FarmMapsApiClient_WURtest\FarmmapsApi\Services\GeneralService.cs returns a a stacked geotiff in -# i.e. the cellstats are calculated from the rectangle +cellStats(r.wenr.rd.wdvi,'mean') #0.1350561 # Furthermore we can see # shows coordinates in RD @@ -69,6 +68,15 @@ r.wenr.wgs84.wdvi <- projectRaster(r.wenr.rd.wdvi, crs = CRS('+init=EPSG:4326')) # plot(p1,add=TRUE, col="transparent",border="black") # Draw the polygon on top of the raster # Polygon p2 from C:\git\FarmMapsApiClient_WURtest\FarmmapsDataDownload\DataDownloadInput.json +p2 <- data.frame(id = 1, wkt = gsub("\n","",'POLYGON(( + 4.960707146896585 52.800583669708487, + 4.960645975538824 52.800470217610922, + 4.962140695752897 52.799177147194797, + 4.967523821195745 52.801502400041208, + 4.966336768950911 52.802543735879809, + 4.961711880764330 52.801009996856429, + 4.960707146896585 52.800583669708487))')) +# Polygon p2 from C:\git\FarmMapsApiClient_WURtest\FarmmapsBulkSatDownload\BulkSatDownloadInput.json p2 <- data.frame(id = 1, wkt = gsub("\n","",'POLYGON(( 3.37837807779104 51.3231095796538, 3.38065689232502 51.3212527499355, @@ -123,11 +131,29 @@ dev.off() plot(r.wenr.wgs84.wdvi.pol,main=paste("wdvi",imgdate),xlab="LON",ylab="LAT") plot(p2.wgs84,add=TRUE, col="transparent",border="red") #That's what we want! Now compare the stats -cellStats(r.wenr.rd.wdvi,'mean') #0.1654627 # Stats from rectangle, RD -cellStats(r.wenr.wgs84.wdvi,'mean') #0.1656399 # Stats from rectangle, WGS84 -cellStats(r.wenr.rd.wdvi.pol,'mean') #0.1658702 # Stats from raster clipped by polygon, WGS84 -cellStats(r.wenr.wgs84.wdvi.pol,'mean') #0.1658611 # Stats from raster clipped by polygon, WGS84 -# Conclusion: in this example little difference, but can be quite different! +cellStats(r.wenr.rd.wdvi,'mean') # [1] 0.1350561 # Stats from rectangle, RD +cellStats(r.wenr.wgs84.wdvi,'mean') # [1] 0.1351411 # Stats from rectangle, WGS84 +cellStats(r.wenr.rd.wdvi.pol,'mean') # [1] 0.05723957 # Stats from raster clipped by polygon, RD +cellStats(r.wenr.wgs84.wdvi.pol,'mean') # [1] 0.05723607 # Stats from raster clipped by polygon, WGS84 +# file SatelliteDataStatistics_test_satData_wdvi_2020-09-22.csv +# "mean": 0.057430520945401985 # SatelliteDataStatistics_test_satData_wdvi_2020.csv returns stats for the clipped raster (.pol). 'mean' almost the same, maybe +# cellStats cannot return median, just a few stats. +cellStats(r.wenr.wgs84.wdvi.pol,'median') # Error in .local(x, stat, ...) : invalid 'stat'. Should be sum, min, max, sd, mean, or 'countNA' +r.wenr.wgs84.wdvi.vals <- values(r.wenr.wgs84.wdvi) +median(r.wenr.wgs84.wdvi.vals) # [1] NA +median(r.wenr.wgs84.wdvi.vals,na.rm=TRUE) # [1] 0.076 +r.wenr.wgs84.wdvi.pol.vals <- values(r.wenr.wgs84.wdvi.pol) +median(r.wenr.wgs84.wdvi.pol.vals) # [1] NA +median(r.wenr.wgs84.wdvi.pol.vals,na.rm=TRUE) # [1] 0.048 +# "median": 0.04800000041723251 # SatelliteDataStatistics_test_satData_wdvi_2020.csv returns stats for the clipped raster (.pol). +# An image may contain NA values. Check: +cellStats(r.wenr.wgs84.wdvi,'countNA') # [1] 22956 +ncell(r.wenr.wgs84.wdvi) # [1] 221696 +cellStats(r.wenr.wgs84.wdvi,'countNA') / ncell(r.wenr.wgs84.wdvi) # [1] 0.1035472 # 10% no data? doesn't show in the plot? +cellStats(r.wenr.wgs84.wdvi.pol,'countNA') # [1] 147387 +summary(r.wenr.wgs84.wdvi.pol.vals) # shows the same: NA's: 147387 +ncell(r.wenr.wgs84.wdvi.pol) # [1] 221696 +cellStats(r.wenr.wgs84.wdvi.pol,'countNA') / ncell(r.wenr.wgs84.wdvi.pol) # [1] 0.6648158 # 66% no data? doesn't show in the plot? # The project FarmmapsNbs can generate a wenr.tif file, application.tif, uptake.tif (in rtest1.uptake.zip)and shape.shp (in rtest1.taskmap.zip) r.application <- raster("C:/git/FarmMapsApiClient_WURtest/FarmmapsNbs/bin/Debug/netcoreapp3.1/Downloads/application.tif") diff --git a/FarmmapsDataDownload/DataDownloadApplication.cs b/FarmmapsDataDownload/DataDownloadApplication.cs index 6001596..c0faa10 100644 --- a/FarmmapsDataDownload/DataDownloadApplication.cs +++ b/FarmmapsDataDownload/DataDownloadApplication.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using System.Threading.Tasks; using FarmmapsApi; @@ -16,7 +17,7 @@ namespace FarmmapsDataDownload { public class DataDownloadApplication : IApplication { - private const string DownloadFolder = "Downloads"; + //private const string DownloadFolder = "Downloads"; private const string SettingsFile = "settings.json"; private readonly ILogger _logger; @@ -38,10 +39,8 @@ namespace FarmmapsDataDownload public async Task RunAsync() { var fieldsInputJson = File.ReadAllText("DataDownloadInput.json"); - List fieldsInputs = JsonConvert.DeserializeObject>(fieldsInputJson); - if (!Directory.Exists(DownloadFolder)) - Directory.CreateDirectory(DownloadFolder); + List fieldsInputs = JsonConvert.DeserializeObject>(fieldsInputJson); // !! this call is needed the first time an api is called with a fresh clientid and secret !! await _farmmapsApiService.GetCurrentUserCodeAsync(); @@ -62,12 +61,23 @@ namespace FarmmapsDataDownload private async Task Process(List roots, DataDownloadInput input) { + string downloadFolder = input.DownloadFolder; + if (string.IsNullOrEmpty(downloadFolder)) { + downloadFolder = "Downloads"; + } + if (!Directory.Exists(downloadFolder)) + Directory.CreateDirectory(downloadFolder); + // !!specify if you are using an already created cropfield: bool useCreatedCropfield = input.UseCreatedCropfield; var cropYear = input.CropYear; var fieldName = input.fieldName; - bool storeSatelliteStatistics = input.StoreSatelliteStatistics; + bool storeSatelliteStatistics = input.StoreSatelliteStatisticsSingleImage; bool storeSatelliteStatisticsCropYear = input.StoreSatelliteStatisticsCropYear; + List SatelliteBands = new List(1) { input.SatelliteBand }; + string headerLineStats = $"FieldName,satelliteDate,satelliteBand,max,min,mean,mode,median,stddev,minPlus,curtosis,maxMinus,skewness,variance,populationCount,variationCoefficient,confidenceIntervalLow, confidenceIntervalHigh,confidenceIntervalErrorMargin" + Environment.NewLine; + + string settingsfile = $"Settings_{fieldName}.json"; LoadSettings(settingsfile); @@ -92,7 +102,7 @@ namespace FarmmapsDataDownload { _logger.LogInformation("Creating cropfield"); cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code, - $"DataCropfield {input.OutputFileName}", cropYear.Year, input.GeometryJson.ToString(Formatting.None)); + $"DataCropfield {input.OutputFileName}", cropYear, input.GeometryJson.ToString(Formatting.None)); _settings.CropfieldItemCode = cropfieldItem.Code; SaveSettings(settingsfile); } @@ -117,12 +127,10 @@ namespace FarmmapsDataDownload _logger.LogInformation("Downloading shadow map"); await _farmmapsApiService.DownloadItemAsync(shadowItem.Code, - Path.Combine(DownloadFolder, $"{input.OutputFileName}_shadow.zip")); + Path.Combine(downloadFolder, $"{input.OutputFileName}_shadow.zip")); } - - // Get satellite data if (input.GetSatelliteData) { @@ -134,54 +142,72 @@ namespace FarmmapsDataDownload SaveSettings(settingsfile); } - int selectedLayer= 2; - if (input.SatelliteBand == "ndvi") selectedLayer = 0; - if (input.SatelliteBand == "wdvi") selectedLayer = 1; - if (input.SatelliteBand == "natural") selectedLayer = 2; - // Select a particular satellite item from satelliteTask - Item satalliteItem = await _generalService.FindSatelliteItem(cropfieldItem, _settings.SatelliteTaskCode, fieldName, selectedLayer, storeSatelliteStatisticsCropYear, DownloadFolder); + // Select all satellite items + List satelliteItemsCropYear = await _generalService.FindSatelliteItems(cropfieldItem, _settings.SatelliteTaskCode); + satelliteItemsCropYear = satelliteItemsCropYear.OrderBy(x => x.DataDate).ToList(); - - var satelliteBand = satalliteItem.Data["layers"][selectedLayer]["name"]; - - //Store satellite data to csv - if (storeSatelliteStatistics == true && (selectedLayer == 0 || selectedLayer ==1)) - { - - - var satelliteStatistics = satalliteItem.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; - Console.WriteLine($"Satellite image date: {satalliteItem.DataDate}"); - - var SatelliteStatsFile = $"{DownloadFolder}/SatelliteDataStatistics_{fieldName}_{input.SatelliteBand}_{satalliteItem.DataDate.Value:yyyy-MM-dd}.csv"; - using var w = new StreamWriter(SatelliteStatsFile); + if (input.StoreSatelliteStatisticsSingleImage == true) { + _logger.LogInformation("Available satellite images:"); + var count = 0; + TimeSpan.FromSeconds(0.5); + foreach (var item in satelliteItemsCropYear) { - foreach (var item in satelliteStatistics) + + Console.WriteLine($"Satellite image #{count}: {item.DataDate}"); + count++; + } + + _logger.LogInformation("Enter satellite image number"); + int element = Int32.Parse(Console.ReadLine()); + var selectedSatelliteItem = satelliteItemsCropYear[element]; + + var SatelliteDate = selectedSatelliteItem.DataDate.Value.ToString("yyyyMMdd"); + string fileName = string.Format($"satelliteGeotiff_{fieldName}_{SatelliteDate}"); // no need to add satelliteBand in the name because the tif contains all bands + string fileNameZip = string.Format($"{fileName}.zip"); + string fileNameGeotiff = string.Format($"{fileName}.tif"); + await _farmmapsApiService.DownloadItemAsync(selectedSatelliteItem.Code, Path.Combine(downloadFolder, fileNameZip)); + + // Download a csv file with stats + List selectedSatalliteItems = new List(1) { selectedSatelliteItem }; + string fileNameStats = Path.Combine(downloadFolder, string.Format($"satelliteStats_{fieldName}_{SatelliteDate}.csv")); + string downloadedStats = await _generalService.DownloadSatelliteStats(selectedSatalliteItems, fieldName, SatelliteBands, downloadFolder); + //rename the csv file with stats + File.Delete(fileNameStats); + File.Move(downloadedStats, fileNameStats); + // wenr.tif. Contains 5 layers: (1) ndvi, (2) wdvi, (3) Red, (4) Green and (5) Blue + // download the geotiffs. Returns a zip file with always these three files: + // data.dat.aux.xml + // thumbnail.jpg + // wenr.tif. Contains 5 layers: (1) ndvi, (2) wdvi, (3) Red, (4) Green and (5) Blue + if (true) + { + // Extract the file "wenr.tif" from zip, rename it to fileNameGeotiff + ZipFile.ExtractToDirectory(Path.Combine(downloadFolder, fileNameZip), downloadFolder, true); + File.Delete(Path.Combine(downloadFolder, fileNameGeotiff)); // Delete the fileNameGeotiff file if exists + File.Move(Path.Combine(downloadFolder, "wenr.tif"), Path.Combine(downloadFolder, fileNameGeotiff)); // Rename the oldFileName into newFileName + + // Cleanup + string[] filesToDelete = new string[] { fileNameZip, "wenr.tif", "thumbnail.jpg", "data.dat.aux.xml" }; + foreach (string f in filesToDelete) { - var line = string.Format("{0}", item); - w.WriteLine(line); - w.Flush(); + File.Delete(Path.Combine(downloadFolder, f)); } } + _logger.LogInformation($"Downloaded files {fileNameGeotiff} and {fileNameStats} to {downloadFolder}"); - var inputType = (satalliteItem.Data["layers"] as JArray)?[selectedLayer]["name"].ToString(); - if (string.IsNullOrEmpty(inputType)) { - _logger.LogError("Could not get the input type name from the satellite item"); - return; - } - - // download the geotiff of needed inputtype - var SatelliteImageDate = (DateTime)satalliteItem.DataDate; - var SatelliteDate = SatelliteImageDate.ToString("yyyyMMdd"); - _logger.LogInformation("Downloading geotiff file"); - await _farmmapsApiService.DownloadItemAsync(satalliteItem.Code, - Path.Combine(DownloadFolder, $"satelliteGeotiff_{input.OutputFileName}_{inputType}_{SatelliteDate}.zip")); + } + if (input.StoreSatelliteStatisticsCropYear == true) { + string fileNameStats = Path.Combine(downloadFolder, string.Format($"satelliteStats_{fieldName}_{cropYear}.csv")); + File.Delete(fileNameStats); + string downloadedStats = await _generalService.DownloadSatelliteStats(satelliteItemsCropYear, fieldName, SatelliteBands, downloadFolder); + File.Move(downloadedStats, fileNameStats); + _logger.LogInformation($"Downloaded file {fileNameStats} with stats for field '{fieldName}', cropyear {cropYear}"); } } - // Get vanDerSat data if (input.GetVanDerSatData) { diff --git a/FarmmapsDataDownload/DataDownloadInput.json b/FarmmapsDataDownload/DataDownloadInput.json index 11d80ac..4081f5b 100644 --- a/FarmmapsDataDownload/DataDownloadInput.json +++ b/FarmmapsDataDownload/DataDownloadInput.json @@ -3,15 +3,15 @@ "UseCreatedCropfield": true, "outputFileName": "testSatData2", "fieldName": "test_satData2", + "DownloadFolder": "C:\\workdir\\groenmonitor\\", //"C:\\workdir\\groenmonitor\\", // "Downloads", -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\ "GetShadowData": false, "GetSatelliteData": true, "SatelliteBand": "wdvi", // "natural", "ndvi" or "wdvi" - "StoreSatelliteStatistics": false, + "StoreSatelliteStatisticsSingleImage": true, "StoreSatelliteStatisticsCropYear": true, - "GetVanDerSatData": false, "StoreVanDerSatStatistics": false, - "CropYear": "2020-01-01", + "CropYear": 2020, "geometryJson": { "type": "Polygon", "coordinates": [ diff --git a/FarmmapsDataDownload/Models/DataDownloadInput.cs b/FarmmapsDataDownload/Models/DataDownloadInput.cs index 348f95e..82d8eef 100644 --- a/FarmmapsDataDownload/Models/DataDownloadInput.cs +++ b/FarmmapsDataDownload/Models/DataDownloadInput.cs @@ -9,14 +9,15 @@ namespace FarmmapsDataDownload.Models public string File { get; set; } public string InputVariable { get; set; } public string OutputFileName { get; set; } - public DateTime CropYear { get; set; } + public string DownloadFolder { get; set; } + public int CropYear { get; set; } public JObject GeometryJson { get; set; } public string InputLayerName { get; set; } public string fieldName { get; set; } public bool GetSatelliteData { get; set; } public bool GetVanDerSatData { get; set; } public string SatelliteBand { get; set; } - public bool StoreSatelliteStatistics { get; set; } + public bool StoreSatelliteStatisticsSingleImage { get; set; } public bool StoreSatelliteStatisticsCropYear { get; set; } public bool StoreVanDerSatStatistics { get; set; } public bool GetShadowData { get; set; } From 111b4e576fe9b489252334ee6dc9872195962459 Mon Sep 17 00:00:00 2001 From: pepijn van oort Date: Fri, 28 May 2021 17:25:31 +0200 Subject: [PATCH 32/35] =?UTF-8?q?solved=20issue=20where=20sometimes=20sate?= =?UTF-8?q?lliteItem=20contains=20only=202=20layers=20Normally=203=20layer?= =?UTF-8?q?s=200:=20ndvi,=201:=20wdvi,=202:=20natural.=20In=20some=20cases?= =?UTF-8?q?=20only=202=20layers:=200:=20wdvi,=201:=20natural.=20Which=20ca?= =?UTF-8?q?used=20error=20when=20requesting=20statistics.=20This=20happene?= =?UTF-8?q?d=20for=20example=20for=20cropfieldItem.Code=20=E2=80=98e08d71b?= =?UTF-8?q?d92334dbab9e645ad6e72da63=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FarmmapsApi/Services/GeneralService.cs | 72 +++--- .../BulkSatDownloadApplication.cs | 210 +++++++----------- .../BulkSatDownloadInput.json | 112 +++++++++- .../FarmmapsBulkSatDownload.csproj | 4 + .../Models/GroenmonitorTable.cs | 28 --- 5 files changed, 232 insertions(+), 194 deletions(-) delete mode 100644 FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index 83f9061..dd2a637 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -423,32 +423,28 @@ namespace FarmmapsApi.Services string satelliteDataStatisticsFile = Path.Combine(downloadFolder, $"satelliteStats_{fieldName}.csv"); File.Delete(satelliteDataStatisticsFile); // Delete the SatelliteFile file if exists - List selectedLayers = new List(); - foreach (string satelliteBand in satelliteBands) - { - if (satelliteBand == "ndvi") selectedLayers.Add(0); - if (satelliteBand == "wdvi") selectedLayers.Add(1); - }; 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 item in satelliteTiffs) - foreach (int selectedLayer in selectedLayers) + 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)) { - var satelliteBand = item.Data["layers"][selectedLayer]["name"]; - var satelliteImageDate = (DateTime)item.DataDate; - var satelliteStatisticsJtoken = item.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; + JToken satelliteStatisticsJtoken = layer["renderer"]["band"]["statistics"]; if (satelliteStatisticsJtoken == null) { - _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found"); - //Console.WriteLine($"Available data: {item.Data}"); + _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.ToString(); + satelliteStatistics.satelliteBand = satelliteBand; File.AppendAllText(satelliteDataStatisticsFile, $"" + $"{satelliteStatistics.fieldName}," + $"{satelliteStatistics.satelliteDate.ToString("yyyy-MM-dd")}," + @@ -473,37 +469,41 @@ namespace FarmmapsApi.Services } } } + } return satelliteDataStatisticsFile; } public async Task> ListSatelliteStatistics(Item satelliteTiff, List satelliteBands = null, string fieldName = null) { SatelliteStatistics satelliteStatistics; List listSatelliteStatistics = new List(); - List selectedLayers = new List(); - foreach (string satelliteBand in satelliteBands) + List layers = satelliteTiff.Data["layers"].Children().ToList(); + foreach (JToken layer in layers) { - if (satelliteBand == "ndvi") selectedLayers.Add(0); - if (satelliteBand == "wdvi") selectedLayers.Add(1); - }; - - foreach (int selectedLayer in selectedLayers) - { - var satelliteBand = satelliteTiff.Data["layers"][selectedLayer]["name"]; - var satelliteImageDate = (DateTime)satelliteTiff.DataDate; - var satelliteStatisticsJtoken = satelliteTiff.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; - if (satelliteStatisticsJtoken == null) + DateTime satelliteImageDate = (DateTime)satelliteTiff.DataDate; + string satelliteBand = layer["name"].ToString(); + //_logger.LogInformation($"Date '{satelliteImageDate.ToString("yyyy-MM-dd")}': satelliteBand: {satelliteBand}"); + if (satelliteBands.Contains(satelliteBand)) { - _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found"); - //Console.WriteLine($"Available data: {item.Data}"); - } - else - { - satelliteStatistics = satelliteStatisticsJtoken.ToObject(); - satelliteStatistics.fieldName = fieldName.ToString(); - satelliteStatistics.satelliteDate = satelliteImageDate; - satelliteStatistics.satelliteBand = satelliteBand.ToString(); - listSatelliteStatistics.Add(satelliteStatistics); + 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; diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs index c6ea1a3..35f3bf6 100644 --- a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs +++ b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs @@ -57,16 +57,15 @@ namespace FarmmapsBulkSatDownload // Crashes if "DBsettings.secrets.json" is absent or empty DB dbparcels = JsonConvert.DeserializeObject(File.ReadAllText("DBsettings.secrets.json")); string schemaname = "bigdata"; - string parceltablename = "parcel_disac";//"parcelsijbrandij" "parcel"; "parcel_flowerbulbs"; "parcel_disac" - string groenmonitortablename = "groenmonitor_disac";//"groenmonitorsijbrandij" "groenmonitor" "groenmonitor_flowerbulbs" "groenmonitor_disac" + string parceltablename = "parcel_flowerbulbs";//"parcelsijbrandij" "parcel"; "parcel_flowerbulbs"; "parcel_disac" + string groenmonitortablename = "groenmonitor_flowerbulbs";//"groenmonitorsijbrandij" "groenmonitor" "groenmonitor_flowerbulbs" "groenmonitor_disac" // The view 'groenmonitorlatestviewname' contains per parcelid (arbid) the year in which it "exists" and the date of the latest image downloaded. It is used to prevent unneccessary downloading of image statistics already in the database - string groenmonitorlatestviewname = "groenmonitorlatest_disac"; //"groenmonitorsijbrandijlatest" "groenmonitorlatest" "groenmonitorlatest_flowerbulbs" "groenmonitorlatest_disac" - //GroenmonitorTable gmtb; + string groenmonitorlatestviewname = "groenmonitorlatest_flowerbulbs"; //"groenmonitorsijbrandijlatest" "groenmonitorlatest" "groenmonitorlatest_flowerbulbs" "groenmonitorlatest_disac" // Database query and connection. Geometry must be in WGS84 coordinate system, EPSG 4326 // Apparently the FarmmapsApi cannot handle MultiPolygon, so we need to convert to single Polygon // In case database returns a MultiPolygon use ST_NumGeometries(pt.geom) to count the number of polygons - // If necessary use WHERE T_NumGeometries(pt.geom) = 1 to select only single polygons + // If necessary use WHERE ST_NumGeometries(pt.geom) = 1 to select only single polygons // // FarmMaps get's its satellite images from www.groenmonitor.nl through the https://agrodatacube.wur.nl/. // Many images are available at www.groenmonitor.nl, the https://agrodatacube.wur.nl/ serves only the clean images, 10-30 per year, 2019 onwards. Possibly more images will be added for earlier years @@ -79,9 +78,11 @@ namespace FarmmapsBulkSatDownload SELECT pt.arbid, pt.year, gml.lastwenrdate, ST_AsGeoJSON(ST_Transform((ST_DUMP(pt.geom)).geom::geometry(Polygon),4326)) AS geojson_polygon_wgs84, COALESCE(pt.cropfielditemcode,'') AS cropfielditemcode FROM {0}.{1} pt, {0}.{2} gml WHERE - pt.arbid = gml.arbid + pt.arbid = gml.arbid AND + pt.crop NOT IN ('Lelie','Tulp') AND + pt.year > 2018 AND pt.arbid >= 8155 ORDER BY pt.arbid -LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT x for testing +LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT x for testing using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) { @@ -182,12 +183,16 @@ LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT DateTime dtSatelliteDate; string strSatelliteDate; List satelliteItemsCropYear; - GroenmonitorTable groenmonitorTable = new GroenmonitorTable(); + StringBuilder sbInsertSql; + StringBuilder sbInsertSqlInto; + StringBuilder sbInsertSqlUpdate; + StringBuilder sbInsertSqlValues; List listSatelliteStatistics; SatelliteStatistics satelliteStatistics_wdvi; SatelliteStatistics satelliteStatistics_ndvi; int cntDatesDownloaded; + string satelliteSource = "akkerwebwenr"; //same as in groenmonitorlatestviewname SQL code string fieldName = input.fieldName; int cropYear = input.cropYear; List satelliteBands = input.SatelliteBands; @@ -199,7 +204,6 @@ LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT string satelllitetable = input.satelllitetable; DateTime lastDownloadedSatelliteDate = input.lastdownloadedimagedate; cropfielditemcode = input.cropfielditemcode; - string insertSql = InsertSQLfromClass(schemaname, satelllitetable); LoadSettings(settingsfile); @@ -236,7 +240,7 @@ LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT //Console.WriteLine(insertCmd.CommandText); int r = updateCmd.ExecuteNonQuery(); if (r != 1) - throw new Exception("// FarmmapsBulkSatDownload: Insert Failed"); + throw new Exception("// FarmmapsBulkSatDownload: Update Failed"); connection.Close(); } _logger.LogInformation($"// FarmmapsBulkSatDownload: Added cropfieldItem.Code '{cropfieldItem.Code}' for parcelid {input.fieldID} to {schemaname}.{cropfieldtable} "); @@ -260,7 +264,7 @@ LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT // SaveSettings(settingsfile); // Getting satellite items - _logger.LogInformation(string.Format($"Running FindSatelliteItemsCropYear for cropfieldItem.Code '{cropfieldItem.Code}', SatelliteTaskCode '{satelliteTaskCode}'")); + _logger.LogInformation(string.Format($"Running FindSatelliteItems for cropfieldItem.Code '{cropfieldItem.Code}', SatelliteTaskCode '{satelliteTaskCode}'")); satelliteItemsCropYear = await _generalService.FindSatelliteItems(cropfieldItem, satelliteTaskCode); // Checking if satellite items found @@ -268,14 +272,14 @@ LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT if (satelliteItemsCropYear == null) { satelliteItemsAvailable = false; - _logger.LogInformation($"No satellite tiffs found for fieldName '{fieldName}', cropYear {cropYear}"); + _logger.LogInformation($"No satellite tiffs found for fieldName '{fieldName}', cropYear {cropYear}, cropfielditemcode '{cropfielditemcode}'"); } else { if (satelliteItemsCropYear.Count == 0) { satelliteItemsAvailable = false; - _logger.LogInformation($"No satellite tiffs found for fieldName '{fieldName}', cropYear {cropYear}"); + _logger.LogInformation($"No satellite tiffs found for fieldName '{fieldName}', cropYear {cropYear}, cropfielditemcode '{cropfielditemcode}'"); } } @@ -335,77 +339,94 @@ LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT // And we assume farmMaps always nicely generates statistics, no hick-ups // Under this assumptions, we only need to compare with the lastDownloadedSatelliteDate from the database cntDatesDownloaded = 0; - foreach (Item satelliteItem in satelliteItemsCropYear) + using (NpgsqlConnection connection = new NpgsqlConnection(database.GetConnectionString())) { - dtSatelliteDate = satelliteItem.DataDate.Value; - strSatelliteDate = dtSatelliteDate.ToString("yyyy-MM-dd"); - listSatelliteStatistics = await _generalService.ListSatelliteStatistics(satelliteItem, satelliteBands, fieldName); - statsAvailable = true; - if (listSatelliteStatistics == null) + connection.Open(); + foreach (Item satelliteItem in satelliteItemsCropYear) { - statsAvailable = false; - _logger.LogWarning($"No stats found for satellite, fielName '{fieldName}', date '{strSatelliteDate}'"); - } - else - { - if (listSatelliteStatistics.Count == 0) + dtSatelliteDate = satelliteItem.DataDate.Value; + strSatelliteDate = dtSatelliteDate.ToString("yyyy-MM-dd"); + listSatelliteStatistics = await _generalService.ListSatelliteStatistics(satelliteItem, satelliteBands, fieldName); + statsAvailable = true; + if (listSatelliteStatistics == null) { statsAvailable = false; - _logger.LogWarning($"No stats found for satellite, fielName '{fieldName}', date '{strSatelliteDate}'"); - } - } - if (statsAvailable) - { - if (dtSatelliteDate <= input.lastdownloadedimagedate) - { - _logger.LogInformation($"// Stats for parcelid {input.fieldID}, date '{strSatelliteDate}' already there in {schemaname}.{satelllitetable}"); + _logger.LogWarning($"No stats found for satellite, fieldName '{fieldName}', date '{strSatelliteDate}'"); } else { - cntDatesDownloaded++; - // Map satelliteStatistics to groenmonitorTable - satelliteStatistics_wdvi = listSatelliteStatistics.SingleOrDefault(p => p.satelliteBand == "wdvi"); - satelliteStatistics_ndvi = listSatelliteStatistics.SingleOrDefault(p => p.satelliteBand == "ndvi"); - groenmonitorTable.parcelid = input.fieldID; - groenmonitorTable.date = strSatelliteDate; - groenmonitorTable.source = "akkerwebwenr"; // Like this so SQL in bigdata.groenmonitorlatest_flowerbulbs works properly - groenmonitorTable.wdvi_pixelcount = satelliteStatistics_wdvi.populationCount; //count of pixels with data - groenmonitorTable.wdvi_max = satelliteStatistics_wdvi.max; - groenmonitorTable.wdvi_mean = satelliteStatistics_wdvi.mean; - groenmonitorTable.wdvi_min = satelliteStatistics_wdvi.min; - groenmonitorTable.wdvi_stdev = satelliteStatistics_wdvi.stddev; - groenmonitorTable.wdvi_median = satelliteStatistics_wdvi.median; - groenmonitorTable.wdvi_p90 = -99; // Example of a statistics (90% not included in satelliteStatistics - groenmonitorTable.ndvi_pixelcount = satelliteStatistics_ndvi.populationCount; //count of pixels with data - groenmonitorTable.ndvi_max = satelliteStatistics_ndvi.max; - groenmonitorTable.ndvi_mean = satelliteStatistics_ndvi.mean; - groenmonitorTable.ndvi_min = satelliteStatistics_ndvi.min; - groenmonitorTable.ndvi_stdev = satelliteStatistics_ndvi.stddev; - groenmonitorTable.ndvi_median = satelliteStatistics_ndvi.median; - groenmonitorTable.ndvi_p90 = -99; // Example of a statistics (90% not included in satelliteStatistics - - // fill the insertSql query with fieldValues from the groenmonitorTable, then run the query - using (NpgsqlConnection connection = new NpgsqlConnection(database.GetConnectionString())) + if (listSatelliteStatistics.Count == 0) { - connection.Open(); + statsAvailable = false; + _logger.LogWarning($"No stats found for satellite, fieldName '{fieldName}', date '{strSatelliteDate}'"); + } + } + if (statsAvailable) + { + if (dtSatelliteDate <= input.lastdownloadedimagedate) + { + _logger.LogInformation($"// Stats for parcelid {input.fieldID}, date '{strSatelliteDate}' already there in {schemaname}.{satelllitetable}"); + } + else + { + cntDatesDownloaded++; + // Map satelliteStatistics to groenmonitorTable + satelliteStatistics_wdvi = listSatelliteStatistics.SingleOrDefault(p => p.satelliteBand == "wdvi"); + satelliteStatistics_ndvi = listSatelliteStatistics.SingleOrDefault(p => p.satelliteBand == "ndvi"); + sbInsertSql = new StringBuilder(); + sbInsertSqlInto = new StringBuilder(); + sbInsertSqlUpdate = new StringBuilder(); + sbInsertSqlValues = new StringBuilder(); + sbInsertSqlInto.Append($"INSERT INTO {schemaname}.{satelllitetable} (parcelid,date,source"); + sbInsertSqlValues.Append($"VALUES ({input.fieldID},'{strSatelliteDate}','{satelliteSource}'"); + sbInsertSqlUpdate.Append($"ON CONFLICT (parcelid,date,source) DO UPDATE SET parcelid={input.fieldID}, date='{strSatelliteDate}',source='{satelliteSource}'"); + if (satelliteBands.Contains("wdvi")) + { + if (satelliteStatistics_wdvi != null) + { + sbInsertSqlInto.Append(",wdvi_pixelcount,wdvi_max,wdvi_mean,wdvi_min,wdvi_stdev,wdvi_median"); + sbInsertSqlValues.Append($",{satelliteStatistics_wdvi.populationCount},{satelliteStatistics_wdvi.max},{satelliteStatistics_wdvi.mean},{satelliteStatistics_wdvi.min},{satelliteStatistics_wdvi.stddev},{satelliteStatistics_wdvi.median}"); + sbInsertSqlUpdate.Append($",wdvi_pixelcount={satelliteStatistics_wdvi.populationCount},wdvi_max={satelliteStatistics_wdvi.max},wdvi_mean={satelliteStatistics_wdvi.mean},wdvi_min={satelliteStatistics_wdvi.min},wdvi_stdev={satelliteStatistics_wdvi.stddev},wdvi_median={satelliteStatistics_wdvi.median}"); + } + else + { + _logger.LogWarning($"wdvi missing for cropfielditemcode {cropfielditemcode}, date '{strSatelliteDate}'"); + } + } + if (satelliteBands.Contains("ndvi")) + { + if (satelliteStatistics_ndvi != null) + { + sbInsertSqlInto.Append(",ndvi_pixelcount,ndvi_max,ndvi_mean,ndvi_min,ndvi_stdev,ndvi_median"); + sbInsertSqlValues.Append($",{satelliteStatistics_ndvi.populationCount},{satelliteStatistics_ndvi.max},{satelliteStatistics_ndvi.mean},{satelliteStatistics_ndvi.min},{satelliteStatistics_ndvi.stddev},{satelliteStatistics_ndvi.median}"); + sbInsertSqlUpdate.Append($",ndvi_pixelcount={satelliteStatistics_ndvi.populationCount},ndvi_max={satelliteStatistics_ndvi.max},ndvi_mean={satelliteStatistics_ndvi.mean},ndvi_min={satelliteStatistics_ndvi.min},ndvi_stdev={satelliteStatistics_ndvi.stddev},ndvi_median={satelliteStatistics_ndvi.median}"); + } + else + { + _logger.LogWarning($"ndvi missing for cropfielditemcode {cropfielditemcode}, date '{strSatelliteDate}'"); + } + } + sbInsertSqlInto.Append(")"); + sbInsertSqlValues.Append(")"); + sbInsertSql.AppendLine(sbInsertSqlInto.ToString()); + sbInsertSql.AppendLine(sbInsertSqlValues.ToString()); + sbInsertSql.AppendLine(sbInsertSqlUpdate.ToString()); + //string strInsertSql = sbInsertSql.ToString(); NpgsqlCommand insertCmd = connection.CreateCommand(); - object[] fieldValues = groenmonitorTable.GetType() - .GetFields() - .Select(field => field.GetValue(groenmonitorTable)) - .ToArray(); - insertCmd.CommandText = string.Format(insertSql, fieldValues); + insertCmd.CommandText = sbInsertSql.ToString(); //Console.WriteLine(insertCmd.CommandText); int r = insertCmd.ExecuteNonQuery(); if (r != 1) throw new Exception("// FarmmapsBulkSatDownload: Insert Failed"); - connection.Close(); + _logger.LogInformation($"// Added stats to {schemaname}.{satelllitetable} for parcelid {input.fieldID}, date '{strSatelliteDate}'. cntDatesDownloaded: {cntDatesDownloaded}"); } - _logger.LogInformation($"// Added stats to {schemaname}.{satelllitetable} for parcelid {input.fieldID}, date '{strSatelliteDate}'. cntDatesDownloaded: {cntDatesDownloaded}"); } } + connection.Close(); } } } + // Functions to save previously created cropfields private void LoadSettings(string file) { @@ -434,64 +455,5 @@ LIMIT 15;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT { return String.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds); } - private string InsertSQLfromClass(string schemaname, string groenmonitortablename) - { - // Generates an INSERT query for GroenmonitorTable. - // When writing to a different table structure, just make a new class for the statellite statistics table, similar to GroenmonitorTable.cs - // @" - //INSERT INTO bigdata.groenmonitor (parcelid,date,wdvi_pixelcount,wdvi_max,wdvi_mean,wdvi_min,wdvi_stdev,wdvi_median,wdvi_p90,ndvi_pixelcount,ndvi_max,ndvi_mean,ndvi_min,ndvi_stdev,ndvi_median,ndvi_p90) - //VALUES({0},'{1}',{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}) - //ON CONFLICT (parcelid, date) - //DO UPDATE SET parcelid={0},date='{1}',wdvi_pixelcount={2},wdvi_max={3},wdvi_mean={4},wdvi_min={5},wdvi_stdev={6},wdvi_median={7},wdvi_p90={8},ndvi_pixelcount={9},ndvi_max={10},ndvi_mean={11},ndvi_min={12},ndvi_stdev={13},ndvi_median={14},ndvi_p90={15} - //"; - string fieldName; - var fieldNames = typeof(GroenmonitorTable).GetFields() - .Select(field => field.Name) - .ToList(); - StringBuilder sbInsertSql = new StringBuilder(); - string insertSql; - StringBuilder insertSql1 = new StringBuilder(); - insertSql1.Append(@"INSERT INTO " + schemaname + "." + groenmonitortablename + " ("); - StringBuilder insertSql2 = new StringBuilder(); - insertSql2.Append("VALUES("); - StringBuilder insertSql3 = new StringBuilder(); - insertSql3.Append("ON CONFLICT(parcelid, date, source)"); - StringBuilder insertSql4 = new StringBuilder(); - insertSql4.Append("DO UPDATE SET "); - for (int i = 0; i < fieldNames.Count; i++) - { - fieldName = fieldNames[i]; - insertSql1.Append(fieldName); - if (fieldName == "date" | fieldName == "source") - { - //add extra quotes ' for fields in the Postgress table for which we know they are date or varchar - insertSql2.Append("'{" + i + "}'"); - insertSql4.Append(fieldName + "='{" + i + "}'"); - } - else - { - insertSql2.Append("{" + i + "}"); - insertSql4.Append(fieldName + "={" + i + "}"); - } - if (i < fieldNames.Count - 1) - { - insertSql1.Append(","); - insertSql2.Append(","); - insertSql4.Append(","); - } - else - { - insertSql1.Append(")"); - insertSql2.Append(")"); - } - } - sbInsertSql.AppendLine(insertSql1.ToString()); - sbInsertSql.AppendLine(insertSql2.ToString()); - sbInsertSql.AppendLine(insertSql3.ToString()); - sbInsertSql.AppendLine(insertSql4.ToString()); - insertSql = sbInsertSql.ToString(); - return insertSql; - } - } } diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadInput.json b/FarmmapsBulkSatDownload/BulkSatDownloadInput.json index d045560..6edabb0 100644 --- a/FarmmapsBulkSatDownload/BulkSatDownloadInput.json +++ b/FarmmapsBulkSatDownload/BulkSatDownloadInput.json @@ -44,7 +44,6 @@ ] ] }, - "cropfielditemcode": null, //BulkSatDownloadApplication returns a Settings.json file with cropfielditems.Code. After first run, you can copy-paste that here to speed up "downloadFolder": "C:\\workdir\\groenmonitor\\", "fileNameStats": "BulkSatDownload.csv", "database": null, @@ -54,7 +53,7 @@ }, { "fieldName": "5641", // FarmMaps minimum needs are: fieldName, cropYear & geometryJson - "cropYear": 2021, + "cropYear": 2021, //For testing a year for which we know no satellite data available, program shouldn't crash "fieldID": 5641, "SatelliteBands": [ "wdvi", "ndvi" ], // ["ndvi"] or ["wdvi"] or both: [ "wdvi", "ndvi" ] "lastdownloadedimagedate": "2021-01-01", //downloads images from this date till end of the year @@ -97,15 +96,13 @@ ] ] }, - "cropfielditemcode": null, //BulkSatDownloadApplication returns a Settings.json file with cropfielditems.Code. After first run, you can copy-paste that here to speed up "downloadFolder": "C:\\workdir\\groenmonitor\\", "fileNameStats": "BulkSatDownload.csv", "database": null, "schemaname": null, "cropfieldtable": null, "satelllitetable": null - } - , + }, { "fieldName": "5641", // FarmMaps minimum needs are: fieldName, cropYear & geometryJson "cropYear": 2020, @@ -151,7 +148,110 @@ ] ] }, - "cropfielditemcode": null, //BulkSatDownloadApplication returns a Settings.json file with cropfielditems.Code. After first run, you can copy-paste that here to speed up + "downloadFolder": "C:\\workdir\\groenmonitor\\", + "fileNameStats": "BulkSatDownload.csv", + "database": null, + "schemaname": null, + "cropfieldtable": null, + "satelllitetable": null + }, + { + "fieldName": "5641", // FarmMaps minimum needs are: fieldName, cropYear & geometryJson + "cropYear": 2019, + "fieldID": 5641, + "SatelliteBands": [ "wdvi", "ndvi" ], // ["ndvi"] or ["wdvi"] or both: [ "wdvi", "ndvi" ] + "lastdownloadedimagedate": "2019-01-01", //downloads images from this date till end of the year + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + }, + "downloadFolder": "C:\\workdir\\groenmonitor\\", + "fileNameStats": "BulkSatDownload.csv", + "database": null, + "schemaname": null, + "cropfieldtable": null, + "satelllitetable": null + }, + { + "fieldName": "5641", // FarmMaps minimum needs are: fieldName, cropYear & geometryJson + "cropYear": 2018, //little to no images for 2018 + "fieldID": 5641, + "SatelliteBands": [ "wdvi", "ndvi" ], // ["ndvi"] or ["wdvi"] or both: [ "wdvi", "ndvi" ] + "lastdownloadedimagedate": "2018-01-01", //downloads images from this date till end of the year + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 3.37837807779104, 51.3231095796538 ], + [ 3.38065689232502, 51.3212527499355 ], + [ 3.38022924592256, 51.3210683536359 ], + [ 3.37980548452565, 51.3208801127141 ], + [ 3.37959556105776, 51.3207540143696 ], + [ 3.3793691292654, 51.3205959677371 ], + [ 3.37822219207335, 51.3215667913007 ], + [ 3.37816999925795, 51.3216109809456 ], + [ 3.37646704574705, 51.3208025481261 ], + [ 3.37646695791282, 51.3208025061493 ], + [ 3.37608401443192, 51.3206231652693 ], + [ 3.37607169507628, 51.3206173959751 ], + [ 3.37606021048754, 51.320612017601 ], + [ 3.37582728410659, 51.3205029306946 ], + [ 3.37580409779263, 51.3206502985963 ], + [ 3.37575872019649, 51.3207993094705 ], + [ 3.37575476634361, 51.3208122883487 ], + [ 3.37571181656268, 51.3208797459348 ], + [ 3.3756624532907, 51.3209415238446 ], + [ 3.37557609963811, 51.3210110142077 ], + [ 3.37541089899821, 51.3211055871218 ], + [ 3.37477516102591, 51.3214102985009 ], + [ 3.37473173914127, 51.3214311108204 ], + [ 3.37455904622072, 51.3215138815012 ], + [ 3.37415098054777, 51.3217199232877 ], + [ 3.37313700916272, 51.3222422862785 ], + [ 3.37748824689601, 51.3242852920348 ], + [ 3.37749760805371, 51.3242713084009 ], + [ 3.37811903757028, 51.3233437635596 ], + [ 3.37818758851947, 51.3232647797363 ], + [ 3.37823803668144, 51.3232236798646 ], + [ 3.37837807779104, 51.3231095796538 ] + ] + ] + }, "downloadFolder": "C:\\workdir\\groenmonitor\\", "fileNameStats": "BulkSatDownload.csv", "database": null, diff --git a/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj b/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj index 01e90bd..d902582 100644 --- a/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj +++ b/FarmmapsBulkSatDownload/FarmmapsBulkSatDownload.csproj @@ -5,6 +5,10 @@ netcoreapp3.1 + + 5 + + diff --git a/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs b/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs deleted file mode 100644 index 9437e62..0000000 --- a/FarmmapsBulkSatDownload/Models/GroenmonitorTable.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace FarmmapsBulkSatDownload.Models -{ - public class GroenmonitorTable - { - //double? makes a member nullable - public int parcelid; - public string date; - public string source; - public int wdvi_pixelcount; //count of pixels with data - public double wdvi_max; - public double wdvi_mean; - public double wdvi_min; - public double wdvi_stdev; - public double wdvi_median; - public double wdvi_p90; - public int ndvi_pixelcount; //count of pixels with data - public double ndvi_max; - public double ndvi_mean; - public double ndvi_min; - public double ndvi_stdev; - public double ndvi_median; - public double ndvi_p90; - } -} From d1747427bc84bace8d8d1450f65c1ced236ce8aa Mon Sep 17 00:00:00 2001 From: pepijn van oort Date: Sat, 29 May 2021 12:50:33 +0200 Subject: [PATCH 33/35] also logging satellitetaskcode to cropfieldtable in database --- .../BulkSatDownloadApplication.cs | 66 ++++++++++++++----- .../Models/BulkSatDownloadInput.cs | 1 + 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs index 35f3bf6..7017f46 100644 --- a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs +++ b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs @@ -75,12 +75,14 @@ namespace FarmmapsBulkSatDownload string connectionString = dbparcels.GetConnectionString(); string readSql = string.Format( @" -SELECT pt.arbid, pt.year, gml.lastwenrdate, ST_AsGeoJSON(ST_Transform((ST_DUMP(pt.geom)).geom::geometry(Polygon),4326)) AS geojson_polygon_wgs84, COALESCE(pt.cropfielditemcode,'') AS cropfielditemcode +SELECT pt.arbid, pt.year, gml.lastwenrdate, ST_AsGeoJSON(ST_Transform((ST_DUMP(pt.geom)).geom::geometry(Polygon),4326)) AS geojson_polygon_wgs84, + COALESCE(pt.cropfielditemcode,'') AS cropfielditemcode, + CASE WHEN pt.year >= DATE_PART('year', CURRENT_DATE) THEN '' ELSE COALESCE(pt.satellitetaskcode,'') END AS satellitetaskcode FROM {0}.{1} pt, {0}.{2} gml WHERE pt.arbid = gml.arbid AND pt.crop NOT IN ('Lelie','Tulp') AND - pt.year > 2018 AND pt.arbid >= 8155 + pt.year > 2018 AND pt.arbid IN(8276,8314,8315) ORDER BY pt.arbid LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT x for testing @@ -102,6 +104,7 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI bulkSatDownloadInput.GeometryJson = JObject.Parse(dr.GetString(3)); bulkSatDownloadInput.SatelliteBands = satelliteBands; bulkSatDownloadInput.cropfielditemcode = dr.GetString(4); + bulkSatDownloadInput.satellitetaskcode = dr.GetString(5); bulkSatDownloadInput.database = dbparcels; bulkSatDownloadInput.schemaname = schemaname; bulkSatDownloadInput.cropfieldtable = parceltablename; @@ -177,6 +180,7 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI private async Task Process(List roots, BulkSatDownloadInput input) { string cropfielditemcode; + string satellitetaskcode; Item cropfieldItem; bool satelliteItemsAvailable; bool statsAvailable; @@ -193,6 +197,7 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI int cntDatesDownloaded; string satelliteSource = "akkerwebwenr"; //same as in groenmonitorlatestviewname SQL code + int fieldID = input.fieldID; string fieldName = input.fieldName; int cropYear = input.cropYear; List satelliteBands = input.SatelliteBands; @@ -204,6 +209,7 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI string satelllitetable = input.satelllitetable; DateTime lastDownloadedSatelliteDate = input.lastdownloadedimagedate; cropfielditemcode = input.cropfielditemcode; + satellitetaskcode = input.satellitetaskcode; LoadSettings(settingsfile); @@ -230,20 +236,20 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI // If working with a database, add this cropfieldItem.Code to the database so that next case same cropField is requested, will be faster if (database != null) { - // add this CropfieldItemCode to the parceltable + // add this cropfielditemcode to the cropfieldtable using (NpgsqlConnection connection = new NpgsqlConnection(database.GetConnectionString())) { connection.Open(); NpgsqlCommand updateCmd = connection.CreateCommand(); - string updateSql = string.Format($"UPDATE {schemaname}.{cropfieldtable} SET cropfielditemcode = '{cropfieldItem.Code}' WHERE arbid = {input.fieldID};"); + string updateSql = string.Format($"UPDATE {schemaname}.{cropfieldtable} SET cropfielditemcode = '{cropfieldItem.Code}' WHERE arbid = {fieldID};"); updateCmd.CommandText = updateSql; //Console.WriteLine(insertCmd.CommandText); int r = updateCmd.ExecuteNonQuery(); if (r != 1) - throw new Exception("// FarmmapsBulkSatDownload: Update Failed"); + throw new Exception("// FarmmapsBulkSatDownload: Update cropfielditemcode Failed"); connection.Close(); } - _logger.LogInformation($"// FarmmapsBulkSatDownload: Added cropfieldItem.Code '{cropfieldItem.Code}' for parcelid {input.fieldID} to {schemaname}.{cropfieldtable} "); + _logger.LogInformation($"// FarmmapsBulkSatDownload: Added cropfieldItem.Code '{cropfieldItem.Code}' for parcelid {fieldID} to {schemaname}.{cropfieldtable} "); } } else @@ -252,20 +258,48 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI cropfieldItem = new Item(); cropfieldItem.Code = cropfielditemcode; cropfieldItem.Name = "DataCropfield " + fieldName; - _logger.LogInformation($"// FarmmapsBulkSatDownload: CropfieldItem.Code for parcelid {input.fieldID} already there in {schemaname}.{cropfieldtable}: '{cropfieldItem.Code}'"); + _logger.LogInformation($"// FarmmapsBulkSatDownload: CropfieldItem.Code for parcelid {fieldID} already there in {schemaname}.{cropfieldtable}: '{cropfieldItem.Code}'"); } _settings.cropFieldItems.Add(cropfieldItem); SaveSettings(settingsfile); //Create satelliteTaskCode & save satelliteTaskCode.Code to settingsfile for retracing last call (can be useful if failed) - _logger.LogInformation(string.Format($"Running RunSatelliteTask for cropfieldItem '{cropfielditemcode}' and saving settings to {settingsfile}")); - var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); - // POSSIBLE & DESIRABLE TO ALSO LOG satelliteTaskCode? + //_logger.LogInformation(string.Format($"Running RunSatelliteTask for cropfieldItem '{cropfielditemcode}' and saving settings to {settingsfile}")); + //var satelliteTaskCode = await _generalService.RunSatelliteTask(cropfieldItem); + if (string.IsNullOrEmpty(satellitetaskcode)) + { + _logger.LogInformation(string.Format($"Running RunSatelliteTask for cropfieldItem '{cropfielditemcode}'")); + satellitetaskcode = await _generalService.RunSatelliteTask(cropfieldItem); + // If working with a database, add this cropfieldItem.Code to the database so that next case same cropField is requested, will be faster + if (database != null) + { + // add this satellitetaskcode to the cropfieldtable + using (NpgsqlConnection connection = new NpgsqlConnection(database.GetConnectionString())) + { + connection.Open(); + NpgsqlCommand updateCmd = connection.CreateCommand(); + string updateSql = string.Format($"UPDATE {schemaname}.{cropfieldtable} SET satellitetaskcode = '{satellitetaskcode}' WHERE arbid = {fieldID};"); + updateCmd.CommandText = updateSql; + //Console.WriteLine(insertCmd.CommandText); + int r = updateCmd.ExecuteNonQuery(); + if (r != 1) + throw new Exception("// FarmmapsBulkSatDownload: Update satellitetaskcode Failed"); + connection.Close(); + } + _logger.LogInformation($"// FarmmapsBulkSatDownload: Added satellitetaskcode '{satellitetaskcode}' for cropfieldItem.Code '{cropfieldItem.Code}' to {schemaname}.{cropfieldtable} "); + } + } + else + { + _logger.LogInformation($"// FarmmapsBulkSatDownload: satellitetaskcode for parcelid {fieldID} already there in {schemaname}.{cropfieldtable}: '{satellitetaskcode}'"); + } + + // TODO also log satellitetaskcode to settings, how? // SaveSettings(settingsfile); // Getting satellite items - _logger.LogInformation(string.Format($"Running FindSatelliteItems for cropfieldItem.Code '{cropfieldItem.Code}', SatelliteTaskCode '{satelliteTaskCode}'")); - satelliteItemsCropYear = await _generalService.FindSatelliteItems(cropfieldItem, satelliteTaskCode); + _logger.LogInformation(string.Format($"Running FindSatelliteItems for cropfieldItem.Code '{cropfieldItem.Code}', SatelliteTaskCode '{satellitetaskcode}'")); + satelliteItemsCropYear = await _generalService.FindSatelliteItems(cropfieldItem, satellitetaskcode); // Checking if satellite items found satelliteItemsAvailable = true; @@ -365,7 +399,7 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI { if (dtSatelliteDate <= input.lastdownloadedimagedate) { - _logger.LogInformation($"// Stats for parcelid {input.fieldID}, date '{strSatelliteDate}' already there in {schemaname}.{satelllitetable}"); + _logger.LogInformation($"// Stats for parcelid {fieldID}, date '{strSatelliteDate}' already there in {schemaname}.{satelllitetable}"); } else { @@ -378,8 +412,8 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI sbInsertSqlUpdate = new StringBuilder(); sbInsertSqlValues = new StringBuilder(); sbInsertSqlInto.Append($"INSERT INTO {schemaname}.{satelllitetable} (parcelid,date,source"); - sbInsertSqlValues.Append($"VALUES ({input.fieldID},'{strSatelliteDate}','{satelliteSource}'"); - sbInsertSqlUpdate.Append($"ON CONFLICT (parcelid,date,source) DO UPDATE SET parcelid={input.fieldID}, date='{strSatelliteDate}',source='{satelliteSource}'"); + sbInsertSqlValues.Append($"VALUES ({fieldID},'{strSatelliteDate}','{satelliteSource}'"); + sbInsertSqlUpdate.Append($"ON CONFLICT (parcelid,date,source) DO UPDATE SET parcelid={fieldID}, date='{strSatelliteDate}',source='{satelliteSource}'"); if (satelliteBands.Contains("wdvi")) { if (satelliteStatistics_wdvi != null) @@ -418,7 +452,7 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI int r = insertCmd.ExecuteNonQuery(); if (r != 1) throw new Exception("// FarmmapsBulkSatDownload: Insert Failed"); - _logger.LogInformation($"// Added stats to {schemaname}.{satelllitetable} for parcelid {input.fieldID}, date '{strSatelliteDate}'. cntDatesDownloaded: {cntDatesDownloaded}"); + _logger.LogInformation($"// Added stats to {schemaname}.{satelllitetable} for parcelid {fieldID}, date '{strSatelliteDate}'. cntDatesDownloaded: {cntDatesDownloaded}"); } } } diff --git a/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs b/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs index e23b914..cddc01c 100644 --- a/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs +++ b/FarmmapsBulkSatDownload/Models/BulkSatDownloadInput.cs @@ -13,6 +13,7 @@ namespace FarmmapsBulkSatDownload.Models public DateTime lastdownloadedimagedate { get; set; } public JObject GeometryJson { get; set; } public string cropfielditemcode { get; set; } + public string satellitetaskcode { get; set; } public string downloadFolder { get; set; } public string fileNameStats { get; set; } public DB database { get; set; } From 915df2ac6be78934ab2bb127983b745090f301da Mon Sep 17 00:00:00 2001 From: pepijn van oort Date: Mon, 7 Jun 2021 16:38:42 +0200 Subject: [PATCH 34/35] new project FarmmapsCleanUp to clean up cropfielditems and satellitetaskcodes used only once --- FarmmapsApiSamples.sln | 8 +- .../BulkSatDownloadApplication.cs | 64 ++++---- FarmmapsCleanUp/CleanUpApplication.cs | 143 ++++++++++++++++++ FarmmapsCleanUp/CleanUpService.cs | 28 ++++ FarmmapsCleanUp/FarmmapsCleanUp.csproj | 23 +++ FarmmapsCleanUp/Program.cs | 21 +++ 6 files changed, 260 insertions(+), 27 deletions(-) create mode 100644 FarmmapsCleanUp/CleanUpApplication.cs create mode 100644 FarmmapsCleanUp/CleanUpService.cs create mode 100644 FarmmapsCleanUp/FarmmapsCleanUp.csproj create mode 100644 FarmmapsCleanUp/Program.cs diff --git a/FarmmapsApiSamples.sln b/FarmmapsApiSamples.sln index f7bf4b2..e44d74d 100644 --- a/FarmmapsApiSamples.sln +++ b/FarmmapsApiSamples.sln @@ -27,7 +27,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsDataDownload", "Far EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsBulkSatDownload", "FarmmapsBulkSatDownload\FarmmapsBulkSatDownload.csproj", "{772DBDCD-9FAA-40A7-8551-2C1620C4AB67}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Secrets", "Secrets\Secrets.csproj", "{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Secrets", "Secrets\Secrets.csproj", "{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsCleanUp", "FarmmapsCleanUp\FarmmapsCleanUp.csproj", "{5E4387F9-5953-4A9B-BCA5-DF3964EED3CB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -75,6 +77,10 @@ Global {C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Release|Any CPU.Build.0 = Release|Any CPU + {5E4387F9-5953-4A9B-BCA5-DF3964EED3CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E4387F9-5953-4A9B-BCA5-DF3964EED3CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E4387F9-5953-4A9B-BCA5-DF3964EED3CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E4387F9-5953-4A9B-BCA5-DF3964EED3CB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs index 7017f46..52ed864 100644 --- a/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs +++ b/FarmmapsBulkSatDownload/BulkSatDownloadApplication.cs @@ -57,10 +57,10 @@ namespace FarmmapsBulkSatDownload // Crashes if "DBsettings.secrets.json" is absent or empty DB dbparcels = JsonConvert.DeserializeObject(File.ReadAllText("DBsettings.secrets.json")); string schemaname = "bigdata"; - string parceltablename = "parcel_flowerbulbs";//"parcelsijbrandij" "parcel"; "parcel_flowerbulbs"; "parcel_disac" - string groenmonitortablename = "groenmonitor_flowerbulbs";//"groenmonitorsijbrandij" "groenmonitor" "groenmonitor_flowerbulbs" "groenmonitor_disac" + string parceltablename = "parcel_bollenrevolutie_tulips2020"; //"parcelsijbrandij" "parcel"; "parcel_flowerbulbs"; "parcel_disac"; ""parcel_bollenrevolutie_tulips2020"" + string groenmonitortablename = "groenmonitor_bollenrevolutie_tulips2020"; //"groenmonitorsijbrandij" "groenmonitor" "groenmonitor_flowerbulbs" "groenmonitor_disac" "groenmonitor_bollenrevolutie_tulips2020" // The view 'groenmonitorlatestviewname' contains per parcelid (arbid) the year in which it "exists" and the date of the latest image downloaded. It is used to prevent unneccessary downloading of image statistics already in the database - string groenmonitorlatestviewname = "groenmonitorlatest_flowerbulbs"; //"groenmonitorsijbrandijlatest" "groenmonitorlatest" "groenmonitorlatest_flowerbulbs" "groenmonitorlatest_disac" + string groenmonitorlatestviewname = "groenmonitorlatest_bollenrevolutie_tulips2020"; //"groenmonitorsijbrandijlatest" "groenmonitorlatest" "groenmonitorlatest_flowerbulbs" "groenmonitorlatest_disac" "groenmonitorlatest_bollenrevolutie_tulips2020" // Database query and connection. Geometry must be in WGS84 coordinate system, EPSG 4326 // Apparently the FarmmapsApi cannot handle MultiPolygon, so we need to convert to single Polygon @@ -80,11 +80,10 @@ SELECT pt.arbid, pt.year, gml.lastwenrdate, ST_AsGeoJSON(ST_Transform((ST_DUMP(p CASE WHEN pt.year >= DATE_PART('year', CURRENT_DATE) THEN '' ELSE COALESCE(pt.satellitetaskcode,'') END AS satellitetaskcode FROM {0}.{1} pt, {0}.{2} gml WHERE - pt.arbid = gml.arbid AND - pt.crop NOT IN ('Lelie','Tulp') AND - pt.year > 2018 AND pt.arbid IN(8276,8314,8315) + pt.arbid = gml.arbid + AND pt.satellitetaskcode IS NULL ORDER BY pt.arbid -LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT x for testing +LIMIT 5;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMIT x for testing using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) { @@ -117,30 +116,30 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI // Option 2: Example without database. Comment out this part if you want to use database // Read cropfields "BulkSatDownloadInput.json" and write all stats to a single csv file // Write all stats for multiple fields will be written to a single csv file - string downloadFolder; - string fileNameStats; - string headerLineStats = $"FieldName,satelliteDate,satelliteBand,max,min,mean,mode,median,stddev,minPlus,curtosis,maxMinus,skewness,variance,populationCount,variationCoefficient,confidenceIntervalLow, confidenceIntervalHigh,confidenceIntervalErrorMargin" + Environment.NewLine; - var fieldsInputJson = File.ReadAllText("BulkSatDownloadInput.json"); - bulkSatDownloadInputListCsv = JsonConvert.DeserializeObject>(fieldsInputJson); - for (int i = 0; i < bulkSatDownloadInputListCsv.Count; i++) - { - downloadFolder = bulkSatDownloadInputListCsv[i].downloadFolder; - fileNameStats = Path.Combine(downloadFolder, bulkSatDownloadInputListCsv[i].fileNameStats); - if (!Directory.Exists(downloadFolder)) - Directory.CreateDirectory(downloadFolder); - bulkSatDownloadInputListCsv[i].fileNameStats = fileNameStats; - // Header same as in GeneralService.DownloadSatelliteStats - // Delete fileNameStats if existing. Create a new file. Add a header to csv file - File.Delete(fileNameStats); - File.AppendAllText(fileNameStats, headerLineStats); - } + //string downloadFolder; + //string fileNameStats; + //string headerLineStats = $"FieldName,satelliteDate,satelliteBand,max,min,mean,mode,median,stddev,minPlus,curtosis,maxMinus,skewness,variance,populationCount,variationCoefficient,confidenceIntervalLow, confidenceIntervalHigh,confidenceIntervalErrorMargin" + Environment.NewLine; + //var fieldsInputJson = File.ReadAllText("BulkSatDownloadInput.json"); + //bulkSatDownloadInputListCsv = JsonConvert.DeserializeObject>(fieldsInputJson); + //for (int i = 0; i < bulkSatDownloadInputListCsv.Count; i++) + //{ + // downloadFolder = bulkSatDownloadInputListCsv[i].downloadFolder; + // fileNameStats = Path.Combine(downloadFolder, bulkSatDownloadInputListCsv[i].fileNameStats); + // if (!Directory.Exists(downloadFolder)) + // Directory.CreateDirectory(downloadFolder); + // bulkSatDownloadInputListCsv[i].fileNameStats = fileNameStats; + // // Header same as in GeneralService.DownloadSatelliteStats + // // Delete fileNameStats if existing. Create a new file. Add a header to csv file + // File.Delete(fileNameStats); + // File.AppendAllText(fileNameStats, headerLineStats); + //} // Now choose which list you want to use bulkSatDownloadInputList = bulkSatDownloadInputListDB; //bulkSatDownloadInputListDB; //bulkSatDownloadInputListCsv; // Whichever option (database or json/csv), continue here // Delete the settingsfile - File.Delete(settingsfile); + // File.Delete(settingsfile); // For each input download all images. Keep track to time, important when doing bulk downloads var watch = System.Diagnostics.Stopwatch.StartNew(); @@ -157,13 +156,26 @@ LIMIT 2000;", schemaname, parceltablename, groenmonitorlatestviewname); //LIMI try { await Process(roots, bulkSatDownloadInput); - } catch (Exception ex) { _logger.LogError(ex.Message); } watch.Stop(); + // add this downloadtime to the cropfieldtable. + // Only if cropfieldtable has a field called 'downloadtime' type 'time'! + //using (NpgsqlConnection connection = new NpgsqlConnection(dbparcels.GetConnectionString())) + //{ + // connection.Open(); + // NpgsqlCommand updateCmd = connection.CreateCommand(); + // string updateSql = string.Format($"UPDATE {schemaname}.{parceltablename} SET downloadtime = '{strTime(watch.Elapsed)}' WHERE arbid = {bulkSatDownloadInput.fieldID};"); + // updateCmd.CommandText = updateSql; + // int r = updateCmd.ExecuteNonQuery(); + // if (r != 1) + // throw new Exception("// FarmmapsBulkSatDownload: Update downloadtime Failed"); + // connection.Close(); + //} + //_logger.LogInformation($"// FarmmapsBulkSatDownload: Added downloadtime = '{strTime(watch.Elapsed)}' to {schemaname}.{parceltablename} "); tsSofar = tsSofar + watch.Elapsed; tsTotalEstimated = tsSofar / (i + 1) * bulkSatDownloadInputList.Count; tsRemaining = tsTotalEstimated - tsSofar; diff --git a/FarmmapsCleanUp/CleanUpApplication.cs b/FarmmapsCleanUp/CleanUpApplication.cs new file mode 100644 index 0000000..ab95e24 --- /dev/null +++ b/FarmmapsCleanUp/CleanUpApplication.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Threading.Tasks; +using FarmmapsApi; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using FarmmapsBulkSatDownload.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Npgsql; +using Newtonsoft.Json.Linq; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsCleanup +{ + public class CleanupApplication : IApplication + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly CleanupService _cleanupService; + private readonly GeneralService _generalService; + + public CleanupApplication(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService, CleanupService cleanupService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + _cleanupService = cleanupService; + } + + public async Task RunAsync() + { + // !! 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(); + + //Establish a database connection. Expecting you are using same database server as in project FarmmapsBulkSatDownload + DB dbparcels = JsonConvert.DeserializeObject(File.ReadAllText("DBsettings.secrets.json")); + string schemaname = "bigdata"; + string parceltablename = "parcel_bollenrevolutie_tulips2020"; //"parcelsijbrandij" "parcel"; "parcel_flowerbulbs"; "parcel_disac"; ""parcel_bollenrevolutie_tulips2020"" + + //Query to get lists of items to delete from FarmmapsDatabase and from parceltablename + string cropfielditemcode; + string satellitetaskcode; + List cropfieldItemCodes = new List(); + List satellitetaskCodes = new List(); + string connectionString = dbparcels.GetConnectionString(); + string readSql = string.Format( +@" +SELECT pt.cropfielditemcode, pt.satellitetaskcode +FROM {0}.{1} pt +WHERE + pt.arbid IN(1,2) +ORDER BY pt.arbid +;", schemaname, parceltablename); + + string updateCropfieldItemCodesSql = string.Format( +@" +UPDATE {0}.{1} + SET cropfielditemcode=NULL + WHERE arbid IN(1,2) +;", schemaname, parceltablename); //Same WHERE AS above + string updateSatellitetaskCodesSql = string.Format( +@" +UPDATE {0}.{1} + SET satellitetaskcode=NULL + WHERE arbid IN(1,2) +;", schemaname, parceltablename); //Same WHERE AS above + + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + + // Read data (run query) = build a list of fields for which to download images + NpgsqlCommand command = connection.CreateCommand(); + command.CommandText = readSql; + NpgsqlDataReader dr = command.ExecuteReader(); + while (dr.Read()) + { + cropfielditemcode = dr.GetString(0); + satellitetaskcode = dr.GetString(1); + if(!string.IsNullOrEmpty(cropfielditemcode)) + cropfieldItemCodes.Add(cropfielditemcode); + if (!string.IsNullOrEmpty(satellitetaskcode)) + satellitetaskCodes.Add(satellitetaskcode); + } + connection.Close(); + } + _logger.LogWarning($"// FarmmapsCleanUp: WARNING: you are about to delete {cropfieldItemCodes.Count} cropfieldItemCodes and {satellitetaskCodes.Count} satellitetaskCodes from the FarmMaps database and your own table {schemaname}.{parceltablename}"); + _logger.LogInformation($"// Nice of you to clean up after the work is done. You would typically do this for cropfieldItemCodes used only once."); + _logger.LogInformation($"// You do NOT want to do this if you think you may later on still want to use these items."); + _logger.LogInformation($"// Please carefully check the SQL queries 'readSql' 'updateSql' in CleanUpApplication.cs before proceeding."); + + _logger.LogInformation($"// FarmmapsCleanUp: delete selected cropfieldItemCodes from FarmMaps database and table {schemaname}.{parceltablename}? 0 = no, 1 = yes"); + int i; + i = Int32.Parse(Console.ReadLine()); + if (i == 1) + { + await _farmmapsApiService.DeleteItemsAsync(cropfieldItemCodes); + //TODO _farmmapsApiService.DeleteItemsAsync throws an error: {StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers: {...}} + //what is wrong with cropfieldItemCodes? + //and shouldn't we be telling _farmmapsApiService.DeleteItemsAsync what item type to delete? + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + NpgsqlCommand updateCmd = connection.CreateCommand(); + updateCmd.CommandText = updateCropfieldItemCodesSql; + int r = updateCmd.ExecuteNonQuery(); + if (r == -1) + throw new Exception("// FarmmapsCleanUp: Update cropfielditemcode Failed"); + connection.Close(); + } + } + _logger.LogInformation($"// FarmmapsCleanUp: delete selected satellitetaskCodes from FarmMaps database and table {schemaname}.{parceltablename}? 0 = no, 1 = yes"); + i = Int32.Parse(Console.ReadLine()); + if (i == 1) + { + await _farmmapsApiService.DeleteItemsAsync(satellitetaskCodes); + //TODO _farmmapsApiService.DeleteItemsAsync throws an error: {StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers: {...}} + //what is wrong with satellitetaskCodes? + //and shouldn't we be telling _farmmapsApiService.DeleteItemsAsync what item type to delete? + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + NpgsqlCommand updateCmd = connection.CreateCommand(); + updateCmd.CommandText = updateSatellitetaskCodesSql; + int r = updateCmd.ExecuteNonQuery(); + if (r == -1) + throw new Exception("// FarmmapsCleanUp: Update cropfielditemcode Failed"); + connection.Close(); + } + } + _logger.LogInformation($"// FarmmapsCleanUp: done! Hit any key to exit ..."); + Console.ReadKey(); + + + } + } +} diff --git a/FarmmapsCleanUp/CleanUpService.cs b/FarmmapsCleanUp/CleanUpService.cs new file mode 100644 index 0000000..b569e19 --- /dev/null +++ b/FarmmapsCleanUp/CleanUpService.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; +using FarmmapsApi.Models; +using FarmmapsApi.Services; +using Microsoft.Extensions.Logging; +using static FarmmapsApi.Extensions; +using static FarmmapsApiSamples.Constants; + +namespace FarmmapsCleanup +{ + public class CleanupService + { + private readonly ILogger _logger; + private readonly FarmmapsApiService _farmmapsApiService; + private readonly GeneralService _generalService; + + public CleanupService(ILogger logger, FarmmapsApiService farmmapsApiService, + GeneralService generalService) + { + _logger = logger; + _farmmapsApiService = farmmapsApiService; + _generalService = generalService; + } + + + } +} \ No newline at end of file diff --git a/FarmmapsCleanUp/FarmmapsCleanUp.csproj b/FarmmapsCleanUp/FarmmapsCleanUp.csproj new file mode 100644 index 0000000..74cf8ea --- /dev/null +++ b/FarmmapsCleanUp/FarmmapsCleanUp.csproj @@ -0,0 +1,23 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + Always + + + + + + + + + diff --git a/FarmmapsCleanUp/Program.cs b/FarmmapsCleanUp/Program.cs new file mode 100644 index 0000000..182d69e --- /dev/null +++ b/FarmmapsCleanUp/Program.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using FarmmapsApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace FarmmapsCleanup +{ + class Program : FarmmapsProgram + { + private static async Task Main(string[] args) + { + await new Program().Start(args); + } + + protected override void Configure(IServiceCollection serviceCollection) + { + serviceCollection.AddLogging() + .AddTransient(); + } + } +} \ No newline at end of file From 7c5bbf0fbe4decd9c0645cd5e8ee54bbf67f6d69 Mon Sep 17 00:00:00 2001 From: Sijbrandij Date: Wed, 9 Jun 2021 11:53:14 +0200 Subject: [PATCH 35/35] small update for finding shape taskmap --- FarmmapsApi/Services/GeneralService.cs | 6 +- FarmmapsNbs/NitrogenInput.json | 2 +- FarmmapsPoten/PotenInput.json | 92 +++++++++++++------------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/FarmmapsApi/Services/GeneralService.cs b/FarmmapsApi/Services/GeneralService.cs index dd2a637..98199a4 100644 --- a/FarmmapsApi/Services/GeneralService.cs +++ b/FarmmapsApi/Services/GeneralService.cs @@ -167,7 +167,7 @@ namespace FarmmapsApi.Services else if (outputType== "shape") { taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, - SHAPE_PROCESSED_ITEMTYPE, itemName); + SHAPE_PROCESSED_ITEMTYPE, outputType); } else { @@ -178,7 +178,7 @@ namespace FarmmapsApi.Services if (taskMapItem == null) { - _logger.LogError("Could not find the shape/isoxml taskmap as a child item under the input"); + _logger.LogError("Could not find the shape/isoxml taskmap as a child item under the cropfield"); return null; } @@ -218,7 +218,7 @@ namespace FarmmapsApi.Services dataItem = uploadedFilesChildren.FirstOrDefault(func); if (dataItem != null || tries == maxTries) { source.Cancel(); - } + } tries++; }); diff --git a/FarmmapsNbs/NitrogenInput.json b/FarmmapsNbs/NitrogenInput.json index 064956d..559db17 100644 --- a/FarmmapsNbs/NitrogenInput.json +++ b/FarmmapsNbs/NitrogenInput.json @@ -62,7 +62,7 @@ //}, { - "UseCreatedCropfield": true, + "UseCreatedCropfield": false, "file": "", // keep emptpy to use satellite image "inputVariable": "wdvi", "InputLayerName": "wdvi", diff --git a/FarmmapsPoten/PotenInput.json b/FarmmapsPoten/PotenInput.json index 6753abd..ef46910 100644 --- a/FarmmapsPoten/PotenInput.json +++ b/FarmmapsPoten/PotenInput.json @@ -1,53 +1,53 @@ [ - { - "File": "PlantingSampleDataLutumANDec.zip", - "InputLayerName": "EC0-60", - "OutputFileName": "2021.05.11_vraPoten_SampleDataMultipleLayers", - "FieldName": "lutum", - "PlantingYear": 2021, - "MeanDensity": "30", - "Variation": "20", - "UseShadow": false, - "CountPerArea": true, // don't forget to change ddi if isoxml is created - "geometryJson": { - "type": "Polygon", - "coordinates": [ - [ - [ 5.66886041703652044, 52.52929999060298627 ], - [ 5.6716230923214912, 52.52946316399909676 ], - [ 5.67185376229668581, 52.5280565894154563 ], - [ 5.66903207841337231, 52.52790646510525363 ], - [ 5.66886041703652044, 52.52929999060298627 ] - ] - ] - }, + { + "File": "PlantingSampleDataLutum.zip", + //"InputLayerName": "EC0-60", + "OutputFileName": "2021.06.09_vraPoten_SampleData", + "FieldName": "lutum", + "PlantingYear": 2021, + "MeanDensity": "30", + "Variation": "20", + "UseShadow": false, + "CountPerArea": true, // don't forget to change ddi if isoxml is created + "geometryJson": { + "type": "Polygon", + "coordinates": [ + [ + [ 5.66886041703652044, 52.52929999060298627 ], + [ 5.6716230923214912, 52.52946316399909676 ], + [ 5.67185376229668581, 52.5280565894154563 ], + [ 5.66903207841337231, 52.52790646510525363 ], + [ 5.66886041703652044, 52.52929999060298627 ] + ] + ] + }, - "GenerateTaskmap": true, - "OutputType": "isoxml", // "shape" or "isoxml" if isoxml also add ddiCode - "Precision": "2", - "MaximumClasses": "5", - "DdiCode": "0016", - "CellWidth": "3", - "CellHeight": "10", - "Centered": "true", - "StartPoint": { - "type": "Point", - //"coordinates": [ 5.66886041703652044, 52.52929999060298627 ] // 1 - //"coordinates": [ 5.6716230923214912, 52.52946316399909676 ] // 2 - //"coordinates": [ 5.67185376229668581, 52.5280565894154563 ] // 3 - "coordinates": [ 5.66903207841337231, 52.52790646510525363 ] // 4 - }, - "EndPoint": { - "type": "Point", - "coordinates": [ 5.66886041703652044, 52.52929999060298627 ] // 1 - //"coordinates": [ 5.6716230923214912, 52.52946316399909676 ] // 2 - //"coordinates": [ 5.67185376229668581, 52.5280565894154563 ] // 3 - //"coordinates": [ 5.66903207841337231, 52.52790646510525363 ] // 4 - } // if no angle + "GenerateTaskmap": true, + "OutputType": "shape", // "shape" or "isoxml" if isoxml also add ddiCode + "Precision": "2", + "MaximumClasses": "5", + "DdiCode": "0016", // DDI 0011 for count per area; DDI 0016 for distance (CM) + "CellWidth": "3", + "CellHeight": "10", + "Centered": "true", + "StartPoint": { + "type": "Point", + //"coordinates": [ 5.66886041703652044, 52.52929999060298627 ] // 1 + //"coordinates": [ 5.6716230923214912, 52.52946316399909676 ] // 2 + //"coordinates": [ 5.67185376229668581, 52.5280565894154563 ] // 3 + "coordinates": [ 5.66903207841337231, 52.52790646510525363 ] // 4 + }, + "EndPoint": { + "type": "Point", + "coordinates": [ 5.66886041703652044, 52.52929999060298627 ] // 1 + //"coordinates": [ 5.6716230923214912, 52.52946316399909676 ] // 2 + //"coordinates": [ 5.67185376229668581, 52.5280565894154563 ] // 3 + //"coordinates": [ 5.66903207841337231, 52.52790646510525363 ] // 4 + } // if no angle - //"Angle": "317.0" // if no endpoint - } + //"Angle": "317.0" // if no endpoint + } ]