Skip to content

Commit

Permalink
Enforce coordinate system singleton through interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmada committed Oct 27, 2023
1 parent f8e1ce2 commit e4d60fd
Show file tree
Hide file tree
Showing 26 changed files with 126 additions and 228 deletions.
11 changes: 5 additions & 6 deletions src/NetFabric.Numerics.Geography/Geodetic2/CoordinateSystem.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
using System.Collections.ObjectModel;
using System.Numerics;

namespace NetFabric.Numerics.Geography.Geodetic2;

public readonly record struct CoordinateSystem<TDatum, TAngle>
: IGeodeticCoordinateSystem<TDatum>
public class CoordinateSystem<TDatum, T>
: IGeodeticCoordinateSystem<CoordinateSystem<TDatum, T>, TDatum>
where TDatum : IDatum<TDatum>
where TAngle : struct, IFloatingPoint<TAngle>, IMinMaxValue<TAngle>
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
{
public Datum<TDatum> Datum
=> new();

static readonly IReadOnlyList<Coordinate> coordinates
= new[] {
new Coordinate("Latitude", typeof(Angle<Degrees, TAngle>)),
new Coordinate("Longitude", typeof(Angle<Degrees, TAngle>)),
new Coordinate("Latitude", typeof(Angle<Degrees, T>)),
new Coordinate("Longitude", typeof(Angle<Degrees, T>)),
};

public IReadOnlyList<Coordinate> Coordinates
Expand Down
14 changes: 3 additions & 11 deletions src/NetFabric.Numerics.Geography/Geodetic2/Point.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace NetFabric.Numerics.Geography.Geodetic2;
[System.Diagnostics.DebuggerDisplay("Latitude = {Latitude}, Longitude = {Longitude}")]
[SkipLocalsInit]
public readonly record struct Point<TDatum, TAngleUnits, T>(Angle<TAngleUnits, T> Latitude, Angle<TAngleUnits, T> Longitude)
: IGeodeticPoint<Point<TDatum, TAngleUnits, T>, TDatum>
: IGeodeticPoint<Point<TDatum, TAngleUnits, T>, CoordinateSystem<TDatum, T>, TDatum>
where TDatum : IDatum<TDatum>
where TAngleUnits : struct, IAngleUnits<TAngleUnits>
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
Expand Down Expand Up @@ -74,7 +74,7 @@ public static Point<TDatum, TAngleUnits, T> CreateTruncating<TOther>(in Point<TD
public static readonly Point<TDatum, TAngleUnits, T> Zero
= new(Angle<TAngleUnits, T>.Zero, Angle<TAngleUnits, T>.Zero);

static Point<TDatum, TAngleUnits, T> IGeometricBase<Point<TDatum, TAngleUnits, T>>.Zero
static Point<TDatum, TAngleUnits, T> IGeometricBase<Point<TDatum, TAngleUnits, T>, CoordinateSystem<TDatum, T>>.Zero
=> Zero;

/// <summary>
Expand All @@ -96,15 +96,7 @@ static Point<TDatum, TAngleUnits, T> IMinMaxValue<Point<TDatum, TAngleUnits, T>>

#endregion

/// <summary>
/// Gets the coordinate system.
/// </summary>
public CoordinateSystem<TDatum, T> CoordinateSystem
=> new();
ICoordinateSystem IGeometricBase<Point<TDatum, TAngleUnits, T>>.CoordinateSystem
=> CoordinateSystem;

object IGeometricBase<Point<TDatum, TAngleUnits, T>>.this[int index]
object IGeometricBase<Point<TDatum, TAngleUnits, T>, CoordinateSystem<TDatum, T>>.this[int index]
=> index switch
{
0 => Latitude,
Expand Down
13 changes: 6 additions & 7 deletions src/NetFabric.Numerics.Geography/Geodetic3/CoordinateSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

namespace NetFabric.Numerics.Geography.Geodetic3;

public readonly record struct CoordinateSystem<TDatum, TAngle, THeight>
: IGeodeticCoordinateSystem<TDatum>
public class CoordinateSystem<TDatum, T>
: IGeodeticCoordinateSystem<CoordinateSystem<TDatum, T>, TDatum>
where TDatum : IDatum<TDatum>
where TAngle : struct, IFloatingPoint<TAngle>, IMinMaxValue<TAngle>
where THeight : struct, INumber<THeight>
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
{
public Datum<TDatum> Datum
=> new();

static readonly IReadOnlyList<Coordinate> coordinates
= new[] {
new Coordinate("Latitude", typeof(Angle<Degrees, TAngle>)),
new Coordinate("Longitude", typeof(Angle<Degrees, TAngle>)),
new Coordinate("Height", typeof(THeight)),
new Coordinate("Latitude", typeof(Angle<Degrees, T>)),
new Coordinate("Longitude", typeof(Angle<Degrees, T>)),
new Coordinate("Height", typeof(T)),
};

public IReadOnlyList<Coordinate> Coordinates
Expand Down
113 changes: 49 additions & 64 deletions src/NetFabric.Numerics.Geography/Geodetic3/Point.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,116 +5,101 @@ namespace NetFabric.Numerics.Geography.Geodetic3;

[System.Diagnostics.DebuggerDisplay("Latitude = {Latitude}, Longitude = {Longitude}, Height = {Height}")]
[SkipLocalsInit]
public readonly record struct Point<TDatum, TAngleUnits, TAngle, THeight>(Angle<TAngleUnits, TAngle> Latitude, Angle<TAngleUnits, TAngle> Longitude, THeight Height)
: IPoint<Point<TDatum, TAngleUnits, TAngle, THeight>>
public readonly record struct Point<TDatum, TAngleUnits, T>(Angle<TAngleUnits, T> Latitude, Angle<TAngleUnits, T> Longitude, T Height)
: IGeodeticPoint<Point<TDatum, TAngleUnits, T>, CoordinateSystem<TDatum, T>, TDatum>
where TDatum : IDatum<TDatum>
where TAngleUnits : struct, IAngleUnits<TAngleUnits>
where TAngle : struct, IFloatingPoint<TAngle>, IMinMaxValue<TAngle>
where THeight : struct, INumber<THeight>, IMinMaxValue<THeight>
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
{
public Angle<TAngleUnits, TAngle> Latitude { get; }
= Latitude < -Angle<TAngleUnits, TAngle>.Right || Latitude > Angle<TAngleUnits, TAngle>.Right
? Throw.ArgumentOutOfRangeException<Angle<TAngleUnits, TAngle>>(nameof(Latitude), Latitude, "Latitude must be in [-90.0º, 90.0º]")
public Angle<TAngleUnits, T> Latitude { get; }
= Latitude < -Angle<TAngleUnits, T>.Right || Latitude > Angle<TAngleUnits, T>.Right
? Throw.ArgumentOutOfRangeException<Angle<TAngleUnits, T>>(nameof(Latitude), Latitude, "Latitude must be in [-90.0º, 90.0º]")
: Latitude;

public Angle<TAngleUnits, TAngle> Longitude { get; }
= Longitude.Value <= -TAngle.CreateChecked(TAngleUnits.Straight) || Longitude.Value > TAngle.CreateChecked(TAngleUnits.Straight)
? Throw.ArgumentOutOfRangeException<Angle<TAngleUnits, TAngle>>(nameof(Longitude), Longitude, "Longitude must be in ]-180.0º, 180.0º]")
public Angle<TAngleUnits, T> Longitude { get; }
= Longitude.Value <= -T.CreateChecked(TAngleUnits.Straight) || Longitude.Value > T.CreateChecked(TAngleUnits.Straight)
? Throw.ArgumentOutOfRangeException<Angle<TAngleUnits, T>>(nameof(Longitude), Longitude, "Longitude must be in ]-180.0º, 180.0º]")
: Longitude;

/// <summary>
/// Creates an instance of the current type from a value,
/// throwing an overflow exception for any values that fall outside the representable range of the current type.
/// </summary>
/// <typeparam name="TAngleOther">The type of the latitude and longitude components of <paramref name="point"/>.</typeparam>
/// <typeparam name="THeightOther">The type of the height components of <paramref name="point"/>.</typeparam>
/// <param name="point">The value which is used to create the instance of <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/></param>
/// <returns>An instance of <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/> created from <paramref name="point" />.</returns>
/// <exception cref="NotSupportedException"><typeparamref name="TAngleOther" /> is not supported.</exception>
/// <exception cref="OverflowException"><paramref name="point" /> is not representable by <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/>.</exception>
public static Point<TDatum, TAngleUnits, TAngle, THeight> CreateChecked<TAngleOther, THeightOther>(in Point<TDatum, TAngleUnits, TAngleOther, THeightOther> point)
where TAngleOther : struct, IFloatingPoint<TAngleOther>, IMinMaxValue<TAngleOther>
where THeightOther : struct, INumber<THeightOther>, IMinMaxValue<THeightOther>
/// <typeparam name="TOther">The type of the components of <paramref name="point"/>.</typeparam>
/// <param name="point">The value which is used to create the instance of <see cref="Point{TDatum, TAngleUnits, T}"/></param>
/// <returns>An instance of <see cref="Point{TDatum, TAngleUnits, T}"/> created from <paramref name="point" />.</returns>
/// <exception cref="NotSupportedException"><typeparamref name="TOther" /> is not supported.</exception>
/// <exception cref="OverflowException"><paramref name="point" /> is not representable by <see cref="Point{TDatum, TAngleUnits, T}"/>.</exception>
public static Point<TDatum, TAngleUnits, T> CreateChecked<TOther>(in Point<TDatum, TAngleUnits, TOther> point)
where TOther : struct, IFloatingPoint<TOther>, IMinMaxValue<TOther>
=> new(
Angle<TAngleUnits, TAngle>.CreateChecked(point.Latitude),
Angle<TAngleUnits, TAngle>.CreateChecked(point.Longitude),
THeight.CreateChecked(point.Height)
Angle<TAngleUnits, T>.CreateChecked(point.Latitude),
Angle<TAngleUnits, T>.CreateChecked(point.Longitude),
T.CreateChecked(point.Height)
);

/// <summary>
/// Creates an instance of the current type from a value,
/// saturating any values that fall outside the representable range of the current type.
/// </summary>
/// <typeparam name="TAngleOther">The type of the latitude and longitude components of <paramref name="point"/>.</typeparam>
/// <typeparam name="THeightOther">The type of the height components of <paramref name="point"/>.</typeparam>
/// <param name="point">The value which is used to create the instance of <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/></param>
/// <returns>An instance of <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/> created from <paramref name="point" />.</returns>
/// <exception cref="NotSupportedException"><typeparamref name="TAngleOther" /> is not supported.</exception>
/// <exception cref="OverflowException"><paramref name="point" /> is not representable by <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/>.</exception>
public static Point<TDatum, TAngleUnits, TAngle, THeight> CreateSaturating<TAngleOther, THeightOther>(in Point<TDatum, TAngleUnits, TAngleOther, THeightOther> point)
where TAngleOther : struct, IFloatingPoint<TAngleOther>, IMinMaxValue<TAngleOther>
where THeightOther : struct, INumber<THeightOther>, IMinMaxValue<THeightOther>
/// <typeparam name="TOther">The type of the components of <paramref name="point"/>.</typeparam>
/// <param name="point">The value which is used to create the instance of <see cref="Point{TDatum, TAngleUnits, T}"/></param>
/// <returns>An instance of <see cref="Point{TDatum, TAngleUnits, T}"/> created from <paramref name="point" />.</returns>
/// <exception cref="NotSupportedException"><typeparamref name="TOther" /> is not supported.</exception>
/// <exception cref="OverflowException"><paramref name="point" /> is not representable by <see cref="Point{TDatum, TAngleUnits, T}"/>.</exception>
public static Point<TDatum, TAngleUnits, T> CreateSaturating<TOther>(in Point<TDatum, TAngleUnits, TOther> point)
where TOther : struct, IFloatingPoint<TOther>, IMinMaxValue<TOther>
=> new(
Angle<TAngleUnits, TAngle>.CreateSaturating(point.Latitude),
Angle<TAngleUnits, TAngle>.CreateSaturating(point.Longitude),
THeight.CreateSaturating(point.Height)
Angle<TAngleUnits, T>.CreateSaturating(point.Latitude),
Angle<TAngleUnits, T>.CreateSaturating(point.Longitude),
T.CreateSaturating(point.Height)
);

/// <summary>
/// Creates an instance of the current type from a value,
/// truncating any values that fall outside the representable range of the current type.
/// </summary>
/// <typeparam name="TAngleOther">The type of the latitude and longitude components of <paramref name="point"/>.</typeparam>
/// <typeparam name="THeightOther">The type of the height components of <paramref name="point"/>.</typeparam>
/// <param name="point">The value which is used to create the instance of <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/></param>
/// <returns>An instance of <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/> created from <paramref name="point" />.</returns>
/// <exception cref="NotSupportedException"><typeparamref name="TAngleOther" /> is not supported.</exception>
/// <exception cref="OverflowException"><paramref name="point" /> is not representable by <see cref="Point{TDatum, TAngleUnits, TAngle, THeight}"/>.</exception>
public static Point<TDatum, TAngleUnits, TAngle, THeight> CreateTruncating<TAngleOther, THeightOther>(in Point<TDatum, TAngleUnits, TAngleOther, THeightOther> point)
where TAngleOther : struct, IFloatingPoint<TAngleOther>, IMinMaxValue<TAngleOther>
where THeightOther : struct, INumber<THeightOther>, IMinMaxValue<THeightOther>
/// <typeparam name="TOther">The type of the components of <paramref name="point"/>.</typeparam>
/// <param name="point">The value which is used to create the instance of <see cref="Point{TDatum, TAngleUnits, T}"/></param>
/// <returns>An instance of <see cref="Point{TDatum, TAngleUnits, T}"/> created from <paramref name="point" />.</returns>
/// <exception cref="NotSupportedException"><typeparamref name="TOther" /> is not supported.</exception>
/// <exception cref="OverflowException"><paramref name="point" /> is not representable by <see cref="Point{TDatum, TAngleUnits, T}"/>.</exception>
public static Point<TDatum, TAngleUnits, T> CreateTruncating<TOther>(in Point<TDatum, TAngleUnits, TOther> point)
where TOther : struct, IFloatingPoint<TOther>, IMinMaxValue<TOther>
=> new(
Angle<TAngleUnits, TAngle>.CreateTruncating(point.Latitude),
Angle<TAngleUnits, TAngle>.CreateTruncating(point.Longitude),
THeight.CreateTruncating(point.Height)
Angle<TAngleUnits, T>.CreateTruncating(point.Latitude),
Angle<TAngleUnits, T>.CreateTruncating(point.Longitude),
T.CreateTruncating(point.Height)
);

#region constants

public static readonly Point<TDatum, TAngleUnits, TAngle, THeight> Zero
= new(Angle<TAngleUnits, TAngle>.Zero, Angle<TAngleUnits, TAngle>.Zero, THeight.Zero);
public static readonly Point<TDatum, TAngleUnits, T> Zero
= new(Angle<TAngleUnits, T>.Zero, Angle<TAngleUnits, T>.Zero, T.Zero);

static Point<TDatum, TAngleUnits, TAngle, THeight> IGeometricBase<Point<TDatum, TAngleUnits, TAngle, THeight>>.Zero
static Point<TDatum, TAngleUnits, T> IGeometricBase<Point<TDatum, TAngleUnits, T>, CoordinateSystem<TDatum, T>>.Zero
=> Zero;

/// <summary>
/// Represents the minimum value. This field is read-only.
/// </summary>
public static readonly Point<TDatum, TAngleUnits, TAngle, THeight> MinValue
= new(-Angle<TAngleUnits, TAngle>.Right, new(TAngle.CreateTruncating(-TAngleUnits.Straight + Utils.Epsilon)), THeight.MinValue);
public static readonly Point<TDatum, TAngleUnits, T> MinValue
= new(-Angle<TAngleUnits, T>.Right, new(T.CreateTruncating(-TAngleUnits.Straight + Utils.Epsilon)), T.MinValue);

/// <summary>
/// Represents the maximum value. This field is read-only.
/// </summary>
public static readonly Point<TDatum, TAngleUnits, TAngle, THeight> MaxValue
= new(Angle<TAngleUnits, TAngle>.Right, Angle<TAngleUnits, TAngle>.Straight, THeight.MinValue);
public static readonly Point<TDatum, TAngleUnits, T> MaxValue
= new(Angle<TAngleUnits, T>.Right, Angle<TAngleUnits, T>.Straight, T.MinValue);

static Point<TDatum, TAngleUnits, TAngle, THeight> IMinMaxValue<Point<TDatum, TAngleUnits, TAngle, THeight>>.MinValue
static Point<TDatum, TAngleUnits, T> IMinMaxValue<Point<TDatum, TAngleUnits, T>>.MinValue
=> MinValue;
static Point<TDatum, TAngleUnits, TAngle, THeight> IMinMaxValue<Point<TDatum, TAngleUnits, TAngle, THeight>>.MaxValue
static Point<TDatum, TAngleUnits, T> IMinMaxValue<Point<TDatum, TAngleUnits, T>>.MaxValue
=> MaxValue;

#endregion

/// <summary>
/// Gets the coordinate system.
/// </summary>
public CoordinateSystem<TDatum, TAngle, THeight> CoordinateSystem
=> new();
ICoordinateSystem IGeometricBase<Point<TDatum, TAngleUnits, TAngle, THeight>>.CoordinateSystem
=> CoordinateSystem;

object IGeometricBase<Point<TDatum, TAngleUnits, TAngle, THeight>>.this[int index]
object IGeometricBase<Point<TDatum, TAngleUnits, T>, CoordinateSystem<TDatum, T>>.this[int index]
=> index switch
{
0 => Latitude,
Expand Down
15 changes: 15 additions & 0 deletions src/NetFabric.Numerics.Geography/IGeodeticBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace NetFabric.Numerics.Geography;

/// <summary>
/// Represents a base interface for geodetic structures and types.
/// </summary>
/// <typeparam name="TSelf">The type of the implementing structure or type.</typeparam>
/// <typeparam name="TCoordinateSystem">The type of the associated coordinate system.</typeparam>
/// <typeparam name="TDatum">The type of the datum associated with the geodetic structure or type.</typeparam>
public interface IGeodeticBase<TSelf, TCoordinateSystem, TDatum>
: IGeometricBase<TSelf, TCoordinateSystem>
where TSelf : struct, IGeodeticBase<TSelf, TCoordinateSystem, TDatum>?
where TCoordinateSystem : class, IGeodeticCoordinateSystem<TCoordinateSystem, TDatum>, new()
where TDatum : IDatum<TDatum>
{
}
16 changes: 13 additions & 3 deletions src/NetFabric.Numerics.Geography/IGeodeticCoordinateSystem.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
namespace NetFabric.Numerics.Geography;

public interface IGeodeticCoordinateSystem<TDatum>
: ICoordinateSystem
/// <summary>
/// Represents a geodetic coordinate system, which is a specialized type of coordinate system
/// used for geographic or geospatial applications.
/// </summary>
/// <typeparam name="TSelf">The type of the implementing class.</typeparam>
/// <typeparam name="TDatum">The type of the datum associated with this coordinate system.</typeparam>
public interface IGeodeticCoordinateSystem<TSelf, TDatum>
: ICoordinateSystem<TSelf>
where TSelf : class, IGeodeticCoordinateSystem<TSelf, TDatum>, new()
where TDatum : IDatum<TDatum>
{
/// <summary>
/// Gets the datum associated with this geodetic coordinate system.
/// </summary>
Datum<TDatum> Datum { get; }
}
}
8 changes: 5 additions & 3 deletions src/NetFabric.Numerics.Geography/IGeodeticPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
/// <summary>
/// Represents a point in a geodetic coordinate system.
/// </summary>
public interface IGeodeticPoint<TSelf, TDatum>
: IPoint<TSelf>
where TSelf : struct, IPoint<TSelf>?
public interface IGeodeticPoint<TSelf, TCoordinateSystem, TDatum>
: IGeodeticBase<TSelf, TCoordinateSystem, TDatum>
, IPoint<TSelf, TCoordinateSystem>
where TSelf : struct, IGeodeticPoint<TSelf, TCoordinateSystem, TDatum>?
where TCoordinateSystem : class, IGeodeticCoordinateSystem<TCoordinateSystem, TDatum>, new()
where TDatum : IDatum<TDatum>
{

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Title>NetFabric.Numerics.Geography</Title>
<TargetFrameworks>net7.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description></Description>
<Description>
</Description>
<Version>1.0.0-beta06</Version>
<PackageIcon>Icon.png</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand All @@ -18,27 +18,22 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\Icon.png" Pack="true" PackagePath="" />
<None Include="..\..\LICENSE" Pack="true" PackagePath="" />
<None Include=".\README.md" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NetFabric.Numerics.Angle\NetFabric.Numerics.Angle.csproj" />
<ProjectReference Include="..\NetFabric.Numerics\NetFabric.Numerics.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit e4d60fd

Please sign in to comment.