diff --git a/src/Stars.Console/Terradue.Stars.Console.csproj b/src/Stars.Console/Terradue.Stars.Console.csproj index bcd44839..2de6dd4a 100644 --- a/src/Stars.Console/Terradue.Stars.Console.csproj +++ b/src/Stars.Console/Terradue.Stars.Console.csproj @@ -2,7 +2,7 @@ Exe net6.0 - 2.18.1 + 2.19.0 Stars is a CLI for working with Spatio Temporal Catalog such as STAC but not only $(Version)-$(VersionSuffix) diff --git a/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-MUL-B-1-1-1100761-150924T044238-ID.json b/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-MUL-B-1-1-1100761-150924T044238-ID.json index 95ea71b4..85369093 100644 --- a/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-MUL-B-1-1-1100761-150924T044238-ID.json +++ b/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-MUL-B-1-1-1100761-150924T044238-ID.json @@ -54,7 +54,9 @@ 6792, 3057 ], - "view:azimuth": 84.08, + "view:incidence_angle": 2.31, + "view:sun_azimuth": 6.84, + "view:sun_elevation": 84.08, "processing:level": "L1B", "title": "BKA MSS L1B 2015-09-24 04:43:01", "providers": [ diff --git a/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-PAN-B-1-1-1100761-150924T044238-ID.json b/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-PAN-B-1-1-1100761-150924T044238-ID.json index 3cd71c32..47e1a134 100644 --- a/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-PAN-B-1-1-1100761-150924T044238-ID.json +++ b/src/Stars.Data.Tests/Resources/BKA/L1B/MetadataExtractorsTests_4077631-0220-PAN-B-1-1-1100761-150924T044238-ID.json @@ -1,8 +1,10 @@ { "stac_version": "1.0.0", "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", "https://stac-extensions.github.io/processing/v1.0.0/schema.json", "https://stac-extensions.github.io/projection/v1.0.0/schema.json", + "https://stac-extensions.github.io/raster/v1.0.0/schema.json", "https://stac-extensions.github.io/sat/v1.0.0/schema.json", "https://stac-extensions.github.io/view/v1.0.0/schema.json" ], @@ -52,7 +54,9 @@ 34087, 17123 ], - "view:azimuth": 84.08, + "view:incidence_angle": 2.31, + "view:sun_azimuth": 6.8, + "view:sun_elevation": 84.08, "processing:level": "L1B", "title": "BKA MSS L1B 2015-09-24 04:43:02", "providers": [ @@ -86,7 +90,21 @@ "href": "data/4077631-0220-PAN-B-1-1-1100761-150924T044238-ID/4077631-0220-PAN-B-1-1-1100761-150924T044238-ID.tif", "title": "L1B PAN", "filename": "4077631-0220-PAN-B-1-1-1100761-150924T044238-ID.tif", - "file:size": 0 + "file:size": 0, + "eo:bands": [ + { + "name": "PAN", + "common_name": "pan", + "center_wavelength": 0.66, + "full_width_half_max": 0.208 + } + ], + "raster:bands": [ + { + "data_type": "int8", + "offset": 0.0 + } + ] }, "metadata": { "type": "application/xml", diff --git a/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-MUL-C-1-1-1100761-150924T044238-ID.json b/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-MUL-C-1-1-1100761-150924T044238-ID.json index df12b007..f3fc544b 100644 --- a/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-MUL-C-1-1-1100761-150924T044238-ID.json +++ b/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-MUL-C-1-1-1100761-150924T044238-ID.json @@ -54,7 +54,9 @@ 6791, 3052 ], - "view:azimuth": 84.08, + "view:incidence_angle": 2.31, + "view:sun_azimuth": 6.84, + "view:sun_elevation": 84.08, "processing:level": "L1C", "title": "BKA MSS L1C 2015-09-24 04:43:01", "providers": [ diff --git a/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-PAN-C-1-1-1100761-150924T044238-ID.json b/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-PAN-C-1-1-1100761-150924T044238-ID.json index 222e68b5..f7958deb 100644 --- a/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-PAN-C-1-1-1100761-150924T044238-ID.json +++ b/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4077631-0220-PAN-C-1-1-1100761-150924T044238-ID.json @@ -1,8 +1,10 @@ { "stac_version": "1.0.0", "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", "https://stac-extensions.github.io/processing/v1.0.0/schema.json", "https://stac-extensions.github.io/projection/v1.0.0/schema.json", + "https://stac-extensions.github.io/raster/v1.0.0/schema.json", "https://stac-extensions.github.io/sat/v1.0.0/schema.json", "https://stac-extensions.github.io/view/v1.0.0/schema.json" ], @@ -52,7 +54,9 @@ 34082, 17110 ], - "view:azimuth": 84.08, + "view:incidence_angle": 2.31, + "view:sun_azimuth": 6.8, + "view:sun_elevation": 84.08, "processing:level": "L1C", "title": "BKA MSS L1C 2015-09-24 04:43:02", "providers": [ @@ -86,7 +90,21 @@ "href": "data/4077631-0220-PAN-C-1-1-1100761-150924T044238-ID/4077631-0220-PAN-C-1-1-1100761-150924T044238-ID.tif", "title": "L1C PAN", "filename": "4077631-0220-PAN-C-1-1-1100761-150924T044238-ID.tif", - "file:size": 0 + "file:size": 0, + "eo:bands": [ + { + "name": "PAN", + "common_name": "pan", + "center_wavelength": 0.66, + "full_width_half_max": 0.208 + } + ], + "raster:bands": [ + { + "data_type": "int8", + "offset": 0.0 + } + ] }, "metadata": { "type": "application/xml", diff --git a/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4089411-0220-C-1-1-1139831-160712T150120-BR.json b/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4089411-0220-C-1-1-1139831-160712T150120-BR.json new file mode 100644 index 00000000..656e7011 --- /dev/null +++ b/src/Stars.Data.Tests/Resources/BKA/L1C/MetadataExtractorsTests_4089411-0220-C-1-1-1139831-160712T150120-BR.json @@ -0,0 +1,196 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/eo/v1.0.0/schema.json", + "https://stac-extensions.github.io/processing/v1.0.0/schema.json", + "https://stac-extensions.github.io/projection/v1.0.0/schema.json", + "https://stac-extensions.github.io/raster/v1.0.0/schema.json", + "https://stac-extensions.github.io/sat/v1.0.0/schema.json", + "https://stac-extensions.github.io/view/v1.0.0/schema.json" + ], + "type": "Feature", + "id": "4089411-0220-C-1-1-1139831-160712T150120-BR", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -45.95907552, + -23.95074002 + ], + [ + -45.72936198, + -23.90813368 + ], + [ + -45.81505208, + -23.51828993 + ], + [ + -46.04410156, + -23.5607921 + ], + [ + -45.95907552, + -23.95074002 + ] + ] + ] + }, + "properties": { + "datetime": "2016-07-12T15:01:20.0000000Z", + "created": "2023-02-21T13:53:27.0000000Z", + "platform": "bka", + "constellation": "bka", + "mission": "bka/kanopus-v", + "instruments": [ + "mss" + ], + "sensor_type": "optical", + "spectral_mode": "MUL/PAN", + "sat:platform_international_designator": "2012-039B", + "proj:epsg": 32723, + "proj:shape": [ + 4441, + 2633 + ], + "view:incidence_angle": 1.65, + "view:sun_azimuth": 2.58, + "view:sun_elevation": 44.38, + "processing:level": "L1C", + "title": "BKA MSS L1C 2016-07-12 15:01:20", + "providers": [ + { + "name": "NAS", + "description": "BKA (formerly known as BelKa 2) is a Belarusian remote sensing satellite developed under an agreement between the National Academy of Sciences of Belarus (NAS) and the Federal Space Agency of the Russian Federation. The BKA satellite is almost an exact copy of the Russian Kanopus-Vulkan N1 Environmental Satellite (Kanopus-V 1).", + "roles": [ + "producer", + "processor", + "licensor" + ], + "url": "https://gis.by/en/tech/bka/" + } + ], + "licence": "proprietary", + "gsd": 10.59 + }, + "bbox": [ + -46.04410156, + -23.95074002, + -45.72936198, + -23.51828993 + ], + "assets": { + "L1C_MUL": { + "type": "image/tiff; application=geotiff", + "roles": [ + "data", + "dn" + ], + "href": "data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560/PRODUCT/4089411-0220-MUL-C-1-1-1139831-160712T150120-BR/4089411-0220-MUL-C-1-1-1139831-160712T150120-BR.tif", + "title": "L1C MUL", + "filename": "4089411-0220-MUL-C-1-1-1139831-160712T150120-BR.tif", + "eo:bands": [ + { + "name": "MS1", + "common_name": "blue", + "center_wavelength": 0.492, + "full_width_half_max": 0.04 + }, + { + "name": "MS2", + "common_name": "green", + "center_wavelength": 0.558, + "full_width_half_max": 0.07 + }, + { + "name": "MS3", + "common_name": "red", + "center_wavelength": 0.675, + "full_width_half_max": 0.09 + }, + { + "name": "MS4", + "common_name": "nir", + "center_wavelength": 0.782, + "full_width_half_max": 0.1 + } + ], + "raster:bands": [ + { + "data_type": "int8", + "offset": 0.0 + }, + { + "data_type": "int8", + "offset": 0.0 + }, + { + "data_type": "int8", + "offset": 0.0 + }, + { + "data_type": "int8", + "offset": 0.0 + } + ] + }, + "metadata_MUL": { + "type": "application/xml", + "roles": [ + "metadata" + ], + "href": "data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560/PRODUCT/4089411-0220-MUL-C-1-1-1139831-160712T150120-BR/4089411-0220-MUL-C-1-1-1139831-160712T150120-BR_pasp-en.xml", + "filename": "4089411-0220-MUL-C-1-1-1139831-160712T150120-BR_pasp-en.xml" + }, + "overview_MUL": { + "type": "image/jpeg", + "roles": [ + "overview" + ], + "href": "data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560/PRODUCT/4089411-0220-MUL-C-1-1-1139831-160712T150120-BR/4089411-0220-MUL-C-1-1-1139831-160712T150120-BR_preview.jpg", + "filename": "4089411-0220-MUL-C-1-1-1139831-160712T150120-BR_preview.jpg" + }, + "L1C_PAN": { + "type": "image/tiff; application=geotiff", + "roles": [ + "data", + "dn" + ], + "href": "data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560/PRODUCT/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR.tif", + "title": "L1C PAN", + "filename": "4089411-0220-PAN-C-1-1-1139831-160712T150120-BR.tif", + "eo:bands": [ + { + "name": "PAN", + "common_name": "pan", + "center_wavelength": 0.66, + "full_width_half_max": 0.208 + } + ], + "raster:bands": [ + { + "data_type": "int8", + "offset": 0.0 + } + ] + }, + "metadata_PAN": { + "type": "application/xml", + "roles": [ + "metadata" + ], + "href": "data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560/PRODUCT/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR_pasp-en.xml", + "filename": "4089411-0220-PAN-C-1-1-1139831-160712T150120-BR_pasp-en.xml" + }, + "overview_PAN": { + "type": "image/jpeg", + "roles": [ + "overview" + ], + "href": "data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560/PRODUCT/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR_preview.jpg", + "filename": "4089411-0220-PAN-C-1-1-1139831-160712T150120-BR_preview.jpg" + } + }, + "links": [] +} \ No newline at end of file diff --git a/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560.zip b/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560.zip new file mode 100644 index 00000000..6988b0bd Binary files /dev/null and b/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/925_bka_1028560.zip differ diff --git a/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/urn_ogc_def_EOP_NAS_NAS_1028560-icon.jpg b/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/urn_ogc_def_EOP_NAS_NAS_1028560-icon.jpg new file mode 100644 index 00000000..1592e73d Binary files /dev/null and b/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/urn_ogc_def_EOP_NAS_NAS_1028560-icon.jpg differ diff --git a/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/urn_ogc_def_EOP_NAS_NAS_1028560-preview.jpg b/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/urn_ogc_def_EOP_NAS_NAS_1028560-preview.jpg new file mode 100644 index 00000000..98986fed Binary files /dev/null and b/src/Stars.Data.Tests/Resources/BKA/L1C/data/4089411-0220-PAN-C-1-1-1139831-160712T150120-BR/urn_ogc_def_EOP_NAS_NAS_1028560-preview.jpg differ diff --git a/src/Stars.Data/Model/Metadata/Bka/BkaMetadataExtractor.cs b/src/Stars.Data/Model/Metadata/Bka/BkaMetadataExtractor.cs index cd3dcffe..e779c142 100644 --- a/src/Stars.Data/Model/Metadata/Bka/BkaMetadataExtractor.cs +++ b/src/Stars.Data/Model/Metadata/Bka/BkaMetadataExtractor.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.IO.Abstractions; using System.Linq; using System.Net.Mime; using System.Threading.Tasks; @@ -15,6 +16,9 @@ using Terradue.Stars.Interface.Supplier.Destination; using Terradue.Stars.Services.Model.Stac; using Terradue.Stars.Geometry.GeoJson; +using Terradue.Stars.Services.Processing; +using Terradue.Stars.Services.Supplier.Carrier; +using Terradue.Stars.Services.Supplier.Destination; using System.Xml; using System.Xml.Serialization; using Stac.Extensions.Sat; @@ -31,9 +35,14 @@ public class BkaMetadataExtractor : MetadataExtraction public override string Label => "BKA (Belarus) mission product metadata extractor"; public static XmlSerializer metadataSerializer = new XmlSerializer(typeof(BkaMetadata)); - - public BkaMetadataExtractor(ILogger logger, IResourceServiceProvider resourceServiceProvider) : base(logger, resourceServiceProvider) + + private readonly IFileSystem _fileSystem; + private readonly CarrierManager _carrierManager; + + public BkaMetadataExtractor(ILogger logger, IResourceServiceProvider resourceServiceProvider, IFileSystem fileSystem, CarrierManager carrierManager) : base(logger, resourceServiceProvider) { + _fileSystem = fileSystem; + _carrierManager = carrierManager; } public override bool CanProcess(IResource route, IDestination destination) @@ -42,9 +51,17 @@ public override bool CanProcess(IResource route, IDestination destination) if (item == null) return false; try { - IAsset metadataAsset = GetMetadataAsset(item); - BkaMetadata metadata = ReadMetadata(metadataAsset).GetAwaiter().GetResult(); - return metadata != null; + IAsset[] metadataAssets = GetMetadataAssets(item); + if (metadataAssets == null || metadataAssets.Length == 0) + { + IAsset topZipAsset = GetTopZipAsset(item); + return (topZipAsset != null); + } + else + { + BkaMetadata[] metadata = ReadMetadata(metadataAssets).GetAwaiter().GetResult(); + return metadata != null; + } } catch (Exception) { @@ -54,20 +71,60 @@ public override bool CanProcess(IResource route, IDestination destination) protected override async Task ExtractMetadata(IItem item, string suffix) { - IAsset metadataAsset = GetMetadataAsset(item); - BkaMetadata metadata = await ReadMetadata(metadataAsset); + IAsset topZipAsset = GetTopZipAsset(item); + List innerZipAssetContainers = null; + + if (topZipAsset != null) + { + ZipArchiveAsset topZipArchiveAsset = new ZipArchiveAsset(topZipAsset, logger, resourceServiceProvider, _fileSystem); + var tmpDestination = LocalFileDestination.Create(_fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(topZipArchiveAsset.Uri.AbsolutePath)), item, true); + IAssetsContainer topZipAssets = await topZipArchiveAsset.ExtractToDestinationAsync(tmpDestination, _carrierManager, System.Threading.CancellationToken.None); + + IAsset productZipAsset = GetProductZipAsset(topZipAssets); + if (productZipAsset != null) + { + ZipArchiveAsset productZipArchiveAsset = new ZipArchiveAsset(productZipAsset, logger, resourceServiceProvider, _fileSystem); + var tmpDestination2 = LocalFileDestination.Create(_fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(productZipArchiveAsset.Uri.AbsolutePath)), item, true); + IAssetsContainer productZipAssets = await productZipArchiveAsset.ExtractToDestinationAsync(tmpDestination2, _carrierManager, System.Threading.CancellationToken.None); + + IEnumerable innerZipAssets = GetInnerZipAssets(productZipAssets); + if (innerZipAssets != null) + { + foreach (IAsset innerZipAsset in innerZipAssets) + { + ZipArchiveAsset innerZipArchiveAsset = new ZipArchiveAsset(innerZipAsset, logger, resourceServiceProvider, _fileSystem); + var tmpDestination3 = LocalFileDestination.Create(_fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(innerZipArchiveAsset.Uri.AbsolutePath)), item, true); + IAssetsContainer innerZipAssetContainer = await innerZipArchiveAsset.ExtractToDestinationAsync(tmpDestination3, _carrierManager, System.Threading.CancellationToken.None); + if (innerZipAssetContainers == null) innerZipAssetContainers = new List(); + innerZipAssetContainers.Add(innerZipAssetContainer); + } + } + + } + + } + + IAsset[] metadataAssets = GetMetadataAssets(item, innerZipAssetContainers); + + BkaMetadata[] metadata = await ReadMetadata(metadataAssets); StacItem stacItem = CreateStacItem(metadata); - AddAssets(stacItem, item, metadata); + AddAssets(stacItem, item, innerZipAssetContainers?.ToArray()); return StacItemNode.Create(stacItem, item.Uri); ; } - internal virtual StacItem CreateStacItem(BkaMetadata metadata) + internal virtual StacItem CreateStacItem(BkaMetadata[] metadata) { - string identifier = metadata.Processing.Scene01FileName.Replace(".tif", String.Empty); + bool multipleProducts = metadata.Length > 1; + string identifier = metadata[0].Processing.Scene01FileName.Replace(".tif", String.Empty); + if (multipleProducts) + { + identifier = identifier.Replace("-MUL-", "-").Replace("-PAN-", "-"); + } + StacItem stacItem = new StacItem(identifier, GetGeometry(metadata), GetCommonMetadata(metadata)); AddSatStacExtension(metadata, stacItem); AddProjStacExtension(metadata, stacItem); @@ -80,60 +137,110 @@ internal virtual StacItem CreateStacItem(BkaMetadata metadata) return stacItem; } - private void AddEoStacExtension(BkaMetadata metadata, StacItem stacItem) + private void AddEoStacExtension(BkaMetadata[] metadata, StacItem stacItem) { EoStacExtension eo = stacItem.EoExtension(); } - private void AddSatStacExtension(BkaMetadata metadata, StacItem stacItem) + private void AddSatStacExtension(BkaMetadata[] metadata, StacItem stacItem) { SatStacExtension sat = stacItem.SatExtension(); sat.PlatformInternationalDesignator = "2012-039B"; } - private void AddProjStacExtension(BkaMetadata metadata, StacItem stacItem) + private void AddProjStacExtension(BkaMetadata[] metadata, StacItem stacItem) { ProjectionStacExtension proj = stacItem.ProjectionExtension(); - proj.Epsg = metadata.GeoReference?.EpsgCode; - if (metadata.ImageInfo != null && metadata.ImageInfo.Height != null && metadata.ImageInfo.Width != null) + proj.Epsg = metadata[0].GeoReference?.EpsgCode; + int height = 0, width = 0; + foreach (BkaMetadata m in metadata) + { + if (m.ImageInfo != null && m.ImageInfo.Height != null && m.ImageInfo.Width != null) + { + if (height == 0 || m.ImageInfo.Height.Value < height) height = m.ImageInfo.Height.Value; + if (width == 0 || m.ImageInfo.Width.Value < width) width = m.ImageInfo.Width.Value; + } + } + if (height != 0 && width != 0) { - proj.Shape = new int[2] { metadata.ImageInfo.Height.Value, metadata.ImageInfo.Width.Value }; + proj.Shape = new int[2] { height, width }; } } - private void AddProjViewExtension(BkaMetadata metadata, StacItem stacItem) + private void AddProjViewExtension(BkaMetadata[] metadata, StacItem stacItem) { ViewStacExtension view = stacItem.ViewExtension(); - if (metadata.SatelliteData != null && metadata.SatelliteData.SceneAcquisition != null) + int viewingAngleCount = 0, sunAzimuthCount = 0, sunElevationCount = 0; + double viewingAngleSum = 0, sunAzimuthSum = 0, sunElevationSum = 0; + foreach (BkaMetadata m in metadata) { - BkaSceneAcquisition sceneAcquisition = metadata.SatelliteData.SceneAcquisition; - if (sceneAcquisition.ViewingAngle != null) view.Azimuth = sceneAcquisition.ViewingAngle.Value; - if (sceneAcquisition.SunAzimuth != null) view.Azimuth = sceneAcquisition.SunAzimuth.Value; - if (sceneAcquisition.SunElevation != null) view.Azimuth = sceneAcquisition.SunElevation.Value; + if (m.SatelliteData != null && m.SatelliteData.SceneAcquisition != null) + { + BkaSceneAcquisition sceneAcquisition = m.SatelliteData.SceneAcquisition; + if (sceneAcquisition.ViewingAngle != null) + { + viewingAngleCount++; + viewingAngleSum += sceneAcquisition.ViewingAngle.Value; + } + if (sceneAcquisition.SunAzimuth != null) + { + sunAzimuthCount++; + sunAzimuthSum += sceneAcquisition.SunAzimuth.Value; + } + if (sceneAcquisition.SunElevation != null) + { + sunElevationCount++; + sunElevationSum += sceneAcquisition.SunElevation.Value; + } + } } + if (viewingAngleCount != 0) view.IncidenceAngle = viewingAngleSum / viewingAngleCount; + if (sunAzimuthCount != 0) view.SunAzimuth = sunAzimuthSum / sunAzimuthCount; + if (sunElevationCount != 0) view.SunElevation = sunElevationSum / sunElevationCount; } - private void AddProcessingStacExtension(BkaMetadata metadata, StacItem stacItem) + private void AddProcessingStacExtension(BkaMetadata[] metadata, StacItem stacItem) { var proc = stacItem.ProcessingExtension(); - if (metadata.Processing != null && metadata.Processing.Level != null) + string processingLevel = GetProcessingLevel(metadata); + if (processingLevel != null) { - proc.Level = GetProcessingLevel(metadata); + proc.Level = processingLevel; } } - private string GetProcessingLevel(BkaMetadata metadata) + private string GetProcessingLevel(BkaMetadata[] metadata) { - return String.Format("L1{0}", metadata.Processing.Level); + if (metadata[0].Processing?.Level != null) + { + return String.Format("L1{0}", metadata[0].Processing.Level); + } + return null; } - private string GetInstrument(BkaMetadata metadata) + private string GetInstrument(BkaMetadata[] metadata) { return "MSS"; } - private IDictionary GetCommonMetadata(BkaMetadata metadata) + private string GetSpectralMode(BkaMetadata[] metadata) + { + string spectralMode = null; + foreach (BkaMetadata m in metadata) + { + if (m.Processing?.Instrument != null) + { + if (spectralMode == null) spectralMode = String.Empty; + else spectralMode += "/"; + spectralMode += m.Processing.Instrument; + } + } + return spectralMode; + } + + + private IDictionary GetCommonMetadata(BkaMetadata[] metadata) { Dictionary properties = new Dictionary(); @@ -144,7 +251,7 @@ private IDictionary GetCommonMetadata(BkaMetadata metadata) return properties; } - private void FillBasicsProperties(BkaMetadata metadata, IDictionary properties) + private void FillBasicsProperties(BkaMetadata[] metadata, IDictionary properties) { CultureInfo culture = new CultureInfo("fr-FR"); // title @@ -158,116 +265,176 @@ private void FillBasicsProperties(BkaMetadata metadata, IDictionary properties = item.Properties; if (IncludeProviderProperty) { AddSingleProvider( properties, - "NAS", + "NAS", "BKA (formerly known as BelKa 2) is a Belarusian remote sensing satellite developed under an agreement between the National Academy of Sciences of Belarus (NAS) and the Federal Space Agency of the Russian Federation. The BKA satellite is almost an exact copy of the Russian Kanopus-Vulkan N1 Environmental Satellite (Kanopus-V 1).", new StacProviderRole[] { StacProviderRole.producer, StacProviderRole.processor, StacProviderRole.licensor }, new Uri("https://gis.by/en/tech/bka/") ); } properties["licence"] = "proprietary"; - if (metadata.GeoReference.SceneGeoposition.GroundResolution != null) + double gsd = 0; + foreach (BkaMetadata m in metadata) { - item.Gsd = metadata.GeoReference.SceneGeoposition.GroundResolution.PixelSizeNorthing; + BkaSceneGroundResolution groundResolution = m.GeoReference?.SceneGeoposition?.GroundResolution; + if (groundResolution != null) + { + if (gsd == 0 || groundResolution.PixelSizeNorthing > gsd) gsd = groundResolution.PixelSizeNorthing; + } } + if (gsd != 0) item.Gsd = gsd; } - private void FillDateTimeProperties(BkaMetadata metadata, Dictionary properties) + private void FillDateTimeProperties(BkaMetadata[] metadata, Dictionary properties) { var format = "yyyy-MM-ddTHH:mm:ss"; var format2 = "dd.MM.yyyy HH:mm:ss"; - if (DateTime.TryParseExact(metadata.SatelliteData.SceneAcquisition.AcquisitionTime, format, null, DateTimeStyles.AssumeUniversal, out DateTime dt)) - { - properties["datetime"] = dt.ToUniversalTime().ToString("O"); - } - if (DateTime.TryParseExact(metadata.Production?.CreationDate, format, null, DateTimeStyles.AssumeUniversal, out dt)) - { - properties["created"] = dt.ToUniversalTime().ToString("O"); - } - else if (DateTime.TryParseExact(metadata.Production?.CreationDate, format2, null, DateTimeStyles.AssumeUniversal, out dt)) + DateTime datetime = DateTime.MaxValue; + DateTime created = DateTime.MaxValue; + + foreach (BkaMetadata m in metadata) { - properties["created"] = dt.ToUniversalTime().ToString("O"); + + if (DateTime.TryParseExact(m.SatelliteData.SceneAcquisition.AcquisitionTime, format, null, DateTimeStyles.AssumeUniversal, out DateTime dt)) + { + if (dt.ToUniversalTime() < datetime) datetime = dt.ToUniversalTime(); + properties["datetime"] = datetime.ToString("O"); + } + if (DateTime.TryParseExact(m.Production?.CreationDate, format, null, DateTimeStyles.AssumeUniversal, out dt)) + { + if (dt.ToUniversalTime() < created) { + created = dt.ToUniversalTime(); + properties["created"] = created.ToString("O"); + } + } + else if (DateTime.TryParseExact(m.Production?.CreationDate, format2, null, DateTimeStyles.AssumeUniversal, out dt)) + { + if (dt.ToUniversalTime() < created) { + created = dt.ToUniversalTime(); + properties["created"] = created.ToString("O"); + } + } } } - private void FillPlatformDefinition(BkaMetadata metadata, Dictionary properties) + private void FillPlatformDefinition(BkaMetadata[] metadata, Dictionary properties) { string platform = "bka"; - if (metadata.Processing?.Satellite != null) platform = metadata.Processing.Satellite.ToLower(); + if (metadata[0].Processing?.Satellite != null) platform = metadata[0].Processing.Satellite.ToLower(); string mission = "bka"; - if (metadata.Processing?.Mission != null) mission = metadata.Processing.Mission.ToLower(); + if (metadata[0].Processing?.Mission != null) mission = metadata[0].Processing.Mission.ToLower(); properties["platform"] = platform; properties["constellation"] = platform; properties["mission"] = mission; properties["instruments"] = new string[] { GetInstrument(metadata).ToLower() }; properties["sensor_type"] = "optical"; - if (metadata.Processing?.Instrument != null) - properties["spectral_mode"] = metadata.Processing?.Instrument; + string spectralMode = GetSpectralMode(metadata); + if (spectralMode != null) properties["spectral_mode"] = spectralMode; } - private GeoJSON.Net.Geometry.IGeometryObject GetGeometry(BkaMetadata metadata) + private GeoJSON.Net.Geometry.IGeometryObject GetGeometry(BkaMetadata[] metadata) { - BkaGeodeticCoordinates coordinates = metadata.GeoReference.SceneGeoposition.GeodeticCoordinates; + BkaGeodeticCoordinates baseCoordinates = metadata[0].GeoReference.SceneGeoposition.GeodeticCoordinates; + bool falling = baseCoordinates.Corner2NWLat < baseCoordinates.Corner3NELat; // orbit goes NW -> SE or SE -> NW + + double swLon = 180, swLat = 0, seLon = -180, seLat = 0, neLon = 0, neLat = 0, nwLon = 0, nwLat = 0; + foreach (BkaMetadata m in metadata) + { + if (m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner1SWLon < swLon) + { + swLon = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner1SWLon; + swLat = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner1SWLat; + nwLon = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner2NWLon; + nwLat = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner2NWLat; + } + if (m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner4SELon > seLon) + { + seLon = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner4SELon; + seLat = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner4SELat; + neLon = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner3NELon; + neLat = m.GeoReference.SceneGeoposition.GeodeticCoordinates.Corner3NELat; + } + } + + List positions = new List { - new GeoJSON.Net.Geometry.Position(coordinates.Corner1SWLat, coordinates.Corner1SWLon), - new GeoJSON.Net.Geometry.Position(coordinates.Corner4SELat, coordinates.Corner4SELon), - new GeoJSON.Net.Geometry.Position(coordinates.Corner3NELat, coordinates.Corner3NELon), - new GeoJSON.Net.Geometry.Position(coordinates.Corner2NWLat, coordinates.Corner2NWLon), - new GeoJSON.Net.Geometry.Position(coordinates.Corner1SWLat, coordinates.Corner1SWLon) + new GeoJSON.Net.Geometry.Position(swLat, swLon), + new GeoJSON.Net.Geometry.Position(seLat, seLon), + new GeoJSON.Net.Geometry.Position(neLat, neLon), + new GeoJSON.Net.Geometry.Position(nwLat, nwLon), + new GeoJSON.Net.Geometry.Position(swLat, swLon) }; GeoJSON.Net.Geometry.LineString lineString = new GeoJSON.Net.Geometry.LineString(positions.ToArray()); return new GeoJSON.Net.Geometry.Polygon(new GeoJSON.Net.Geometry.LineString[] { lineString }).NormalizePolygon(); } - protected void AddAssets(StacItem stacItem, IItem item, BkaMetadata metadata) + protected void AddAssets(StacItem stacItem, IItem item, IAssetsContainer[] assetContainers) { - IAsset imageAsset = FindFirstAssetFromFileNameRegex(item, @".*\.tif"); - if (imageAsset != null) + if (assetContainers == null || assetContainers.Length == 0) { - StacAsset stacAsset = StacAsset.CreateDataAsset(stacItem, imageAsset.Uri, new ContentType("image/tiff; application=geotiff")); - stacAsset.Roles.Add("dn"); - - string key = String.Format("{0}_{1}", - GetProcessingLevel(metadata), - metadata.Processing?.Instrument - ); - stacAsset.Properties["title"] = key.Replace("_", " "); - stacAsset.Properties.AddRange(imageAsset.Properties); + assetContainers = new IAssetsContainer[] { item }; + } + foreach (IAssetsContainer container in assetContainers) + { + IAsset[] metadataAssets = GetMetadataAssets(container, null); + BkaMetadata[] metadata = ReadMetadata(metadataAssets).GetAwaiter().GetResult(); - BkaSpectral spectral = metadata.Radiometry.Spectral; - if (spectral.BandMS1 != null) AddBandAsset(spectral.BandMS1, stacAsset); - if (spectral.BandMS2 != null) AddBandAsset(spectral.BandMS2, stacAsset); - if (spectral.BandMS3 != null) AddBandAsset(spectral.BandMS3, stacAsset); - if (spectral.BandMS4 != null) AddBandAsset(spectral.BandMS4, stacAsset); + if (metadata.Length != 1) + { + throw new Exception("Missing/invalid metadata"); + } + IAsset metadataAsset = metadataAssets[0]; + BkaMetadata subProductMetadata = metadata[0]; - stacItem.Assets.Add(key, stacAsset); - } + IAsset imageAsset = FindFirstAssetFromFileNameRegex(container, @".*\.tif"); + if (imageAsset != null) + { + StacAsset stacAsset = StacAsset.CreateDataAsset(stacItem, imageAsset.Uri, new ContentType("image/tiff; application=geotiff")); + stacAsset.Roles.Add("dn"); + + string key = String.Format("{0}_{1}", + GetProcessingLevel(metadata), + subProductMetadata.Processing?.Instrument + ); + stacAsset.Properties["title"] = key.Replace("_", " "); + stacAsset.Properties.AddRange(imageAsset.Properties); + + BkaSpectral spectral = subProductMetadata.Radiometry.Spectral; + if (spectral.BandMS1 != null) AddBandAsset(spectral.BandMS1, stacAsset); + if (spectral.BandMS2 != null) AddBandAsset(spectral.BandMS2, stacAsset); + if (spectral.BandMS3 != null) AddBandAsset(spectral.BandMS3, stacAsset); + if (spectral.BandMS4 != null) AddBandAsset(spectral.BandMS4, stacAsset); + if (spectral.BandPAN != null) AddBandAsset(spectral.BandPAN, stacAsset); + + stacItem.Assets.Add(key, stacAsset); + } - IAsset metadataAsset = GetMetadataAsset(item); - if (metadataAsset != null) - { - StacAsset stacAsset = StacAsset.CreateMetadataAsset(stacItem, metadataAsset.Uri, new ContentType(MimeTypes.GetMimeType(metadataAsset.Uri.ToString()))); - stacItem.Assets.Add("metadata", stacAsset); - stacAsset.Properties.AddRange(metadataAsset.Properties); - } + string suffix = (assetContainers.Length == 1) ? String.Empty : String.Format("_{0}", subProductMetadata.Processing?.Instrument); + if (metadataAsset != null) + { + StacAsset stacAsset = StacAsset.CreateMetadataAsset(stacItem, metadataAsset.Uri, new ContentType(MimeTypes.GetMimeType(metadataAsset.Uri.ToString()))); + stacItem.Assets.Add(String.Format("metadata{0}", suffix), stacAsset); + stacAsset.Properties.AddRange(metadataAsset.Properties); + } - IAsset overviewAsset = FindFirstAssetFromFileNameRegex(item, @".*_preview\.jpg"); - if (overviewAsset != null) - { - StacAsset stacAsset = StacAsset.CreateOverviewAsset(stacItem, overviewAsset.Uri, new ContentType(MimeTypes.GetMimeType(overviewAsset.Uri.ToString()))); - stacAsset.Properties.AddRange(overviewAsset.Properties); - stacItem.Assets.Add("overview", stacAsset); + IAsset overviewAsset = FindFirstAssetFromFileNameRegex(container, @".*_preview\.jpg"); + if (overviewAsset != null) + { + StacAsset stacAsset = StacAsset.CreateOverviewAsset(stacItem, overviewAsset.Uri, new ContentType(MimeTypes.GetMimeType(overviewAsset.Uri.ToString()))); + stacAsset.Properties.AddRange(overviewAsset.Properties); + stacItem.Assets.Add(String.Format("overview{0}", suffix), stacAsset); + } } } @@ -302,6 +469,11 @@ private void AddBandAsset(BkaBandInfo bandInfo, StacAsset stacAsset = null) fullWidthHalfMax = 0.1; commonName = EoBandCommonName.nir; break; + case "PAN": + waveLength = 0.66; + fullWidthHalfMax = 0.208; + commonName = EoBandCommonName.pan; + break; default: return; } @@ -329,7 +501,7 @@ private void AddBandAsset(BkaBandInfo bandInfo, StacAsset stacAsset = null) List bands = new List(eo.Bands) { eoBandObject }; eo.Bands = bands.ToArray(); } - + if (rasterBand != null) { RasterStacExtension raster = stacAsset.RasterExtension(); @@ -346,25 +518,62 @@ private void AddBandAsset(BkaBandInfo bandInfo, StacAsset stacAsset = null) } - protected virtual IAsset GetMetadataAsset(IItem item) + protected virtual IAsset[] GetMetadataAssets(IAssetsContainer container, IEnumerable innerAssetContainers = null) + { + IAsset metadataAsset = FindFirstAssetFromFileNameRegex(container, @"^.*_pasp-en\.xml"); + if (metadataAsset != null) return new IAsset[] { metadataAsset }; + + if (innerAssetContainers != null) + { + List metadataAssets = new List(); + foreach (IAssetsContainer innerAssetContainer in innerAssetContainers) + { + metadataAsset = FindFirstAssetFromFileNameRegex(innerAssetContainer, @"^.*_pasp-en\.xml"); + if (metadataAsset != null) metadataAssets.Add(metadataAsset); + } + if (metadataAssets.Count != 0) return metadataAssets.ToArray(); + } + + return null; + } + + + protected virtual IAsset GetTopZipAsset(IItem item) + { + IAsset zipAsset = FindFirstAssetFromFileNameRegex(item, @".*\.zip"); + return zipAsset; + } + + protected virtual IAsset GetProductZipAsset(IAssetsContainer container) { - IAsset metadataAsset = FindFirstAssetFromFileNameRegex(item, @"^.*_pasp-en\.xml"); - if (metadataAsset != null) return metadataAsset; + IAsset zipAsset = FindFirstAssetFromFileNameRegex(container, @"PRODUCT\.zip"); + return zipAsset; + } - throw new FileNotFoundException(String.Format("Unable to find the summary file asset")); + protected virtual IEnumerable GetInnerZipAssets(IAssetsContainer container) + { + IEnumerable zipAssets = this.FindAssetsFromFileNameRegex(container, @".*\.zip"); + return zipAssets; } - public virtual async Task ReadMetadata(IAsset metadataAsset) + public virtual async Task ReadMetadata(IEnumerable metadataAssets) { - using (var stream = await resourceServiceProvider.GetAssetStreamAsync(metadataAsset, System.Threading.CancellationToken.None)) + List metadata = new List(); + + foreach (IAsset metadataAsset in metadataAssets) { - XmlReaderSettings settings = new XmlReaderSettings() { DtdProcessing = DtdProcessing.Ignore }; - var reader = XmlReader.Create(stream, settings); - - logger.LogDebug("Deserializing metadata file {0}", metadataAsset.Uri); + using (var stream = await resourceServiceProvider.GetAssetStreamAsync(metadataAsset, System.Threading.CancellationToken.None)) + { + XmlReaderSettings settings = new XmlReaderSettings() { DtdProcessing = DtdProcessing.Ignore }; + var reader = XmlReader.Create(stream, settings); - return (BkaMetadata)metadataSerializer.Deserialize(reader); + logger.LogDebug("Deserializing metadata file {0}", metadataAsset.Uri); + + metadata.Add((BkaMetadata)metadataSerializer.Deserialize(reader)); + } } + + return metadata.ToArray(); } } } \ No newline at end of file diff --git a/src/Stars.Data/Model/Metadata/Saocom1/Saocom1MetadataExtractor.cs b/src/Stars.Data/Model/Metadata/Saocom1/Saocom1MetadataExtractor.cs index a028912e..da93debd 100644 --- a/src/Stars.Data/Model/Metadata/Saocom1/Saocom1MetadataExtractor.cs +++ b/src/Stars.Data/Model/Metadata/Saocom1/Saocom1MetadataExtractor.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.IO.Abstractions; using System.Linq; using System.Net.Mime; using System.Threading.Tasks; @@ -23,7 +24,6 @@ using Terradue.Stars.Services.Processing; using Microsoft.Extensions.DependencyInjection; -using System.IO.Abstractions; using Terradue.Stars.Services.Supplier.Destination; using Terradue.Stars.Services.Supplier.Carrier; using Terradue.Stars.Services; diff --git a/src/Stars.Data/Terradue.Stars.Data.csproj b/src/Stars.Data/Terradue.Stars.Data.csproj index e216cf85..3f0c361a 100644 --- a/src/Stars.Data/Terradue.Stars.Data.csproj +++ b/src/Stars.Data/Terradue.Stars.Data.csproj @@ -4,7 +4,7 @@ Terradue.Stars.Data Terradue.Stars.Data Collection of data Plugins for Terradue.Stars - 2.18.1 + 2.19.0 $(Version)-$(VersionSuffix) NU1603 diff --git a/src/Stars.Services/Terradue.Stars.Services.csproj b/src/Stars.Services/Terradue.Stars.Services.csproj index 1be843fd..64855258 100644 --- a/src/Stars.Services/Terradue.Stars.Services.csproj +++ b/src/Stars.Services/Terradue.Stars.Services.csproj @@ -3,7 +3,7 @@ netstandard2.0 Terradue.Stars Stars is a set of services for working with Spatio Temporal Catalog such as STAC but not only - 2.18.1 + 2.19.0 $(Version)-$(VersionSuffix) Terradue.Stars.Services