|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +""" |
| 3 | +""" |
| 4 | +from numpy import sin, pi, cos, arctan2, empty, nan, absolute, floor, ones, linspace, interp |
| 5 | +from numba import njit, prange |
| 6 | +from numpy.linalg import lstsq |
| 7 | + |
| 8 | + |
| 9 | +@njit(cache=True, fastmath=True, parallel=False) |
| 10 | +def distance_grid(lon0, lat0, lon1, lat1): |
| 11 | + """ |
| 12 | + Args: |
| 13 | + lon0: |
| 14 | + lat0: |
| 15 | + lon1: |
| 16 | + lat1: |
| 17 | +
|
| 18 | + Returns: |
| 19 | + nan value for far away point, and km for other |
| 20 | + """ |
| 21 | + nb_0 = lon0.shape[0] |
| 22 | + nb_1 = lon1.shape[0] |
| 23 | + dist = empty((nb_0, nb_1)) |
| 24 | + D2R = pi / 180. |
| 25 | + for i in prange(nb_0): |
| 26 | + for j in prange(nb_1): |
| 27 | + dlat = absolute(lat1[j] - lat0[i]) |
| 28 | + if dlat > 15: |
| 29 | + dist[i, j] = nan |
| 30 | + continue |
| 31 | + dlon = absolute(lon1[j] - lon0[i]) |
| 32 | + if dlon > 180: |
| 33 | + dlon = absolute((dlon + 180) % 360 - 180) |
| 34 | + if dlon > 20: |
| 35 | + dist[i, j] = nan |
| 36 | + continue |
| 37 | + sin_dlat = sin((dlat) * 0.5 * D2R) |
| 38 | + sin_dlon = sin((dlon) * 0.5 * D2R) |
| 39 | + cos_lat1 = cos(lat0[i] * D2R) |
| 40 | + cos_lat2 = cos(lat1[j] * D2R) |
| 41 | + a_val = sin_dlon ** 2 * cos_lat1 * cos_lat2 + sin_dlat ** 2 |
| 42 | + dist[i, j] = 6370.997 * 2 * arctan2(a_val ** 0.5, (1 - a_val) ** 0.5) |
| 43 | + return dist |
| 44 | + |
| 45 | + |
| 46 | +@njit(cache=True, fastmath=True) |
| 47 | +def distance(lon0, lat0, lon1, lat1): |
| 48 | + D2R = pi / 180. |
| 49 | + sin_dlat = sin((lat1 - lat0) * 0.5 * D2R) |
| 50 | + sin_dlon = sin((lon1 - lon0) * 0.5 * D2R) |
| 51 | + cos_lat1 = cos(lat0 * D2R) |
| 52 | + cos_lat2 = cos(lat1 * D2R) |
| 53 | + a_val = sin_dlon ** 2 * cos_lat1 * cos_lat2 + sin_dlat ** 2 |
| 54 | + return 6370997.0 * 2 * arctan2(a_val ** 0.5, (1 - a_val) ** 0.5) |
| 55 | + |
| 56 | + |
| 57 | +@njit(cache=True) |
| 58 | +def distance_vincenty(lon0, lat0, lon1, lat1): |
| 59 | + """ better than haversine but buggy ??""" |
| 60 | + D2R = pi / 180. |
| 61 | + dlon = (lon1 - lon0) * D2R |
| 62 | + cos_dlon = cos(dlon) |
| 63 | + cos_lat1 = cos(lat0 * D2R) |
| 64 | + cos_lat2 = cos(lat1 * D2R) |
| 65 | + sin_lat1 = sin(lat0 * D2R) |
| 66 | + sin_lat2 = sin(lat1 * D2R) |
| 67 | + return 6370997.0 * arctan2( |
| 68 | + ((cos_lat2 * sin(dlon) ** 2) + (cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon) ** 2) ** .5, |
| 69 | + sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos_dlon) |
| 70 | + |
| 71 | + |
| 72 | +@njit(cache=True, fastmath=True) |
| 73 | +def interp2d_geo(x_g, y_g, z_g, m_g, x, y): |
| 74 | + """For geographic grid, test of cicularity |
| 75 | + Maybe test if we are out of bounds |
| 76 | + """ |
| 77 | + x_ref = x_g[0] |
| 78 | + y_ref = y_g[0] |
| 79 | + x_step = x_g[1] - x_ref |
| 80 | + y_step = y_g[1] - y_ref |
| 81 | + nb_x = x_g.shape[0] |
| 82 | + is_circular = (x_g[-1] + x_step) % 360 == x_g[0] % 360 |
| 83 | + z = empty(x.shape) |
| 84 | + for i in prange(x.size): |
| 85 | + x_ = (x[i] - x_ref) / x_step |
| 86 | + y_ = (y[i] - y_ref) / y_step |
| 87 | + i0 = int(floor(x_)) |
| 88 | + i1 = i0 + 1 |
| 89 | + if is_circular: |
| 90 | + xd = (x_ - i0) |
| 91 | + i0 %= nb_x |
| 92 | + i1 %= nb_x |
| 93 | + j0 = int(floor(y_)) |
| 94 | + j1 = j0 + 1 |
| 95 | + yd = (y_ - j0) |
| 96 | + z00 = z_g[i0, j0] |
| 97 | + z01 = z_g[i0, j1] |
| 98 | + z10 = z_g[i1, j0] |
| 99 | + z11 = z_g[i1, j1] |
| 100 | + if m_g[i0, j0] or m_g[i0, j1] or m_g[i1, j0] or m_g[i1, j1]: |
| 101 | + z[i] = nan |
| 102 | + else: |
| 103 | + z[i] = (z00 * (1 - xd) + (z10 * xd)) * (1 - yd) + (z01 * (1 - xd) + z11 * xd) * yd |
| 104 | + return z |
| 105 | + |
| 106 | +@njit(cache=True, fastmath=True, parallel=True) |
| 107 | +def custom_convolution(data, mask, kernel): |
| 108 | + """do sortin at high lattitude big part of value are masked""" |
| 109 | + nb_x = kernel.shape[0] |
| 110 | + demi_x = int((nb_x - 1) / 2) |
| 111 | + demi_y = int((kernel.shape[1] - 1) / 2) |
| 112 | + out = empty(data.shape[0] - nb_x + 1) |
| 113 | + for i in prange(out.shape[0]): |
| 114 | + if mask[i + demi_x, demi_y] == 1: |
| 115 | + w = (mask[i:i + nb_x] * kernel).sum() |
| 116 | + if w != 0: |
| 117 | + out[i] = (data[i:i + nb_x] * kernel).sum() / w |
| 118 | + else: |
| 119 | + out[i] = nan |
| 120 | + else: |
| 121 | + out[i] = nan |
| 122 | + return out |
| 123 | + |
| 124 | + |
| 125 | +@njit(cache=True) |
| 126 | +def fit_circle(x_vec, y_vec): |
| 127 | + nb_elt = x_vec.shape[0] |
| 128 | + p_inon_x = empty(nb_elt) |
| 129 | + p_inon_y = empty(nb_elt) |
| 130 | + |
| 131 | + x_mean = x_vec.mean() |
| 132 | + y_mean = y_vec.mean() |
| 133 | + |
| 134 | + norme = (x_vec - x_mean) ** 2 + (y_vec - y_mean) ** 2 |
| 135 | + norme_max = norme.max() |
| 136 | + scale = norme_max ** .5 |
| 137 | + |
| 138 | + # Form matrix equation and solve it |
| 139 | + # Maybe put f4 |
| 140 | + datas = ones((nb_elt, 3)) |
| 141 | + datas[:, 0] = 2. * (x_vec - x_mean) / scale |
| 142 | + datas[:, 1] = 2. * (y_vec - y_mean) / scale |
| 143 | + |
| 144 | + (center_x, center_y, radius), residuals, rank, s = lstsq(datas, norme / norme_max) |
| 145 | + |
| 146 | + # Unscale data and get circle variables |
| 147 | + radius += center_x ** 2 + center_y ** 2 |
| 148 | + radius **= .5 |
| 149 | + center_x *= scale |
| 150 | + center_y *= scale |
| 151 | + # radius of fitted circle |
| 152 | + radius *= scale |
| 153 | + # center X-position of fitted circle |
| 154 | + center_x += x_mean |
| 155 | + # center Y-position of fitted circle |
| 156 | + center_y += y_mean |
| 157 | + |
| 158 | + # area of fitted circle |
| 159 | + c_area = (radius ** 2) * pi |
| 160 | + # Find distance between circle center and contour points_inside_poly |
| 161 | + for i_elt in range(nb_elt): |
| 162 | + # Find distance between circle center and contour points_inside_poly |
| 163 | + dist_poly = ((x_vec[i_elt] - center_x) ** 2 + (y_vec[i_elt] - center_y) ** 2) ** .5 |
| 164 | + # Indices of polygon points outside circle |
| 165 | + # p_inon_? : polygon x or y points inside & on the circle |
| 166 | + if dist_poly > radius: |
| 167 | + p_inon_y[i_elt] = center_y + radius * (y_vec[i_elt] - center_y) / dist_poly |
| 168 | + p_inon_x[i_elt] = center_x - (center_x - x_vec[i_elt]) * (center_y - p_inon_y[i_elt]) / ( |
| 169 | + center_y - y_vec[i_elt]) |
| 170 | + else: |
| 171 | + p_inon_x[i_elt] = x_vec[i_elt] |
| 172 | + p_inon_y[i_elt] = y_vec[i_elt] |
| 173 | + |
| 174 | + # Area of closed contour/polygon enclosed by the circle |
| 175 | + p_area_incirc = 0 |
| 176 | + p_area = 0 |
| 177 | + for i_elt in range(nb_elt - 1): |
| 178 | + # Indices of polygon points outside circle |
| 179 | + # p_inon_? : polygon x or y points inside & on the circle |
| 180 | + p_area_incirc += p_inon_x[i_elt] * p_inon_y[1 + i_elt] - p_inon_x[i_elt + 1] * p_inon_y[i_elt] |
| 181 | + # Shape test |
| 182 | + # Area and centroid of closed contour/polygon |
| 183 | + p_area += x_vec[i_elt] * y_vec[1 + i_elt] - x_vec[1 + i_elt] * y_vec[i_elt] |
| 184 | + p_area = abs(p_area) * .5 |
| 185 | + p_area_incirc = abs(p_area_incirc) * .5 |
| 186 | + |
| 187 | + a_err = (c_area - 2 * p_area_incirc + p_area) * 100. / c_area |
| 188 | + return center_x, center_y, radius, a_err |
| 189 | + |
| 190 | + |
| 191 | +@njit(cache=True, fastmath=True) |
| 192 | +def uniform_resample(x_val, y_val, num_fac=2, fixed_size=None): |
| 193 | + """ |
| 194 | + Resample contours to have (nearly) equal spacing |
| 195 | + x_val, y_val : input contour coordinates |
| 196 | + num_fac : factor to increase lengths of output coordinates |
| 197 | + """ |
| 198 | + # Get distances |
| 199 | + dist = empty(x_val.shape) |
| 200 | + dist[0] = 0 |
| 201 | + dist[1:] = distance(x_val[:-1], y_val[:-1], x_val[1:], y_val[1:]) |
| 202 | + # To be still monotonous (dist is store in m) |
| 203 | + dist[1:][dist[1:]<1e-3] = 1e-3 |
| 204 | + dist = dist.cumsum() |
| 205 | + # Get uniform distances |
| 206 | + if fixed_size is None: |
| 207 | + fixed_size = dist.size * num_fac |
| 208 | + d_uniform = linspace(0, dist[-1], fixed_size) |
| 209 | + x_new = interp(d_uniform, dist, x_val) |
| 210 | + y_new = interp(d_uniform, dist, y_val) |
| 211 | + return x_new, y_new |
0 commit comments