diff --git a/src/DotNetStac.Test/DotNetStac.Test.csproj b/src/DotNetStac.Test/DotNetStac.Test.csproj index 72fb9dbd..ed5f4106 100644 --- a/src/DotNetStac.Test/DotNetStac.Test.csproj +++ b/src/DotNetStac.Test/DotNetStac.Test.csproj @@ -14,7 +14,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/DotNetStac/StacExtent.cs b/src/DotNetStac/Collection/StacExtent.cs similarity index 89% rename from src/DotNetStac/StacExtent.cs rename to src/DotNetStac/Collection/StacExtent.cs index 8e4d950b..de8ec1d9 100644 --- a/src/DotNetStac/StacExtent.cs +++ b/src/DotNetStac/Collection/StacExtent.cs @@ -1,10 +1,8 @@ -using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using Stac.Item; -namespace Stac +namespace Stac.Collection { [JsonObject] public class StacExtent @@ -15,7 +13,7 @@ public class StacExtent [JsonProperty("temporal")] public StacTemporalExtent Temporal { get; set; } - public static StacExtent Create(IEnumerable items) + public static StacExtent Create(IEnumerable items) { return new StacExtent() { diff --git a/src/DotNetStac/Converters/StacExtensionsConverter.cs b/src/DotNetStac/Converters/StacExtensionsConverter.cs new file mode 100644 index 00000000..7aff5ef2 --- /dev/null +++ b/src/DotNetStac/Converters/StacExtensionsConverter.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Stac.Collection; +using Stac.Extensions; +using Stac.Extensions; + +namespace Stac.Converters +{ + internal class StacExtensionsConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(IEnumerable)); + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + IEnumerable extensions = serializer.Deserialize>(reader); + + return new StacExtensions(extensions); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Dictionary summaries = (Dictionary)value; + + serializer.Serialize(writer, summaries.ToDictionary(k => k.Key, k => k.Value.AsJToken)); + } + } +} \ No newline at end of file diff --git a/src/DotNetStac/DotNetStac.csproj b/src/DotNetStac/DotNetStac.csproj index 7430b433..1424b9da 100644 --- a/src/DotNetStac/DotNetStac.csproj +++ b/src/DotNetStac/DotNetStac.csproj @@ -21,10 +21,12 @@ - + + + diff --git a/src/DotNetStac/Exceptions/DuplicateKeyException.cs b/src/DotNetStac/Exceptions/DuplicateKeyException.cs new file mode 100644 index 00000000..293c8c60 --- /dev/null +++ b/src/DotNetStac/Exceptions/DuplicateKeyException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; + +namespace Stac.Extensions +{ + [Serializable] + internal class DuplicateKeyException : Exception + { + public DuplicateKeyException() + { + } + + public DuplicateKeyException(string message) : base(message) + { + } + + public DuplicateKeyException(string message, Exception innerException) : base(message, innerException) + { + } + + protected DuplicateKeyException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/ExtensionNotAssignedException.cs b/src/DotNetStac/Exceptions/ExtensionNotAssignedException.cs similarity index 96% rename from src/DotNetStac/Extensions/ExtensionNotAssignedException.cs rename to src/DotNetStac/Exceptions/ExtensionNotAssignedException.cs index 1e0f19f3..4c6a59b0 100644 --- a/src/DotNetStac/Extensions/ExtensionNotAssignedException.cs +++ b/src/DotNetStac/Exceptions/ExtensionNotAssignedException.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; -namespace Stac.Extensions +namespace Stac.Exceptions { [Serializable] public class ExtensionNotAssignedException : Exception diff --git a/src/DotNetStac/Exceptions/InvalidStacDataException.cs b/src/DotNetStac/Exceptions/InvalidStacDataException.cs index 02ea3c97..e5cdcae5 100644 --- a/src/DotNetStac/Exceptions/InvalidStacDataException.cs +++ b/src/DotNetStac/Exceptions/InvalidStacDataException.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; -namespace Stac +namespace Stac.Exceptions { [Serializable] internal class InvalidStacDataException : Exception diff --git a/src/DotNetStac/Exceptions/InvalidStacSchemaException.cs b/src/DotNetStac/Exceptions/InvalidStacSchemaException.cs new file mode 100644 index 00000000..5d9aea1a --- /dev/null +++ b/src/DotNetStac/Exceptions/InvalidStacSchemaException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; + +namespace Stac.Exceptions +{ + [Serializable] + internal class InvalidStacSchemaException : Exception + { + public InvalidStacSchemaException() + { + } + + public InvalidStacSchemaException(string message) : base(message) + { + } + + public InvalidStacSchemaException(string message, Exception innerException) : base(message, innerException) + { + } + + protected InvalidStacSchemaException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/AssignableStacExtension.cs b/src/DotNetStac/Extensions/AssignableStacExtension.cs deleted file mode 100644 index 312af1bf..00000000 --- a/src/DotNetStac/Extensions/AssignableStacExtension.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Reflection; -using Newtonsoft.Json.Linq; -using Stac.Item; - -namespace Stac.Extensions -{ - public abstract class AssignableStacExtension : IStacExtension - { - private string prefix; - private IStacPropertiesContainer stacPropertiesContainer; - - public AssignableStacExtension(string prefix, IStacObject stacObject) : this(prefix, stacObject as IStacPropertiesContainer) - { - } - - public AssignableStacExtension(string prefix, IStacPropertiesContainer stacPropertiesContainer) - { - this.prefix = prefix; - this.stacPropertiesContainer = stacPropertiesContainer; - if (stacPropertiesContainer is IStacObject) - InitStacObject(stacPropertiesContainer as IStacObject); - - } - - public virtual string Id => prefix; - - public IStacObject StacObject - { - get - { - if (!(StacPropertiesContainer is IStacObject)) - throw new ExtensionNotAssignedException("Extension {0} is not assigned to a Stac object", Id); - return stacPropertiesContainer as IStacObject; - } - } - - public IStacPropertiesContainer StacPropertiesContainer - { - get - { - if (stacPropertiesContainer == null) - throw new ExtensionNotAssignedException("Extension {0} is not assigned to a Stac properties container", Id); - return stacPropertiesContainer; - } - } - - public void InitStacObject(IStacObject stacObject) - { - stacObject.StacExtensions.Remove(prefix); - stacObject.StacExtensions.Add(prefix, this); - } - - protected void SetField(string key, object value) - { - StacPropertiesContainer.Properties.Remove(prefix + ":" + key); - StacPropertiesContainer.Properties.Add(prefix + ":" + key, value); - } - - protected object GetField(string fieldName) - { - string key = prefix + ":" + fieldName; - if (!StacPropertiesContainer.Properties.ContainsKey(key)) - return null; - return StacPropertiesContainer.Properties[key]; - } - - protected T GetField(string fieldName) - { - var @object = GetField(fieldName); - if (@object == null) return default(T); - if (@object is JToken) - return (@object as JToken).ToObject(); - if (typeof(T).GetTypeInfo().IsEnum) - return (T)Enum.Parse(typeof(T), @object.ToString()); - return (T)Convert.ChangeType(@object, typeof(T)); - } - - } -} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/DummyStacExtension.cs b/src/DotNetStac/Extensions/DummyStacExtension.cs new file mode 100644 index 00000000..40823bc5 --- /dev/null +++ b/src/DotNetStac/Extensions/DummyStacExtension.cs @@ -0,0 +1,12 @@ +namespace Stac.Extensions +{ + internal class DummyStacExtension : IStacExtension + { + public DummyStacExtension(string identifier) + { + Identifier = identifier; + } + + public string Identifier { get; private set; } + } +} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/Eo/EoStacExtension.cs b/src/DotNetStac/Extensions/Eo/EoStacExtension.cs index c171eaca..c1279dda 100644 --- a/src/DotNetStac/Extensions/Eo/EoStacExtension.cs +++ b/src/DotNetStac/Extensions/Eo/EoStacExtension.cs @@ -1,64 +1,54 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json.Linq; -using Stac; -using Stac.Extensions; -using Stac.Item; namespace Stac.Extensions.Eo { - public class EoStacExtension : AssignableStacExtension, IStacExtension + public class EoStacExtension : StacPropertiesContainerExtension, IStacExtension { + private static IDictionary itemFields; - public const string Prefix = "eo"; - public const string BandsField = "bands"; - public const string CloudCoverField = "cloud_cover"; + public const string JsonSchemaUrl = "https://stac-extensions.github.io/eo/v1.0.0/schema.json"; + public const string BandsField = "eo:bands"; + public const string CloudCoverField = "eo:cloud_cover"; - public double CloudCover - { - get { return base.GetField(CloudCoverField); } - set { base.SetField(CloudCoverField, value); } - } - - public EoStacExtension(IStacObject stacObject) : base(Prefix, stacObject) + public EoStacExtension(IStacPropertiesContainer stacpropertiesContainer) : base(JsonSchemaUrl, stacpropertiesContainer) { + itemFields = new Dictionary(); + itemFields.Add(BandsField, typeof(EoBandObject[]) ); + itemFields.Add(CloudCoverField, typeof(double) ); } - public EoStacExtension(StacAsset stacAsset) : base(Prefix, stacAsset) + public double CloudCover { + get { return StacPropertiesContainer.GetProperty(CloudCoverField); } + set { StacPropertiesContainer.SetProperty(CloudCoverField, value); } } public EoBandObject[] Bands { - get { return base.GetField(BandsField); } - set { base.SetField(BandsField, value); } + get { return StacPropertiesContainer.GetProperty(BandsField); } + set { StacPropertiesContainer.SetProperty(BandsField, value); } } - public EoBandObject[] GetAssetBandObjects(StacAsset stacAsset) + public override IDictionary ItemFields => itemFields; + } + + public static class EoStacExtensionExtensions + { + public static EoStacExtension EoExtension(this StacItem stacItem) { - string key = Id + ":" + BandsField; - if (stacAsset.Properties.ContainsKey(key)) - return stacAsset.GetProperty(key); - return null; + return new EoStacExtension(stacItem); } - public StacAsset GetAsset(EoBandCommonName commonName) + public static EoStacExtension EoExtension(this StacAsset stacAsset) { - StacItem item = null; - try { item = StacObject as StacItem; } - catch { } - if (item != null) - return item.Assets.Values.FirstOrDefault(a => GetAssetBandObjects(a).Any(b => b.CommonName == commonName)); - return null; + return new EoStacExtension(stacAsset); } - public void SetAssetBandObjects(StacAsset stacAsset, EoBandObject[] eoBandObjects) + public static StacAsset GetAsset(this StacItem stacItem, EoBandCommonName commonName) { - string key = Id + ":" + BandsField; - if (stacAsset.Properties.ContainsKey(key)) - stacAsset.Properties.Remove(key); - stacAsset.Properties.Add(key, eoBandObjects); + return stacItem.Assets.Values.FirstOrDefault(a => a.EoExtension().Bands.Any(b => b.CommonName == commonName)); } } } diff --git a/src/DotNetStac/Extensions/Eo/EoStacExtensionExtensions.cs b/src/DotNetStac/Extensions/Eo/EoStacExtensionExtensions.cs index 8f898e86..bd541688 100644 --- a/src/DotNetStac/Extensions/Eo/EoStacExtensionExtensions.cs +++ b/src/DotNetStac/Extensions/Eo/EoStacExtensionExtensions.cs @@ -1,11 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json.Linq; -using Stac; -using Stac.Extensions; -using Stac.Item; - namespace Stac.Extensions.Eo { public static class EoStacExtensionExtensions diff --git a/src/DotNetStac/Extensions/GenericStacExtension.cs b/src/DotNetStac/Extensions/GenericStacExtension.cs index 3366b838..3bc38561 100644 --- a/src/DotNetStac/Extensions/GenericStacExtension.cs +++ b/src/DotNetStac/Extensions/GenericStacExtension.cs @@ -1,13 +1,13 @@ -using System; -using Stac.Item; +using Newtonsoft.Json.Schema; namespace Stac.Extensions { - public class GenericStacExtension : AssignableStacExtension + public class GenericStacExtension : StacPropertiesContainerExtension { - public GenericStacExtension(string prefix, IStacObject stacObject) : base(prefix, stacObject) + public GenericStacExtension(JSchema jsonSchema, string fieldNamePrefix) : base(jsonSchema, fieldNamePrefix) { } + // TODO helpers to dicover automatically the fields } } \ No newline at end of file diff --git a/src/DotNetStac/Extensions/IStacExtension.cs b/src/DotNetStac/Extensions/IStacExtension.cs index 638c2fa2..28679c43 100644 --- a/src/DotNetStac/Extensions/IStacExtension.cs +++ b/src/DotNetStac/Extensions/IStacExtension.cs @@ -1,10 +1,11 @@ -using Stac.Item; +using System; +using System.Collections.Generic; namespace Stac.Extensions { public interface IStacExtension { - string Id { get; } + string Identifier { get; } } } \ No newline at end of file diff --git a/src/DotNetStac/Extensions/ITypeStacExtension.cs b/src/DotNetStac/Extensions/ITypeStacExtension.cs new file mode 100644 index 00000000..d22c4d86 --- /dev/null +++ b/src/DotNetStac/Extensions/ITypeStacExtension.cs @@ -0,0 +1,6 @@ +namespace Stac.Extensions +{ + internal interface ITypeStacExtension + { + } +} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/Processing/ProcessingStacExtension.cs b/src/DotNetStac/Extensions/Processing/ProcessingStacExtension.cs index da614820..19b7b5c1 100644 --- a/src/DotNetStac/Extensions/Processing/ProcessingStacExtension.cs +++ b/src/DotNetStac/Extensions/Processing/ProcessingStacExtension.cs @@ -1,18 +1,13 @@ -using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.Linq; -using Newtonsoft.Json.Linq; -using ProjNet.CoordinateSystems; -using Stac; -using Stac.Extensions; -using Stac.Item; +using Stac.Model; namespace Stac.Extensions.Processing { - public class ProcessingStacExtension : AssignableStacExtension, IStacExtension + public class ProcessingStacExtension : StacPropertiesContainerExtension, IStacExtension { + public const string JsonSchemaUrl = "https://stac-extensions.github.io/processing/v1.0.0/schema.json"; public const string Prefix = "processing"; public const string LineageField = "lineage"; public const string LevelField = "level"; diff --git a/src/DotNetStac/Extensions/Projection/ProjectionStacExtension.cs b/src/DotNetStac/Extensions/Projection/ProjectionStacExtension.cs index c478c140..809e5e08 100644 --- a/src/DotNetStac/Extensions/Projection/ProjectionStacExtension.cs +++ b/src/DotNetStac/Extensions/Projection/ProjectionStacExtension.cs @@ -1,18 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json.Linq; using ProjNet.CoordinateSystems; -using Stac; -using Stac.Extensions; -using Stac.Item; namespace Stac.Extensions.Projection { - public class ProjectionStacExtension : AssignableStacExtension, IStacExtension + public class ProjectionStacExtension : StacPropertiesContainerExtension, IStacExtension { - public const string Prefix = "proj"; + public const string JsonSchemaUrl = "https://stac-extensions.github.io/projection/v1.0.0/schema.json"; + public const string EpsgField = "epsg"; public const string Wkt2Field = "wkt2"; @@ -29,7 +23,7 @@ public string Wkt2 set { base.SetField(Wkt2Field, value); } } - public ProjectionStacExtension(IStacObject stacObject) : base(Prefix, stacObject) + public ProjectionStacExtension(IStacObject stacObject) : base() { } diff --git a/src/DotNetStac/Extensions/Sar/SarStacExtension.cs b/src/DotNetStac/Extensions/Sar/SarStacExtension.cs index b9dad982..124d67c3 100644 --- a/src/DotNetStac/Extensions/Sar/SarStacExtension.cs +++ b/src/DotNetStac/Extensions/Sar/SarStacExtension.cs @@ -1,143 +1,154 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json.Linq; -using Stac; -using Stac.Extensions; -using Stac.Item; namespace Stac.Extensions.Sar { - public class SarStacExtension : AssignableStacExtension, IStacExtension + public class SarStacExtension : StacPropertiesContainerExtension, IStacExtension { - public const string Prefix = "sar"; - - public const string InstrumentModeField = "instrument_mode"; - public const string FrequencyBandField = "frequency_band"; - public const string CenterFrequencyField = "center_frequency"; - public const string PolarizationsField = "polarizations"; - public const string ProductTypeField = "product_type"; - public const string ResolutionRangeField = "resolution_range"; - public const string ResolutionAzimuthField = "resolution_azimuth"; - public const string PixelSpacingRangeField = "pixel_spacing_range"; - public const string PixelSpacingAzimuthField = "pixel_spacing_azimuth"; - public const string LooksRangeField = "looks_range"; - public const string LooksAzimuthField = "looks_azimuth"; - public const string LooksEquivalentNumberField = "looks_equivalent_number"; - public const string ObservationDirectionField = "observation_direction"; - - internal SarStacExtension(IStacObject stacObject) : base(Prefix, stacObject) - { - } - - public SarStacExtension(IStacObject stacObject, string instrumentMode, SarCommonFrequencyBandName frequencyBandName, string[] polarizations, string productType) : base(Prefix, stacObject) - { - InstrumentMode = instrumentMode; - FrequencyBand = frequencyBandName; - Polarizations = polarizations; - ProductType = productType; + public const string JsonSchemaUrl = "https://stac-extensions.github.io/sar/v1.0.0/schema.json"; + + public const string InstrumentModeField = "sar:instrument_mode"; + public const string FrequencyBandField = "sar:frequency_band"; + public const string CenterFrequencyField = "sar:center_frequency"; + public const string PolarizationsField = "sar:polarizations"; + public const string ProductTypeField = "sar:product_type"; + public const string ResolutionRangeField = "sar:resolution_range"; + public const string ResolutionAzimuthField = "sar:resolution_azimuth"; + public const string PixelSpacingRangeField = "sar:pixel_spacing_range"; + public const string PixelSpacingAzimuthField = "sar:pixel_spacing_azimuth"; + public const string LooksRangeField = "sar:looks_range"; + public const string LooksAzimuthField = "sar:looks_azimuth"; + public const string LooksEquivalentNumberField = "sar:looks_equivalent_number"; + public const string ObservationDirectionField = "sar:observation_direction"; + private readonly IDictionary itemFields; + + internal SarStacExtension(IStacPropertiesContainer stacPropertiesContainer) : base(JsonSchemaUrl, stacPropertiesContainer) + { + itemFields = new Dictionary(); + itemFields.Add(InstrumentModeField, typeof(string[])); + itemFields.Add(FrequencyBandField, typeof(SarCommonFrequencyBandName)); + itemFields.Add(CenterFrequencyField, typeof(double)); + itemFields.Add(PolarizationsField, typeof(string[])); + itemFields.Add(ProductTypeField, typeof(string)); + itemFields.Add(ResolutionRangeField, typeof(double)); + itemFields.Add(ResolutionAzimuthField, typeof(double)); + itemFields.Add(PixelSpacingRangeField, typeof(double)); + itemFields.Add(PixelSpacingAzimuthField, typeof(double)); + itemFields.Add(LooksRangeField, typeof(double)); + itemFields.Add(LooksAzimuthField, typeof(double)); + itemFields.Add(LooksEquivalentNumberField, typeof(double)); + itemFields.Add(ObservationDirectionField, typeof(string)); } public string InstrumentMode { - get { return base.GetField(InstrumentModeField); } - set { base.SetField(InstrumentModeField, value); } + get { return StacPropertiesContainer.GetProperty(InstrumentModeField); } + set { StacPropertiesContainer.SetProperty(InstrumentModeField, value); } } public SarCommonFrequencyBandName FrequencyBand { - get { return base.GetField(FrequencyBandField); } - set { base.SetField(FrequencyBandField, value); } + get { return StacPropertiesContainer.GetProperty(FrequencyBandField); } + set { StacPropertiesContainer.SetProperty(FrequencyBandField, value); } } public double CenterFrequency { - get { return base.GetField(CenterFrequencyField); } - set { base.SetField(CenterFrequencyField, value); } + get { return StacPropertiesContainer.GetProperty(CenterFrequencyField); } + set { StacPropertiesContainer.SetProperty(CenterFrequencyField, value); } } public string[] Polarizations { - get { return base.GetField(PolarizationsField); } - set { base.SetField(PolarizationsField, value); } + get { return StacPropertiesContainer.GetProperty(PolarizationsField); } + set { StacPropertiesContainer.SetProperty(PolarizationsField, value); } } public string ProductType { - get { return base.GetField(ProductTypeField); } - set { base.SetField(ProductTypeField, value); } + get { return StacPropertiesContainer.GetProperty(ProductTypeField); } + set { StacPropertiesContainer.SetProperty(ProductTypeField, value); } } public double ResolutionRange { - get { return base.GetField(ResolutionRangeField); } - set { base.SetField(ResolutionRangeField, value); } + get { return StacPropertiesContainer.GetProperty(ResolutionRangeField); } + set { StacPropertiesContainer.SetProperty(ResolutionRangeField, value); } } public double ResolutionAzimuth { - get { return base.GetField(ResolutionAzimuthField); } - set { base.SetField(ResolutionAzimuthField, value); } + get { return StacPropertiesContainer.GetProperty(ResolutionAzimuthField); } + set { StacPropertiesContainer.SetProperty(ResolutionAzimuthField, value); } } public double PixelSpacingRange { - get { return base.GetField(PixelSpacingRangeField); } - set { base.SetField(PixelSpacingRangeField, value); } + get { return StacPropertiesContainer.GetProperty(PixelSpacingRangeField); } + set { StacPropertiesContainer.SetProperty(PixelSpacingRangeField, value); } } public double PixelSpacingAzimuth { - get { return base.GetField(PixelSpacingAzimuthField); } - set { base.SetField(PixelSpacingAzimuthField, value); } + get { return StacPropertiesContainer.GetProperty(PixelSpacingAzimuthField); } + set { StacPropertiesContainer.SetProperty(PixelSpacingAzimuthField, value); } } public double LooksRange { - get { return base.GetField(LooksRangeField); } - set { base.SetField(LooksRangeField, value); } + get { return StacPropertiesContainer.GetProperty(LooksRangeField); } + set { StacPropertiesContainer.SetProperty(LooksRangeField, value); } } public double LooksAzimuth { - get { return base.GetField(LooksAzimuthField); } - set { base.SetField(LooksAzimuthField, value); } + get { return StacPropertiesContainer.GetProperty(LooksAzimuthField); } + set { StacPropertiesContainer.SetProperty(LooksAzimuthField, value); } } public double LooksEquivalentNumber { - get { return base.GetField(LooksEquivalentNumberField); } - set { base.SetField(LooksEquivalentNumberField, value); } + get { return StacPropertiesContainer.GetProperty(LooksEquivalentNumberField); } + set { StacPropertiesContainer.SetProperty(LooksEquivalentNumberField, value); } } public string ObservationDirection { - get { return base.GetField(ObservationDirectionField); } - set { base.SetField(ObservationDirectionField, value); } + get { return StacPropertiesContainer.GetProperty(ObservationDirectionField); } + set { StacPropertiesContainer.SetProperty(ObservationDirectionField, value); } } + } - public static string[] GetAssetPolarization(StacAsset stacAsset) + public static class SarStacExtensionExtensions + { + public static SarStacExtension SarExtension(this StacItem stacItem) { - string key = Prefix + ":" + PolarizationsField; - if (stacAsset.Properties.ContainsKey(key)) - return (string[])stacAsset.Properties[key]; - return null; + return new SarStacExtension(stacItem); } - public StacAsset GetAsset(string polarization) + public static SarStacExtension SarExtension(this StacAsset stacAsset) { - StacItem item = StacObject as StacItem; - if (item == null) return null; - return item.Assets.Values.FirstOrDefault(a => GetAssetPolarization(a).Contains(polarization)); + return new SarStacExtension(stacAsset); } - public static void SetAssetBandObjects(StacAsset stacAsset, string[] polarizations) + public static StacAsset GetAsset(this StacItem stacItem, string polarization) { - string key = Prefix + ":" + PolarizationsField; - if (stacAsset.Properties.ContainsKey(key)) - stacAsset.Properties.Remove(key); - stacAsset.Properties.Add(key, polarizations); + return stacItem.Assets.Values.FirstOrDefault(a => a.SarExtension().Polarizations.Contains(polarization)); } + public static void Required(this SarStacExtension sarStacExtension, + string instrumentMode, + SarCommonFrequencyBandName frequencyBandName, + string[] polarizations, + string productType) + { + sarStacExtension.InstrumentMode = instrumentMode; + sarStacExtension.FrequencyBand = frequencyBandName; + sarStacExtension.Polarizations = polarizations; + sarStacExtension.ProductType = productType; + } } + + } diff --git a/src/DotNetStac/Extensions/Sar/SarStacExtensionExtensions.cs b/src/DotNetStac/Extensions/Sar/SarStacExtensionExtensions.cs deleted file mode 100644 index ff224809..00000000 --- a/src/DotNetStac/Extensions/Sar/SarStacExtensionExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Stac.Extensions.Sar -{ - public static class SarStacExtensionExtensions - { - - public static string[] GetPolarizations(this StacAsset stacAsset) - { - string key = Sar.SarStacExtension.Prefix + ":" + Sar.SarStacExtension.PolarizationsField; - if (stacAsset.Properties.ContainsKey(key)) - return (string[])stacAsset.Properties[key]; - return null; - } - - public static void SetEoBandObjects(this StacAsset stacAsset, string[] eoBandObjects) - { - string key = Sar.SarStacExtension.Prefix + ":" + Sar.SarStacExtension.PolarizationsField; - if (stacAsset.Properties.ContainsKey(key)) - stacAsset.Properties.Remove(key); - stacAsset.Properties.Add(key, eoBandObjects); - } - - } -} diff --git a/src/DotNetStac/Extensions/Sat/SatStacExtension.cs b/src/DotNetStac/Extensions/Sat/SatStacExtension.cs index bd03d9d0..f16879ef 100644 --- a/src/DotNetStac/Extensions/Sat/SatStacExtension.cs +++ b/src/DotNetStac/Extensions/Sat/SatStacExtension.cs @@ -2,15 +2,12 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; -using Stac; -using Stac.Extensions; -using Stac.Item; namespace Stac.Extensions.Sat { - public class SatStacExtension : AssignableStacExtension, IStacExtension + public class SatStacExtension : StacPropertiesContainerExtension, IStacExtension { - public const string Prefix = "sat"; + public const string JsonSchemaUrl = "https://stac-extensions.github.io/sat/v1.0.0/schema.json"; public const string OrbitStateVectorField = "orbit_state_vectors"; public const string AscendingNodeCrossingDateTimeField = "anx_datetime"; public const string SceneCenterCoordinatesField = "scene_center_coordinates"; @@ -20,6 +17,10 @@ public class SatStacExtension : AssignableStacExtension, IStacExtension public const string PlatformInternationalDesignatorField = "platform_international_designator"; + public SatStacExtension(IStacObject stacObject) : base(JsonSchemaUrl, "sat", stacObject) + { + } + public SortedDictionary OrbitStateVectors { get @@ -73,16 +74,14 @@ public string OrbitState private SortedDictionary SortOrbitStateVectors(JToken osvarray) { if (!(osvarray is JArray)) - throw new FormatException(string.Format("[{0}] field {1}: not an array", Id, OrbitStateVectorField)); + throw new FormatException(string.Format("[{0}] field {1}: not an array", FieldNamePrefix, OrbitStateVectorField)); var osvlist = osvarray.ToObject>(); return new SortedDictionary(osvlist.ToDictionary(osv => osv.Time, osv => osv)); } - public SatStacExtension(IStacObject stacObject) : base(Prefix, stacObject) - { - } + } } diff --git a/src/DotNetStac/Extensions/Sat/SatStacExtensionHelpers.cs b/src/DotNetStac/Extensions/Sat/SatStacExtensionHelpers.cs index 61b8cc71..c9b8e706 100644 --- a/src/DotNetStac/Extensions/Sat/SatStacExtensionHelpers.cs +++ b/src/DotNetStac/Extensions/Sat/SatStacExtensionHelpers.cs @@ -11,13 +11,13 @@ public static class SatStacExtensionHelpers { public static BaselineVector CalculateBaseline(this SatStacExtension sat1, SatStacExtension sat2) { - if (sat1.StacObject.IsCatalog) - throw new OperationCanceledException(string.Format("{0} must be an item to calculate baseline", sat1.StacObject.Id)); - if (sat2.StacObject.IsCatalog) - throw new OperationCanceledException(string.Format("{0} must be an item to calculate baseline", sat1.StacObject.Id)); + if (!(sat1.StacItem is StacItem)) + throw new OperationCanceledException(string.Format("{0} must be an item to calculate baseline", sat1.StacItem.Id)); + if (!(sat2.StacItem is StacItem)) + throw new OperationCanceledException(string.Format("{0} must be an item to calculate baseline", sat1.StacItem.Id)); - IStacItem masterItem = sat1.StacObject as IStacItem; - IStacItem slaveItem = sat2.StacObject as IStacItem; + StacItem masterItem = sat1.StacItem as StacItem; + StacItem slaveItem = sat2.StacItem as StacItem; if (sat1.OrbitStateVectors.Count() == 0) throw new OperationCanceledException("sat1 has no orbit state vectors"); diff --git a/src/DotNetStac/Extensions/SchemaBasedStacExtension.cs b/src/DotNetStac/Extensions/SchemaBasedStacExtension.cs new file mode 100644 index 00000000..f56e2222 --- /dev/null +++ b/src/DotNetStac/Extensions/SchemaBasedStacExtension.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Stac.Exceptions; + +namespace Stac.Extensions +{ + public class SchemaBasedStacExtension : StacPropertiesContainerExtension, IStacExtension + { + private readonly IStacObject stacObject; + + public SchemaBasedStacExtension(JSchema jsonSchema, + IStacObject stacObject) : base(jsonSchema.Id.ToString(), + null, + stacObject) + { + Preconditions.CheckNotNull(jsonSchema, "jsonSchema"); + JsonSchema = jsonSchema; + this.stacObject = stacObject; + } + + public SchemaBasedStacExtension(Uri schemaUri, + StacSchemaResolver stacSchemaResolver, + IStacObject stacObject) : base(schemaUri.ToString(), + null, + stacObject) + { + Preconditions.CheckNotNull(schemaUri, "schemaUri"); + this.stacObject = stacObject; + JsonSchema = stacSchemaResolver.LoadSchema(schemaUri.ToString()); + } + + + public static SchemaBasedStacExtension Create(string shortcut, + StacSchemaResolver stacSchemaResolver, + IStacObject stacObject) + { + if ( StacSchemaResolver.CoreTypes.Contains(shortcut) ) + throw new Exceptions.InvalidStacSchemaException(shortcut + "is not an extension"); + var jsonSchema = stacSchemaResolver.LoadSchema(version: stacObject.StacVersion.ToString(), shortcut: shortcut); + return new SchemaBasedStacExtension(jsonSchema, stacObject); + } + + public override string Identifier => JsonSchema.Id.ToString(); + + public JSchema JsonSchema { get; } + + } +} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/StacAssetExtension.cs b/src/DotNetStac/Extensions/StacAssetExtension.cs new file mode 100644 index 00000000..3128ee35 --- /dev/null +++ b/src/DotNetStac/Extensions/StacAssetExtension.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Stac.Exceptions; + +namespace Stac.Extensions +{ + public abstract class StacAssetExtension : IStacExtension + { + private readonly string identifier; + + public StacAssetExtension(string identifier, StacAsset stacAsset) + { + this.identifier = identifier; + StacAsset = stacAsset; + } + + public virtual string Identifier => identifier; + + public StacAsset StacAsset { get; private set; } + + } +} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/StacExtensions.cs b/src/DotNetStac/Extensions/StacExtensions.cs deleted file mode 100644 index 334bb07e..00000000 --- a/src/DotNetStac/Extensions/StacExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using Stac.Item; - -namespace Stac.Extensions -{ - public class StacExtensions : Dictionary, IEnumerable - { - public StacExtensions(IEnumerable stacExtensions = null) - { - if (stacExtensions != null) - { - foreach (var stacExtension in stacExtensions) - { - Add(stacExtension); - } - } - } - - public void InitStacObject(IStacObject stacObject) - { - foreach (var stacExtension in Values.OfType().ToList()) - { - stacExtension.InitStacObject(stacObject); - } - } - - public void Add(IStacExtension extension) - { - Add(extension.Id, extension); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this.Values.GetEnumerator(); - } - } -} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/StacExtensionsConverter.cs b/src/DotNetStac/Extensions/StacExtensionsConverter.cs deleted file mode 100644 index 407a926e..00000000 --- a/src/DotNetStac/Extensions/StacExtensionsConverter.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Stac -{ - internal class StacExtensionsConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(IEnumerable)); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - Dictionary summaries = new Dictionary(); - Dictionary objDic = serializer.Deserialize>(reader); - - foreach (var key in objDic.Keys) - { - if (objDic[key] is JArray) - { - JArray enumerable = (objDic[key] as JArray); - switch (enumerable.First().Type) - { - case JTokenType.Boolean: - summaries.Add(key, new StacSummaryValueSet(enumerable)); - break; - case JTokenType.Date: - summaries.Add(key, new StacSummaryValueSet(enumerable)); - break; - case JTokenType.String: - summaries.Add(key, new StacSummaryValueSet(enumerable)); - break; - case JTokenType.Integer: - summaries.Add(key, new StacSummaryValueSet(enumerable)); - break; - case JTokenType.Float: - summaries.Add(key, new StacSummaryValueSet(enumerable)); - break; - case JTokenType.Object: - summaries.Add(key, new StacSummaryValueSet(enumerable)); - break; - } - } - if (objDic[key] is JObject) - { - JObject obj = (objDic[key] as JObject); - if (obj.ContainsKey("min") && obj.ContainsKey("max")) - { - switch (obj["min"].Type) - { - case JTokenType.Date: - summaries.Add(key, new StacSummaryStatsObject(obj)); - break; - case JTokenType.String: - summaries.Add(key, new StacSummaryStatsObject(obj)); - break; - case JTokenType.Integer: - summaries.Add(key, new StacSummaryStatsObject(obj)); - break; - case JTokenType.Float: - summaries.Add(key, new StacSummaryStatsObject(obj)); - break; - } - } - } - } - - return summaries; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - Dictionary summaries = (Dictionary)value; - - serializer.Serialize(writer, summaries.ToDictionary(k => k.Key, k => k.Value.AsJToken)); - } - } -} -} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/StacExtensionsFactory.cs b/src/DotNetStac/Extensions/StacExtensionsFactory.cs deleted file mode 100644 index 91f1196b..00000000 --- a/src/DotNetStac/Extensions/StacExtensionsFactory.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Stac.Extensions -{ - public class StacExtensionsFactory : IStacExtensionsFactory - { - - Dictionary stacExtensionsDictionary = new Dictionary(); - - public StacExtensionsFactory(Dictionary stacExtensions) - { - this.stacExtensionsDictionary = stacExtensions; - } - - internal StacExtensionsFactory() - { - this.stacExtensionsDictionary.Add("sat", typeof(Sat.SatStacExtension)); - this.stacExtensionsDictionary.Add("eo", typeof(Eo.EoStacExtension)); - this.stacExtensionsDictionary.Add("view", typeof(View.ViewStacExtension)); - this.stacExtensionsDictionary.Add("proj", typeof(Projection.ProjectionStacExtension)); - this.stacExtensionsDictionary.Add("sar", typeof(Sar.SarStacExtension)); - } - - public static StacExtensionsFactory @default; - - public static StacExtensionsFactory Default - { - get - { - if (@default == null) - @default = StacExtensionsFactory.CreateDefaultFactory(); - return @default; - } - } - - public Dictionary StacExtensionsDictionary => stacExtensionsDictionary; - - public static StacExtensionsFactory CreateDefaultFactory() - { - return new StacExtensionsFactory(); - } - - public IStacExtension InitStacExtension(string prefix, IStacObject stacObject) - { - if (stacExtensionsDictionary.ContainsKey(prefix)) - { - var ctor = stacExtensionsDictionary[prefix].GetConstructor(new Type[1] { typeof(IStacObject) }); - if (ctor == null) return null; - return (IStacExtension)ctor.Invoke(new object[1] { stacObject }); - } - - return new GenericStacExtension(prefix, stacObject); - } - - public StacExtensions LoadStacExtensions(IEnumerable extensionPrefixes, IStacObject stacObject) - { - StacExtensions stacExtensions = new StacExtensions(); - if (extensionPrefixes != null) - { - foreach (var extensionPrefix in extensionPrefixes) - { - var stacExtension = InitStacExtension(extensionPrefix, stacObject); - if (stacExtension != null) - stacExtensions.Add(stacExtension); - } - } - return stacExtensions; - } - - } -} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/StacPropertiesContainerExtension.cs b/src/DotNetStac/Extensions/StacPropertiesContainerExtension.cs new file mode 100644 index 00000000..38c0f9b6 --- /dev/null +++ b/src/DotNetStac/Extensions/StacPropertiesContainerExtension.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Stac.Exceptions; + +namespace Stac.Extensions +{ + public abstract class StacPropertiesContainerExtension : IStacExtension + { + private readonly string identifier; + + public StacPropertiesContainerExtension(string identifier, IStacPropertiesContainer stacPropertiesContainer) + { + this.identifier = identifier; + StacPropertiesContainer = stacPropertiesContainer; + } + + public virtual string Identifier => identifier; + + public IStacPropertiesContainer StacPropertiesContainer { get; private set; } + + public abstract IDictionary ItemFields { get; } + } +} \ No newline at end of file diff --git a/src/DotNetStac/Extensions/View/ViewStacExtension.cs b/src/DotNetStac/Extensions/View/ViewStacExtension.cs index fbe902f1..3366ce2b 100644 --- a/src/DotNetStac/Extensions/View/ViewStacExtension.cs +++ b/src/DotNetStac/Extensions/View/ViewStacExtension.cs @@ -1,24 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json.Linq; -using Stac; -using Stac.Extensions; -using Stac.Item; - namespace Stac.Extensions.View { - public class ViewStacExtension : AssignableStacExtension, IStacExtension + public class ViewStacExtension : StacPropertiesContainerExtension, IStacExtension { + public const string JsonSchemaUrl = "https://stac-extensions.github.io/sat/v1.0.0/schema.json"; public static string OffNadirField => "off_nadir"; public static string IncidenceAngleField => "incidence_angle"; public static string AzimuthField => "azimuth"; public static string SunAzimuthField => "sun_azimuth"; public static string SunElevationField => "sun_elevation"; - - public ViewStacExtension(IStacObject stacObject) : base("view", stacObject) + public ViewStacExtension(IStacObject stacObject) : base(JsonSchemaUrl, "view", stacObject) { } diff --git a/src/DotNetStac/IStacExtensionAssignable.cs b/src/DotNetStac/IStacExtensionAssignable.cs deleted file mode 100644 index 427037eb..00000000 --- a/src/DotNetStac/IStacExtensionAssignable.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Stac.Extensions; - -namespace Stac -{ - public interface IStacExtensionAssignable - { - StacExtensions StacExtensions { get; } - } -} \ No newline at end of file diff --git a/src/DotNetStac/IStacObject.cs b/src/DotNetStac/IStacObject.cs index 962efc26..3a24f7cf 100644 --- a/src/DotNetStac/IStacObject.cs +++ b/src/DotNetStac/IStacObject.cs @@ -11,7 +11,7 @@ namespace Stac /// /// Common interface for all Stac objects /// - public interface IStacObject : IStacPropertiesContainer, IStacExtensionAssignable + public interface IStacObject : IStacPropertiesContainer { string Id { get; } @@ -19,5 +19,8 @@ public interface IStacObject : IStacPropertiesContainer, IStacExtensionAssignabl Collection Links { get; } + ContentType MediaType { get; } + + Collection StacExtensions { get; } } } \ No newline at end of file diff --git a/src/DotNetStac/IStacParent.cs b/src/DotNetStac/IStacParent.cs index 2aec02bc..f3ea20ac 100644 --- a/src/DotNetStac/IStacParent.cs +++ b/src/DotNetStac/IStacParent.cs @@ -12,6 +12,6 @@ namespace Stac /// public interface IStacParent : IStacObject { - + } } \ No newline at end of file diff --git a/src/DotNetStac/Item/StacItemExtensions.cs b/src/DotNetStac/Item/StacItemExtensions.cs deleted file mode 100644 index d58d9cb6..00000000 --- a/src/DotNetStac/Item/StacItemExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Stac; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Stac.Extensions; - -namespace Stac.Item -{ - public partial class StacItem : IStacItem, IStacObject - { - - public static async Task LoadUri(Uri uri) - { - var catalog = await StacFactory.LoadUriAsync(uri); - if (catalog is IStacItem) - return (IStacItem)catalog; - throw new InvalidOperationException(string.Format("This is not a STAC item {0}", catalog.Uri)); - } - - public static async Task LoadStacLink(StacLink link) - { - var catalog = await link.LoadAsync(); - if (catalog is IStacItem) - return (IStacItem)catalog; - throw new InvalidOperationException(string.Format("This is not a STAC item {0}", catalog.Uri)); - } - - public static IStacItem LoadJToken(JToken jsonRoot, Uri uri) - { - IStacItem item = LoadStacItem(jsonRoot); - ((IInternalStacObject)item).Uri = uri; - return item; - } - - - - - - } -} diff --git a/src/DotNetStac/Preconditions.cs b/src/DotNetStac/Preconditions.cs index 2e69944a..e1d343ae 100644 --- a/src/DotNetStac/Preconditions.cs +++ b/src/DotNetStac/Preconditions.cs @@ -6,11 +6,11 @@ namespace Stac { public static class Preconditions { - public static T CheckNotNull(T value) where T : class + public static T CheckNotNull(T value, string argName = null) where T : class { if (value == null) { - throw new ArgumentNullException(); + throw new ArgumentNullException(argName); } return value; } diff --git a/src/DotNetStac/Schemas/StacSchemaResolver.cs b/src/DotNetStac/Schemas/StacSchemaResolver.cs new file mode 100644 index 00000000..34d8f04c --- /dev/null +++ b/src/DotNetStac/Schemas/StacSchemaResolver.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Schema; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Stac.Extensions +{ + public class StacSchemaResolver + { + private readonly JSchemaResolver jSchemaResolver; + private IDictionary schemaCompiled; + + public static string[] CoreTypes = new string[] { "item", "catalog", "collection" }; + + public StacSchemaResolver(JSchemaResolver jSchemaResolver) + { + this.jSchemaResolver = jSchemaResolver; + this.schemaCompiled = new Dictionary(); + } + + private static IDictionary schemaMap = new Dictionary(); + + public JSchema LoadSchema(string baseUrl = null, string version = null, string shortcut = null) + { + string vversion = string.IsNullOrEmpty(version) ? "unversioned" : "v" + version; + Uri baseUri = null; + if (string.IsNullOrEmpty(baseUrl)) + { + if (CoreTypes.Contains(shortcut)) + baseUri = new Uri($"https://schemas.stacspec.org/{vversion}/"); + else + baseUri = new Uri($"https://schemas.stacspec.org/"); + } + else + baseUri = new Uri(baseUrl); + + Uri schemaUri = null; + bool isExtension = false; + if (shortcut == "item" || shortcut == "catalog" || shortcut == "collection") + schemaUri = new Uri(baseUri, $"{shortcut}-spec/json-schema/{shortcut}.json"); + else if (!string.IsNullOrEmpty(shortcut)) + { + if (shortcut == "proj") + { + // Capture a very common mistake and give a better explanation (see #4) + throw new Exception("'stac_extensions' must contain 'projection instead of 'proj'."); + } + schemaUri = new Uri(baseUri, $"extensions/{shortcut}/json-schema/schema.json`"); + isExtension = true; + } + else + { + schemaUri = baseUri; + } + + if (!string.IsNullOrEmpty(baseUrl) && schemaMap.ContainsKey(baseUrl)) + { + schemaUri = schemaMap[baseUrl]; + } + + if (schemaCompiled.ContainsKey(schemaUri.ToString())) + { + return schemaCompiled[schemaUri.ToString()]; + } + else + { + var stream = jSchemaResolver.GetSchemaResource(null, new SchemaReference() { BaseUri = schemaUri } ); + var sr = new StreamReader(stream); + schemaCompiled[schemaUri.ToString()] = JSchema.Parse(sr.ReadToEnd(), jSchemaResolver); + return schemaCompiled[schemaUri.ToString()]; + } + } + } +} \ No newline at end of file diff --git a/src/DotNetStac/StacAccessorsHelpers.cs b/src/DotNetStac/StacAccessorsHelpers.cs index b22d5a3d..ff4f17f9 100644 --- a/src/DotNetStac/StacAccessorsHelpers.cs +++ b/src/DotNetStac/StacAccessorsHelpers.cs @@ -13,9 +13,9 @@ public static void SetProperty(this IStacObject stacObject, string key, object v stacObject.Properties.SetProperty(key, value); } - public static void SetProperty(this StacAsset stacAsset, string key, object value) + public static void SetProperty(this IStacPropertiesContainer stacPropertiesContainer, string key, object value) { - stacAsset.Properties.SetProperty(key, value); + stacPropertiesContainer.Properties.SetProperty(key, value); } public static void SetProperty(this IDictionary properties, string key, object value) @@ -24,24 +24,14 @@ public static void SetProperty(this IDictionary properties, stri properties.Add(key, value); } - public static object GetProperty(this IStacObject stacObject, string key) + public static object GetProperty(this IStacPropertiesContainer propertiesContainer, string key) { - return stacObject.Properties.GetProperty(key); + return propertiesContainer.Properties.GetProperty(key); } - public static T GetProperty(this IStacObject stacObject, string key) + public static T GetProperty(this IStacPropertiesContainer propertiesContainer, string key) { - return stacObject.Properties.GetProperty(key); - } - - public static object GetProperty(this StacAsset stacAsset, string key) - { - return stacAsset.Properties.GetProperty(key); - } - - public static T GetProperty(this StacAsset stacAsset, string key) - { - return stacAsset.Properties.GetProperty(key); + return propertiesContainer.Properties.GetProperty(key); } public static object GetProperty(this IDictionary properties, string key) diff --git a/src/DotNetStac/StacCatalog.cs b/src/DotNetStac/StacCatalog.cs index 263a44f6..cc09978f 100644 --- a/src/DotNetStac/StacCatalog.cs +++ b/src/DotNetStac/StacCatalog.cs @@ -6,6 +6,7 @@ using Stac.Extensions; using System.Net.Mime; using Semver; +using System.Runtime.Serialization; namespace Stac { @@ -44,7 +45,7 @@ public StacCatalog(string id, string description, IEnumerable links = this.Links = new Collection(links.ToList()); this.Properties = new Dictionary(); this.Summaries = new Dictionary(); - this.StacExtensions = new StacExtensions(); + this.StacExtensions = new StacExtensions(this); } # region IStacObject @@ -82,6 +83,9 @@ public Collection Links get; internal set; } + [JsonIgnore] + public ContentType MediaType => CATALOG_MEDIATYPE; + # endregion IStacObject /// @@ -118,5 +122,15 @@ public Collection Links [JsonExtensionData] public IDictionary Properties { get; internal set; } + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + foreach (StacLink link in Links) + { + link.Parent = this; + } + StacExtensions.stacObject = this; + } + } } diff --git a/src/DotNetStac/StacCollection.cs b/src/DotNetStac/StacCollection.cs index d92dafa9..29f8aa83 100644 --- a/src/DotNetStac/StacCollection.cs +++ b/src/DotNetStac/StacCollection.cs @@ -6,8 +6,10 @@ using Stac.Extensions; using Semver; using System.Linq; +using System.Runtime.Serialization; +using Stac.Collection; -namespace Stac.Collection +namespace Stac { /// /// STAC Collection Object implementing STAC Collection spec (https://github.com/radiantearth/stac-spec/blob/master/collection-spec/collection-spec.md) @@ -40,7 +42,7 @@ public StacCollection(string id, else this.Assets = new Dictionary(assets); this.Summaries = new Dictionary(); - this.StacExtensions = new StacExtensions(); + this.StacExtensions = new StacExtensions(this); this.License = license; this.Keywords = new Collection(); this.Extent = extent; @@ -81,6 +83,9 @@ public Collection Links get; internal set; } + [JsonIgnore] + public ContentType MediaType => COLLECTION_MEDIATYPE; + # endregion IStacObject /// @@ -149,5 +154,14 @@ public Collection Links [JsonProperty("assets")] public IDictionary Assets { get; internal set; } + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + foreach (StacLink link in Links) + { + link.Parent = this; + } + StacExtensions.stacObject = this; + } } } diff --git a/src/DotNetStac/StacExtensionsExtensions.cs b/src/DotNetStac/StacExtensionsExtensions.cs new file mode 100644 index 00000000..316c350c --- /dev/null +++ b/src/DotNetStac/StacExtensionsExtensions.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json.Schema; +using Stac.Exceptions; + +namespace Stac.Extensions +{ + public static class StacExtensionsExtensions + { + public static void AddExtension(this IStacObject stacObject, JSchema jsonSchema) + { + if ( stacObject.StacExtensions.ContainsKey(jsonSchema.Id.ToString())) + throw new DuplicateKeyException(jsonSchema.Id.ToString()); + } + } +} \ No newline at end of file diff --git a/src/DotNetStac/StacGeometryHelpers.cs b/src/DotNetStac/StacGeometryHelpers.cs index f2869b29..3c2f8d16 100644 --- a/src/DotNetStac/StacGeometryHelpers.cs +++ b/src/DotNetStac/StacGeometryHelpers.cs @@ -1,15 +1,13 @@ -using System.Collections; using System.Linq; using GeoJSON.Net; using GeoJSON.Net.Geometry; -using Stac.Item; namespace Stac { public static class StacGeometryHelpers { - public static double[] GetBoundingBoxFromGeometryExtent(this IStacItem stacItem) + public static double[] GetBoundingBoxFromGeometryExtent(this StacItem stacItem) { var boundingBoxes = stacItem.Geometry.GetBoundingBox(); if (boundingBoxes[0].Altitude.HasValue) diff --git a/src/DotNetStac/StacItem.cs b/src/DotNetStac/StacItem.cs index 9d380f69..afb184f1 100644 --- a/src/DotNetStac/StacItem.cs +++ b/src/DotNetStac/StacItem.cs @@ -10,131 +10,78 @@ using System.Linq; using System.Net.Mime; using Newtonsoft.Json.Linq; +using Semver; namespace Stac { [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] - public class StacItem : GeoJSON.Net.Feature.Feature, IStacObject, IInternalStacObject, IStacItem + public class StacItem : GeoJSON.Net.Feature.Feature, IStacObject { public const string MEDIATYPE = "application/json; profile=stac-item"; public readonly static ContentType ITEM_MEDIATYPE = new ContentType(MEDIATYPE); - private Collection links; - - private Dictionary assets; - - private string stacVersion = StacVersionList.Current; - - private string collection; - - private Uri sourceUri; - - private string[] stacExtensionsStrings = new string[0]; - [JsonConstructor] - public StacItem(IGeometryObject geometry, IDictionary properties = null, string id = null) : base(Preconditions.CheckNotNull(geometry), properties, id) + public StacItem(string id, + IGeometryObject geometry, + IDictionary properties = null) : + base(Preconditions.CheckNotNull(geometry, "geometry"), properties, id) { - if (geometry == null) throw new ArgumentNullException("geometry"); + Preconditions.CheckNotNull(id, "id"); + StacExtensions = new Collection(); + StacVersion = Versions.StacVersionList.Current; + Links = new Collection(); + Assets = new Dictionary(); } - public StacItem(IGeometryObject geometry, object properties, string id = null) : base(Preconditions.CheckNotNull(geometry), properties, id) + public StacItem(StacItem stacItem) : base(Preconditions.CheckNotNull(stacItem, "geometry").Geometry, + new Dictionary(Preconditions.CheckNotNull(stacItem).Properties), + Preconditions.CheckNotNull(stacItem, "id").Id) { - if (geometry == null) throw new ArgumentNullException("geometry"); - if (properties == null) throw new ArgumentNullException("properties"); + this.StacExtensions = stacItem.StacExtensions; + this.StacVersion = stacItem.StacVersion; + this.Links = new Collection(stacItem.Links); + this.Assets = new Dictionary(stacItem.Assets); + this.Collection = stacItem.Collection; } - public StacItem(StacItem stacItem) : this(Preconditions.CheckNotNull(stacItem).Geometry, - new Dictionary(Preconditions.CheckNotNull(stacItem).Properties), - Preconditions.CheckNotNull(stacItem).Id) - { - this.stacExtensionsStrings = stacItem.stacExtensionsStrings; - this.stacVersion = stacItem.stacVersion; - this.links = new Collection(stacItem.Links); - this.assets = new Dictionary(stacItem.Assets); - this.collection = stacItem.collection; - this.sourceUri = stacItem.sourceUri; - this.extensions = new StacExtensions(stacItem.StacExtensions); - } - - private static IStacItem LoadStacItem(JToken jsonRoot) - { - Type itemType = null; - if (jsonRoot["stac_version"] == null) - { - throw new InvalidStacDataException("The document is not a valid STAC document. No 'stac_version' property found"); - } - - try - { - itemType = Stac.Model.SchemaDictionary.GetItemTypeFromVersion(jsonRoot["stac_version"].Value()); - } - catch (KeyNotFoundException) - { - throw new NotSupportedException(string.Format("The document has a non supprted version: '{0}'.", jsonRoot["stac_version"].Value())); - } - - return (IStacItem)jsonRoot.ToObject(itemType); - } - - [JsonProperty("stac_extensions")] - public string[] StacExtensionsStrings { get => stacExtensionsStrings; set => stacExtensionsStrings = value; } - + # region IStacObject + /// + /// The STAC version the Item implements + /// + /// [JsonProperty("stac_version")] - public string StacVersion - { - get - { - return stacVersion; - } + public SemVersion StacVersion { get; set; } - set - { - stacVersion = value; - } - } + /// + /// A list of extension identifiers the Item implements + /// + /// + [JsonProperty("stac_extensions")] + [JsonConverter(typeof(StacExtensionsConverter))] + public Collection StacExtensions { get; private set; } + /// + /// A list of references to other documents. + /// + /// [JsonConverter(typeof(CollectionConverter))] [JsonProperty("links")] public Collection Links { - get - { - if (links == null) - links = new Collection(); - return links; - } - set - { - links = value; - } + get; private set; } - [JsonProperty("assets")] - public IDictionary Assets - { - get - { - if (assets == null) - assets = new Dictionary(); - return assets; - } - } + [JsonIgnore] + public ContentType MediaType => ITEM_MEDIATYPE; - [JsonProperty("collection")] - public string Collection - { - get - { - return collection; - } - set - { - collection = value; - } - } + # endregion IStacObject + [JsonProperty("assets")] + public IDictionary Assets { get; private set; } + [JsonProperty("collection")] + public string Collection { get; internal set; } [OnDeserialized] internal void OnDeserializedMethod(StreamingContext context) @@ -143,7 +90,7 @@ internal void OnDeserializedMethod(StreamingContext context) { link.Parent = this; } - StacExtensions = StacExtensionsFactory.Default.LoadStacExtensions(StacExtensionsStrings.ToList(), this); + StacExtensions.ResolveExtensions(this); } [OnSerializing] @@ -151,29 +98,6 @@ internal void OnSerializingMethod(StreamingContext context) { if (BoundingBoxes == null) BoundingBoxes = this.GetBoundingBoxFromGeometryExtent(); - StacExtensionsStrings = StacExtensionsStrings.Concat(StacExtensions.Keys).Distinct().ToArray(); - } - - [JsonIgnore] - public ContentType MediaType => ITEM_MEDIATYPE; - - [JsonIgnore] - public Uri Uri - { - get - { - if (sourceUri == null) - { - return new Uri(Id + ".json", UriKind.Relative); - } - return sourceUri; - } - set { sourceUri = value; } - } - - public IStacObject Upgrade() - { - return this; } [JsonIgnore] @@ -243,27 +167,6 @@ public Itenso.TimePeriod.ITimePeriod DateTime } } - private StacExtensions extensions; - - [JsonIgnore] - public StacExtensions StacExtensions - { - get - { - if (extensions == null) - { - extensions = new StacExtensions(); - extensions.InitStacObject(this); - } - return extensions; - } - set - { - extensions = value; - extensions.InitStacObject(this); - } - } - [JsonIgnore] public string Title { diff --git a/src/DotNetStac/StacLink.cs b/src/DotNetStac/StacLink.cs index 5ac5ab98..518c34ca 100644 --- a/src/DotNetStac/StacLink.cs +++ b/src/DotNetStac/StacLink.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; using System.IO; using System.Net.Mime; using System.Threading.Tasks; using Newtonsoft.Json; -using Stac.Converters; namespace Stac { @@ -60,11 +58,6 @@ public static StacLink CreateItemLink(IStacObject stacObject, IStacObject hostOb #endregion - Uri href; - protected string rel, title; - ContentType type; - protected IStacObject hostObject; - protected readonly ulong contentLength; public StacLink() { @@ -77,8 +70,8 @@ public StacLink(Uri uri) public StacLink(Uri uri, IStacObject hostObject) { - Uri = uri; - this.hostObject = hostObject; + this.Uri = uri; + this.Parent = hostObject; } public StacLink(Uri uri, string relationshipType, string title, string mediaType, ulong contentLength = 0) @@ -86,90 +79,45 @@ public StacLink(Uri uri, string relationshipType, string title, string mediaType Uri = uri; RelationshipType = relationshipType; Title = title; - MediaType = mediaType == null ? null : new ContentType(mediaType); - this.contentLength = contentLength; + ContentType = mediaType == null ? null : new ContentType(mediaType); + this.Length = contentLength; } public StacLink(StacLink source) { if (source == null) throw new ArgumentNullException("source"); - href = source.href; - rel = source.rel; - title = source.title; - type = source.type; - hostObject = source.hostObject; - contentLength = source.Length; + Uri = source.Uri; + RelationshipType = source.RelationshipType; + Title = source.Title; + ContentType = source.ContentType; + Parent = source.Parent; + Length = source.Length; } [JsonProperty("type")] [JsonConverter(typeof(ContentTypeConverter))] - public virtual ContentType MediaType - { - get { return type; } - set { type = value; } - } + public virtual ContentType ContentType { get; set; } [JsonProperty("rel")] - public virtual string RelationshipType - { - get { return rel; } - set { rel = value; } - } + public virtual string RelationshipType { get; set; } [JsonProperty("title")] - public virtual string Title - { - get { return title; } - set { title = value; } - } + public virtual string Title { get; set; } [JsonProperty("href")] - public virtual Uri Uri - { - get { return href; } - set { href = value; } - } - - [JsonIgnore] - private Uri AbsoluteUri - { - get - { - if (Uri.IsAbsoluteUri) - return Uri; - - if (hostObject != null) - return new Uri(new Uri(hostObject.Uri.AbsoluteUri.Substring(0, hostObject.Uri.AbsoluteUri.LastIndexOf('/') + 1)), Uri); - - return null; - } - } + public virtual Uri Uri { get; set; } [JsonIgnore] - public IStacObject Parent - { - get => hostObject; - internal set - { - hostObject = value; - } - } + public IStacObject Parent { get; set; } [JsonIgnore] - public ulong Length => contentLength; + public ulong Length { get; set; } public virtual StacLink Clone() { return new StacLink(this); } - public virtual async Task LoadAsync() - { - if (AbsoluteUri == null) - throw new FileNotFoundException(string.Format("Cannot load STAC object from link ({0}) : No absolute entry point", Uri)); - return await StacFactory.LoadUriAsync(AbsoluteUri); - } - } } diff --git a/src/DotNetStac/StacNavigationHelpers.cs b/src/DotNetStac/StacNavigationHelpers.cs deleted file mode 100644 index 5b623cd5..00000000 --- a/src/DotNetStac/StacNavigationHelpers.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Runtime.Serialization; -using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Stac.Catalog; -using Stac.Collection; -using Stac.Extensions; -using Stac.Item; -using Stac.Model; - -namespace Stac -{ - public static class StacNavigationHelpers - { - public static void AddChild(this IStacObject stacObject, IStacItem stacItem) - { - stacObject.Links.Add(new StacObjectLink(stacItem, stacObject)); - } - - public static IDictionary GetChildren(this IStacObject stacObject) - { - return GetChildrenAsync(stacObject).GetAwaiter().GetResult(); - } - - public static async Task> GetChildrenAsync(this IStacObject stacObject) - { - Dictionary children = new Dictionary(); - foreach (var link in stacObject.Links.Where(l => l.RelationshipType == "child")) - { - children.Add(link.Uri, (await link.LoadAsync()) as IStacCatalog); - } - return children; - } - - public static IDictionary GetItems(this IStacObject stacObject) - { - return GetItemsAsync(stacObject).GetAwaiter().GetResult(); - } - - public static async Task> GetItemsAsync(this IStacObject stacObject) - { - Dictionary items = new Dictionary(); - foreach (var link in stacObject.Links.Where(l => l.RelationshipType == "item")) - { - items.Add(link.Uri, await StacItem.LoadStacLink(link)); - } - return items; - } - - public static IEnumerable GetChildrenLinks(this IStacObject stacObject, bool absolute = true) - { - return stacObject.Links.Where(l => l.RelationshipType == "child"); - } - - public static IEnumerable GetItemLinks(this IStacObject stacObject, bool absolute = true) - { - return stacObject.Links.Where(l => l.RelationshipType == "item"); - } - - public static StacItem UpgradeToCurrentVersion(this IStacItem item1) - { - IStacObject item = (IStacObject)item1; - while (!(item is Item.StacItem)) - { - item = item.Upgrade(); - } - return (Item.StacItem)item; - } - - public static StacCatalog UpgradeToCurrentVersion(this IStacCatalog catalog1) - { - IStacObject catalog = (IStacObject)catalog1; - while (!(catalog is StacCatalog)) - { - catalog = catalog.Upgrade(); - } - return (StacCatalog)catalog; - } - - public static T GetExtension(this IStacObject stacObject) where T : IStacExtension - { - return (T)stacObject.StacExtensions.Values.FirstOrDefault(e => e is T); - } - } -} diff --git a/src/DotNetStac/StacObjectLink.cs b/src/DotNetStac/StacObjectLink.cs index 6c34e0ec..49a0085c 100644 --- a/src/DotNetStac/StacObjectLink.cs +++ b/src/DotNetStac/StacObjectLink.cs @@ -14,15 +14,15 @@ internal StacObjectLink(IStacObject stacObject, IStacObject hostObject = null) { this.stacObject = stacObject; this.hostObject = hostObject; - if ( stacObject is IStacItem ) + if ( stacObject is StacItem ) this.RelationshipType = "item"; - if ( stacObject is IStacCatalog ) + if ( stacObject is StacCatalog || stacObject is StacCollection ) this.RelationshipType = "child"; } [JsonProperty("type")] [JsonConverter(typeof(ContentTypeConverter))] - public override ContentType MediaType + public override ContentType ContentType { get => stacObject.MediaType; set @@ -58,10 +58,5 @@ public override Uri Uri } } - public override Task LoadAsync() - { - return Task.FromResult(stacObject); - } - } } diff --git a/src/DotNetStac/Utils.cs b/src/DotNetStac/Utils.cs index 853b905e..1a0bc451 100644 --- a/src/DotNetStac/Utils.cs +++ b/src/DotNetStac/Utils.cs @@ -1,8 +1,6 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Stac.Catalog; -using Stac.Collection; namespace Stac {