diff --git a/.vscode/launch.json b/.vscode/launch.json index b5abe22e..9d60c653 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -803,7 +803,7 @@ // "--nocopy-cog", "--output", "/tmp/stars", - "https://earth-search.aws.element84.com/v0/collections/sentinel-s2-l2a-cogs/items/S2B_36RTT_20191205_0_L2A" + "https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a/items/S2A_17UNQ_20230511_0_L2A" ], "cwd": "${workspaceFolder}/src", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console diff --git a/src/Stars.Data/Model/Atom/StarsAtomItem.cs b/src/Stars.Data/Model/Atom/StarsAtomItem.cs index 5fc97acb..2bd06acd 100644 --- a/src/Stars.Data/Model/Atom/StarsAtomItem.cs +++ b/src/Stars.Data/Model/Atom/StarsAtomItem.cs @@ -355,6 +355,35 @@ public bool AddImageOverlayOffering(StacItemNode stacItemNode) return false; } + public bool AddImageOverlayOffering(StacCollectionNode stacCollectionNode) + { + var imageOverview = SelectOverlayOverviewAssets(stacCollectionNode.StacCollection).FirstOrDefault(); + if (imageOverview.Key != null) + { + Uri assetUri = imageOverview.Value.Uri; + try + { + // if relative + if (!assetUri.IsAbsoluteUri) + // then make absolute from item + assetUri = new Uri(stacCollectionNode.Uri, assetUri); + } + catch { return false; } + ElementExtensions.Add(new OwcOffering() + { + + Contents = new OwcContent[]{new OwcContent() + { + Url = assetUri, + Type = imageOverview.Value.MediaType.ToString() + }}, + Code = "http://www.opengis.net/spec/owc-atom/1.0/req/img" + }.CreateReader()); + return true; + } + return false; + } + public bool TryAddEGMSOffering(StacCollectionNode stacCollectionNode, EgmsService egmsService) { var egmsServiceUri = stacCollectionNode.StacCollection.Links.FirstOrDefault(l => l.RelationshipType == "self").Uri; @@ -508,6 +537,22 @@ private static Dictionary SelectOverlayOverviewAssets(StacIte return new Dictionary(); } + private static Dictionary SelectOverlayOverviewAssets(StacCollection stacCollection) + { + + Dictionary overviewAssets = stacCollection.Assets + .Where(a => a.Value.Roles.Contains("overview") || + a.Value.Roles.Contains("visual")) + .OrderBy(a => a.Value.GetProperty("size")) + .Where(a => a.Value.MediaType.MediaType.StartsWith("image/") && + a.Value.FileExtension().Size < 20971520) + .Take(1) + .ToDictionary(a => a.Key, a => a.Value); + if (overviewAssets.Count == 1) return overviewAssets; + + return new Dictionary(); + } + SyndicationElementExtension earthObservationExtension = null; public SyndicationElementExtension EarthObservationExtension diff --git a/src/Stars.Data/ThirdParty/Publication/GeosquareService.cs b/src/Stars.Data/ThirdParty/Publication/GeosquareService.cs index 4c1eb279..62c58768 100644 --- a/src/Stars.Data/ThirdParty/Publication/GeosquareService.cs +++ b/src/Stars.Data/ThirdParty/Publication/GeosquareService.cs @@ -24,6 +24,7 @@ using Terradue.Stars.Services.Model; using System.Collections.Specialized; using System.Web; +using Terradue.ServiceModel.Ogc.Owc.AtomEncoding; namespace Terradue.Stars.Data.ThirdParty.Geosquare { @@ -207,6 +208,25 @@ public async Task PrepareAtomItem(AtomItem atomItem, GeosquarePublicationState g link.Uri = geosquareConfiguration.MapUri(link.Uri); geosquarePublicationState.GeosquarePublicationModel.UpdateLink(link, atomItem, assetsContainer); } + foreach (var extension in atomItem.ElementExtensions.ToArray()) + { + if (extension.OuterName == "offering" && extension.OuterNamespace == OwcNamespaces.Owc) + { + var xml = extension.GetReader().ReadOuterXml(); + var offering = OwcContextHelper.OwcOfferingSerializer.Deserialize(new System.IO.StringReader(xml)) as OwcOffering; + atomItem.ElementExtensions.Remove(extension); + foreach (var content in offering.Contents) + { + SyndicationLink link = new SyndicationLink(content.Url, "enclosure", "", content.Type, 0); + geosquarePublicationState.GeosquarePublicationModel.UpdateLink(link, atomItem, assetsContainer); + content.Url = geosquareConfiguration.MapUri(link.Uri); + } + atomItem.ElementExtensions.Add(offering.CreateReader()); + } + } + + atomItem.ElementExtensions.ReadElementExtensions("offering", OwcNamespaces.Owc, new System.Xml.Serialization.XmlSerializer(typeof(OwcOffering))); + // create eventual opensearch link if (atomItem is StarsAtomItem) diff --git a/src/Stars.Data/Translators/StacCollectionToAtomItemTranslator.cs b/src/Stars.Data/Translators/StacCollectionToAtomItemTranslator.cs index 6631acee..7c885d7d 100644 --- a/src/Stars.Data/Translators/StacCollectionToAtomItemTranslator.cs +++ b/src/Stars.Data/Translators/StacCollectionToAtomItemTranslator.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Terradue.Stars.Interface.Router.Translator; using Terradue.Stars.Services.Model.Atom; using Stac; @@ -71,10 +71,15 @@ private StarsAtomItem CreateAtomItem(StacCollectionNode stacCollectionNode) if (link.RelationshipType == "wms" && link.Uri != null) { atomItem.AddWMSOffering(link); + imageOfferingSet = true; } } } + // if no previous image offering set, then let's try a simple overlay offering + if (!imageOfferingSet) + atomItem.AddImageOverlayOffering(stacCollectionNode); + return atomItem; } } diff --git a/src/Stars.Data/Translators/StacItemToAtomItemTranslator.cs b/src/Stars.Data/Translators/StacItemToAtomItemTranslator.cs index bd3ec4d4..c10912d4 100644 --- a/src/Stars.Data/Translators/StacItemToAtomItemTranslator.cs +++ b/src/Stars.Data/Translators/StacItemToAtomItemTranslator.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Terradue.Stars.Interface.Router.Translator; using Terradue.Stars.Services.Model.Atom; using Stac; diff --git a/src/Stars.Services/Model/Stac/StacRouter.cs b/src/Stars.Services/Model/Stac/StacRouter.cs index 4d65e34b..c408eb7a 100644 --- a/src/Stars.Services/Model/Stac/StacRouter.cs +++ b/src/Stars.Services/Model/Stac/StacRouter.cs @@ -96,6 +96,8 @@ private async Task AffineRouteAsync(IResource route, CancellationToke public async Task RouteAsync(IResource route, CancellationToken ct) { var routeFound = await AffineRouteAsync(route, ct); + if (routeFound is StacCollectionNode) + return routeFound as StacCollectionNode; if (routeFound is StacCatalogNode) return routeFound as StacCatalogNode; if (routeFound is StacItemNode) @@ -106,8 +108,11 @@ public async Task RouteAsync(IResource route, CancellationToken ct) { var json = await (routeFound as IStreamResource).ReadAsStringAsync(ct); IStacObject stacObject = StacConvert.Deserialize(json); - if (stacObject is IStacCatalog) + if (stacObject is IStacCatalog){ + if (stacObject is StacCollection) + return new StacCollectionNode(stacObject as StacCollection, routeFound.Uri); return new StacCatalogNode(stacObject as IStacCatalog, routeFound.Uri); + } else return new StacItemNode(stacObject as StacItem, routeFound.Uri); } diff --git a/src/Stars.Tests/S3Tests.cs b/src/Stars.Tests/S3Tests.cs index 9f0464cf..7ede13f3 100644 --- a/src/Stars.Tests/S3Tests.cs +++ b/src/Stars.Tests/S3Tests.cs @@ -107,7 +107,7 @@ public async Task ImportHttpStreamabletoS3() S3Resource s3Route = await s3ClientFactory.CreateAsync(S3Url.Parse("s3://local-http/S2B_MSIL2A_20211022T045839_N0301_R119_T44NLN_20211022T071547.jpg")); BlockingStream stream = new BlockingStream(0, 100); S3StreamingCarrier s3StreamingCarrier = serviceProvider.GetRequiredService(); - var httpRoute = await resourceServiceProvider.CreateStreamResourceAsync(new GenericResource(new Uri("https://store.terradue.com/api/scihub/sentinel2/S2MSI2A/2021/10/22/quicklooks/v1/S2B_MSIL2A_20211022T045839_N0301_R119_T44NLN_20211022T071547.jpg")), CancellationToken.None); + var httpRoute = await resourceServiceProvider.CreateStreamResourceAsync(new GenericResource(new Uri("https://store.terradue.com/scihub/sentinel2/S2MSI2A/2023/09/17/quicklooks/v1/S2A_MSIL2A_20230917T100031_N0509_R122_T32TQM_20230917T143103.jpg")), CancellationToken.None); var newRoute = await s3StreamingCarrier.StreamToS3Object(httpRoute, s3Route, CancellationToken.None); var metadata = await s3Route.Client.GetObjectMetadataAsync(s3Route.S3Uri.Bucket, s3Route.S3Uri.Key); Assert.Equal(httpRoute.ContentLength, Convert.ToUInt64(metadata.ContentLength));