Skip to content

Commit

Permalink
Merge pull request #236 from Tronald/develop
Browse files Browse the repository at this point in the history
2.23.1.1
  • Loading branch information
Tronald committed May 19, 2024
2 parents 0e93934 + da6deab commit b04af81
Show file tree
Hide file tree
Showing 8 changed files with 1,062 additions and 23 deletions.
4 changes: 2 additions & 2 deletions CoordinateSharp.Magnetic/CoordinateSharp.Magnetic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ For more information, please contact Signature Group, LLC at this address: sales
<TargetFrameworks>net40; netstandard1.3; netstandard1.4; netstandard2.0; netstandard2.1; net50; net60; net70; net80</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>1.1.12.0</Version>
<Version>1.1.13.0</Version>
<Authors>Signature Group, LLC</Authors>
<Company />
<PackageProjectUrl>https://github.com/Tronald/CoordinateSharp</PackageProjectUrl>
Expand All @@ -61,7 +61,7 @@ For more information, please contact Signature Group, LLC at this address: sales
<PackageIconUrl></PackageIconUrl>
<PackageId>CoordinateSharp.Magnetic</PackageId>
<Title>CoordinateSharp.Magnetic</Title>
<AssemblyVersion>1.1.12.0</AssemblyVersion>
<AssemblyVersion>1.1.13.0</AssemblyVersion>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<SignAssembly>true</SignAssembly>
<PackageIcon>128x128.png</PackageIcon>
Expand Down
6 changes: 1 addition & 5 deletions CoordinateSharp/Celestial/Solar/SunCalculations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,7 @@ public static void CalculateSunTime(double lat, double lng, DateTime date, Celes
{
// No sunset this date
c.sunCondition = CelestialStatus.NoSet;
}
else
{

}
}
}

//Sat day and night time spans within 24 hours period
Expand Down
8 changes: 4 additions & 4 deletions CoordinateSharp/CoordinateSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,26 @@ Please visit http://coordinatesharp.com/licensing or contact Signature Group, LL
<TargetFrameworks>net40; netstandard1.3; netstandard1.4; netstandard2.0; netstandard2.1; net50; net60; net70; net80</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>2.22.1.1</Version>
<Version>2.23.1.1</Version>
<Authors>Signature Group, LLC</Authors>
<Company />
<PackageProjectUrl>https://github.com/Tronald/CoordinateSharp</PackageProjectUrl>
<PackageLicenseUrl></PackageLicenseUrl>
<Copyright>Copyright 2024</Copyright>
<Description>CoordinateSharp is a high powered, lightweight .NET library that can convert geographical coordinates, perform distance logic, and calculate location based sun, moon, and magnetic information with minimal code.</Description>
<PackageReleaseNotes>-Adds Day and Night TimeSpan properties to the CelestialInfo class for easier calculation of total day and night hours for a date at a specified location.</PackageReleaseNotes>
<PackageReleaseNotes>-Adds the ability to densify polylines to mitigate geofence spherical distortions over long distances.</PackageReleaseNotes>
<PackageTags>Conversion; Latitude; Longitude; Coordinates; Geography; Sun; Moon; Solar; Lunar; Time; MGRS; UTM; EPSG:3857; ECEF; GEOREF; Web Mercator;</PackageTags>
<!-- <PackageLicenseExpression>AGPL-3.0-or-later</PackageLicenseExpression>-->
<PackageLicenseFile>License.txt</PackageLicenseFile> <PackageIconUrl></PackageIconUrl>
<PackageId>CoordinateSharp</PackageId>
<Title>CoordinateSharp</Title>
<AssemblyVersion>2.22.1.1</AssemblyVersion>
<AssemblyVersion>2.23.1.1</AssemblyVersion>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<SignAssembly>true</SignAssembly>
<PackageIcon>128x128.png</PackageIcon>
<AssemblyOriginatorKeyFile>CoordinateSharp Strong Name.snk</AssemblyOriginatorKeyFile>
<DelaySign>false</DelaySign>
<FileVersion>2.22.1.1</FileVersion>
<FileVersion>2.23.1.1</FileVersion>
<RepositoryUrl>https://github.com/Tronald/CoordinateSharp</RepositoryUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand Down
166 changes: 154 additions & 12 deletions CoordinateSharp/GeoFence/GeoFence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,21 @@
Please visit http://coordinatesharp.com/licensing or contact Signature Group, LLC to purchase a commercial license, or for any questions regarding the AGPL 3.0 license requirements or free use license: sales@signatgroup.com.
*/

