Files
the-living-world/tests/TheLivingWorld.Tests/GeoTests.cs
T

135 lines
4.1 KiB
C#

using System.Numerics;
using TheLivingWorld.Core.Geo;
using TheLivingWorld.Core.Worlds;
namespace TheLivingWorld.Tests;
public class LocalProjectionTests
{
// Robert Lee, Texas - the town the first worlds were generated around.
private static readonly GeoPoint Origin = new(31.8966010, -100.4858591);
[Fact]
public void Origin_projects_to_world_zero()
{
var projection = new LocalProjection(Origin);
var projected = projection.Project(Origin);
Assert.Equal(0f, projected.X, 3);
Assert.Equal(0f, projected.Y, 3);
}
[Fact]
public void One_kilometre_north_lands_one_kilometre_up()
{
var projection = new LocalProjection(Origin);
var north = new GeoPoint(Origin.Latitude + 1000.0 / LocalProjection.MetersPerDegreeLatitude, Origin.Longitude);
var projected = projection.Project(north);
Assert.Equal(0f, projected.X, 2);
Assert.Equal(1000f, projected.Y, 1);
}
[Fact]
public void Unproject_reverses_project()
{
var projection = new LocalProjection(Origin);
var point = new Vector2(-4321.5f, 6789.25f);
var roundTripped = projection.Project(projection.Unproject(point));
Assert.Equal(point.X, roundTripped.X, 1);
Assert.Equal(point.Y, roundTripped.Y, 1);
}
[Fact]
public void Rejects_coordinates_outside_the_globe()
{
Assert.Throws<ArgumentOutOfRangeException>(() => new LocalProjection(new GeoPoint(91, 0)));
}
}
public class GeoBoundsTests
{
[Theory]
[InlineData(31.8966010, -100.4858591, 10_000)]
[InlineData(60.0, 30.3, 20_000)]
[InlineData(-33.87, 151.21, 5_000)]
public void FromCenter_produces_a_square_of_the_requested_size(double lat, double lon, double sizeMeters)
{
var center = new GeoPoint(lat, lon);
var bounds = GeoBounds.FromCenter(center, sizeMeters);
var projection = new LocalProjection(center);
var southWest = projection.Project(bounds.South, bounds.West);
var northEast = projection.Project(bounds.North, bounds.East);
// Half a metre of slack over a 20 km span is the float rounding in the projection.
Assert.Equal(sizeMeters, northEast.X - southWest.X, 0.5);
Assert.Equal(sizeMeters, northEast.Y - southWest.Y, 0.5);
}
[Fact]
public void Overpass_bbox_is_south_west_north_east()
{
var bounds = new GeoBounds(South: 1, West: 2, North: 3, East: 4);
Assert.Equal("1.0000000,2.0000000,3.0000000,4.0000000", bounds.ToOverpassBbox());
}
}
public class ChunkGridTests
{
[Fact]
public void Covers_the_whole_world_square()
{
var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512);
Assert.Equal(4, grid.CountX);
Assert.Equal(4, grid.CountY);
Assert.Equal(-1024f, grid.MinX);
}
[Fact]
public void Rounds_the_count_up_when_the_size_does_not_divide_evenly()
{
var grid = new ChunkGrid(worldSizeMeters: 10_000, chunkSizeMeters: 512);
Assert.Equal(20, grid.CountX);
}
[Fact]
public void Maps_points_to_the_chunk_containing_them()
{
var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512);
Assert.Equal(new ChunkCoord(0, 0), grid.CoordOf(new Vector2(-1024, -1024)));
Assert.Equal(new ChunkCoord(2, 2), grid.CoordOf(new Vector2(1, 1)));
Assert.Equal(new ChunkCoord(3, 3), grid.CoordOf(new Vector2(1023, 1023)));
}
[Fact]
public void Clamps_points_that_fall_outside_the_world()
{
var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512);
Assert.Equal(new ChunkCoord(0, 0), grid.CoordOf(new Vector2(-99_999, -99_999)));
Assert.Equal(new ChunkCoord(3, 3), grid.CoordOf(new Vector2(99_999, 99_999)));
}
[Fact]
public void Chunk_bounds_line_up_with_the_grid()
{
var grid = new ChunkGrid(worldSizeMeters: 2048, chunkSizeMeters: 512);
var bounds = grid.BoundsOf(new ChunkCoord(1, 2));
Assert.Equal(-512f, bounds.MinX);
Assert.Equal(0f, bounds.MinY);
Assert.Equal(0f, bounds.MaxX);
Assert.Equal(512f, bounds.MaxY);
}
}