-
Notifications
You must be signed in to change notification settings - Fork 1
/
create_cross_points.py_org
executable file
·156 lines (119 loc) · 5.12 KB
/
create_cross_points.py_org
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from recenter_utils import haversine
import numpy as np
import matplotlib.pyplot as plt
import sys
import constants
from geopy.distance import great_circle
def get_valid_cross_points(lons1d,lats1d,lonpoints,latpoints):
minlon = np.min(lons1d)
maxlon = np.max(lons1d)
minlat = np.min(lats1d)
maxlat = np.max(lats1d)
validpoints = np.zeros(len(lonpoints),dtype=bool)
for i,(lon,lat) in enumerate(zip(lonpoints,latpoints)):
if lon<=maxlon and lon>=minlon and lat<=maxlat and lat>=minlat:
validpoints[i] = 1
return validpoints
def create_start_end_cross(center,inbound_outbound,angle):
"""
This function takes the TC center, inbound/outbound distance in km, and angle relative to a meridian, and calculates the cross section start and end points
INPUTS:
1: center, tuple if tc center, (lat,lon)
2: inbound_outbound, how far in km you want the cross section to go out on each side of the TC center
3: angle, angle in degrees relative to a meridian that you want the cross section to go, can be between -90 and 90
"""
#Input checks
if type(center) is not tuple:
print("'center' input needs to be a tuple, exitting...")
sys.exit()
else:
if len(center) != 2:
print("'center' tuple must be of length 2, exitting...")
sys.exit()
if np.abs(angle) > 90.:
print("Angle has to be between -90 and 90, exitting...")
if angle == -90:
angle = 90
dang=0.01 #Degrees
angd=0.0
#right side of cross section
while True:
dlat=angd*np.sin(np.deg2rad(angle))
dlon=angd*np.cos(np.deg2rad(angle))
lat = center[0]+dlat
lon = center[1]+dlon
dist = haversine(center[1],center[0],lon,lat)
if dist > inbound_outbound:
break
angd+=dang
end=(lat,lon)
#left side of cross section
angle = angle-180.
if angle < -180.:
angle = angle + 360
while True:
dlat=angd*np.sin(np.deg2rad(angle))
dlon=angd*np.cos(np.deg2rad(angle))
lat = center[0]+dlat
lon = center[1]+dlon
dist = haversine(center[1],center[0],lon,lat)
if dist > inbound_outbound:
break
angd+=dang
start=(lat,lon)
return start,end
def create_cross_latlon_points(lons2d,lats2d,lon_center,lat_center,inbound_outbound,angle):
'''
This function calculates the lat/lon points for a cross section as well as the radial distance of these points from the center point of the cross section.
INPUTS
------
lons2d : numpy.ndarray
2d mesh of longitudes
lats2d : numpy.ndarray
2d mesh of latitudes
lon_center : float
longitude of the center that the cross section will go through
lat_center : float
latitude of the center that the cross section will go through
inbound_outbound : float
distance from the center on both sides of the center in km that the cross section will go out (roughly)
angle : float
angle relative to a meridian that the ross section will go through, should be between -90 and 90
OUTPUTS
-------
lonpoints : list
list of longitude points for the cross section path
latpoints : list
list of latitude points for the cross section path
distance_from_center : list
list of distances (in km) of the latpoint/lonpoint pairs from the center of the cross section
'''
#Get 1d arrays of lats and lons
lats1d = lats2d[:,0]
lons1d = lons2d[0,:]
#Find start and end points for cross section. This will be through the center and
# a certain distance (inbound_outbound) from the center on both sides
start,end = create_start_end_cross((lat_center,lon_center),inbound_outbound,angle)
#Determine number of points in the cross section depending on grid spacing
dy = np.max(lats2d[1::]-lats2d[0:-1])*2*np.pi*constants.RE/360.0
npoints = int(np.floor(great_circle(start,end).meters/dy))
#Set up points for cross section
distance = np.sqrt((end[0]-start[0])**2+(end[1]-start[1])**2)
dr = distance/(npoints-1)
theta = np.arctan2(end[0]-start[0],end[1]-start[1])
dlat = dr*np.sin(theta)
dlon = dr*np.cos(theta)
#Lat and lon points
latpoints = np.array([ start[0]+i*dlat for i in range(npoints)])
lonpoints = np.array([ start[1]+i*dlon for i in range(npoints)])
#Check that all lats and lons are within domain
valid_points = get_valid_cross_points(lons1d,lats1d,lonpoints,latpoints)
if not len(lonpoints) == np.sum(valid_points):
print("inbound_outbound distance wanted for cross section went off the available grid, cutting it to be shorter so it will fit on grid")
#Only take lat and lon points that are within the domain, otherwise the interpolation will fail
lonpoints = lonpoints[np.ix_(valid_points)]
latpoints = latpoints[np.ix_(valid_points)]
#Calculate the distance of each lat/lon point form the cross section center
distance_from_center = np.array([ haversine(lonpoints[i],latpoints[i],lon_center,lat_center) for i in range(len(latpoints)) ])
distance_from_center[0:int(np.floor(len(distance_from_center)/2))] = -distance_from_center[0:int(np.floor(len(distance_from_center)/2))]
return lonpoints,latpoints,distance_from_center