forked from FarmMaps/FarmMapsApiClient
Get polygon area from geometry and give to operations if operation was on whole field
Time tracker for how much time the KPI calculations take
This commit is contained in:
parent
da808362c3
commit
cfba86cf75
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
@ -38,13 +39,14 @@ namespace FarmmapsKPI
|
||||
|
||||
public async Task RunAsync()
|
||||
{
|
||||
// var fieldsInputJson = File.ReadAllText("KPIInputChemie.json"); // hier gebleven, bad gateway?!
|
||||
KPIInput input;
|
||||
string fnKPIinput;
|
||||
|
||||
Console.WriteLine("Type name of input json file. Example: KPIinput.json (in same directory as FarmmapsKPI.exe) or C:/temp/KPIinputChemieTmp.json");
|
||||
fnKPIinput = Console.ReadLine();
|
||||
if (string.IsNullOrEmpty(fnKPIinput))
|
||||
fnKPIinput = "KPIinput.json";
|
||||
//Console.WriteLine("Type name of input json file. Example: KPIinput.json (in same directory as FarmmapsKPI.exe) or C:/temp/KPIinputChemieTmp.json");
|
||||
//fnKPIinput = Console.ReadLine();
|
||||
//if (string.IsNullOrEmpty(fnKPIinput))
|
||||
|
||||
fnKPIinput = "KPIinput.json";
|
||||
|
||||
var fieldsInputJson = File.ReadAllText(fnKPIinput);
|
||||
|
||||
@ -71,11 +73,21 @@ namespace FarmmapsKPI
|
||||
List<string> headerList = new List<string> { "parentName", "area_ha", "cropTypeCode", "cropTypeName", "KPIid", "KPIvariable", "KPIvalue", "KPIunit", "KPItargetvalue", "KPIthresholdValue" };
|
||||
//Create a new csv file. Means if existing then overwritten !!!
|
||||
sw = new StreamWriter(KPIItemPathCsv);
|
||||
sw.WriteLine($"FarmmapsKPI backend calculations on input file '{fnKPIinput}' downloaded on {DateTime.Now} with the FarmmapsKPI application in the FarmmapsApSamples.sln");
|
||||
sw.WriteLine();
|
||||
sw.WriteLine(string.Join(",", headerList));
|
||||
|
||||
//Now loop through the list of cropfields in KPIinput.json and get the KPI's for each cropfield
|
||||
foreach (var input in fieldsInputs)
|
||||
// For each input download all KPI's. Keep track to time, important when doing bulk calculations
|
||||
var watch = System.Diagnostics.Stopwatch.StartNew();
|
||||
TimeSpan tsSofar = new TimeSpan();
|
||||
TimeSpan tsRemaining;
|
||||
TimeSpan tsTotalEstimated;
|
||||
|
||||
for (int i = 0; i < fieldsInputs.Count; i++)
|
||||
{
|
||||
watch.Restart();
|
||||
input = fieldsInputs[i];
|
||||
_logger.LogInformation(string.Format($"// FarmmapsKPI: Downloading KPI's for field {i + 1} out of {fieldsInputs.Count} to single csv file {KPIItemPathCsv}"));
|
||||
try
|
||||
{
|
||||
await Process(roots, input, sw);
|
||||
@ -84,11 +96,16 @@ namespace FarmmapsKPI
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
}
|
||||
watch.Stop();
|
||||
tsSofar = tsSofar + watch.Elapsed;
|
||||
tsTotalEstimated = tsSofar / (i + 1) * fieldsInputs.Count;
|
||||
tsRemaining = tsTotalEstimated - tsSofar;
|
||||
_logger.LogInformation(string.Format($"// Time (hh:mm:ss): this field: {strTime(watch.Elapsed)}. Sofar: {strTime(tsSofar)}. Estimated total: {strTime(tsTotalEstimated)}. Remaining: {strTime(tsRemaining)}"));
|
||||
}
|
||||
|
||||
//Close the csv file, write message to screen
|
||||
sw.Close();
|
||||
_logger.LogInformation($"Done! Written all KPI for all fields in 'KPIinput.json' to output file '{KPIItemPathCsv}'");
|
||||
_logger.LogInformation(string.Format($"// FarmmapsKPI:"));
|
||||
_logger.LogInformation($"Done! Written all KPI for all fields in '{fnKPIinput}' to output file '{KPIItemPathCsv}'");
|
||||
}
|
||||
|
||||
private async Task Process(List<UserRoot> roots, KPIInput input, StreamWriter sw)
|
||||
@ -134,7 +151,7 @@ namespace FarmmapsKPI
|
||||
//3 useExistingCropfieldWithChildren = true && input.CropfieldItemCode like "deb48a74c5b54299bb852f17288010e9" in KPIinput -> use this one
|
||||
if (useExistingCropfieldWithChildren == false)
|
||||
{
|
||||
_logger.LogInformation("Creating cropfield, writting to settings file");
|
||||
_logger.LogInformation($"Creating cropfield with name '{fieldName}'");
|
||||
cropfieldItem = await _generalService.CreateCropfieldItemAsync(myDriveRoot.Code,
|
||||
$"{fieldName}", cropYear, input.GeometryJson.ToString(Formatting.None), input.DataCropfield.ToString(Formatting.None));
|
||||
_settings.CropfieldItemCode = cropfieldItem.Code;
|
||||
@ -147,13 +164,49 @@ namespace FarmmapsKPI
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("reading CropfieldItemCode from KPIinput.json");
|
||||
_logger.LogInformation("reading CropfieldItemCode from KPIinput json");
|
||||
cropfieldItem = await _farmmapsApiService.GetItemAsync(input.CropfieldItemCode);
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
|
||||
// The cropfieldCharacteristicItem is used to enter crop yields
|
||||
Item cropfieldCharacteristicItem;
|
||||
//1 useExistingCropfieldWithChildren = false -> create new
|
||||
//2 useExistingCropfieldWithChildren = true && input.CropfieldCharacteristicItemCode = "" or absent -> read from settings json
|
||||
//3 useExistingCropfieldWithChildren = true && input.CropfieldCharacteristicItemCode like "deb48a74c5b54299bb852f17288010e9" in KPIinput -> use this one
|
||||
if (useExistingCropfieldWithChildren == false)
|
||||
{
|
||||
_logger.LogInformation("CreateCropfieldCharacteristicItemAsync ...");
|
||||
cropfieldCharacteristicItem = await _generalService.CreateCropfieldCharacteristicItemAsync(cropfieldItem.Code, cropYear, fieldGeom, input.DataCropfieldCharacteristic.ToString(Formatting.None));
|
||||
_settings.CropfieldCharacteristicItemCode = cropfieldCharacteristicItem.Code;
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(input.CropfieldItemCode))
|
||||
{
|
||||
_logger.LogInformation("reading OperationItemCode from settings file");
|
||||
cropfieldCharacteristicItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldCharacteristicItemCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("reading CropfieldCharacteristicItemCode from KPIinput json");
|
||||
cropfieldCharacteristicItem = await _farmmapsApiService.GetItemAsync(input.CropfieldCharacteristicItemCode);
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
|
||||
// Use already created croprecording or create new one, added a Data input, with field specific data for the KPI calculation
|
||||
// Now we can do a first KPI calculation and get the polygon area from the KPI output (where it is based on geometry)
|
||||
// We need that because for operations, you need to provide the area on which the operation was applied
|
||||
// And if we put that to the crop area, then we neatly get everything on a per ha basis
|
||||
_logger.LogInformation($"Getting polygon area (ha))");
|
||||
List<Item> KPIItems = await _generalService.GetKpiItemsForCropField(cropfieldItem);
|
||||
_logger.LogInformation($"Found {KPIItems.Count} KPI items");
|
||||
kpio = JsonConvert.DeserializeObject<KPIOutput>(KPIItems[0].Data.ToString());
|
||||
string area_ha = kpio.data.area;
|
||||
// turn the area into a JObject for later merging with operation data;
|
||||
string strJarea = JsonConvert.SerializeObject(new { area = area_ha });
|
||||
JObject Jarea = JObject.Parse(strJarea);
|
||||
|
||||
// A cropfield has 1 crop recording and the crop recording has 0:many operations
|
||||
//So first at the crop recording
|
||||
Item crprecItem;
|
||||
//1 useExistingCropfieldWithChildren = false -> create new
|
||||
//2 useExistingCropfieldWithChildren = true && input.CropRecordingItemCode = "" or absent -> read from settings json
|
||||
@ -172,12 +225,12 @@ namespace FarmmapsKPI
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("reading CropRecordingItemCode from KPIinput.json");
|
||||
_logger.LogInformation("reading CropRecordingItemCode from KPIinput json");
|
||||
crprecItem = await _farmmapsApiService.GetItemAsync(input.CropRecordingItemCode);
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
|
||||
// Use already created operations or create new one,s added a Data input, with field specific data for the KPI calculation
|
||||
// Now add the operations
|
||||
List<Item> crpOperationItems = new List<Item> { };
|
||||
List<string> crpOperationItemCodes = new List<string> { };
|
||||
Item crpOperationItem;
|
||||
@ -192,7 +245,30 @@ namespace FarmmapsKPI
|
||||
{
|
||||
dataOperation = input.DataOperations[i].ToString(Formatting.None);
|
||||
dynamic data = JObject.Parse(dataOperation);
|
||||
_logger.LogInformation($"CreateOperationItemAsync ... for operation {i}: '{data.name}', {data.n} kg N/ha, on date '{data.from}'");
|
||||
_logger.LogInformation($"CreateOperationItemAsync ... for operation {i}: '{data.name}', on date '{data.from}'");
|
||||
// Now check if the operation has a field called area
|
||||
string? opArea = data["area"];
|
||||
if (string.IsNullOrEmpty(opArea))
|
||||
{
|
||||
// if not having field area: add it to dataOperation ...
|
||||
input.DataOperations[i].Merge(Jarea);
|
||||
// ... and update the string
|
||||
dataOperation = input.DataOperations[i].ToString(Formatting.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if yes having field area: compare with polygon area. If not the same, throw warning
|
||||
if (data.area != area_ha)
|
||||
{
|
||||
double differencePercent = 100.0*(Convert.ToDouble(area_ha) / Convert.ToDouble(data.area) - 1.0);
|
||||
_logger.LogWarning($"cropfield has area {area_ha}, but operation has area {data.area}" +
|
||||
$" Difference is {area_ha} / {data.area} - 100% = {differencePercent}%." +
|
||||
$" Is that correct? Example if operation was applied in part of field, e.g. in case of variable rate (VRA) application." +
|
||||
$" Or did you accidentally fill in area in the KPIinput json? To use cropfield area, omit field 'area' from json" +
|
||||
$" then the KPI applicataion will fill in area calculated from geometry");
|
||||
}
|
||||
}
|
||||
//Now after optionally adding the area, add the Operation to the crop recording
|
||||
crpOperationItem = await _generalService.CreateOperationItemAsync(crprecItem.Code, cropYear, fieldGeom, dataOperation);
|
||||
crpOperationItems.Add(crpOperationItem);
|
||||
crpOperationItemCodes.Add(crpOperationItem.Code);
|
||||
@ -213,7 +289,7 @@ namespace FarmmapsKPI
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("reading OperationItemCodes from KPIinput.json");
|
||||
_logger.LogInformation("reading OperationItemCodes from KPIinput json");
|
||||
for (int i = 0; i < input.OperationItemCodes.Length; i++)
|
||||
{
|
||||
codeOperation = input.OperationItemCodes[i];
|
||||
@ -225,35 +301,8 @@ namespace FarmmapsKPI
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
|
||||
// The cropfieldCharacteristicItem is used to enter crop yields
|
||||
// So once we have added an operation for fertilizer application and a crop yield, then KPIapp can calculate
|
||||
// Nutrient balance.
|
||||
// Use already created cropfieldCharacteristicItem or create new one, added a Data input, with field specific data for the KPI calculation
|
||||
Item cropfieldCharacteristicItem;
|
||||
//1 useExistingCropfieldWithChildren = false -> create new
|
||||
//2 useExistingCropfieldWithChildren = true && input.CropfieldCharacteristicItemCode = "" or absent -> read from settings json
|
||||
//3 useExistingCropfieldWithChildren = true && input.CropfieldCharacteristicItemCode like "deb48a74c5b54299bb852f17288010e9" in KPIinput -> use this one
|
||||
if (useExistingCropfieldWithChildren == false)
|
||||
{
|
||||
_logger.LogInformation("CreateCropfieldCharacteristicItemAsync ...");
|
||||
cropfieldCharacteristicItem = await _generalService.CreateCropfieldCharacteristicItemAsync(cropfieldItem.Code, cropYear, fieldGeom, input.DataCropfieldCharacteristic.ToString(Formatting.None));
|
||||
_settings.CropfieldCharacteristicItemCode = cropfieldCharacteristicItem.Code;
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(input.CropfieldItemCode))
|
||||
{
|
||||
_logger.LogInformation("reading OperationItemCode from settings file");
|
||||
cropfieldCharacteristicItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldCharacteristicItemCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("reading CropfieldCharacteristicItemCode from KPIinput.json");
|
||||
cropfieldCharacteristicItem = await _farmmapsApiService.GetItemAsync(input.CropfieldCharacteristicItemCode);
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
|
||||
// Inspect the children and grandchildren. If all is well, cropfield will have:
|
||||
// one crprec, with 0-many operations as children. And the Data of an operation will have specification of how much fertilizer was applied
|
||||
// one crprec, with 0-many operations as children. And the Data of an operation will have specification of how much fertilizer / cropprotection agent was applied
|
||||
// one edicrop.characteristic (with yield in the data)
|
||||
var cropfieldChildren = await _farmmapsApiService.GetItemChildrenAsync(cropfieldItem.Code);
|
||||
var crprecChildren = await _farmmapsApiService.GetItemChildrenAsync(crprecItem.Code);
|
||||
@ -261,7 +310,7 @@ namespace FarmmapsKPI
|
||||
//Now get the KPIs for this cropfield, mounted with operations & cropyield
|
||||
// Get KPI data for saving it in a file, here the generalsedrvice is called to get the KPI data
|
||||
_logger.LogInformation($"GetKpiItemsForCropField('{cropfieldItem.Code}')");
|
||||
List<Item> KPIItems = await _generalService.GetKpiItemsForCropField(cropfieldItem);
|
||||
KPIItems = await _generalService.GetKpiItemsForCropField(cropfieldItem);
|
||||
_logger.LogInformation($"Found {KPIItems.Count} KPI items");
|
||||
|
||||
//Download KPI's into a json output file for this specific cropfield (with unique cropfieldItem.Code)
|
||||
@ -305,7 +354,6 @@ namespace FarmmapsKPI
|
||||
//Total N applied
|
||||
double totalNapplied = 0.0;
|
||||
double operationNapplied;
|
||||
JObject opData;
|
||||
for (int i = 0; i < crpOperationItemCodes.Count; i++)
|
||||
{
|
||||
codeOperation = crpOperationItemCodes[i];
|
||||
@ -321,13 +369,13 @@ namespace FarmmapsKPI
|
||||
dataList = new List<string> { };
|
||||
//Seems sometimes duplicate KPI items are returned. So check that here and only write if this kpio is different from previous
|
||||
dataList.Add(kpioPrevious.parentName);
|
||||
dataList.Add(kpioPrevious.data.area);
|
||||
dataList.Add(kpioPrevious.data.cropTypeCode);
|
||||
dataList.Add(kpioPrevious.data.cropTypeName);
|
||||
try { dataList.Add(kpioPrevious.data.area); } catch { dataList.Add(""); };
|
||||
try { dataList.Add(kpioPrevious.data.cropTypeCode); } catch { dataList.Add(""); };
|
||||
try { dataList.Add(kpioPrevious.data.cropTypeName); } catch { dataList.Add(""); };
|
||||
dataList.Add("");
|
||||
dataList.Add("totalNapplied");
|
||||
dataList.Add(totalNapplied.ToString());
|
||||
dataList.Add(kpioPrevious.unit);
|
||||
dataList.Add("kg/ha");
|
||||
dataList.Add("");
|
||||
dataList.Add("");
|
||||
sw.WriteLine(string.Join(",", dataList));
|
||||
@ -386,6 +434,9 @@ namespace FarmmapsKPI
|
||||
File.WriteAllText(file, json);
|
||||
|
||||
}
|
||||
|
||||
private string strTime(TimeSpan ts)
|
||||
{
|
||||
return String.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user