def test_inverse_vs_geographiclib(): glib = Geodesic.WGS84 lats = np.arange(-90, 90, 45) lons = np.arange(-180, 180, 30) vp1 = [(p1, p2) for p1 in lats for p2 in lons] vp2 = [(12.5, 175.2) for _ in range(len(vp1))] g1s = [glib.Inverse(*vp1[i], *vp2[i]) for i in range(len(vp1))] g2s = geod.inverse(*list(zip(*vp1)), *list(zip(*vp2))) s12 = [g1s[i]['s12'] for i in range(len(g1s))] azi1 = [g1s[i]['azi1'] for i in range(len(g1s))] np.testing.assert_allclose(g2s['s12'], s12, rtol=1e-3, atol=1e-3) np.testing.assert_allclose( g2s['azi1'], utils.wrap360deg(np.asarray(azi1)), rtol=1e-3, atol=1e-3 ) # test equal longitudes lats = np.arange(-89, 89, 45) lons = np.arange(-179, 179, 90) vp1 = [(p1, p2) for p1 in lats for p2 in lons] vp2 = [(0, p2) for _ in lats for p2 in lons] g1s = [glib.Inverse(*vp1[i], *vp2[i]) for i in range(len(vp1))] g2s = geod.inverse(*list(zip(*vp1)), *list(zip(*vp2))) s12 = [g1s[i]['s12'] for i in range(len(g1s))] azi1 = [g1s[i]['azi1'] for i in range(len(g1s))] np.testing.assert_allclose(g2s['s12'], s12, rtol=1e-5, atol=1e-5) np.testing.assert_allclose( g2s['azi1'], utils.wrap360deg(np.asarray(azi1)), rtol=1e-3, atol=1e-3 ) # test close longitudes vp1 = [ (0, -179.99999), (0, -179.9999999), (0, -179.9999999), (0, 179.9999999), (12.5, 179.9999999), (12.99999999, 175), (0, 178.9999999), ] vp2 = [ (0, 180), (0, -180), (0, 180), (0, 180), (12.5, 180), (13, 175), (0, 179.9999999), ] g1s = [glib.Inverse(*vp1[i], *vp2[i]) for i in range(len(vp1))] g2s = geod.inverse(*list(zip(*vp1)), *list(zip(*vp2))) s12 = [g1s[i]['s12'] for i in range(len(g1s))] np.testing.assert_allclose(g2s['s12'], s12, rtol=1, atol=1e-2)
def get_gcr_points(lat1, lon1, lat2, lon2, n_points=10): """Discretize gcr between two scalar coordinate points.""" points = [(lat1, lon1)] inv = geod.inverse([lat1], [lon1], [lat2], [lon2]) dist = inv['s12'] / (n_points) for i in range(n_points): dir = geod.direct(lat1, lon1, inv['azi1'], dist) points.append((dir['lat2'][0], dir['lon2'][0])) lat1 = dir['lat2'][0] lon1 = dir['lon2'][0] inv = geod.inverse([lat1], [lon1], [lat2], [lon2]) return points
def test_inverse_base(expect): r = geod.inverse( [-90, -89, 0, 89, 90, 0, 0], [-180, -170, 0, 150, 180, 0, -180], [90, 89, 0, -89, -90, 0, 0], [180, 170, 0, -150, -180, -180, 180], ) t = { 's12': [ 20003931.4586233, 19783938.13763718, 0.0, 19810477.039079, 20003931.4586233, 20003931.4586233, 0.0, ], 'azi1': [0.0, 349.99759033, 0, 149.99358, 0.0, 0.0, 0], 'azi2': [180.0, 349.99759033, 0, 149.99358, 180.0, 180.0, 0], 'iterations': 3, } np.testing.assert_allclose(r['s12'], t['s12'], rtol=1e-7, atol=1e-7) np.testing.assert_allclose(r['azi1'], t['azi1'], rtol=1e-7, atol=1e-7) np.testing.assert_allclose(r['azi2'], t['azi2'], rtol=1e-7, atol=1e-7) expect(r['iterations']) == t['iterations']
def test_direct_large_size(): np.random.seed(42) lats1 = np.random.uniform(-90, 90, 100000) lons1 = np.random.uniform(-180, 180, 100000) brgs = np.random.uniform(0, 360, 100000) dist = np.random.uniform(0, 20e6, 100000) _ = geod.inverse(lats1, lons1, brgs, dist)
def test_inverse_large_size(): np.random.seed(42) lats1 = np.random.uniform(-90, 90, 100000) lons1 = np.random.uniform(-180, 180, 100000) lats2 = np.random.uniform(-90, 90, 100000) lons2 = np.random.uniform(-180, 180, 100000) _ = geod.inverse(lats1, lons1, lats2, lons2)
def test_recursive_routing(self): """Unit test.""" start = (43.5, 7.2) finish = (33.8, 35.5) start_time = dt.datetime.strptime('2020111607', '%Y%m%d%H') gcr = geod.inverse([start[0]], [start[1]], [finish[0]], [finish[1]]) iso = isochrone.Isochrone( count=0, start=start, finish=finish, gcr_azi=gcr['azi1'], lats1=np.array([[start[0]]]), lons1=np.array([[start[1]]]), azi12=np.array([[None]]), s12=np.array([[0]]), azi02=gcr['azi1'], s02=np.array([]), time1=start_time, elapsed=dt.timedelta(seconds=0) ) boat = polars.boat_properties('data/polar-ITA70.csv') model = '2020111600' winds = weather.read_wind_functions(model, 24) params = { 'ROUTER_HDGS_SEGMENTS': 30, 'ROUTER_HDGS_INCREMENTS_DEG': 1, 'ISOCHRONE_EXPECTED_SPEED_KTS': 8, 'ISOCHRONE_RESOLUTION_RAD': 1, 'ISOCHRONE_PRUNE_SECTOR_DEG_HALF': 15, 'ISOCHRONE_PRUNE_SEGMENTS': 5} delta_time_sec = 3600 for i in range(2): iso = router.recursive_routing( iso, boat, winds, delta_time_sec, params, verbose=False) expected_azi02 = np.array([ 100.45741406, 113.20471345, 95.06500765, 117.72590944, 106.25658986]) expected_s02 = np.array([ 39090.76869898, 42504.71278082, 36630.65067096, 43113.25351592, 41573.10032953]) self.assertTrue(np.allclose(iso.azi02, expected_azi02)) self.assertTrue(np.allclose(iso.s02, expected_s02)) return None
def routing(start, finish, boat, winds, start_time, delta_time, steps, params, verbose=False): """ Do full isochrone routing. Parameters: iso1 (Isochrone) - starting isochrone start_point (tuple) - starting point of the route end_point (tuple) - end point of the route x1_coords (tuple) - tuple of arrays (lats, lons) x2_coords (tuple) - tuple of arrays (lats, lons) boat (dict) - boat profile winds (dict) - wind functions start_time (datetime) - start time delta_time (float) - time to move in seconds params (dict) - isochrone calculation parameters Returns: iso (Isochrone) - next isochrone """ gcr = geod.inverse([start[0]], [start[1]], [finish[0]], [finish[1]]) iso = Isochrone( count=0, start=start, finish=finish, gcr_azi=gcr['azi1'], lats1=np.array([[start[0]]]), lons1=np.array([[start[1]]]), azi12=np.array([[None]]), s12=np.array([[0]]), azi02=gcr['azi1'], s02=np.array([]), time1=start_time, elapsed=dt.timedelta(seconds=0) ) for i in range(steps): iso = recursive_routing( iso, boat, winds, delta_time, params, verbose=False) return iso
def test_inverse_near_antipodal(): glib = Geodesic.WGS84 lats1 = [0, 0, 16.24568372, 0] lons1 = [0, -180, 124.84613035, 0] lats2 = [0, 0, -16.70728358, 0.5] lons2 = [180, 180, -55.2234313, 179.7] vInverse = np.vectorize(glib.Inverse) g1s = vInverse(lats1, lons1, lats2, lons2) g2s = geod.inverse(lats1, lons1, lats2, lons2) s12 = [g1s[i]['s12'] for i in range(len(g1s))] np.testing.assert_allclose(g2s['s12'], s12, rtol=1e-10, atol=1e-10)
def test_recursive_isochrone_method(self): """Test isochrone.""" start = (43.5, 7.2) finish = (33.8, 35.5) start_time = dt.datetime.strptime('2020111607', '%Y%m%d%H') gcr = geod.inverse([start[0]], [start[1]], [finish[0]], [finish[1]]) iso = isochrone.Isochrone( count=0, start=start, finish=finish, gcr_azi=gcr['azi1'], lats1=np.array([[start[0]]]), lons1=np.array([[start[1]]]), azi12=np.array([[None]]), s12=np.array([[0]]), azi02=gcr['azi1'], s02=np.array([0]), time1=start_time, elapsed=dt.timedelta(seconds=0) ) boat = polars.boat_properties('data/polar-ITA70.csv') model = '2020111600' winds = weather.read_wind_functions(model, 24) params = { 'ROUTER_HDGS_SEGMENTS': 30, 'ROUTER_HDGS_INCREMENTS_DEG': 1, 'ISOCHRONE_EXPECTED_SPEED_KTS': 8, 'ISOCHRONE_RESOLUTION_RAD': 1, 'ISOCHRONE_PRUNE_SECTOR_DEG_HALF': 15, 'ISOCHRONE_PRUNE_SEGMENTS': 5} delta_time_sec = 3600 result = router.recursive_routing( iso, boat, winds, delta_time_sec, params, verbose=False) expected_s12 = np.array([ [13235.5216, 12843.9842, 13150.357, 13541.8945, 13933.4320], [0., 0., 0., 0., 0.]]) self.assertTrue(np.allclose(result.s12, expected_s12)) self.assertTrue( result.time1, start_time+dt.timedelta(seconds=delta_time_sec)) return None
def recursive_routing(iso1, boat, winds, delta_time, params, verbose=False): """ Progress one isochrone with pruning. Parameters: iso1 (Isochrone) - starting isochrone start_point (tuple) - starting point of the route end_point (tuple) - end point of the route x1_coords (tuple) - tuple of arrays (lats, lons) x2_coords (tuple) - tuple of arrays (lats, lons) boat (dict) - boat profile winds (dict) - wind functions start_time (datetime) - start time delta_time (float) - time to move in seconds params (dict) - isochrone calculation parameters Returns: iso (Isochrone) - next isochrone """ # branch out for multiple headings lats = np.repeat(iso1.lats1, params['ROUTER_HDGS_SEGMENTS'] + 1, axis=1) lons = np.repeat(iso1.lons1, params['ROUTER_HDGS_SEGMENTS'] + 1, axis=1) azi12 = np.repeat(iso1.azi12, params['ROUTER_HDGS_SEGMENTS'] + 1, axis=1) s12 = np.repeat(iso1.s12, params['ROUTER_HDGS_SEGMENTS'] + 1, axis=1) start_lats = np.repeat(iso1.start[0], lats.shape[1]) start_lons = np.repeat(iso1.start[1], lons.shape[1]) # determine new headings - centered around gcrs X0 -> X_prev_step hdgs = iso1.azi02 delta_hdgs = np.linspace( -params['ROUTER_HDGS_SEGMENTS'] * params['ROUTER_HDGS_INCREMENTS_DEG'], +params['ROUTER_HDGS_SEGMENTS'] * params['ROUTER_HDGS_INCREMENTS_DEG'], params['ROUTER_HDGS_SEGMENTS'] + 1) delta_hdgs = np.tile(delta_hdgs, iso1.lats1.shape[1]) hdgs = np.repeat(hdgs, params['ROUTER_HDGS_SEGMENTS'] + 1) hdgs = hdgs - delta_hdgs # move boat with defined headings N_coords x (ROUTER_HDGS_SEGMENTS+1) times move = move_boat_direct(lats[0, :], lons[0, :], hdgs, boat, winds, iso1.time1, delta_time, verbose=False) # create new isochrone before pruning lats = np.vstack((move['lats2'], lats)) lons = np.vstack((move['lons2'], lons)) azi12 = np.vstack((move['azi1'], azi12)) s12 = np.vstack((move['s12'], s12)) # determine gcrs from start to new isochrone gcrs = geod.inverse(start_lats, start_lons, move['lats2'], move['lons2']) # remove those which ended on land is_on_land = globe.is_land(move['lats2'], move['lons2']) gcrs['s12'][is_on_land] = 0 azi02 = gcrs['azi1'] s02 = gcrs['s12'] iso2 = Isochrone( start=iso1.start, finish=iso1.finish, gcr_azi=iso1.gcr_azi, count=iso1.count+1, elapsed=iso1.elapsed+dt.timedelta(seconds=delta_time), time1=iso1.time1+dt.timedelta(seconds=delta_time), lats1=lats, lons1=lons, azi12=azi12, s12=s12, azi02=azi02, s02=s02 ) # ---- pruning isochrone ---- # new gcr azimuth to finish from the current isochrone mean_dist = np.mean(iso2.s02) gcr_point = geod.direct( [iso1.start[0]], [iso1.start[1]], iso1.gcr_azi, mean_dist) new_azi = geod.inverse( gcr_point['lat2'], gcr_point['lon2'], [iso1.finish[0]], [iso1.finish[1]] ) azi0s = np.repeat( new_azi['azi1'], params['ISOCHRONE_PRUNE_SEGMENTS'] + 1) # determine bins delta_hdgs = np.linspace( -params['ISOCHRONE_PRUNE_SECTOR_DEG_HALF'], +params['ISOCHRONE_PRUNE_SECTOR_DEG_HALF'], params['ISOCHRONE_PRUNE_SEGMENTS']+1) bins = azi0s - delta_hdgs bins = np.sort(bins) iso2 = prune_isochrone(iso2, 'azi02', 's02', bins, True) return iso2