def test_great_distance_scalars(self): # One decimal degree at the equator is about 111.32km latitude_start = 0. latitude_end = 0. longitude_start = 50. longitude_end = 52. gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.round(gd['distance'] / 1000, 2) == 111.32 * 2 # One decimal degree is 111000m latitude_start = 0. latitude_end = 0. longitude_start = [49., 75.] longitude_end = [50., 76.] gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.allclose(gd["distance"] / 1000, 111.32) latitude_start = np.nan latitude_end = np.nan longitude_start = np.nan longitude_end = np.nan gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.isnan(gd['distance'])
def test_great_distance_numpy(self): latitude_start = np.asarray([0.]) latitude_end = np.asarray([0.]) longitude_start = np.asarray([50.]) longitude_end = np.asarray([52.]) gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.round(gd['distance'] / 1000, 2) == 111.32 * 2 latitude_start = np.asarray([0.]) latitude_end = np.asarray([0.]) longitude_start = 50. longitude_end = 52. gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.round(gd['distance'] / 1000, 2) == 111.32 * 2
def great_circle(start_point, end_point, n_points): # Check if input are Points if start_point is None or end_point is None: raise Exception('geom is required') if start_point.type != 'Point' or end_point.type != 'Point': raise Exception('start_point and end_point should be a LineString') start_coords = list(geojson.utils.coords(start_point)) end_coords = list(geojson.utils.coords(end_point)) distance_dict = pygc.great_distance( start_latitude=start_coords[0][1], start_longitude=start_coords[0][0], end_latitude=end_coords[0][1], end_longitude=end_coords[0][0], ) segments = linspace(start=0, stop=distance_dict['distance'], num=n_points) points_dict = pygc.great_circle( distance=segments, azimuth=distance_dict['azimuth'], latitude=start_coords[0][1], longitude=start_coords[0][0], ) coords = [] for i in range(n_points): coords.append( [points_dict['longitude'][i], points_dict['latitude'][i]]) return geojson.LineString(coords, precision=PRECISION)
def distance_to_point(point_a, point_b): """Calculate distance between to points """ result = great_distance(start_latitude=point_a.lat, start_longitude=point_a.lng, end_latitude=point_b.lat, end_longitude=point_b.lng) return int(result['distance'])
def heading_to_point(point_a, point_b): """Calculate heading between two points """ result = great_distance(start_latitude=point_a.lat, start_longitude=point_a.lng, end_latitude=point_b.lat, end_longitude=point_b.lng) return int(result['azimuth'])
def grcrcl2(startlong, startlat, endlong, endlat): res = great_distance(start_latitude=startlat, start_longitude=startlong, end_latitude=endlat, end_longitude=endlong) return float(res['distance'])
def test_great_distance_empty_numpy(self): latitude_start = np.ma.asarray([]) latitude_end = np.ma.asarray([]) longitude_start = np.ma.asarray([]) longitude_end = np.ma.asarray([]) gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.all(gd['distance'].mask == True) # noqa assert np.all(gd['azimuth'].mask == True) # noqa assert np.all(gd['reverse_azimuth'].mask == True) # noqa
def test_great_distance_numpy(self): # One decimal degree is 111000m latitude_start = 0. latitude_end = 0. longitude_start = [49., 75.] longitude_end = [50., 76.] gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.allclose(gd["distance"] / 1000, 111.32)
def test_great_distance_scalars(self): # One decimal degree at the equator is about 111.32km latitude_start = 0. latitude_end = 0. longitude_start = 50. longitude_end = 52. gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert round(gd['distance'] / 1000, 2) == 111.32 * 2
def _reproject(lats, lngs, latA, lngA, latB, lngB): """ lat,lng -- wgs84 input arrays latA,lngA,latB,lngB -- ref coordinates return -- x,y numpy array """ lats = np.array(lats) lngs = np.array(lngs) vincenty = great_distance(start_latitude=latA, start_longitude=lngA, end_latitude=latB, end_longitude=lngB) alpha = vincenty['azimuth'] p1 = pyproj.Proj(proj='longlat', datum='WGS84') if alpha == 0 or alpha == 180 or (latA == 0 and (alpha == 90 or alpha == 270)) or ( latA >= 90 or latA <= -90): # TODO: Use transverse mercator / simple mercator when appropriate, else libproj4 will throw # "RuntimeError: lat_1=lat_2 or lat_1=0 or lat_2=90" raise NotImplementedError( "Proj.4 doesn't support omerc projection perfectly aligned with a meridian, perfectly aligned with equator, or at pole" ) if alpha <= 90 or alpha > 270: p2 = pyproj.Proj( proj='omerc', lat_0=latA, lonc=lngA, alpha=alpha, gamma=0, ) xs, ys = pyproj.transform(p1, p2, lngs, lats) else: # Workaround bug in Proj.4 when alpha is in south direction: https://github.com/OSGeo/proj.4/issues/331 # For south facing angles, use opposite north facing direction, and flip results alpha = (alpha - 180) % 360 p2 = pyproj.Proj( proj='omerc', lat_0=latA, lonc=lngA, alpha=alpha, gamma=0, ) xs, ys = pyproj.transform(p1, p2, lngs, lats) xs = -xs ys = -ys return xs, ys
def test_great_distance_masked_numpy(self): with self.assertRaises(ValueError): latitude_start = np.ma.asarray([0.]) latitude_end = 0. longitude_start = 50. longitude_end = 52. great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) latitude_start = np.ma.asarray([0.]) latitude_end = np.ma.asarray([0.]) longitude_start = np.ma.asarray([50.]) longitude_end = np.ma.asarray([52.]) gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.round(gd['distance'] / 1000, 2) == 111.32 * 2 xmask = np.load(os.path.join(os.path.dirname(__file__), 'xmask.npy')) ymask = np.load(os.path.join(os.path.dirname(__file__), 'ymask.npy')) xdata = np.load(os.path.join(os.path.dirname(__file__), 'x.npy')) x = np.ma.fix_invalid(xdata, mask=xmask) ydata = np.load(os.path.join(os.path.dirname(__file__), 'y.npy')) y = np.ma.fix_invalid(ydata, mask=ymask) gd = great_distance(start_latitude=y[0:-1], start_longitude=x[0:-1], end_latitude=y[1:], end_longitude=x[1:]) latitude_start = np.ma.MaskedArray(np.ma.asarray([0.]), mask=[1]) latitude_end = np.ma.MaskedArray(np.ma.asarray([0.]), mask=[1]) longitude_start = np.ma.MaskedArray(np.ma.asarray([50.]), mask=[1]) longitude_end = np.ma.MaskedArray(np.ma.asarray([52.]), mask=[1]) gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert np.all(gd['distance'].mask == True) # noqa assert np.all(gd['azimuth'].mask == True) # noqa assert np.all(gd['reverse_azimuth'].mask == True) # noqa latitude_start = np.ma.MaskedArray(np.ma.asarray([0., 1.]), mask=[1, 0]) latitude_end = np.ma.MaskedArray(np.ma.asarray([0., 1.]), mask=[1, 0]) longitude_start = np.ma.MaskedArray(np.ma.asarray([49., 75.]), mask=[1, 0]) longitude_end = np.ma.MaskedArray(np.ma.asarray([50., 76.]), mask=[1, 0]) gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert gd['distance'].mask.tolist() == [True, False] assert gd['azimuth'].mask.tolist() == [True, False] assert gd['reverse_azimuth'].mask.tolist() == [True, False] latitude_start = np.ma.MaskedArray(np.ma.asarray([0., 1.]), mask=[1, 1]) latitude_end = np.ma.MaskedArray(np.ma.asarray([0., 1.]), mask=[1, 1]) longitude_start = np.ma.MaskedArray(np.ma.asarray([49., 75.]), mask=[1, 1]) longitude_end = np.ma.MaskedArray(np.ma.asarray([50., 76.]), mask=[1, 1]) gd = great_distance(start_latitude=latitude_start, start_longitude=longitude_start, end_latitude=latitude_end, end_longitude=longitude_end) assert gd['distance'].mask.tolist() == [True, True] assert gd['azimuth'].mask.tolist() == [True, True] assert gd['reverse_azimuth'].mask.tolist() == [True, True]
def grcrcl2(startlong, startlat, endlong, endlat): """ Geodesic distance computation with Pygc https://github.com/axiom-data-science/pygc Geodesic distance calculations using Vincenty's formulae: http://en.wikipedia.org/wiki/Vincenty%27s_formulae Default values used (WGS84): https://github.com/axiom-data-science/pygc/blob/master/pygc/gc.py - Semi-major axis: 6378137.0 - Semi-minor axis: 6356752.3142 Therefore: - Flattening: 0.0033528106718309896 - Inverse flattening: 298.2572229328697 """ res = great_distance(start_latitude=startlat, start_longitude=startlong, end_latitude=endlat, end_longitude=endlong) return float(res['distance'])
def to_dataframe(self, clean_cols=True, clean_rows=True): zvar = self.z_axes()[0] zs = len(self.dimensions[zvar.dimensions[0]]) # Profiles pvar = self.get_variables_by_attributes(cf_role='profile_id')[0] try: p = normalize_array(pvar) except ValueError: p = np.asarray(list(range(len(pvar))), dtype=np.integer) ps = p.size p = p.repeat(zs) logger.debug(['profile data size: ', p.size]) # Z z = generic_masked(zvar[:], attrs=self.vatts(zvar.name)).round(5) try: z = np.tile(z, ps) except ValueError: z = z.flatten() logger.debug(['z data size: ', z.size]) # T tvar = self.t_axes()[0] t = nc4.num2date(tvar[:], tvar.units, getattr(tvar, 'calendar', 'standard')) if isinstance(t, datetime): # Size one t = np.array([t.isoformat()], dtype='datetime64') t = t.repeat(zs) logger.debug(['time data size: ', t.size]) # X xvar = self.x_axes()[0] x = generic_masked(xvar[:].repeat(zs), attrs=self.vatts(xvar.name)).round(5) logger.debug(['x data size: ', x.size]) # Y yvar = self.y_axes()[0] y = generic_masked(yvar[:].repeat(zs), attrs=self.vatts(yvar.name)).round(5) logger.debug(['y data size: ', y.size]) # Distance d = np.ma.zeros(y.size, dtype=np.float64) d[1:] = great_distance(start_latitude=y[0:-1], end_latitude=y[1:], start_longitude=x[0:-1], end_longitude=x[1:])['distance'] d = generic_masked(np.cumsum(d), minv=0).round(2) logger.debug(['distance data size: ', d.size]) df_data = {'t': t, 'x': x, 'y': y, 'z': z, 'profile': p, 'distance': d} building_index_to_drop = np.ones(t.size, dtype=bool) extract_vars = list(set(self.data_vars() + self.ancillary_vars())) for i, dvar in enumerate(extract_vars): vdata = np.ma.fix_invalid( np.ma.MaskedArray(dvar[:].round(3).flatten())) building_index_to_drop = (building_index_to_drop == True) & ( vdata.mask == True) # noqa df_data[dvar.name] = vdata df = pd.DataFrame(df_data) # Drop all data columns with no data if clean_cols: df = df.dropna(axis=1, how='all') # Drop all data rows with no data variable data if clean_rows: df = df.iloc[~building_index_to_drop] return df
def to_dataframe(self, clean_cols=True, clean_rows=True): # The index variable (trajectory_index) is identified by having an # attribute with name of instance_dimension whose value is the instance # dimension name (trajectory in this example). The index variable must # have the profile dimension as its sole dimension, and must be type # integer. Each value in the index variable is the zero-based trajectory # index that the profile belongs to i.e. profile p belongs to trajectory # i=trajectory_index(p), as in section H.2.5. r_index_var = self.get_variables_by_attributes( instance_dimension=lambda x: x is not None)[0] p_dim = self.dimensions[r_index_var.dimensions[0]] # Profile dimension r_dim = self.dimensions[ r_index_var.instance_dimension] # Trajectory dimension # The count variable (row_size) contains the number of elements for # each profile, which must be written contiguously. The count variable # is identified by having an attribute with name sample_dimension whose # value is the sample dimension (obs in this example) being counted. It # must have the profile dimension as its sole dimension, and must be # type integer o_index_var = self.get_variables_by_attributes( sample_dimension=lambda x: x is not None)[0] o_dim = self.dimensions[ o_index_var.sample_dimension] # Sample dimension try: rvar = self.get_variables_by_attributes(cf_role='trajectory_id')[0] traj_indexes = normalize_array(rvar) assert traj_indexes.size == r_dim.size except BaseException: logger.warning( 'Could not pull trajectory values a variable with "cf_role=trajectory_id", using a computed range.' ) traj_indexes = np.arange(r_dim.size) try: pvar = self.get_variables_by_attributes(cf_role='profile_id')[0] profile_indexes = normalize_array(pvar) assert profile_indexes.size == p_dim.size except BaseException: logger.warning( 'Could not pull profile values from a variable with "cf_role=profile_id", using a computed range.' ) profile_indexes = np.arange(p_dim.size) # Profile dimension tvars = self.t_axes() if len(tvars) > 1: tvar = [ v for v in self.t_axes() if v.dimensions == ( p_dim.name, ) and getattr(v, 'axis', '').lower() == 't' ][0] else: tvar = tvars[0] xvars = self.x_axes() if len(xvars) > 1: xvar = [ v for v in self.x_axes() if v.dimensions == ( p_dim.name, ) and getattr(v, 'axis', '').lower() == 'x' ][0] else: xvar = xvars[0] yvars = self.y_axes() if len(yvars) > 1: yvar = [ v for v in self.y_axes() if v.dimensions == ( p_dim.name, ) and getattr(v, 'axis', '').lower() == 'y' ][0] else: yvar = yvars[0] zvars = self.z_axes() if len(zvars) > 1: zvar = [ v for v in self.z_axes() if v.dimensions == ( o_dim.name, ) and getattr(v, 'axis', '').lower() == 'z' ][0] else: zvar = zvars[0] p = np.ma.masked_all(o_dim.size, dtype=profile_indexes.dtype) r = np.ma.masked_all(o_dim.size, dtype=traj_indexes.dtype) t = np.ma.masked_all(o_dim.size, dtype=tvar.dtype) x = np.ma.masked_all(o_dim.size, dtype=xvar.dtype) y = np.ma.masked_all(o_dim.size, dtype=yvar.dtype) si = 0 for i in np.arange(profile_indexes.size): ei = si + o_index_var[i] p[si:ei] = profile_indexes[i] r[si:ei] = traj_indexes[r_index_var[i]] t[si:ei] = tvar[i] x[si:ei] = xvar[i] y[si:ei] = yvar[i] si = ei t_mask = False tfill = get_fill_value(tvar) if tfill is not None: t_mask = np.copy(np.ma.getmaskarray(t)) t[t_mask] = 1 t = np.ma.MaskedArray( nc4.num2date(t, tvar.units, getattr(tvar, 'calendar', 'standard'))) # Patch the time variable back to its original mask, since num2date # breaks any missing/fill values t[t_mask] = np.ma.masked # X and Y x = generic_masked(x, minv=-180, maxv=180).round(5) y = generic_masked(y, minv=-90, maxv=90).round(5) # Distance d = np.ma.zeros(o_dim.size, dtype=np.float64) d[1:] = great_distance(start_latitude=y[0:-1], end_latitude=y[1:], start_longitude=x[0:-1], end_longitude=x[1:])['distance'] d = generic_masked(np.cumsum(d), minv=0).round(2) # Sample dimension z = generic_masked(zvar[:].flatten(), attrs=self.vatts(zvar.name)).round(5) df_data = { 't': t, 'x': x, 'y': y, 'z': z, 'trajectory': r, 'profile': p, 'distance': d } building_index_to_drop = np.ones(o_dim.size, dtype=bool) extract_vars = list(set(self.data_vars() + self.ancillary_vars())) for i, dvar in enumerate(extract_vars): # Profile dimensions if dvar.dimensions == (p_dim.name, ): vdata = np.ma.masked_all(o_dim.size, dtype=dvar.dtype) si = 0 for j in np.arange(profile_indexes.size): ei = si + o_index_var[j] vdata[si:ei] = dvar[j] si = ei # Sample dimensions elif dvar.dimensions == (o_dim.name, ): vdata = generic_masked(dvar[:].flatten(), attrs=self.vatts(dvar.name)).round(3) else: logger.warning( "Skipping variable {}... it didn't seem like a data variable" .format(dvar)) building_index_to_drop = (building_index_to_drop == True) & ( vdata.mask == True) # noqa df_data[dvar.name] = vdata df = pd.DataFrame(df_data) # Drop all data columns with no data if clean_cols: df = df.dropna(axis=1, how='all') # Drop all data rows with no data variable data if clean_rows: df = df.iloc[~building_index_to_drop] return df
def location_test(lon: Sequence[N], lat: Sequence[N], bbox: Tuple[N, N, N, N] = (-180, -90, 180, 90), range_max: N = None) -> np.ma.core.MaskedArray: """Checks that a location is within reasonable bounds. Checks that longitude and latitude are within reasonable bounds defaulting to lon = [-180, 180] and lat = [-90, 90]. Optionally, check for a maximum range parameter in great circle distance defaulting to meters which can also use a unit from the quantities library. Missing and masked data is flagged as UNKNOWN. Args: lon: Longitudes as a numeric numpy array or a list of numbers. lat: Latitudes as a numeric numpy array or a list of numbers. bbox: A length 4 tuple expressed in (minx, miny, maxx, maxy) [optional]. range_max: Maximum allowed range expressed in geodesic curve distance (meters). Returns: A masked array of flag values equal in size to that of the input. """ bboxnt = namedtuple('BBOX', 'minx miny maxx maxy') if bbox is not None: assert isfixedlength(bbox, 4) bbox = bboxnt(*bbox) with warnings.catch_warnings(): warnings.simplefilter("ignore") lat = np.ma.masked_invalid(np.array(lat).astype(np.floating)) lon = np.ma.masked_invalid(np.array(lon).astype(np.floating)) if lon.shape != lat.shape: raise ValueError( 'Lon ({0.shape}) and lat ({1.shape}) are different shapes'.format( lon, lat)) # Save original shape original_shape = lon.shape lon = lon.flatten() lat = lat.flatten() # Start with everything as passing (1) flag_arr = np.ma.ones(lon.size, dtype='uint8') # If either lon or lat are masked we just set the flag to MISSING mloc = lon.mask & lat.mask flag_arr[mloc] = QartodFlags.MISSING # If there is only one masked value fail the location test mismatch = lon.mask != lat.mask flag_arr[mismatch] = QartodFlags.FAIL if range_max is not None and lon.size > 1: # Calculating the great_distance between each point # Flag suspect any distance over range_max d = np.ma.zeros(lon.size, dtype=np.float64) d[1:] = great_distance(start_latitude=lat[:-1], end_latitude=lat[1:], start_longitude=lon[:-1], end_longitude=lon[1:])['distance'] flag_arr[d > range_max] = QartodFlags.SUSPECT # Ignore warnings when comparing NaN values even though they are masked # https://github.com/numpy/numpy/blob/master/doc/release/1.8.0-notes.rst#runtime-warnings-when-comparing-nan-numbers with np.errstate(invalid='ignore'): flag_arr[(lon < bbox.minx) | (lat < bbox.miny) | (lon > bbox.maxx) | (lat > bbox.maxy)] = QartodFlags.FAIL return flag_arr.reshape(original_shape)
import vincenty import pygc import random for i in range(1000): lat1 = random.uniform(-80, 80) lng1 = random.uniform(-180, 180) lat2 = random.uniform(-80, 80) lng2 = random.uniform(-80, 80) d, a_initial, a_final = vincenty.dist_bear_vincenty(lat1, lng1, lat2, lng2) assert a_final >= 0.0 # print d, a_initial, a_final p = pygc.great_distance(start_latitude=lat1, start_longitude=lng1, end_latitude=lat2, end_longitude=lng2) # print p assert math.fabs(d * 1000.0 - p['distance']) < .1 assert math.fabs(a_initial - p['azimuth']) < 1e-6 assert math.fabs(a_final - p['reverse_azimuth']) < 1e-6 print 'direct' for i in range(1000): lat = random.uniform(-80, 80) lng = random.uniform(-180, 180) dist = random.uniform(10, 1000000) bearing = random.uniform(-180, 180)
def to_dataframe(self, clean_cols=True, clean_rows=True): # Z zvar = self.z_axes()[0] z = np.ma.fix_invalid(np.ma.MaskedArray(zvar[:])) z = z.flatten().round(5) logger.debug(['z data size: ', z.size]) # T tvar = self.t_axes()[0] t = np.ma.MaskedArray(nc4.num2date(tvar[:], tvar.units, getattr(tvar, 'calendar', 'standard'))).flatten() # Patch the time variable back to its original mask, since num2date # breaks any missing/fill values if hasattr(tvar[0], 'mask'): t.mask = tvar[:].mask logger.debug(['time data size: ', t.size]) # X xvar = self.x_axes()[0] x = np.ma.fix_invalid(np.ma.MaskedArray(xvar[:])).flatten().round(5) logger.debug(['x data size: ', x.size]) # Y yvar = self.y_axes()[0] y = np.ma.fix_invalid(np.ma.MaskedArray(yvar[:])).flatten().round(5) logger.debug(['y data size: ', y.size]) # Trajectories pvar = self.get_variables_by_attributes(cf_role='trajectory_id')[0] try: p = normalize_array(pvar) except BaseException: logger.exception('Could not pull trajectory values from the variable, using indexes.') p = np.asarray(list(range(len(pvar))), dtype=np.integer) # The Dimension that the trajectory id variable doesn't have is what # the trajectory data needs to be repeated by dim_diff = self.dimensions[list(set(tvar.dimensions).difference(set(pvar.dimensions)))[0]] if dim_diff: p = p.repeat(dim_diff.size) logger.debug(['trajectory data size: ', p.size]) # Distance d = np.append([0], great_distance(start_latitude=y[0:-1], end_latitude=y[1:], start_longitude=x[0:-1], end_longitude=x[1:])['distance']) d = np.ma.fix_invalid(np.ma.MaskedArray(np.cumsum(d)).astype(np.float64).round(2)) logger.debug(['distance data size: ', d.size]) df_data = { 't': t, 'x': x, 'y': y, 'z': z, 'trajectory': p, 'distance': d } building_index_to_drop = np.ones(t.size, dtype=bool) extract_vars = list(set(self.data_vars() + self.ancillary_vars())) for i, dvar in enumerate(extract_vars): vdata = np.ma.fix_invalid(np.ma.MaskedArray(dvar[:].round(3).flatten())) building_index_to_drop = (building_index_to_drop == True) & (vdata.mask == True) # noqa df_data[dvar.name] = vdata df = pd.DataFrame(df_data) # Drop all data columns with no data if clean_cols: df = df.dropna(axis=1, how='all') # Drop all data rows with no data variable data if clean_rows: df = df.iloc[~building_index_to_drop] return df