@@ -85,6 +85,26 @@ def test_doubled_detour_budget_can_join_previously_rejected_coast(self):
8585 self .assertLessEqual (plan ['coastal_through_length_m' ]/ plan ['shortest_checked_length_m' ], 2. )
8686 self .assert_coastal_band (grid (), start , plan )
8787
88+ def test_clear_coastal_run_does_not_keep_grid_staircases (self ):
89+ for amplitude , period in ((0. , 1000. ), (500. , 2000. )):
90+ with self .subTest (shore_amplitude = amplitude ):
91+ terrain = grid ()
92+ x , y = np .meshgrid (terrain ['xs' ], terrain ['ys' ])
93+ terrain ['heights' ] = np .where (
94+ x <= amplitude * np .sin (y / period * 2 * np .pi ), 20. , - 15. ).tolist ()
95+ start = geo (1200 , - 4000 )
96+ plan = coastal_corridor (terrain , SPEC , start , geo (1200 , 9000 ))
97+ self .assertEqual (plan ['mode' ], 'coastal' )
98+ through = plan ['points' ] + plan ['onward_points' ]
99+ # A clear run needs bounded segment ends, not one turn per
100+ # ten grid cells or every small shoreline indentation.
101+ self .assertLessEqual (len (through ), 4 )
102+ self .assertLess (plan ['coastal_through_length_m' ], 14500. )
103+ self .assertTrue (all (distance (a , b ) <= 7501.
104+ for a , b in zip ([start ]+ through [:- 1 ], through )))
105+ self .assert_coastal_band (terrain , start , plan )
106+ self .assert_swept_safe (terrain , start , dict (points = through ))
107+
88108 def test_safe_backward_frontier_is_rejected (self ):
89109 axis = np .linspace (- 10000 ,10000 ,257 )
90110 x ,y = np .meshgrid (axis ,axis )
@@ -195,14 +215,33 @@ def test_shallow_and_unknown_coast_is_not_a_destination(self):
195215 heights [:, (x > 0 ) & (x < 1700 )] = - 1.
196216 terrain ['heights' ] = heights .tolist ()
197217 plan = coastal_corridor (terrain , SPEC , geo (3000 ,- 4000 ),geo (6500 ,9000 ))
198- self .assertIsNotNone (plan )
199- self .assertEqual (plan ['shallow_water_buffer_m' ],200. )
200- self .assert_swept_safe (terrain ,geo (3000 ,- 4000 ),plan )
201- self .assertGreater (FORWARD .transform (* plan ['points' ][- 1 ])[0 ],1900. )
218+ # The bank blocks the coastal band around real land; use checked open water.
219+ self .assertIsNone (plan )
220+ water = corridor (terrain , SPEC , geo (3000 ,- 4000 ),geo (6500 ,9000 ))
221+ self .assert_swept_safe (terrain ,geo (3000 ,- 4000 ),water )
222+ self .assertGreater (FORWARD .transform (* water ['points' ][- 1 ])[0 ],1900. )
202223 heights [:, (x > 0 ) & (x < 1700 )] = np .nan
203224 terrain ['heights' ] = heights .tolist ()
204225 self .assertIsNone (coastal_corridor (terrain , SPEC , geo (3000 , - 4000 ), geo (6500 , 9000 )))
205226
227+ def test_submerged_banks_do_not_attract_coastal_waypoints (self ):
228+ for actual_land in (False , True ):
229+ with self .subTest (actual_land = actual_land ):
230+ terrain = grid ()
231+ x , y = np .meshgrid (terrain ['xs' ], terrain ['ys' ])
232+ heights = np .where (x <= 0 , - 1. , - 15. )
233+ if actual_land :
234+ heights [x <= - 6000 ] = 20.
235+ terrain ['heights' ] = heights .tolist ()
236+ # The safe side of this bank is open water, kilometres from
237+ # real land. It must not become a scenic coastline to follow.
238+ self .assertIsNone (coastal_corridor (
239+ terrain , SPEC , geo (800 , - 4000 ), geo (800 , 9000 )))
240+ water = corridor (terrain , SPEC , geo (800 , - 4000 ), geo (800 , 9000 ))
241+ self .assertEqual (len (water ['points' ]), 1 )
242+ end_x , _ = FORWARD .transform (* water ['points' ][0 ])
243+ self .assertLess (abs (end_x - 800. ), 1. )
244+
206245 def test_two_point_five_metre_coast_is_available_to_wamv (self ):
207246 terrain = grid ();heights = np .array (terrain ['heights' ])
208247 heights [heights < 0 ]= - 2.5 ;terrain ['heights' ]= heights .tolist ()
0 commit comments