initial commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
,,A,,
|
||||
,,120,,
|
||||
1,3,100,,
|
||||
2,4,90,,
|
||||
,,80,5,3
|
||||
,,B,,
|
||||
,,C,,
|
||||
,,30,,
|
||||
4,6,20,,
|
||||
,,D,,
|
||||
|
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from surveyladder.geo import chainage_to_xy, chainage_offset_to_xy, poly_area, poly_centroid, trilaterate_xy
|
||||
|
||||
|
||||
class TestPolyArea(unittest.TestCase):
|
||||
|
||||
def test_poly_area(self):
|
||||
polygon = np.array([[0, 0], [4, 0], [4, 3]])
|
||||
self.assertEqual(poly_area(polygon), 6)
|
||||
|
||||
|
||||
class TestTrilaterateXY(unittest.TestCase):
|
||||
|
||||
def test_trilaterate_xy_on_line(self):
|
||||
A = np.array([0, 0])
|
||||
B = np.array([10, 0])
|
||||
a = 5
|
||||
b = 5
|
||||
C = trilaterate_xy(A, a, B, b)
|
||||
np.testing.assert_almost_equal(C, [5, 0])
|
||||
|
||||
def test_trilaterate_xy_right_triangle(self):
|
||||
A = np.array([0, 0])
|
||||
B = np.array([4, 0])
|
||||
a = 5
|
||||
b = 3
|
||||
C = trilaterate_xy(A, a, B, b)
|
||||
np.testing.assert_almost_equal(C, [4, 3])
|
||||
|
||||
def test_trilaterate_xy_right_triangle_swapped(self):
|
||||
A = np.array([4, 0])
|
||||
B = np.array([0, 0])
|
||||
a = 3
|
||||
b = 5
|
||||
C = trilaterate_xy(A, a, B, b)
|
||||
np.testing.assert_almost_equal(C, [4, -3])
|
||||
|
||||
|
||||
class TestChainageToXY(unittest.TestCase):
|
||||
|
||||
def test_positive_distance(self):
|
||||
np.testing.assert_almost_equal(
|
||||
chainage_to_xy(np.array([3, 5]), np.array([6, 9]), 10),
|
||||
[9, 13])
|
||||
|
||||
def test_negative_distance(self):
|
||||
np.testing.assert_almost_equal(
|
||||
chainage_to_xy(np.array([3, 5]), np.array([6, 9]), -5),
|
||||
[0, 1])
|
||||
|
||||
|
||||
class TestChainageOffsetToXY(unittest.TestCase):
|
||||
|
||||
def test_positive_offset(self):
|
||||
A = np.array([0, 0])
|
||||
B = np.array([0, 1])
|
||||
a_distance = 5
|
||||
perp_distance = 2
|
||||
P = chainage_offset_to_xy(A, B, a_distance, perp_distance)
|
||||
np.testing.assert_almost_equal(P, [-2, 5])
|
||||
|
||||
class TestPolyCentroid(unittest.TestCase):
|
||||
|
||||
def test_pos_square(self):
|
||||
# mathematically positive point ordering
|
||||
poly = np.array([[0,0], [1,0], [1,2], [0,2]])
|
||||
np.testing.assert_almost_equal(poly_centroid(poly), [0.5, 1])
|
||||
|
||||
def test_neg_square(self):
|
||||
# mathematically negative point ordering
|
||||
poly = np.array([[0,0], [1,0], [1,-2], [0,-2]])
|
||||
np.testing.assert_almost_equal(poly_centroid(poly), [0.5, -1])
|
||||
|
||||
|
||||
unittest.main()
|
||||
@@ -0,0 +1,42 @@
|
||||
import unittest
|
||||
|
||||
from surveyladder.ladder import load_ladder_csv, load_ladder_xls, get_seed_points, LadderSegment, OffsetPoint
|
||||
|
||||
class TestGetSeedPoints(unittest.TestCase):
|
||||
|
||||
def test_get_seed_points(self):
|
||||
ladder_segments = ([
|
||||
LadderSegment("D", "F", 161.3, []),
|
||||
LadderSegment("C", "G", 133.2, []),
|
||||
LadderSegment("C", "F", 138.5, []),
|
||||
LadderSegment("G", "F", 157.6, []),
|
||||
LadderSegment("C", "D", 130.5, [])
|
||||
])
|
||||
points = get_seed_points(ladder_segments)
|
||||
self.assertEqual(len(points), 4)
|
||||
for seg in ladder_segments:
|
||||
start_point = points[seg.start_label]
|
||||
end_point = points[seg.end_label]
|
||||
actual_distance = ((start_point[0] - end_point[0]) ** 2 + (start_point[1] - end_point[1]) ** 2) ** 0.5
|
||||
self.assertAlmostEqual(actual_distance, seg.distance, places=1)
|
||||
|
||||
|
||||
class TestLadder(unittest.TestCase):
|
||||
|
||||
def test_load_ladder_xls(self):
|
||||
self.maxDiff = None
|
||||
segments = load_ladder_xls("tests/data/ladder.xlsx")
|
||||
self.assertEqual(segments, ([
|
||||
LadderSegment('A', 'B', 120.0, [OffsetPoint("1", 100.0, 3.0), OffsetPoint("2", 90.0, 4.0), OffsetPoint("3", 80.0, -5.0)]),
|
||||
LadderSegment('C', 'D', 30.0, [OffsetPoint("4", 20.0, 6.0)])
|
||||
]))
|
||||
|
||||
def test_load_ladder_csv(self):
|
||||
self.maxDiff = None
|
||||
segments = load_ladder_csv("tests/data/ladder.csv")
|
||||
self.assertEqual(segments, ([
|
||||
LadderSegment('A', 'B', 120.0, [OffsetPoint("1", 100.0, 3.0), OffsetPoint("2", 90.0, 4.0), OffsetPoint("3", 80.0, -5.0)]),
|
||||
LadderSegment('C', 'D', 30.0, [OffsetPoint("4", 20.0, 6.0)])
|
||||
]))
|
||||
|
||||
unittest.main()
|
||||
@@ -0,0 +1,121 @@
|
||||
import unittest
|
||||
|
||||
import numpy
|
||||
|
||||
from solver import OptimizationSolver, DirectSolver
|
||||
from surveyladder.geo import distance, chainage_offset_to_xy, poly_area
|
||||
|
||||
|
||||
class TestDirectSolver(unittest.TestCase):
|
||||
|
||||
def test_sanity_check_no_distances(self):
|
||||
solver = DirectSolver(['A', 'B'])
|
||||
with self.assertRaises(ValueError):
|
||||
solver.sanity_check()
|
||||
|
||||
def test_sanity_check_not_enough_neighbors(self):
|
||||
solver = DirectSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 3)
|
||||
with self.assertRaises(ValueError):
|
||||
solver.sanity_check()
|
||||
|
||||
def test_simple_triangle_only_distances(self):
|
||||
solver = DirectSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 3)
|
||||
solver.add_distance('B', 'C', 4)
|
||||
solver.add_distance('A', 'C', 5)
|
||||
|
||||
points = solver.solve()
|
||||
|
||||
numpy.testing.assert_almost_equal(distance(points['A'], points['B']), 3, decimal=2)
|
||||
numpy.testing.assert_almost_equal(distance(points['B'], points['C']), 4, decimal=2)
|
||||
numpy.testing.assert_almost_equal(distance(points['A'], points['C']), 5, decimal=2)
|
||||
|
||||
|
||||
class TestSolver(unittest.TestCase):
|
||||
|
||||
def test_distances_cost_optimal_solution(self):
|
||||
solver = OptimizationSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 3)
|
||||
solver.add_distance('B', 'C', 4)
|
||||
solver.add_distance('A', 'C', 5)
|
||||
optimal_solution = numpy.array([[0, 0], [3, 0], [3, 4]]).flatten()
|
||||
cost = solver.distances_cost(optimal_solution)
|
||||
self.assertAlmostEqual(cost, 0.0, places=3)
|
||||
|
||||
def test_distances_cost_suboptimal_solution(self):
|
||||
solver = OptimizationSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 3)
|
||||
solver.add_distance('B', 'C', 4)
|
||||
solver.add_distance('A', 'C', 5)
|
||||
suboptimal_solution = numpy.array([[0, 0], [6, 0], [6, 8]]).flatten()
|
||||
# distance(A, B) = 6 instead of 3 -> (6-3)^2 = 9
|
||||
# distance(B, C) = 8 instead of 4 -> (8-4)^2 = 16
|
||||
# distance(A, C) = 10 instead of 5 -> (10-5)^2 = 25
|
||||
# total cost = 9 + 16 + 25 = 50
|
||||
cost = solver.distances_cost(suboptimal_solution)
|
||||
self.assertAlmostEqual(cost, 50.0, places=3)
|
||||
|
||||
def test_offsets_cost_optimal_solution(self):
|
||||
solver = OptimizationSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 10)
|
||||
solver.add_offset('C', 'A', 'B', 5, 2)
|
||||
optimal_solution = numpy.array([[0, 0], [10, 0], [5, 2]]).flatten()
|
||||
cost = solver.offsets_cost(optimal_solution)
|
||||
self.assertAlmostEqual(cost, 0.0, places=3)
|
||||
|
||||
def test_offsets_cost_suboptimal_solution(self):
|
||||
solver = OptimizationSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 10)
|
||||
solver.add_offset('C', 'A', 'B', 5, 2)
|
||||
suboptimal_solution = numpy.array([[0, 0], [10, 0], [5, 4]]).flatten()
|
||||
cost = solver.offsets_cost(suboptimal_solution)
|
||||
# The expected position of C is (5, 2), but the actual position is (5, 4),
|
||||
# so the cost should be (distance((5, 4), (5, 2)))^2 = 2^2 = 4
|
||||
self.assertAlmostEqual(cost, 4.0, places=3)
|
||||
|
||||
|
||||
def test_simple_triangle_only_distances_ccw(self):
|
||||
solver = OptimizationSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 3)
|
||||
solver.add_distance('B', 'C', 4)
|
||||
solver.add_distance('A', 'C', 5)
|
||||
|
||||
points = solver.solve()
|
||||
|
||||
numpy.testing.assert_almost_equal(distance(points['A'], points['B']), 3, decimal=2)
|
||||
numpy.testing.assert_almost_equal(distance(points['B'], points['C']), 4, decimal=2)
|
||||
numpy.testing.assert_almost_equal(distance(points['A'], points['C']), 5, decimal=2)
|
||||
|
||||
triangle = numpy.array([points['A'], points['B'], points['C']])
|
||||
self.assertAlmostEqual(poly_area(triangle), 6.0, places=3)
|
||||
|
||||
|
||||
def test_simple_triangle_only_distances_cw(self):
|
||||
solver = OptimizationSolver(['A', 'C', 'B'])
|
||||
solver.add_distance('A', 'C', 3)
|
||||
solver.add_distance('B', 'C', 4)
|
||||
solver.add_distance('A', 'B', 5)
|
||||
|
||||
points = solver.solve()
|
||||
|
||||
numpy.testing.assert_almost_equal(distance(points['A'], points['C']), 3, decimal=2)
|
||||
numpy.testing.assert_almost_equal(distance(points['B'], points['C']), 4, decimal=2)
|
||||
numpy.testing.assert_almost_equal(distance(points['A'], points['B']), 5, decimal=2)
|
||||
|
||||
triangle = numpy.array([points['A'], points['C'], points['B']])
|
||||
self.assertAlmostEqual(poly_area(triangle), -6.0, places=3)
|
||||
|
||||
|
||||
def test_simple_triangle_with_offset(self):
|
||||
solver = OptimizationSolver(['A', 'B', 'C'])
|
||||
solver.add_distance('A', 'B', 10)
|
||||
solver.add_offset('C', 'A', 'B', 5, 2)
|
||||
|
||||
points = solver.solve()
|
||||
|
||||
numpy.testing.assert_almost_equal(distance(points['A'], points['B']), 10, decimal=2)
|
||||
numpy.testing.assert_almost_equal(points['C'], chainage_offset_to_xy(points['A'], points['B'], 5, 2), decimal=2)
|
||||
|
||||
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user