Merge pull request 'master' (#16) from abel/FarmMapsApiClient_KB34_MAST:master into master
Reviewed-on: #16
This commit is contained in:
commit
56b337aebf
@ -12,20 +12,27 @@ namespace FarmmapsApiSamples
|
||||
public const string SHAPE_ITEMTYPE = "vnd.farmmaps.itemtype.shape";
|
||||
public const string GEOJSON_ITEMTYPE = "vnd.farmmaps.itemtype.geojson";
|
||||
public const string BLIGHT_ITEMTYPE = "vnd.farmmaps.itemtype.blight";
|
||||
public const string CROPREC_ITEMTYPE = "vnd.farmmaps.itemtype.crprec.operation";
|
||||
public const string CROPREC_ITEMTYPE = "vnd.farmmaps.itemtype.crprec";
|
||||
public const string CROPOP_ITEMTYPE = "vnd.farmmaps.itemtype.crprec.operation";
|
||||
public const string CROPCHAR_ITEMTYPE = "vnd.farmmaps.itemtype.edicrop.characteristic";
|
||||
public const string CROPSCHEME_ITEMTYPE = "vnd.farmmaps.itemtype.croppingscheme"; // deze toegevoegd, misschien is het type van de KPI task wel een croppingscheme
|
||||
//public const string KPI_ITEM = "vnd.farmmaps.itemtype.kpi.data"; //PO20231004: originally with .data
|
||||
public const string KPICONTAINER_ITEM = "vnd.farmmaps.itemtype.kpi.data.container"; //PO20231004: originally without .container
|
||||
|
||||
public const string VRANBS_TASK = "vnd.farmmaps.task.vranbs";
|
||||
public const string VRAHERBICIDE_TASK = "vnd.farmmaps.task.vraherbicide";
|
||||
public const string VRAHAULMKILLING_TASK = "vnd.farmmaps.task.vrahaulmkilling";
|
||||
public const string VRAPLANTING_TASK = "vnd.farmmaps.task.vrapoten";
|
||||
public const string VRAZONERING_TASK = "vnd.farmmaps.task.vrazonering";
|
||||
public const string SATELLITE_TASK = "vnd.farmmaps.task.sentinelhub"; //"vnd.farmmaps.task.satellite";
|
||||
public const string SATELLITE_TASK = "vnd.farmmaps.task.sentinelhub";
|
||||
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 CROPREC_TASK = "vnd.farmmaps.task.crprec";
|
||||
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";
|
||||
public const string KPI_TASK = "vnd.farmmaps.task.kpi"; // dus nieuwe taak om de KPIs te berekenen
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -46,6 +46,10 @@ namespace FarmmapsApi.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Configuration> GetConfiguration()
|
||||
{
|
||||
return _configuration;
|
||||
}
|
||||
public async Task AuthenticateAsync()
|
||||
{
|
||||
if (_httpClient.DefaultRequestHeaders.Authorization != null &&
|
||||
|
@ -22,14 +22,20 @@ namespace FarmmapsApi.Services
|
||||
}
|
||||
|
||||
public async Task<Item> CreateCropfieldItemAsync(string parentItemCode, string name, int year,
|
||||
string fieldGeomJson, string data = "{}") {
|
||||
var currentYear = new DateTime(year, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
string fieldGeomJson, string data = "{}", DateTime startDate = new DateTime(), DateTime endDate = new DateTime() )
|
||||
{
|
||||
//If user provides no startDate or endDate, then set startDate and endDate to full year provided through 'year'
|
||||
if (startDate == new DateTime() || endDate == new DateTime())
|
||||
{
|
||||
startDate = new DateTime(year, 1, 1);
|
||||
endDate = new DateTime(year, 12, 31);
|
||||
}
|
||||
var cropfieldItemRequest = new ItemRequest() {
|
||||
ParentCode = parentItemCode,
|
||||
ItemType = CROPFIELD_ITEMTYPE,
|
||||
Name = name,
|
||||
DataDate = currentYear,
|
||||
DataEndDate = currentYear.AddYears(1).AddDays(-1),
|
||||
DataDate = startDate,
|
||||
DataEndDate = endDate,
|
||||
Data = JObject.Parse(data),
|
||||
Geometry = JObject.Parse(fieldGeomJson)
|
||||
};
|
||||
@ -37,6 +43,55 @@ namespace FarmmapsApi.Services
|
||||
return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest);
|
||||
}
|
||||
|
||||
public async Task<Item> CreateOperationItemAsync(string cropRecordingItemCode, int year,
|
||||
string fieldGeomJson, string data = "{}", DateTime startDate = new DateTime(), DateTime endDate = new DateTime())
|
||||
{
|
||||
//If user provides no startDate or endDate, then set startDate and endDate to full year provided through 'year'
|
||||
if (startDate == new DateTime() || endDate == new DateTime())
|
||||
{
|
||||
startDate = new DateTime(year, 1, 1);
|
||||
endDate = new DateTime(year, 12, 31);
|
||||
}
|
||||
JObject jdata = JObject.Parse(data);
|
||||
string name = string.Format($"CrpRec Operation, {jdata.GetValue("name")}");
|
||||
|
||||
ItemRequest operationItemRequest = new ItemRequest()
|
||||
{
|
||||
ParentCode = cropRecordingItemCode,
|
||||
ItemType = CROPOP_ITEMTYPE,
|
||||
Name = name,
|
||||
DataDate = startDate,
|
||||
DataEndDate = endDate,
|
||||
Data = jdata,
|
||||
Geometry = JObject.Parse(fieldGeomJson)
|
||||
};
|
||||
|
||||
return await _farmmapsApiService.CreateItemAsync(operationItemRequest);
|
||||
}
|
||||
public async Task<Item> CreateCropfieldCharacteristicItemAsync(string cropfieldItemCode, int year,
|
||||
string fieldGeomJson, string data = "{}", DateTime startDate = new DateTime(), DateTime endDate = new DateTime())
|
||||
{
|
||||
//If user provides no startDate or endDate, then set startDate and endDate to full year provided through 'year'
|
||||
if (startDate == new DateTime() || endDate == new DateTime())
|
||||
{
|
||||
startDate = new DateTime(year, 1, 1);
|
||||
endDate = new DateTime(year, 12, 31);
|
||||
}
|
||||
// not sure if here we also need to specify DataDate, DataEndDate & Geometry. Do it just in case
|
||||
string name = "Cropfield characteristic";
|
||||
ItemRequest cropfieldCharactericsticItemRequest = new ItemRequest()
|
||||
{
|
||||
ParentCode = cropfieldItemCode,
|
||||
ItemType = CROPCHAR_ITEMTYPE,
|
||||
Name = name,
|
||||
DataDate = startDate,
|
||||
DataEndDate = endDate,
|
||||
Data = JObject.Parse(data),
|
||||
Geometry = JObject.Parse(fieldGeomJson)
|
||||
};
|
||||
|
||||
return await _farmmapsApiService.CreateItemAsync(cropfieldCharactericsticItemRequest);
|
||||
}
|
||||
public async Task<Item> UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName, string geoJsonString = null)
|
||||
{
|
||||
var startUpload = DateTime.UtcNow.AddSeconds(-3);
|
||||
@ -247,6 +302,39 @@ namespace FarmmapsApi.Services
|
||||
return dataItem;
|
||||
}
|
||||
|
||||
public async Task<Item> RunCropRecordingTask(Item cropfieldItem)
|
||||
{
|
||||
var cropRecordingRequest = new TaskRequest { TaskType = CROPREC_TASK };
|
||||
|
||||
string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, cropRecordingRequest);
|
||||
|
||||
await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => {
|
||||
var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode);
|
||||
_logger.LogInformation($"Waiting on RunCropRecordingTask; status: {itemTaskStatus.State}");
|
||||
if (itemTaskStatus.IsFinished)
|
||||
tokenSource.Cancel();
|
||||
});
|
||||
|
||||
var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode);
|
||||
if (itemTask.State == ItemTaskState.Error)
|
||||
{
|
||||
_logger.LogError($"Something went wrong with task execution: {itemTask.Message}");
|
||||
return null;
|
||||
}
|
||||
|
||||
//the CropRecording data is a child of the cropfield
|
||||
var itemName = "Crprec";
|
||||
var cropRecordingItem = await FindChildItemAsync(cropfieldItem.Code,
|
||||
CROPREC_ITEMTYPE, itemName);
|
||||
if (cropRecordingItem == null)
|
||||
{
|
||||
_logger.LogError("Could not find the CropRecording data as a child item under the cropfield");
|
||||
return null;
|
||||
}
|
||||
|
||||
return cropRecordingItem;
|
||||
}
|
||||
|
||||
public async Task<Item> RunBofekTask(Item cropfieldItem) {
|
||||
var taskmapRequest = new TaskRequest { TaskType = BOFEK_TASK };
|
||||
|
||||
@ -277,6 +365,51 @@ namespace FarmmapsApi.Services
|
||||
return bofekItem;
|
||||
}
|
||||
|
||||
public async Task<List<Item>> GetKpiItemsForCropField(Item cropfieldItem, int waitsecs = 6)
|
||||
{
|
||||
TaskRequest kpiRequest = new TaskRequest { TaskType = KPI_TASK };
|
||||
kpiRequest.attributes["processAggregateKpi"] = "false";
|
||||
int year = cropfieldItem.DataDate.Value.Year;
|
||||
kpiRequest.attributes["year"] = year.ToString();
|
||||
int ms = waitsecs * 1000;
|
||||
|
||||
string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, kpiRequest);
|
||||
|
||||
await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => {
|
||||
var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode);
|
||||
_logger.LogInformation($"Waiting on retreiving KPI data; status: {itemTaskStatus.State}");
|
||||
if (itemTaskStatus.IsFinished)
|
||||
tokenSource.Cancel();
|
||||
});
|
||||
|
||||
var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode);
|
||||
if (itemTask.State == ItemTaskState.Error)
|
||||
{
|
||||
_logger.LogError($"Something went wrong with task execution: {itemTask.Message}");
|
||||
return null;
|
||||
}
|
||||
_logger.LogInformation($"Taking {waitsecs} seconds to look up all KPI items. The longer we wait the more KPI's we get ..." +
|
||||
$"");
|
||||
await Task.Delay(ms); // wait 60000ms = 60 secs for task to be completed
|
||||
|
||||
//After the task is completed we have 1 kpiContainerItem with a code and with no data
|
||||
//Because the data are in the children of this kpiContainerItem. The container will have a list of children each with an "id" aand data,
|
||||
//The ids' are "a1", "b1", "b2", "c1","d1","d3","d5"
|
||||
//with following meanings:
|
||||
//| A1 | Opbrengst | Yield |
|
||||
//| B1 | Stikstofoverschot | Nitrogen surplus |
|
||||
//| B2 | Fosfaatoverschot | Phosphate surplus |
|
||||
//| C1 | Effectieve Organischestof aanvoer| Effective Organic Matter supply |
|
||||
//| D1 | Gebruik bestrijdingsmiddelen | Use of pesticides|
|
||||
//| D3 | Gewasdiversiteit(randdichtheid) | Crop diversity(edge density) |
|
||||
//| D5 | Percentage rustgewassen | Percentage of rest crops |
|
||||
List <Item> kpiContainerItem = await _farmmapsApiService.GetItemChildrenAsync(cropfieldItem.Code, KPICONTAINER_ITEM);
|
||||
string kpiContainerItemCode = kpiContainerItem[0].Code;
|
||||
List<Item> kpiItems = await _farmmapsApiService.GetItemChildrenAsync(kpiContainerItemCode);
|
||||
|
||||
return kpiItems;
|
||||
}
|
||||
|
||||
public async Task<Item> RunAhnTask(Item cropfieldItem) {
|
||||
var taskmapRequest = new TaskRequest { TaskType = AHN_TASK };
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30320.27
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33122.133
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsNbs", "FarmmapsNbs\FarmmapsNbs.csproj", "{E08EF7E9-F09E-42D8-825C-164E458C78F4}"
|
||||
EndProject
|
||||
@ -29,58 +29,114 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsBulkSatDownload", "
|
||||
EndProject
|
||||
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}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsKPI", "FarmmapsKPI\FarmmapsKPI.csproj", "{14575235-9867-4CE5-A22F-3F9FE002FF42}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FarmmapsDownloadCL", "FarmmapsDownloadCL\FarmmapsDownloadCL.csproj", "{63E69101-D804-4DBC-B2E4-A33771CD5C5F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E08EF7E9-F09E-42D8-825C-164E458C78F4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Debug|x64.Build.0 = Debug|x64
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Release|x64.ActiveCfg = Release|x64
|
||||
{1FA9E50B-F45E-4534-953A-37C783D03C74}.Release|x64.Build.0 = Release|x64
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{731A88CD-9DC4-4969-86F2-2315830A6998}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DFA89D0B-5400-4374-B824-8367B76B4B6E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AAFAB03A-6F5C-4D91-991F-867B7898F981}.Release|x64.Build.0 = Release|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{892E0932-5D11-4A37-979E-CEDB39C2E181}.Release|x64.Build.0 = Release|Any CPU
|
||||
{91A58C4A-4A80-4079-B43D-9B851206194F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{91A58C4A-4A80-4079-B43D-9B851206194F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{91A58C4A-4A80-4079-B43D-9B851206194F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{91A58C4A-4A80-4079-B43D-9B851206194F}.Debug|x64.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
|
||||
{91A58C4A-4A80-4079-B43D-9B851206194F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{91A58C4A-4A80-4079-B43D-9B851206194F}.Release|x64.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{32ED9500-AAAB-4030-9C7A-F611A85DF890}.Debug|x64.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
|
||||
{32ED9500-AAAB-4030-9C7A-F611A85DF890}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{32ED9500-AAAB-4030-9C7A-F611A85DF890}.Release|x64.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}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{772DBDCD-9FAA-40A7-8551-2C1620C4AB67}.Debug|x64.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
|
||||
{772DBDCD-9FAA-40A7-8551-2C1620C4AB67}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{772DBDCD-9FAA-40A7-8551-2C1620C4AB67}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Debug|x64.Build.0 = Debug|x64
|
||||
{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
|
||||
{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Release|x64.ActiveCfg = Release|x64
|
||||
{C4EE5ECA-253A-4B71-9F67-D231AC4517D6}.Release|x64.Build.0 = Release|x64
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Debug|x64.Build.0 = Debug|x64
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Release|x64.ActiveCfg = Release|x64
|
||||
{14575235-9867-4CE5-A22F-3F9FE002FF42}.Release|x64.Build.0 = Release|x64
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Debug|x64.Build.0 = Debug|x64
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|x64.ActiveCfg = Release|x64
|
||||
{63E69101-D804-4DBC-B2E4-A33771CD5C5F}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -17,6 +17,8 @@ using System.Text;
|
||||
|
||||
namespace FarmmapsBulkSatDownload
|
||||
{
|
||||
//To run this app, first go to farmmaps datastore at https://farmmaps.eu/en/editor/plan (or on test)
|
||||
//goto 'Apps and Data', goto 'Data', buy (or get for free?): 'SATELLITE'
|
||||
public class BulkSatDownloadApplication : IApplication
|
||||
{
|
||||
private readonly ILogger<BulkSatDownloadApplication> _logger;
|
||||
|
@ -1,143 +0,0 @@
|
||||
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<CleanupApplication> _logger;
|
||||
private readonly FarmmapsApiService _farmmapsApiService;
|
||||
private readonly CleanupService _cleanupService;
|
||||
private readonly GeneralService _generalService;
|
||||
|
||||
public CleanupApplication(ILogger<CleanupApplication> 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<DB>(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<string> cropfieldItemCodes = new List<string>();
|
||||
List<string> satellitetaskCodes = new List<string>();
|
||||
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();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Npgsql" Version="5.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Data\**\*">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FarmmapsApi\FarmmapsApi.csproj" />
|
||||
<ProjectReference Include="..\FarmmapsBulkSatDownload\FarmmapsBulkSatDownload.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -15,6 +15,8 @@ using static FarmmapsApiSamples.Constants;
|
||||
|
||||
namespace FarmmapsDataDownload
|
||||
{
|
||||
//To run this app, first go to farmmaps datastore at https://farmmaps.eu/en/editor/plan (or on test)
|
||||
//goto 'Apps and Data', goto 'Data', buy (or get for free?): 'SATELLITE'
|
||||
public class DataDownloadApplication : IApplication
|
||||
{
|
||||
//private const string DownloadFolder = "Downloads";
|
||||
|
@ -1,41 +1,38 @@
|
||||
# ShowGeotiff.r
|
||||
# Have a look at a downloaded satellite image and check if stats are correctly calculated
|
||||
# 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 the image for date '2022-03-08'
|
||||
# downloaded and calculated the stats for the polygon defined in C:\git\FarmMapsApiClient\FarmmapsDataDownload\DataDownloadInput.json
|
||||
# and in which in the console I requested the image for date '2022-08-25'
|
||||
|
||||
library(raster)
|
||||
library(sf)
|
||||
library(rgdal)
|
||||
setwd("C:/git/FarmMapsApiClient_WURtest/FarmmapsDataDownload/bin/Debug/netcoreapp3.1/Downloads")
|
||||
library(terra)
|
||||
setwd("C:/git/FarmMapsApiClient/FarmmapsDataDownload/bin/Debug/netcoreapp3.1/Downloads")
|
||||
|
||||
# FarmmapsDataDownload
|
||||
fileGeotiff <- "sentinelhub_test_BvdTFieldlabG92_20220308.tif"
|
||||
fileGeotiff <- "sentinelhub_test_BvdTFieldlabG92_20220825.tif"
|
||||
lenfilename <- nchar(fileGeotiff)
|
||||
year <- substr(fileGeotiff,lenfilename-11,lenfilename-8)
|
||||
imgdate <- substr(fileGeotiff,lenfilename-11,lenfilename-4)
|
||||
|
||||
stk.sentinelhub <- stack(x=fileGeotiff)
|
||||
# plot(stk.sentinelhub) shows 6 plots (6 bands)
|
||||
r.sentinelhub <- rast(x=fileGeotiff)
|
||||
# plot(r.sentinelhub) shows 6 plots (6 bands)
|
||||
# 1. ndvi
|
||||
# 2. wdvi Note wdvi-red
|
||||
# 3. ci-red
|
||||
# 4. natural: red
|
||||
# 5. natural: green
|
||||
# 6. natural: blue
|
||||
names(stk.sentinelhub) <- c("ndvi","wdvired","ci-red","red","green","blue")
|
||||
plot(stk.sentinelhub)
|
||||
crs(stk.sentinelhub)
|
||||
names(r.sentinelhub) <- c("ndvi","wdvired","ci-red","red","green","blue")
|
||||
# crs(r.sentinelhub)
|
||||
# CRS arguments: +proj=longlat +datum=WGS84 +no_defs
|
||||
stk.sentinelhub.rd <- projectRaster(stk.sentinelhub, crs = CRS('+init=EPSG:28992'))
|
||||
crs(stk.sentinelhub)
|
||||
r.sentinelhub.rd <- project(r.sentinelhub,'+init=EPSG:28992')
|
||||
# crs(r.sentinelhub.rd)
|
||||
|
||||
r.sentinelhub.rd.wdvi <- subset(stk.sentinelhub.rd,2)
|
||||
dev.off()
|
||||
r.sentinelhub.rd.wdvi <- subset(r.sentinelhub.rd,2)
|
||||
# dev.off()
|
||||
plot(r.sentinelhub.rd.wdvi,main=paste("wdvi",imgdate),xlab="RDX",ylab="RDY")
|
||||
cellStats(r.sentinelhub.rd.wdvi,'mean') # 0.2252725
|
||||
global(r.sentinelhub.rd.wdvi, fun="mean")
|
||||
|
||||
# Convert the .rd.wdvi raster to WGS84
|
||||
r.sentinelhub.wgs84.wdvi <- projectRaster(r.sentinelhub.rd.wdvi, crs = CRS('+init=EPSG:4326'))
|
||||
r.sentinelhub.wgs84.wdvi <- project(r.sentinelhub.rd.wdvi, '+init=EPSG:4326')
|
||||
|
||||
# Draw a polygon on top of the raster
|
||||
# Polygon pol from C:\git\FarmMapsApiClient_WURtest\FarmmapsDataDownload\DataDownloadInput.json
|
||||
@ -48,67 +45,38 @@ pol <- data.frame(id = 1, wkt = gsub("\n","",'POLYGON((
|
||||
))'))
|
||||
|
||||
|
||||
pol.wgs84 <- st_as_sf(pol, wkt = 'wkt', crs = CRS('+init=EPSG:4326'))
|
||||
pol.rd <- st_transform(pol.wgs84, "+init=epsg:28992")
|
||||
|
||||
#Calculate approximate middle of polygon
|
||||
res <- as.data.frame(do.call("rbind", lapply(st_geometry(pol.wgs84), st_bbox)))
|
||||
res$latmid <- (res$ymax+res$ymin)/2.0
|
||||
res$lonmid <- (res$xmax+res$xmin)/2.0
|
||||
res
|
||||
# xmin ymin xmax ymax latmid lonmid
|
||||
# 1 5.563472 52.54048 5.567917 52.54773 52.5441 5.565695
|
||||
pol.wgs84 <- vect(pol$wkt,crs='+init=EPSG:4326')
|
||||
pol.rd <- project(pol.wgs84, "+init=epsg:28992")
|
||||
|
||||
# Have a look at both polygons
|
||||
# wg84
|
||||
dev.off()
|
||||
plot(r.sentinelhub.wgs84.wdvi,main=paste("wdvi",imgdate),xlab="LON",ylab="LAT")
|
||||
plot(pol.wgs84,add=TRUE, col="transparent",border="red")
|
||||
# RD
|
||||
dev.off()
|
||||
plot(r.sentinelhub.rd.wdvi,main=paste("wdvi",imgdate),xlab="RDX",ylab="RDY")
|
||||
plot(pol.rd,add=TRUE, col="transparent",border="red")
|
||||
|
||||
# Clip the polygon from the full rectangle figure
|
||||
r.sentinelhub.rd.wdvi.pol <- mask(r.sentinelhub.rd.wdvi,pol.rd)
|
||||
r.sentinelhub.wgs84.wdvi.pol <- mask(r.sentinelhub.wgs84.wdvi,pol.wgs84)
|
||||
dev.off()
|
||||
plot(r.sentinelhub.wgs84.wdvi.pol,main=paste("wdvi",imgdate),xlab="LON",ylab="LAT")
|
||||
plot(pol.wgs84,add=TRUE, col="transparent",border="red")
|
||||
#That's what we want!
|
||||
|
||||
# Now compare the stats
|
||||
cellStats(r.sentinelhub.wgs84.wdvi,'mean') # [1] 0.2250987 # Stats from rectangle, WGS84
|
||||
cellStats(r.sentinelhub.rd.wdvi,'mean') # [1] 0.2252725 # Stats from rectangle, RD. Almost but not exactly same as above
|
||||
cellStats(r.sentinelhub.wgs84.wdvi.pol,'mean') # [1] 0.2275067 # Stats from raster clipped by polygon, WGS84
|
||||
cellStats(r.sentinelhub.rd.wdvi.pol,'mean') # [1] 0.2275073 # Stats from raster clipped by polygon, RD. Almost but not exactly same as above
|
||||
# file satelliteStats_test_BvdTFieldlabG92_20220308.csv
|
||||
# "wdvi" "mean": 0.22744397204465
|
||||
global(r.sentinelhub.wgs84.wdvi,'mean') # [1] 0.1733634 # Stats from rectangle, WGS84
|
||||
global(r.sentinelhub.rd.wdvi,'mean') # [1] 0.173531 # Stats from rectangle, RD. Almost but not exactly same as above
|
||||
global(r.sentinelhub.wgs84.wdvi.pol,'mean',na.rm=TRUE) # [1] 0.1740224 # Stats from raster clipped by polygon, WGS84
|
||||
global(r.sentinelhub.rd.wdvi.pol,'mean',na.rm=TRUE) # [1] 0.1738386 # Stats from raster clipped by polygon, RD. Almost but not exactly same as above
|
||||
# file satelliteStats_test_BvdTFieldlabG92_20220825.csv
|
||||
# "wdvi" "mean": 0.173905644768987
|
||||
# Mean in csv corresponds with cellStats calculated from clipped tif!
|
||||
# So while the tif returned is a non-clipped image, the downloaded statistics are from the clipped image
|
||||
# Exactly as we wanted.
|
||||
cellStats(r.sentinelhub.wgs84.wdvi.pol,'median') # Error in .local(x, stat, ...) : invalid 'stat'. Should be sum, min, max, sd, mean, or 'countNA'
|
||||
global(r.sentinelhub.wgs84.wdvi.pol,'median', na.rm=TRUE) # Error in fun(values(x[[i]]), ...) : could not find function "fun"
|
||||
r.sentinelhub.wgs84.wdvi.vals <- values(r.sentinelhub.wgs84.wdvi)
|
||||
median(r.sentinelhub.wgs84.wdvi.vals) # [1] NA
|
||||
median(r.sentinelhub.wgs84.wdvi.vals,na.rm=TRUE) # [1] 0.2318
|
||||
median(r.sentinelhub.wgs84.wdvi.vals) # [1] 0.1459627
|
||||
median(r.sentinelhub.wgs84.wdvi.vals,na.rm=TRUE) # [1] 0.1459627
|
||||
r.sentinelhub.wgs84.wdvi.pol.vals <- values(r.sentinelhub.wgs84.wdvi.pol)
|
||||
median(r.sentinelhub.wgs84.wdvi.pol.vals) # [1] NA
|
||||
median(r.sentinelhub.wgs84.wdvi.pol.vals,na.rm=TRUE) # [1] 0.2338
|
||||
# file satelliteStats_test_BvdTFieldlabG92_20220308.csv
|
||||
# "wdvi" "mean": 0.233799993991851
|
||||
# Median is same as for median(r.sentinelhub.wgs84.wdvi.pol.vals,na.rm=TRUE)
|
||||
# in csv corresponds with cellStats calculated from clipped tif!
|
||||
# So while the tif returned is a non-clipped image, the downloaded statistics are from the clipped image
|
||||
# Exactly as we wanted.
|
||||
cellStats(r.sentinelhub.wgs84.wdvi,'countNA') # [1] 27896
|
||||
ncell(r.sentinelhub.wgs84.wdvi) # [1] 272718
|
||||
cellStats(r.sentinelhub.wgs84.wdvi,'countNA') / ncell(r.sentinelhub.wgs84.wdvi) # [1] 0.1022888 # 10% no data? doesn't show in the plot?
|
||||
cellStats(r.sentinelhub.wgs84.wdvi.pol,'countNA') # [1] 57625
|
||||
summary(r.sentinelhub.wgs84.wdvi.pol.vals) # shows the same: NA's: 57625
|
||||
ncell(r.sentinelhub.wgs84.wdvi.pol) # [1] 272718
|
||||
populationCount = ncell(r.sentinelhub.wgs84.wdvi.pol) - cellStats(r.sentinelhub.wgs84.wdvi.pol,'countNA')
|
||||
populationCount # [1] 215093
|
||||
# file satelliteStats_test_BvdTFieldlabG92_20220308.csv
|
||||
# "wdvi" "populationCount": 214688
|
||||
# similar but not same
|
||||
|
||||
median(r.sentinelhub.wgs84.wdvi.pol.vals,na.rm=TRUE) # [1] 0.1453323
|
178
FarmmapsDownloadCL/DownloadCLApplication.cs
Normal file
178
FarmmapsDownloadCL/DownloadCLApplication.cs
Normal file
@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using FarmmapsApi;
|
||||
using FarmmapsApi.Models;
|
||||
using FarmmapsApi.Services;
|
||||
using FarmmapsDownloadCL.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static FarmmapsApiSamples.Constants;
|
||||
|
||||
namespace FarmmapsDownloadCL
|
||||
{
|
||||
public class DownloadCLApplication : IApplication
|
||||
{
|
||||
|
||||
private readonly ILogger<DownloadCLApplication> _logger;
|
||||
private readonly FarmmapsApiService _farmmapsApiService;
|
||||
private readonly DownloadCLService _DownloadCLService;
|
||||
private readonly GeneralService _generalService;
|
||||
public readonly Dictionary<string, List<string>> _dictCl;
|
||||
string _itemcode;
|
||||
public DownloadCLApplication(ILogger<DownloadCLApplication> logger, FarmmapsApiService farmmapsApiService,
|
||||
GeneralService generalService, DownloadCLService DownloadCLService)
|
||||
{
|
||||
_logger = logger;
|
||||
_farmmapsApiService = farmmapsApiService;
|
||||
_generalService = generalService;
|
||||
_DownloadCLService = DownloadCLService;
|
||||
CodelistsClasses clc = new CodelistsClasses();
|
||||
_itemcode = clc.itemcode;
|
||||
_dictCl = clc.dictCl;
|
||||
}
|
||||
|
||||
public async Task RunAsync()
|
||||
{
|
||||
var fieldsInputJson = File.ReadAllText("DownloadCLInput.json");
|
||||
|
||||
|
||||
DownloadCLInput clInput = JsonConvert.DeserializeObject<DownloadCLInput>(fieldsInputJson);
|
||||
|
||||
// !! 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();
|
||||
|
||||
//Where to write the output
|
||||
string downloadFolder = clInput.DownloadFolder;
|
||||
if (string.IsNullOrEmpty(downloadFolder))
|
||||
{
|
||||
downloadFolder = "Downloads";
|
||||
}
|
||||
if (!Directory.Exists(downloadFolder))
|
||||
Directory.CreateDirectory(downloadFolder);
|
||||
|
||||
//Get the most recent codelists
|
||||
foreach (string codelist in clInput.codelists)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Process(roots, codelist, downloadFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Process(List<UserRoot> roots, string codelistname, string downloadFolder)
|
||||
{
|
||||
List<Item> codelist;
|
||||
string itemtype;
|
||||
string className;
|
||||
string body;
|
||||
string header;
|
||||
string value;
|
||||
string[] aboutArray = null;
|
||||
string[] headerArray = null;
|
||||
PropertyInfo[] props;
|
||||
PropertyInfo prop;
|
||||
List<string> dataList;
|
||||
string clJson = Path.Combine(downloadFolder, $"{codelistname}.json");
|
||||
string clCsv = Path.Combine(downloadFolder, $"{codelistname}.csv");
|
||||
|
||||
try
|
||||
{
|
||||
itemtype = _dictCl[codelistname][0];
|
||||
className = _dictCl[codelistname][1];
|
||||
codelist = await _farmmapsApiService.GetItemChildrenAsync(_itemcode, itemtype);
|
||||
|
||||
//Write full codelist in json format to clJson.
|
||||
body = JsonConvert.SerializeObject(codelist);
|
||||
File.WriteAllText(clJson, body);
|
||||
_logger.LogInformation($"Downloaded file {clJson}");
|
||||
|
||||
//Write full list in csv format to clCsv.
|
||||
StreamWriter sw = new StreamWriter(clCsv);
|
||||
//Write metadata in top: when downloaded
|
||||
sw.WriteLine($"Editeelt codelist {codelistname} downloaded on {DateTime.Now} with the FarmmapsDownloadCL application");
|
||||
|
||||
//Generic, for any Codelist as long as it is also defined in FarmmapsDownloadCL.Models in
|
||||
string typerequest = "FarmmapsDownloadCL.Models." + className + ", FarmmapsDownloadCL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
|
||||
Type objType = Type.GetType(typerequest);
|
||||
object clitem = Activator.CreateInstance(objType);
|
||||
props = clitem.GetType().GetProperties();
|
||||
|
||||
//Lookup about info and write to top
|
||||
foreach (PropertyInfo pi in props)
|
||||
{
|
||||
if (pi.Name == "about")
|
||||
{
|
||||
aboutArray = (string[])pi.GetValue(clitem);
|
||||
foreach (string about in aboutArray)
|
||||
{
|
||||
//Add quotes
|
||||
value = "\"" + about + "\"";
|
||||
//Write each about to a separate line in the csv file
|
||||
sw.WriteLine(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//Write an empty line
|
||||
sw.WriteLine();
|
||||
|
||||
//Look up the headerArray and write the header comma separated
|
||||
int i = 0;
|
||||
while (headerArray == null)
|
||||
{
|
||||
prop = props[i];
|
||||
if (prop.Name == "headers")
|
||||
{
|
||||
headerArray = (string[])prop.GetValue(clitem);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
header = string.Join(",", headerArray);
|
||||
sw.WriteLine(header);
|
||||
|
||||
//Add quotes because a value for field 'description' could be '21% Cl, 16.5%CaO, 1330 g/l' and without the quotes that
|
||||
//would show up as separate columns. So per definition we will add quotes, except if we have a fieldname in the listNotToQuote, then value for that fieldname will not be given quotes
|
||||
//e.g. not code "3" but code 3.
|
||||
List<string> listNotToQuote = new List<string>() { "code", "n", "p", "k", "quantity", "cropGroupCode", "cultivationGroupCode" };
|
||||
//Loop through all items in the codelist
|
||||
foreach (Item item in codelist)
|
||||
{
|
||||
dataList = new List<string> { };
|
||||
clitem = JsonConvert.DeserializeObject(item.Data.ToString(), objType);
|
||||
|
||||
//Add values of the cLitem to the dataList in the same order as the header
|
||||
foreach (string h in headerArray)
|
||||
{
|
||||
value = (string)clitem.GetType().GetProperty(h).GetValue(clitem, null);
|
||||
if(listNotToQuote.Contains(h) == false)
|
||||
value = "\"" +value + "\"";
|
||||
|
||||
dataList.Add(value);
|
||||
}
|
||||
string dataLine = string.Join(",", dataList);
|
||||
sw.WriteLine(string.Join(",", dataList));
|
||||
}
|
||||
|
||||
sw.Close();
|
||||
_logger.LogInformation($"Downloaded file {clCsv}");
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogWarning($"Missing lookup information on codelist {codelistname}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
FarmmapsDownloadCL/DownloadCLInput.json
Normal file
5
FarmmapsDownloadCL/DownloadCLInput.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"codeLists": [ "CL022", "CL127", "CL263", "CL265", "CL405", "CL251", "CL104" ]
|
||||
//"codeLists": [ "CL263", "CL265"]
|
||||
//"DownloadFolder": "Downloads", //"C:\\hugoschrererdir\\kpidir\\", // "Downloads", -> if you just put "Downloads" the program will download to somewhere in ..\FarmMapsApiClient_WURtest\FarmmapsDataDownload\bin\Debug\netcoreapp3.1\Downloads\
|
||||
}
|
27
FarmmapsDownloadCL/DownloadCLService.cs
Normal file
27
FarmmapsDownloadCL/DownloadCLService.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using FarmmapsApi.Models;
|
||||
using FarmmapsApi.Services;
|
||||
using FarmmapsDownloadCL.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static FarmmapsApi.Extensions;
|
||||
using static FarmmapsApiSamples.Constants;
|
||||
|
||||
namespace FarmmapsDownloadCL
|
||||
{
|
||||
public class DownloadCLService
|
||||
{
|
||||
private readonly ILogger<DownloadCLService> _logger;
|
||||
private readonly FarmmapsApiService _farmmapsApiService;
|
||||
private readonly GeneralService _generalService;
|
||||
|
||||
public DownloadCLService(ILogger<DownloadCLService> logger, FarmmapsApiService farmmapsApiService,
|
||||
GeneralService generalService)
|
||||
{
|
||||
_logger = logger;
|
||||
_farmmapsApiService = farmmapsApiService;
|
||||
_generalService = generalService;
|
||||
}
|
||||
}
|
||||
}
|
24
FarmmapsDownloadCL/FarmmapsDownloadCL.csproj
Normal file
24
FarmmapsDownloadCL/FarmmapsDownloadCL.csproj
Normal file
@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Collections" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FarmmapsApi\FarmmapsApi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="DownloadCLInput.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
169
FarmmapsDownloadCL/Models/CodelistsClasses.cs
Normal file
169
FarmmapsDownloadCL/Models/CodelistsClasses.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace FarmmapsDownloadCL.Models
|
||||
{
|
||||
public class CodelistsClasses
|
||||
{
|
||||
public CodelistsClasses()
|
||||
{
|
||||
this.itemcode = "938c0aec4efc46e88783fd029f17309e%3ASYSTEM_DATA";
|
||||
this.dictCl = new Dictionary<string, List<string>>();
|
||||
//Add here once you have defined a new CLxxxitem below
|
||||
//To add a new item, look it up in https://test.farmmaps.eu/swagger/index.html. Download the item you are interested in (like "vnd.farmmaps.itemtype.codelist.cl263")
|
||||
//do this under Items <- get/api/v1/items, use the it field. Depending on the size of the list retrieving the list can take a few minutes.
|
||||
//then below create a new CLxxxitem with fieldnames as you see them in the just downloaded new codelist
|
||||
this.dictCl.Add("CL022", new List<string> { "vnd.farmmaps.itemtype.codelist.cl022", "CL022item" });
|
||||
this.dictCl.Add("CL104", new List<string> { "vnd.farmmaps.itemtype.codelist.cl104", "CL104item" });
|
||||
this.dictCl.Add("CL127", new List<string> { "vnd.farmmaps.itemtype.codelist.cl127", "CL127item" });
|
||||
this.dictCl.Add("CL251", new List<string> { "vnd.farmmaps.itemtype.codelist.cl251", "CL251item" });
|
||||
this.dictCl.Add("CL263", new List<string> { "vnd.farmmaps.itemtype.codelist.cl263", "CL263item" });
|
||||
this.dictCl.Add("CL265", new List<string> { "vnd.farmmaps.itemtype.codelist.cl265", "CL265item" });
|
||||
this.dictCl.Add("CL405", new List<string> { "vnd.farmmaps.itemtype.codelist.cl405", "CL405item" });
|
||||
}
|
||||
public string itemcode;
|
||||
//Define here the codelist names, their item type and the class defining the contents of the data type (and defined in the Models.CodelistsClasses)
|
||||
public Dictionary<string, List<string>> dictCl;
|
||||
}
|
||||
|
||||
public class CL022item
|
||||
{
|
||||
public CL022item()
|
||||
{
|
||||
this.headers = new string[] { "codelist", "code", "description", "type", "n", "p", "k", "composition" };
|
||||
this.codelist = "CL022";
|
||||
this.about = new string[] { "EDI-Crop, coderingslijst meststoffen", "type MOR = organische meststof; MAN = anorganische meststof" };
|
||||
}
|
||||
|
||||
public string[] headers { get; }
|
||||
public string codelist { get; }
|
||||
public string[] about { get; }
|
||||
public string k { get; set; }
|
||||
public string n { get; set; }
|
||||
public string p { get; set; }
|
||||
public string code { get; set; }
|
||||
public string type { get; set; }
|
||||
public string composition { get; set; }
|
||||
public string description { get; set; }
|
||||
|
||||
}
|
||||
public class CL104item
|
||||
{
|
||||
public CL104item()
|
||||
{
|
||||
this.headers = new string[] { "codelist", "code", "description" };
|
||||
this.codelist = "CL104";
|
||||
this.about = new string[] { "EDI-crop, coderingslijst groenbemesters" };
|
||||
}
|
||||
|
||||
public string[] headers { get; }
|
||||
public string codelist { get; }
|
||||
public string[] about { get; }
|
||||
public string code { get; set; }
|
||||
public string description { get; set; }
|
||||
}
|
||||
public class CL127item
|
||||
{
|
||||
public CL127item()
|
||||
{
|
||||
this.headers = new string[] { "codelist", "code", "description", "description_nl", "culturalPracticeLevel1", "culturalPracticeLevel2" };
|
||||
this.codelist = "CL127";
|
||||
}
|
||||
|
||||
public string[] headers { get; }
|
||||
public string codelist { get; }
|
||||
public string code { get; set; }
|
||||
public string description { get; set; }
|
||||
public string description_nl { get; set; }
|
||||
public string culturalPracticeLevel1 { get; set; }
|
||||
public string culturalPracticeLevel2 { get; set; }
|
||||
}
|
||||
public class CL251item
|
||||
{
|
||||
public CL251item()
|
||||
{
|
||||
this.headers = new string[] { "codelist", "code", "rvo", "active", "primary", "description", "description_fr", "description_nl" };
|
||||
this.codelist = "CL251";
|
||||
this.about = new string[] { "Codering teeltdoelen (CropProductionPurposeCode)", "primary: 1 = primary; 2 = secondary", "rvo: 1 = yes; 0 = no" };
|
||||
}
|
||||
|
||||
public string[] headers { get; }
|
||||
public string codelist { get; }
|
||||
public string[] about { get; }
|
||||
public string code { get; set; }
|
||||
public string rvo { get; set; }
|
||||
public string active { get; set; }
|
||||
public string primary { get; set; }
|
||||
public string description { get; set; }
|
||||
public string description_nl { get; set; }
|
||||
public string description_fr { get; set; }
|
||||
|
||||
}
|
||||
public class CL263item
|
||||
{
|
||||
public CL263item()
|
||||
{
|
||||
this.headers = new string[] { "codelist", "code", "eppoCode", "botanicName", "description", "description_fr", "description_nl", "cropGroupCode", "cropGroupName", "subCropGroupCode", "subCropGroupName", "cultivationGroupCode", "cultivationGroupName" };
|
||||
this.codelist = "CL263";
|
||||
this.about = new string[] { "EDI-Crop, coderingslijst gewassoorten" };
|
||||
}
|
||||
|
||||
public string[] headers { get; }
|
||||
public string codelist { get; }
|
||||
public string[] about { get; }
|
||||
public string code { get; set; }
|
||||
public string eppoCode { get; set; }
|
||||
public string botanicName { get; set; }
|
||||
public string description { get; set; }
|
||||
public string description_nl { get; set; }
|
||||
public string description_fr { get; set; }
|
||||
public string cropGroupCode { get; set; }
|
||||
public string cropGroupName { get; set; }
|
||||
public string subCropGroupCode { get; set; }
|
||||
public string subCropGroupName { get; set; }
|
||||
public string cultivationGroupCode { get; set; }
|
||||
public string cultivationGroupName { get; set; }
|
||||
public string composition { get; set; } //?
|
||||
|
||||
}
|
||||
public class CL265item
|
||||
{
|
||||
public CL265item()
|
||||
{
|
||||
this.headers = new string[] { "Code", "Formaat", "Eenheid", "Waarde" };
|
||||
this.codelist = "CL265";
|
||||
this.about = new string[] { "EDI-Crop CL265: FarmCharacterictics, FieldCharacteristics, CropFieldCharacteristics, TreatmentCharacteristics",
|
||||
"Betreft codelijst eigenschappen teelt en teelthandelingen CL265.Daar waar in kolom 'Waarde' wordt verwezen naar CL.., wordt de codelijst bedoeld die onder het betreffende nummer bekend is.",
|
||||
"In het EDI-Crop bericht wordt de code gebruikt onder resp. CropFieldCharacteristic c.q.TreatmentCharacteristic middels de dataelementen PropertyVariableCode (invullen vanuit CL265) en PropertyVariableValue(voor de waarde)"};
|
||||
}
|
||||
|
||||
public string[] headers { get; }
|
||||
public string codelist { get; }
|
||||
public string[] about { get; }
|
||||
public string Code { get; set; }
|
||||
public string Formaat { get; set; }
|
||||
public string Eenheid { get; set; }
|
||||
public string Waarde { get; set; }
|
||||
}
|
||||
public class CL405item
|
||||
{
|
||||
public CL405item()
|
||||
{
|
||||
this.headers = new string[] { "codelist", "code", "description", "description_fr", "description_nl", "hoofdgroep" };
|
||||
this.codelist = "CL405";
|
||||
this.about = new string[] { };
|
||||
}
|
||||
|
||||
public string[] headers { get; }
|
||||
public string codelist { get; }
|
||||
public string[] about { get; }
|
||||
public string code { get; set; }
|
||||
public string hoofdgroep { get; set; }
|
||||
public string description { get; set; }
|
||||
public string description_fr { get; set; }
|
||||
public string description_nl { get; set; }
|
||||
|
||||
}
|
||||
}
|
12
FarmmapsDownloadCL/Models/DownloadCLInput.cs
Normal file
12
FarmmapsDownloadCL/Models/DownloadCLInput.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace FarmmapsDownloadCL.Models
|
||||
{
|
||||
public class DownloadCLInput
|
||||
{
|
||||
public string[] codelists { get; set; }
|
||||
public string DownloadFolder { get; set; }
|
||||
|
||||
}
|
||||
}
|
21
FarmmapsDownloadCL/Program.cs
Normal file
21
FarmmapsDownloadCL/Program.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
using FarmmapsApi;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FarmmapsDownloadCL
|
||||
{
|
||||
class Program : FarmmapsProgram<DownloadCLApplication>
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
await new Program().Start(args);
|
||||
}
|
||||
|
||||
protected override void Configure(IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddLogging()
|
||||
.AddTransient<DownloadCLService>();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
20
FarmmapsKPI/FarmmapsKPI.csproj
Normal file
20
FarmmapsKPI/FarmmapsKPI.csproj
Normal file
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FarmmapsApi\FarmmapsApi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="KPIInput.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
895
FarmmapsKPI/KPIApplication.cs
Normal file
895
FarmmapsKPI/KPIApplication.cs
Normal file
@ -0,0 +1,895 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
using FarmmapsApi;
|
||||
using FarmmapsApi.Models;
|
||||
using FarmmapsApi.Services;
|
||||
using FarmmapsKPI.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static FarmmapsApiSamples.Constants;
|
||||
|
||||
namespace FarmmapsKPI
|
||||
{
|
||||
public class KPIApplication : IApplication
|
||||
{
|
||||
//private const string DownloadFolder = "Downloads";
|
||||
private const string SettingsFile = "settings.json";
|
||||
|
||||
private readonly ILogger<KPIApplication> _logger;
|
||||
private readonly FarmmapsApiService _farmmapsApiService;
|
||||
private readonly KPIService _kpiService;
|
||||
private readonly GeneralService _generalService;
|
||||
|
||||
private Settings _settings;
|
||||
|
||||
public KPIApplication(ILogger<KPIApplication> logger, FarmmapsApiService farmmapsApiService,
|
||||
GeneralService generalService, KPIService kpiService)
|
||||
{
|
||||
_logger = logger;
|
||||
_farmmapsApiService = farmmapsApiService;
|
||||
_generalService = generalService;
|
||||
_kpiService = kpiService;
|
||||
}
|
||||
|
||||
public async Task RunAsync()
|
||||
{
|
||||
KPIInput input;
|
||||
string fnKPIinput;
|
||||
|
||||
//Console.WriteLine("Type name of input json file. Example: KPIinput.json (in same directory as FarmmapsKPI.exe) or for example like this: C:/temp/KPIinputChemieTmp.json");
|
||||
//fnKPIinput = Console.ReadLine();
|
||||
//if (string.IsNullOrEmpty(fnKPIinput))
|
||||
//{
|
||||
//fnKPIinput = "KPIinput.json";
|
||||
fnKPIinput = "C:\\git\\FarmMapsApiClient_KB34_MAST\\FarmmapsKPI\\KPIinput.json";
|
||||
//}
|
||||
|
||||
var fieldsInputJson = File.ReadAllText(fnKPIinput);
|
||||
|
||||
List<KPIInput> fieldsInputs = JsonConvert.DeserializeObject<List<KPIInput>>(fieldsInputJson);
|
||||
|
||||
// !! 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();
|
||||
|
||||
//Where to write the output
|
||||
string downloadFolder = fieldsInputs[0].DownloadFolder;
|
||||
if (string.IsNullOrEmpty(downloadFolder))
|
||||
{
|
||||
downloadFolder = "Downloads";
|
||||
}
|
||||
if (!Directory.Exists(downloadFolder))
|
||||
Directory.CreateDirectory(downloadFolder);
|
||||
|
||||
|
||||
//Write the same info to a single csv file. Note this means existing file will be overwritten!
|
||||
StreamWriter sw;
|
||||
string KPIItemCsv = Path.GetFileNameWithoutExtension(fnKPIinput) + "_Items.csv";
|
||||
string KPIItemPathCsv = Path.Combine(downloadFolder, KPIItemCsv);
|
||||
List<string> headerList = new List<string> { "run","parentName", "cropfieldcode", "area_ha", "cropTypeCode", "cropTypeName", "KPIid", "KPIvariable", "KPIvalue", "KPIunit", "KPItargetvalue", "KPIthresholdValue",
|
||||
"mbp_productCode","mbp_productName","mbp_quantity","mbp_unitCode","mbp_date","mbp_KPIvariable","mbp_KPIvalue"};
|
||||
//Create a new csv file. Means if existing then overwritten !!!
|
||||
sw = new StreamWriter(KPIItemPathCsv);
|
||||
Configuration cf = await _farmmapsApiService.GetConfiguration();
|
||||
string endPoint = cf.Endpoint;
|
||||
sw.WriteLine($"Using FarmmapsKPI application in FarmmapsApSamples.sln. Input file: '{fnKPIinput}'. Download DateTime: '{DateTime.Now}'. Calculations on: '{endPoint}'");
|
||||
sw.WriteLine();
|
||||
sw.WriteLine(string.Join(",", headerList));
|
||||
|
||||
// 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;
|
||||
|
||||
//Per default just 1 run per field. For debugging check if when we run multiple times do we get (should be) always the same output?
|
||||
int nrun = 1;
|
||||
for (int run = 1; run <= nrun; run++) {
|
||||
for (int i = 0; i < fieldsInputs.Count; i++)
|
||||
//for (int i = 3; i < 4; i++) // for testing
|
||||
{
|
||||
watch.Restart();
|
||||
input = fieldsInputs[i];
|
||||
_logger.LogInformation(string.Format($"// FarmmapsKPI: Run {run}: Downloading KPI's for field {i + 1} out of {fieldsInputs.Count} to single csv file {KPIItemPathCsv}"));
|
||||
try
|
||||
{
|
||||
await Process(roots, input, sw, run);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_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(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, int run)
|
||||
{
|
||||
List<Item> cropfieldChildren;
|
||||
List<Item> crprecChildren;
|
||||
KPIOutput kpio;
|
||||
KPIOutput kpioPrevious = new KPIOutput(); //creates a new empty
|
||||
//If KPI E1 is calculated, write these sub kpi's to output
|
||||
string[] mbp_KPIvariables = new string[] { "aquaticLife", "groundWater", "soilLife" };
|
||||
string mbp_KPIvalue;
|
||||
int targetKPIitemsCount; //if we know we should be getting 8 KPI items (A1,B1,B2,C1,D1,E1,F1,F2)
|
||||
int maxtries = 5; // but don't keep on trying forever; there is a maximum number of tries
|
||||
int trycnt;
|
||||
//double totalNferiliserInput = 0;
|
||||
//double totalNferiliserCropfield = 0;
|
||||
|
||||
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 useExistingCropfieldWithChildren = input.UseExistingCropfieldWithChildren;
|
||||
int cropYear = input.CropYear;
|
||||
string fieldName = input.fieldName;
|
||||
string fieldGeom = input.GeometryJson.ToString(Formatting.None);
|
||||
|
||||
//Settings
|
||||
string settingsfile = $"Settings_{fieldName}.json";
|
||||
LoadSettings(settingsfile);
|
||||
|
||||
var uploadedRoot = roots.SingleOrDefault(r => r.Name == "USER_IN");
|
||||
if (uploadedRoot == null)
|
||||
{
|
||||
_logger.LogError("Could not find a needed root item");
|
||||
return;
|
||||
}
|
||||
|
||||
var myDriveRoot = roots.SingleOrDefault(r => r.Name == "USER_FILES");
|
||||
if (myDriveRoot == null)
|
||||
{
|
||||
_logger.LogError("Could not find a needed root item");
|
||||
return;
|
||||
}
|
||||
|
||||
// Use already created cropfield or create new one, added a Data input, with field specific data for the KPI calculation
|
||||
Item cropfieldItem;
|
||||
//1 useExistingCropfieldWithChildren = false -> create new
|
||||
//2 useExistingCropfieldWithChildren = true && input.CropfieldItemCode = "" or absent -> read from settings json
|
||||
//3 useExistingCropfieldWithChildren = true && input.CropfieldItemCode like "deb48a74c5b54299bb852f17288010e9" in KPIinput -> use this one
|
||||
if (useExistingCropfieldWithChildren == false)
|
||||
{
|
||||
_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), input.StartDate, input.EndDate);
|
||||
_settings.CropfieldItemCode = cropfieldItem.Code;
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(input.CropfieldItemCode))
|
||||
{
|
||||
_logger.LogInformation("reading CropfieldItemCode from settings file");
|
||||
cropfieldItem = await _farmmapsApiService.GetItemAsync(_settings.CropfieldItemCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("reading CropfieldItemCode from KPIinput json");
|
||||
cropfieldItem = await _farmmapsApiService.GetItemAsync(input.CropfieldItemCode);
|
||||
SaveSettings(settingsfile);
|
||||
}
|
||||
|
||||
// The cropfieldCharacteristicItem is used to enter crop yields and/or straw yields
|
||||
Item cropfieldCharacteristicItem;
|
||||
string dataCropfieldCharacteristic;
|
||||
//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)
|
||||
{
|
||||
for (int i = 0; i < input.DataCropfieldCharacteristics.Length; i++)
|
||||
{
|
||||
dataCropfieldCharacteristic = input.DataCropfieldCharacteristics[i].ToString(Formatting.None);
|
||||
dynamic data = JObject.Parse(dataCropfieldCharacteristic);
|
||||
_logger.LogInformation($"CreateCropfieldCharacteristicItemAsync ... for cropfieldCharacteristic {i}: '{data.label}', value '{data.value}'");
|
||||