// Ignore Spelling: Densify

using System.Collections.Generic;
using System;
using System.Linq;

namespace CoordinateSharp
{
{
/// <summary>
/// The GeoFence class is used to help check if points/coordinates are inside or near a specified polygon/polyline,
/// </summary>
[Serializable]
public partial class GeoFence
{
{
private readonly List<Point> _points = new List<Point>();

/// <summary>
Expand Down Expand Up @@ -108,7 +112,7 @@ public GeoFence(List<Coordinate> coordinates)
/// </summary>
public List<Point> Points
{
get { return _points; }
get { return _points; }
}
private Coordinate ClosestPointOnSegment(Point a, Point b, Coordinate p, DateTime dt, EagerLoad eg)
{
Expand Down Expand Up @@ -136,7 +140,8 @@ private Coordinate ClosestPointOnSegment(Point a, Point b, Coordinate p, DateTim
/// </summary>
/// <param name="point">Point to test</param>
/// <remarks>
/// Points sitting on the edge of a polygon may return true or false.
/// This method utilizes 2D ray casting techniques and does not inherently account for the curvature of the Earth. To mitigate the impact of Earth shape distortion on polygons or polylines that span long distances, users should employ densification.
/// Points sitting on the edge of a polygon may return true or false.
/// </remarks>
/// <returns>bool</returns>
/// <example>
Expand Down Expand Up @@ -189,6 +194,9 @@ public bool IsPointInPolygon(Coordinate point)
/// </summary>
/// <param name="point">Point to test</param>
/// <param name="range">Range in meters</param>
/// <remarks>
/// This method utilizes 2D ray casting techniques and does not inherently account for the curvature of the Earth. To mitigate the impact of Earth shape distortion on polygons or polylines that span long distances, users should employ densification.
/// </remarks>
/// <returns>bool</returns>
/// <example>
/// The following example shows how to determine if a coordinate is within 1000 meters of
Expand Down Expand Up @@ -223,7 +231,7 @@ public bool IsPointInRangeOfLine(Coordinate point, double range)
if (c.Get_Distance_From_Coordinate(point).Meters <= range)
return true;
}

return false;
}

Expand All @@ -232,6 +240,9 @@ public bool IsPointInRangeOfLine(Coordinate point, double range)
/// </summary>
/// <param name="point">Point to test</param>
/// <param name="range">Range is a distance object</param>
/// <remarks>
/// This method utilizes 2D ray casting techniques and does not inherently account for the curvature of the Earth. To mitigate the impact of Earth shape distortion on polygons or polylines that span long distances, users should employ densification.
/// </remarks>
/// <returns>bool</returns>
/// <example>
/// The following example shows how to determine if a coordinate is within 1 km of
Expand Down Expand Up @@ -261,12 +272,13 @@ public bool IsPointInRangeOfLine(Coordinate point, Distance range)
return false;

return IsPointInRangeOfLine(point, range.Meters);
}
}

/// <summary>
/// Gets distance from nearest polyline in shape
/// </summary>
/// </summary>
/// <param name="point">Coordinate</param>
/// <remarks>This method utilizes 2D ray casting techniques and does not inherently account for the curvature of the Earth. To mitigate the impact of Earth shape distortion on polygons or polylines that span long distances, users should employ densification.</remarks>
/// <returns>Distance</returns>
public Distance DistanceFromNearestPolyLine(Coordinate point)
{
Expand All @@ -279,18 +291,148 @@ public Distance DistanceFromNearestPolyLine(Coordinate point)
{
Coordinate c = ClosestPointOnSegment(_points[i], _points[i + 1], point, point.GeoDate, point.EagerLoadSettings);

if (d == null) { d= new Distance(point, c); }
if (d == null) { d = new Distance(point, c); }
else
{
Distance nd = new Distance(point, c);
if (nd.Meters < d.Meters) { d = nd; }
}

}

return d;
}


}
/// <summary>
/// Densifies the polygon by adding additional points along each edge at specified intervals using ellipsoidal (Vincenty) logic.
/// </summary>
/// <param name="distance">The distance between points along the polygon's edges. This distance determines how frequently new points are inserted into the polygon</param>
/// <remarks>
/// This method is particularly useful for large polygons where the curvature of the Earth can cause
/// significant distortion in geographic calculations. By adding more points at regular intervals,
/// the polygon better conforms to the curved surface of the Earth, reducing errors in area calculations,
/// perimeter calculations, and point-in-polygon tests.
///
/// The function automatically calculates intermediate points based on the great-circle distance between
/// existing vertices, ensuring that the new points adhere to the true geographic shape of the polygon.
/// This is essential for maintaining geographic integrity when performing spatial operations or visualizations.
///
/// Note: The densification process increases the number of vertices in the polygon, which may impact performance
/// and memory usage in spatial computations and data storage. Optimal use of this function depends on the required
/// precision and the geographic extent of the application.
/// </remarks>
/// <example>
/// Here is how you might use this function to densify a polygon representing a large geographic area:
/// <code>
/// //Create a four point GeoFence around Utah
/// List&lt;GeoFence.Point&gt; points = new List&lt;GeoFence.Point&gt;();
/// points.Add(new GeoFence.Point(41.003444, -109.045223));
/// points.Add(new GeoFence.Point(41.003444, -102.041524));
/// points.Add(new GeoFence.Point(36.993076, -102.041524));
/// points.Add(new GeoFence.Point(36.993076, -109.045223));
/// points.Add(new GeoFence.Point(41.003444, -109.045223));
///
/// GeoFence gf = new GeoFence(points);
///
/// gf.Densify(new Distance(10, DistanceType.Kilometers));
///
/// //The gf.Points list now contains additional points at intervals of approximately 10 kilometers.
/// </code>
/// </example>
public void Densify(Distance distance)
{
Densify(distance, Shape.Ellipsoid);
}

/// <summary>
/// Densifies the polygon by adding additional points along each edge at specified intervals.
/// </summary>
/// <param name="distance">The distance between points along the polygon's edges. This distance determines how frequently new points are inserted into the polygon</param>
/// <param name="shape">Specify earth shape. Sphere is more efficient, but less precise than ellipsoid.</param>
/// <remarks>
/// This method is particularly useful for large polygons where the curvature of the Earth can cause
/// significant distortion in geographic calculations. By adding more points at regular intervals,
/// the polygon better conforms to the curved surface of the Earth, reducing errors in area calculations,
/// perimeter calculations, and point-in-polygon tests.
///
/// The function automatically calculates intermediate points based on the great-circle distance between
/// existing vertices, ensuring that the new points adhere to the true geographic shape of the polygon.
/// This is essential for maintaining geographic integrity when performing spatial operations or visualizations.
///
/// Note: The densification process increases the number of vertices in the polygon, which may impact performance
/// and memory usage in spatial computations and data storage. Optimal use of this function depends on the required
/// precision and the geographic extent of the application.
/// </remarks>
/// <example>
/// Here is how you might use this function to densify a polygon representing a large geographic area:
/// <code>
/// //Create a four point GeoFence around Utah
/// List&lt;GeoFence.Point&gt; points = new List&lt;GeoFence.Point&gt;();
/// points.Add(new GeoFence.Point(41.003444, -109.045223));
/// points.Add(new GeoFence.Point(41.003444, -102.041524));
/// points.Add(new GeoFence.Point(36.993076, -102.041524));
/// points.Add(new GeoFence.Point(36.993076, -109.045223));
/// points.Add(new GeoFence.Point(41.003444, -109.045223));
///
/// GeoFence gf = new GeoFence(points);
///
/// gf.Densify(new Distance(10, DistanceType.Kilometers), Shape.Sphere);
///
/// //The gf.Points list now contains additional points at intervals of approximately 10 kilometers.
/// </code>
/// </example>
public void Densify(Distance distance, Shape shape)
{
if (_points.Count < 2)
{
throw new InvalidOperationException("You cannot perform densification a Geofence that has less than 2 points.");
}
//Store the original points for reference as _points will be modified
List<Point> ogpoints = new List<Point>(_points);

//Create a collection of point collections to insert into the polygon
List<List<Point>> inserts = new List<List<Point>>();

//Iterate the polygon to create densification points
for (int x = 0; x < ogpoints.Count - 1; x++)
{
var p1 = ogpoints[x];
var p2 = ogpoints[x + 1];

List<Point> ipoints = new List<Point>();

//Coordinate to move
Coordinate mc = new Coordinate(p1.Latitude, p1.Longitude, new EagerLoad(false));

//Destination
Coordinate dc = new Coordinate(p2.Latitude, p2.Longitude, new EagerLoad(false));

while (new Distance(mc, dc).Meters > distance.Meters)
{
mc.Move(dc, distance, shape);
ipoints.Add(new Point(mc.Latitude.ToDouble(), mc.Longitude.ToDouble()));
}

inserts.Add(ipoints);
}

//Clear existing collection
_points.Clear();

//Create new points collection.
for (int x = 0; x < ogpoints.Count - 1; x++)
{
var p = ogpoints[x];
_points.Add(p);
foreach (var dp in inserts[x])
{
_points.Add(dp);
}
}

//Add last point to close shape.
_points.Add(ogpoints.Last());
}

}
}
8 changes: 8 additions & 0 deletions CoordinateSharp_UnitTests/CoordinateSharp.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
<None Remove="CoordinatePartData\Signed.txt" />
<None Remove="CoordinatePartData\SignedDDM.txt" />
<None Remove="CoordinatePartData\SignedDMS.txt" />
<None Remove="GeoFenceData\ColoradoEllipse.txt" />
<None Remove="GeoFenceData\ColoradoSphere.txt" />
<None Remove="MagneticData\MagneticFields2020.txt" />
</ItemGroup>

