diff --git a/FarmmapsApi/FarmmapsApi.csproj b/FarmmapsApi/FarmmapsApi.csproj
index 8719220..8f133f8 100644
--- a/FarmmapsApi/FarmmapsApi.csproj
+++ b/FarmmapsApi/FarmmapsApi.csproj
@@ -5,6 +5,7 @@
+
@@ -12,6 +13,7 @@
+
diff --git a/FarmmapsApi/Models/Configuration.cs b/FarmmapsApi/Models/Configuration.cs
index 72ec057..ee29b7e 100644
--- a/FarmmapsApi/Models/Configuration.cs
+++ b/FarmmapsApi/Models/Configuration.cs
@@ -4,6 +4,7 @@
{
public string Authority { get; set; }
public string Endpoint { get; set; }
+ public string BasePath { get; set; }
public string DiscoveryEndpointUrl { get; set; }
public string RedirectUri { get; set; }
public string ClientId { get; set; }
diff --git a/FarmmapsApi/Models/FileRequest.cs b/FarmmapsApi/Models/FileRequest.cs
new file mode 100644
index 0000000..0e04088
--- /dev/null
+++ b/FarmmapsApi/Models/FileRequest.cs
@@ -0,0 +1,45 @@
+using System.ComponentModel.DataAnnotations;
+using Newtonsoft.Json.Linq;
+
+namespace FarmmapsApi.Models
+{
+ public class FileRequest
+ {
+ ///
+ /// Code of the parent
+ ///
+ /// 41971e7ea8a446069a817e66b608dcae
+ public string ParentCode { get; set; }
+
+ ///
+ /// Geometry meta data
+ ///
+ /// {"type": "Point","coordinates": [5.27, 52.10]}
+ public JObject Geometry { get; set; }
+
+ ///
+ /// Name of the file to upload
+ ///
+ /// MyFile.tiff
+ [Required]
+ public string Name { get; set; }
+
+ ///
+ /// Size of the file to upload
+ ///
+ /// 67351
+ [Required]
+ public long Size { get; set; }
+
+ ///
+ /// If a chunked upload, the size of a single chunk
+ ///
+ /// 1048576
+ public long ChunkSize { get; set; }
+
+ ///
+ /// Optional data for the item coupled with the file
+ ///
+ public JObject Data { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/FarmmapsApi/Models/FileResponse.cs b/FarmmapsApi/Models/FileResponse.cs
new file mode 100644
index 0000000..4bd7cc5
--- /dev/null
+++ b/FarmmapsApi/Models/FileResponse.cs
@@ -0,0 +1,17 @@
+namespace FarmmapsApi.Models
+{
+ public class FileResponse : FileRequest
+ {
+ ///
+ /// Code created for the registered file, use this in subsequent calls
+ ///
+ /// a00fbd18320742c787f99f952aef0dbb
+ public string Code { get; set; }
+
+ ///
+ /// If a chunked upload, the number of chunks to upload
+ ///
+ /// 1
+ public long Chunks { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/FarmmapsApi/Services/FarmmapsApiService.cs b/FarmmapsApi/Services/FarmmapsApiService.cs
index b997c26..6d33957 100644
--- a/FarmmapsApi/Services/FarmmapsApiService.cs
+++ b/FarmmapsApi/Services/FarmmapsApiService.cs
@@ -10,30 +10,31 @@ using System.Text;
using System.Threading.Tasks;
using System.Web;
using FarmmapsApi.Models;
+using Google.Apis.Http;
+using Google.Apis.Upload;
using IdentityModel;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using Winista.Mime;
namespace FarmmapsApi.Services
{
public class FarmmapsApiService
{
- private readonly ILogger _logger;
private readonly HttpClient _httpClient;
private readonly OpenIdConnectService _openIdConnectService;
private readonly Configuration _configuration;
-
+
private HubConnection _hubConnection;
public event Action EventCallback;
- public FarmmapsApiService(HttpClient httpClient, ILogger logger,
+ public FarmmapsApiService(HttpClient httpClient,
OpenIdConnectService openIdConnectService, Configuration configuration)
{
- _logger = logger;
_httpClient = httpClient;
_openIdConnectService = openIdConnectService;
_configuration = configuration;
@@ -65,10 +66,9 @@ namespace FarmmapsApi.Services
private async Task StartEventHub(string accessToken)
{
- var uri = new Uri(_configuration.Endpoint);
- var eventEndpoint = $"{uri.Scheme}://{uri.Host}:{uri.Port}/EventHub";
+ var eventEndpoint = $"{_configuration.Endpoint}/EventHub";
_hubConnection = new HubConnectionBuilder()
- .WithUrl(eventEndpoint, HttpTransportType.WebSockets,
+ .WithUrl(eventEndpoint, HttpTransportType.WebSockets,
options => options.SkipNegotiation = true)
.ConfigureLogging(log => log.AddConsole())
.WithAutomaticReconnect()
@@ -76,9 +76,9 @@ namespace FarmmapsApi.Services
await _hubConnection.StartAsync();
await AuthenticateEventHub(accessToken);
-
+
_hubConnection.Reconnected += async s => await AuthenticateEventHub(accessToken);
- _hubConnection.On("event", (Object @event) => _logger.LogInformation(@event.ToString()));
+ _hubConnection.On("event", (EventMessage eventMessage) => EventCallback?.Invoke(eventMessage));
}
private async Task AuthenticateEventHub(string accessToken)
@@ -88,7 +88,8 @@ namespace FarmmapsApi.Services
public async Task GetCurrentUserCodeAsync()
{
- var response = await _httpClient.GetAsync(ResourceEndpoints.CURRENTUSER_RESOURCE);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.CURRENTUSER_RESOURCE}";
+ var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
@@ -100,7 +101,8 @@ namespace FarmmapsApi.Services
public async Task> GetCurrentUserRootsAsync()
{
- var response = await _httpClient.GetAsync(ResourceEndpoints.MYROOTS_RESOURCE);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.MYROOTS_RESOURCE}";
+ var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
@@ -109,7 +111,7 @@ namespace FarmmapsApi.Services
return JsonConvert.DeserializeObject>(jsonString);
}
-
+
public async Task- GetItemAsync(string itemCode, string itemType = null, JObject dataFilter = null)
{
if (dataFilter == null)
@@ -118,28 +120,29 @@ namespace FarmmapsApi.Services
var items = await GetItemsAsync(itemCode, itemType, dataFilter);
return items[0];
}
-
+
public async Task
> GetItemsAsync(string itemCode, string itemType = null, JObject dataFilter = null)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMS_RESOURCE, itemCode);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode);
var queryString = HttpUtility.ParseQueryString(string.Empty);
-
- if(itemType != null)
+
+ if (itemType != null)
queryString["it"] = itemType;
-
- if(dataFilter != null)
- queryString["df"] = dataFilter.ToString(Formatting.None);
+
+ if (dataFilter != null)
+ queryString["df"] = dataFilter.ToString(Formatting.None);
resourceUri = (queryString.Count > 0 ? $"{resourceUri}?" : resourceUri) + queryString;
-
+
var response = await _httpClient.GetAsync(resourceUri);
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
-
+
throw new Exception(response.ReasonPhrase);
}
@@ -149,36 +152,39 @@ namespace FarmmapsApi.Services
private async Task- GetItemSingleAsync(string itemCode, string itemType = null)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMS_RESOURCE, itemCode);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode);
if (!string.IsNullOrEmpty(itemType))
resourceUri = $"{resourceUri}/{itemType}";
-
+
var response = await _httpClient.GetAsync(resourceUri);
-
+
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
-
+
throw new Exception(response.ReasonPhrase);
}
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject
- (jsonString);
}
-
- public async Task
> GetItemChildrenAsync(string itemCode, string itemType = null, JObject dataFilter = null)
+
+ public async Task> GetItemChildrenAsync(string itemCode, string itemType = null,
+ JObject dataFilter = null)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMS_CHILDREN_RESOURCE, itemCode);
-
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_CHILDREN_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode);
+
var queryString = HttpUtility.ParseQueryString(string.Empty);
-
- if(itemType != null)
+
+ if (itemType != null)
queryString["it"] = itemType;
-
- if(dataFilter != null)
- queryString["df"] = dataFilter.ToString(Formatting.None);
+
+ if (dataFilter != null)
+ queryString["df"] = dataFilter.ToString(Formatting.None);
resourceUri = (queryString.Count > 0 ? $"{resourceUri}?" : resourceUri) + queryString;
@@ -195,31 +201,34 @@ namespace FarmmapsApi.Services
{
var jsonString = JsonConvert.SerializeObject(itemRequest);
- var content = new StringContent(jsonString, Encoding.UTF8,MediaTypeNames.Application.Json);
- var response = await _httpClient.PostAsync(ResourceEndpoints.ITEMS_CREATE_RESOURCE,
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_CREATE_RESOURCE}";
+ var content = new StringContent(jsonString, Encoding.UTF8, MediaTypeNames.Application.Json);
+ var response = await _httpClient.PostAsync(url,
content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
-
+
var jsonStringResponse = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject- (jsonStringResponse);
}
public async Task DeleteItemAsync(string itemCode)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMS_RESOURCE, itemCode);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode);
var response = await _httpClient.DeleteAsync(resourceUri);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
}
-
+
public async Task DeleteItemsAsync(IList itemCodes)
{
var jsonString = JsonConvert.SerializeObject(itemCodes);
- var content = new StringContent(jsonString);
- var response = await _httpClient.PostAsync(ResourceEndpoints.ITEMS_DELETE_RESOURCE, content);
+ var content = new StringContent(jsonString);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_DELETE_RESOURCE}";
+ var response = await _httpClient.PostAsync(url, content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
@@ -227,10 +236,11 @@ namespace FarmmapsApi.Services
public async Task DownloadItemAsync(string itemCode, string filePath)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMS_DOWNLOAD_RESOURCE, itemCode);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_DOWNLOAD_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode);
var response = await _httpClient.GetAsync(resourceUri, HttpCompletionOption.ResponseHeadersRead);
-
- if(response.IsSuccessStatusCode)
+
+ if (response.IsSuccessStatusCode)
{
await using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync();
await using Stream streamToWriteTo = File.Open(filePath, FileMode.Create);
@@ -238,30 +248,32 @@ namespace FarmmapsApi.Services
}
else
{
- throw new FileNotFoundException(response.ReasonPhrase);
+ throw new FileNotFoundException(response.ReasonPhrase);
}
}
public async Task QueueTaskAsync(string itemCode, TaskRequest taskRequest)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMTASK_REQUEST_RESOURCE, itemCode);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMTASK_REQUEST_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode);
var jsonString = JsonConvert.SerializeObject(taskRequest);
- var content = new StringContent(jsonString, Encoding.UTF8,MediaTypeNames.Application.Json);
+ var content = new StringContent(jsonString, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await _httpClient.PostAsync(resourceUri, content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
-
+
var jsonStringResponse = await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject(jsonStringResponse);
-
+
return json["code"].Value();
}
public async Task
> GetTasksStatusAsync(string itemCode)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMTASKS_RESOURCE, itemCode);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMTASKS_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode);
var response = await _httpClient.GetAsync(resourceUri);
if (!response.IsSuccessStatusCode)
@@ -273,7 +285,8 @@ namespace FarmmapsApi.Services
public async Task GetTaskStatusAsync(string itemCode, string itemTaskCode)
{
- var resourceUri = string.Format(ResourceEndpoints.ITEMTASK_RESOURCE, itemCode, itemTaskCode);
+ var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMTASK_RESOURCE}";
+ var resourceUri = string.Format(url, itemCode, itemTaskCode);
var response = await _httpClient.GetAsync(resourceUri);
if (!response.IsSuccessStatusCode)
@@ -282,5 +295,85 @@ namespace FarmmapsApi.Services
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject(jsonString);
}
+
+ ///
+ /// Experimental
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task UploadFile(string filePath, string parentItemCode, Action uploadCallback)
+ {
+ if (!File.Exists(filePath))
+ throw new FileNotFoundException($"File not found {filePath}");
+
+ var mimeTypes = new MimeTypes();
+ var mimeType = mimeTypes.GetMimeTypeFromFile(filePath);
+
+ await using var uploadStream = new FileStream(filePath, FileMode.OpenOrCreate);
+
+ var request = new FileRequest()
+ {
+ Name = Path.GetFileName(filePath),
+ ParentCode = parentItemCode,
+ Size = uploadStream.Length
+ };
+
+ Uri uploadIdentifierUri = null;
+ using var httpClient = CreateConfigurableHttpClient(_httpClient);
+ var farmmapsUploader = new FarmmapsUploader(httpClient, uploadStream, request, mimeType.ToString());
+
+ farmmapsUploader.ProgressChanged += uploadCallback;
+ farmmapsUploader.UploadSessionData += data => uploadIdentifierUri = data.UploadUri;
+
+ await farmmapsUploader.UploadAsync();
+
+ return uploadIdentifierUri;
+ }
+
+ ///
+ /// Experimental
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task ResumeUploadFile(string filePath, Uri location, Action uploadCallback)
+ {
+ if (!File.Exists(filePath))
+ throw new FileNotFoundException($"File not found {filePath}");
+
+ await using var uploadStream = new FileStream(filePath, FileMode.OpenOrCreate);
+
+ var request = new FileRequest()
+ {
+ Name = Path.GetFileName(filePath),
+ ParentCode = string.Empty,
+ Size = uploadStream.Length
+ };
+
+ using var httpClient = CreateConfigurableHttpClient(_httpClient);
+ var farmmapsUploader = new FarmmapsUploader(httpClient, uploadStream, request, string.Empty);
+ farmmapsUploader.ProgressChanged += uploadCallback;
+
+ await farmmapsUploader.ResumeAsync(location);
+
+ return location;
+ }
+
+ private ConfigurableHttpClient CreateConfigurableHttpClient(HttpClient parent)
+ {
+ var googleHttpClient = new HttpClientFactory().CreateHttpClient(new CreateHttpClientArgs {GZipEnabled = true, ApplicationName = "FarmMaps"});
+ googleHttpClient.BaseAddress = parent.BaseAddress;
+ googleHttpClient.DefaultRequestHeaders.Add("Accept", MediaTypeNames.Application.Json);
+
+ var authHeader = parent.DefaultRequestHeaders.Authorization;
+ googleHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authHeader.Scheme, authHeader.Parameter);
+
+ return googleHttpClient;
+ }
}
}
\ No newline at end of file
diff --git a/FarmmapsApi/Services/FarmmapsUploader.cs b/FarmmapsApi/Services/FarmmapsUploader.cs
new file mode 100644
index 0000000..0bd2945
--- /dev/null
+++ b/FarmmapsApi/Services/FarmmapsUploader.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Net.Http;
+using System.Net.Mime;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using FarmmapsApi.Models;
+using Google.Apis.Http;
+using Google.Apis.Json;
+using Google.Apis.Requests;
+using Google.Apis.Upload;
+using Google.Apis.Util;
+using Newtonsoft.Json;
+
+namespace FarmmapsApi.Services
+{
+ public class FarmmapsUploader : ResumableUpload
+ {
+ /// Payload description headers, describing the content itself.
+ private const string PayloadContentTypeHeader = "X-Upload-Content-Type";
+
+ /// Payload description headers, describing the content itself.
+ private const string PayloadContentLengthHeader = "X-Upload-Content-Length";
+
+ private const int UnknownSize = -1;
+
+ /// Gets or sets the service.
+ private HttpClient HttpClient { get; }
+
+ ///
+ /// Gets or sets the path of the method (combined with
+ /// ) to produce
+ /// absolute Uri.
+ ///
+ private string Path { get; }
+
+ /// Gets or sets the HTTP method of this upload (used to initialize the upload).
+ private string HttpMethod { get; }
+
+ /// Gets or sets the stream's Content-Type.
+ private string ContentType { get; }
+
+ /// Gets or sets the body of this request.
+ private FileRequest Body { get; }
+
+ private long _streamLength;
+
+ ///
+ /// Create a resumable upload instance with the required parameters.
+ ///
+ /// The stream containing the content to upload.
+ ///
+ /// Caller is responsible for maintaining the open until the upload is
+ /// completed.
+ /// Caller is responsible for closing the .
+ ///
+ public FarmmapsUploader(ConfigurableHttpClient httpClient, Stream contentStream, FileRequest body, string contentType)
+ : base(contentStream,
+ new ResumableUploadOptions
+ {
+ HttpClient = httpClient,
+ Serializer = new NewtonsoftJsonSerializer(),
+ ServiceName = "FarmMaps"
+ })
+ {
+ httpClient.ThrowIfNull(nameof(httpClient));
+ contentStream.ThrowIfNull(nameof(contentStream));
+
+ _streamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize;
+ Body = body;
+ Path = "api/v1/file";
+ HttpClient = httpClient;
+ HttpMethod = HttpConsts.Post;
+ ContentType = contentType;
+ }
+
+ ///
+ public override async Task InitiateSessionAsync(CancellationToken cancellationToken = default)
+ {
+ HttpRequestMessage request = CreateInitializeRequest();
+ Options?.ModifySessionInitiationRequest?.Invoke(request);
+ var response = await HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw await ExceptionForResponseAsync(response).ConfigureAwait(false);
+ }
+ return response.Headers.Location;
+ }
+
+ /// Creates a request to initialize a request.
+ private HttpRequestMessage CreateInitializeRequest()
+ {
+ var baseAddres = HttpClient.BaseAddress;
+ var uri = new Uri($"{baseAddres.Scheme}://{baseAddres.Host}:{baseAddres.Port}");
+ var builder = new RequestBuilder()
+ {
+ BaseUri = uri,
+ Path = Path,
+ Method = HttpMethod,
+ };
+
+ SetAllPropertyValues(builder);
+
+ HttpRequestMessage request = builder.CreateRequest();
+ if (ContentType != null)
+ {
+ request.Headers.Add(PayloadContentTypeHeader, ContentType);
+ }
+
+ // if the length is unknown at the time of this request, omit "X-Upload-Content-Length" header
+ if (ContentStream.CanSeek)
+ {
+ request.Headers.Add(PayloadContentLengthHeader, _streamLength.ToString());
+ }
+
+ var jsonText = JsonConvert.SerializeObject(Body);
+ request.Content = new StringContent(jsonText, Encoding.UTF8, MediaTypeNames.Application.Json);
+
+ return request;
+ }
+
+ ///
+ /// Reflectively enumerate the properties of this object looking for all properties containing the
+ /// RequestParameterAttribute and copy their values into the request builder.
+ ///
+ private void SetAllPropertyValues(RequestBuilder requestBuilder)
+ {
+ Type myType = this.GetType();
+ var properties = myType.GetProperties();
+
+ foreach (var property in properties)
+ {
+ var attribute = property.GetCustomAttribute();
+ if (attribute != null)
+ {
+ string name = attribute.Name ?? property.Name.ToLower();
+ object value = property.GetValue(this, null);
+ if (value != null)
+ {
+ if (!(value is string) && value is IEnumerable valueAsEnumerable)
+ {
+ foreach (var elem in valueAsEnumerable)
+ {
+ requestBuilder.AddParameter(attribute.Type, name, Utilities.ConvertToString(elem));
+ }
+ }
+ else
+ {
+ // Otherwise just convert it to a string.
+ requestBuilder.AddParameter(attribute.Type, name, Utilities.ConvertToString(value));
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FarmmapsApiSamples/Data/Belgie.zip b/FarmmapsApiSamples/Data/Belgie.zip
new file mode 100644
index 0000000..864aa27
Binary files /dev/null and b/FarmmapsApiSamples/Data/Belgie.zip differ
diff --git a/FarmmapsApiSamples/Data/Scan_1_20190605.zip b/FarmmapsApiSamples/Data/Scan_1_20190605.zip
new file mode 100644
index 0000000..ab00afd
Binary files /dev/null and b/FarmmapsApiSamples/Data/Scan_1_20190605.zip differ
diff --git a/FarmmapsApiSamples/FarmmapsApiSamples.csproj b/FarmmapsApiSamples/FarmmapsApiSamples.csproj
index 4f86bf8..630bed0 100644
--- a/FarmmapsApiSamples/FarmmapsApiSamples.csproj
+++ b/FarmmapsApiSamples/FarmmapsApiSamples.csproj
@@ -9,6 +9,9 @@
Always
+
+ Always
+
diff --git a/FarmmapsApiSamples/NbsApp.cs b/FarmmapsApiSamples/NbsApp.cs
index b344698..499e24b 100644
--- a/FarmmapsApiSamples/NbsApp.cs
+++ b/FarmmapsApiSamples/NbsApp.cs
@@ -1,8 +1,11 @@
using System;
+using System.Globalization;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FarmmapsApi.Models;
using FarmmapsApi.Services;
+using Google.Apis.Upload;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using static FarmmapsApi.Extensions;
@@ -12,6 +15,8 @@ namespace FarmmapsApiSamples
public class NbsApp : IApp
{
private const string USERINPUT_ITEMTYPE = "vnd.farmmaps.itemtype.user.input";
+ private const string GEOTIFF_ITEMTYPE = "vnd.farmmaps.itemtype.geotiff";
+ private const string CROPFIELD_ITEMTYPE = "vnd.farmmaps.itemtype.cropfield";
private const string VRANBS_TASK = "vnd.farmmaps.task.vranbs";
private readonly ILogger _logger;
@@ -41,14 +46,33 @@ namespace FarmmapsApiSamples
_logger.LogInformation("Authenticated client credentials");
var roots = await _farmmapsApiService.GetCurrentUserRootsAsync();
- var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive");
-
- if (myDriveRoot != null)
+
+ // upload data to Uploaded
+ var uploadedRoot = roots.SingleOrDefault(r => r.Name == "Uploaded");
+ if (uploadedRoot != null)
{
- var cropfieldItem = await GetOrCreateCropfieldItem(myDriveRoot.Code);
- var targetN = await CalculateTargetN(cropfieldItem, 60);
+ await _farmmapsApiService.UploadFile(Path.Combine("Data", "Scan_1_20190605.zip"), uploadedRoot.Code,
+ progress =>
+ {
+ _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}");
+ if(progress.Status == UploadStatus.Failed)
+ _logger.LogError($"Uploading failed {progress.Exception.Message}");
+ });
- _logger.LogInformation($"TargetN: {targetN}");
+ // need to transform shape data to geotiff
+
+ var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive");
+ if (myDriveRoot != null)
+ {
+ var cropfieldItem = await GetOrCreateCropfieldItem(myDriveRoot.Code);
+
+ _logger.LogInformation($"Calculating targetN with targetYield: {60}");
+ var targetN = await CalculateTargetN(cropfieldItem, 60);
+ _logger.LogInformation($"TargetN: {targetN}");
+
+ _logger.LogInformation("Calculating nitrogen map");
+// var nitrogenMapItem = CalculateNitrogenMap(cropfieldItem,, targetN);
+ }
}
}
catch (Exception ex)
@@ -60,7 +84,7 @@ namespace FarmmapsApiSamples
private async Task- GetOrCreateCropfieldItem(string parentItemCode)
{
var cropfieldItems = await
- _farmmapsApiService.GetItemChildrenAsync(parentItemCode, "vnd.farmmaps.itemtype.cropfield");
+ _farmmapsApiService.GetItemChildrenAsync(parentItemCode,CROPFIELD_ITEMTYPE);
if (cropfieldItems.Count > 0)
return cropfieldItems[0];
@@ -70,18 +94,24 @@ namespace FarmmapsApiSamples
{
ParentCode = parentItemCode,
ItemType = "vnd.farmmaps.itemtype.cropfield",
- Name = "cropfield for VRA",
+ Name = "Cropfield for VRA",
DataDate = currentYear,
DataEndDate = currentYear.AddYears(1),
Data = JObject.FromObject(new
- {startDate = currentYear, endDate = currentYear.AddYears(1)}),
+ {startDate = currentYear, endDate = currentYear.AddMonths(3)}),
Geometry = JObject.Parse(
- @"{""type"":""Polygon"",""coordinates"":[[[6.09942873984307,53.070025028087],[6.09992507404607,53.0705617890585],[6.10036959220086,53.0710679529031],[6.10065149010421,53.0714062774307],[6.10087493644271,53.0716712354474],[6.10091082982487,53.0716936039203],[6.10165087441291,53.0712041549161],[6.10204994718318,53.0709349338005],[6.10263143118855,53.0705789370018],[6.10311578125011,53.0702657538294],[6.10331686552072,53.0701314102389],[6.103326530575,53.070119463569],[6.10309137950343,53.0699829669055],[6.10184241586523,53.0692902201371],[6.10168497998891,53.0691984306747],[6.10092987659869,53.0694894453514],[6.09942873984307,53.070025028087]]]}")
+ @"{ ""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 ] ] ] }")
};
return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest);
}
+ ///
+ /// Calculates TargetN, makes the assumption the cropfield and user.input(targetn) item have the same parent
+ ///
+ /// The cropfield to base the calculations on
+ /// The target yield input for the TargetN calculation
+ /// The TargetN
private async Task CalculateTargetN(Item cropfieldItem, int targetYield)
{
var targetNItems = await
@@ -99,8 +129,6 @@ namespace FarmmapsApiSamples
targetNItem = targetNItems[0];
}
- _logger.LogInformation($"Calculating targetN with targetYield: {targetYield}");
-
var nbsTargetNRequest = new TaskRequest {TaskType = VRANBS_TASK};
nbsTargetNRequest.attributes["operation"] = "targetn";
nbsTargetNRequest.attributes["inputCode"] = targetNItem.Code;
@@ -111,19 +139,63 @@ namespace FarmmapsApiSamples
await PollTask(TimeSpan.FromSeconds(3), async (tokenSource) =>
{
- var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode);
-
- if (itemTask.State != ItemTaskState.Processing && itemTask.State != ItemTaskState.Scheduled)
+ var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode);
+ if (itemTaskStatus.State != ItemTaskState.Processing && itemTaskStatus.State != ItemTaskState.Scheduled)
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 0;
+ }
+
var item = await _farmmapsApiService.GetItemAsync(targetNItem.Code);
if (item.Data.ContainsKey("TargetN"))
return item.Data["TargetN"].Value();
-
+
return 0;
}
+ private async Task
- CalculateNitrogenMap(Item cropfieldItem, Item inputItem, double targetN)
+ {
+ var nbsNitrogenRequest = new TaskRequest {TaskType = VRANBS_TASK};
+ nbsNitrogenRequest.attributes["operation"] = "nitrogen";
+ nbsNitrogenRequest.attributes["inputCode"] = inputItem.Code;
+ nbsNitrogenRequest.attributes["inputType"] = "irmi";
+ nbsNitrogenRequest.attributes["targetN"] = targetN.ToString(CultureInfo.InvariantCulture);
+
+ string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsNitrogenRequest);
+
+ await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) =>
+ {
+ var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode);
+ if (itemTaskStatus.State != ItemTaskState.Processing && itemTaskStatus.State != ItemTaskState.Scheduled)
+ 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 geotiffItems = await
+ _farmmapsApiService.GetItemChildrenAsync(cropfieldItem.Code, GEOTIFF_ITEMTYPE);
+
+ // how to uniquely know which item is really created!?
+ var nitrogenItem = geotiffItems.SingleOrDefault(i => i.Name.Contains("nitrogen"));
+ if (nitrogenItem == null)
+ {
+ _logger.LogError("Could not find the nitrogen geotiff child item under cropfield");
+ return null;
+ }
+
+ return nitrogenItem;
+ }
+
private ItemRequest CreateTargetNItemRequest(string parentItemCode)
{
return new ItemRequest()
diff --git a/FarmmapsApiSamples/appsettings.json b/FarmmapsApiSamples/appsettings.json
index 337d0e9..c795f28 100644
--- a/FarmmapsApiSamples/appsettings.json
+++ b/FarmmapsApiSamples/appsettings.json
@@ -1,6 +1,7 @@
{
"Authority": "https://accounts.farmmaps.awtest.nl/",
- "Endpoint": "https://farmmaps.awtest.nl/api/v1/",
+ "Endpoint": "http://localhost:8083",
+ "BasePath": "api/v1",
"DiscoveryEndpointUrl": "https://accounts.farmmaps.awtest.nl/.well-known/openid-configuration",
"RedirectUri": "http://example.nl/api",
"ClientId": "",