Expand Down Expand Up @@ -196,6 +198,12 @@
<Content Include="CoordinatePartData\SignedDMS.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="GeoFenceData\ColoradoEllipse.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="GeoFenceData\ColoradoSphere.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="MagneticData\MagneticFields2020.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down
45 changes: 45 additions & 0 deletions CoordinateSharp_UnitTests/GeoFence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CoordinateSharp;
using System.IO;
namespace CoordinateSharp_UnitTests
{
[TestClass]
Expand Down Expand Up @@ -132,7 +133,51 @@ public void Haversine_Precision()
Assert.AreEqual(0, c.Longitude.ToDouble() - gd.Last.Longitude.ToDouble(), .000001);
}

/// <summary>
/// Tests densify logic to ensure proper point placement.
/// </summary>
[TestMethod]
public void Densify()
{
//Create a four point GeoFence around Utah

List<GeoFence.Point> points = new List<GeoFence.Point>();

points.Add(new GeoFence.Point(41.003444, -109.045223));
points.Add(new GeoFence.Point(41.003444, -102.041524));
points.Add(new GeoFence.Point(36.993076, -102.041524));
points.Add(new GeoFence.Point(36.993076, -109.045223));
points.Add(new GeoFence.Point(41.003444, -109.045223));

GeoFence ellipseTest = new GeoFence(points);
GeoFence sphereTest = new GeoFence(new List<GeoFence.Point>(points));


ellipseTest.Densify(new Distance(5, DistanceType.Kilometers));
sphereTest.Densify(new Distance(5, DistanceType.Kilometers), Shape.Sphere);

string[] ellipseTestPoints = File.ReadAllText("GeoFenceData\\ColoradoEllipse.txt").Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
string[] sphereTestPoints = File.ReadAllText("GeoFenceData\\ColoradoSphere.txt").Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

for(int x =0;x< ellipseTestPoints.Length;x++)
{
string[] point = ellipseTestPoints[x].Split(',');
double lat = double.Parse(point[0]);
double lng = double.Parse(point[1]);
Assert.AreEqual(ellipseTest.Points[x].Latitude, lat, .00000000001);
Assert.AreEqual(ellipseTest.Points[x].Longitude, lng, .00000000001);
}

for (int x = 0; x < sphereTestPoints.Length; x++)
{
string[] point = sphereTestPoints[x].Split(',');
double lat = double.Parse(point[0]);
double lng = double.Parse(point[1]);
Assert.AreEqual(sphereTest.Points[x].Latitude, lat, .00000000001);
Assert.AreEqual(sphereTest.Points[x].Longitude, lng, .00000000001);
}
}

/// <summary>
/// Ensures Vincenty precision withing bounds
/// </summary>
Expand Down
Loading

0 comments on commit b04af81

Please sign in to comment.