def test_geometry_length_multilinestring(): geod = geodesic.Geodesic() geom = sgeom.MultiLineString( [sgeom.LineString(np.array([lhr, jfk]).T), sgeom.LineString(np.array([tul, jfk]).T)]) expected = pytest.approx(lhr_to_jfk + jfk_to_tul, abs=1) assert geod.geometry_length(geom) == expected
def geodesy_distance_between_points(p_start, p_end): geodesic = cgeo.Geodesic() distances, start_azimuth, end_azimuth = geodesic.inverse(p_start, p_end).base.T return distances
def _point_along_line(ax, start, distance, angle=0, tol=0.01): """Point at a given distance from start at a given angle. Args: ax: CartoPy axes. start: Starting point for the line in axes coordinates. distance: Positive physical distance to travel. angle: Anti-clockwise angle for the bar, in radians. Default: 0 tol: Relative error in distance to allow. Default: 0.01 Returns: Coordinates of a point (a (2, 1)-shaped NumPy array). """ # Direction vector of the line in axes coordinates. direction = np.array([np.cos(angle), np.sin(angle)]) geodesic = cgeo.Geodesic() # Physical distance between points. def dist_func(a_axes, b_axes): a_phys = _axes_to_lonlat(ax, a_axes) b_phys = _axes_to_lonlat(ax, b_axes) # Geodesic().inverse returns a NumPy MemoryView like [[distance, # start azimuth, end azimuth]]. return geodesic.inverse(a_phys, b_phys).base[0, 0] end = _upper_bound(start, direction, distance, dist_func) return _distance_along_line(start, end, distance, dist_func, tol)
def tissot(rads_km=None, lons=None, lats=None, n_samples=80, globe=None, ax=None, draw=False, **kwargs): import numpy as np import cartopy.crs as ccrs import cartopy.feature as feature import shapely.geometry as sgeom from cartopy import geodesic import cartopy.mpl.patch as cpatch geod = geodesic.Geodesic(radius=globe.semimajor_axis, flattening=globe.flattening) geoms = [] rads_km = np.asarray(rads_km) lons = np.asarray(lons) lats = np.asarray(lats) for i in range(len(lons)): circle = geod.circle(lons[i], lats[i], rads_km[i], n_samples=n_samples) geoms.append(sgeom.Polygon(circle)) polys = cpatch.geos_to_path(geoms) if draw: if ax==None: ax = plt.gca() f = feature.ShapelyFeature(geoms, ccrs.Geodetic(globe=globe), **kwargs) ax.add_feature(f) return polys
def setUp(self): """ Data sampled from the GeographicLib Test Data for Geodesics at: http://geographiclib.sourceforge.net/html/geodesic.html#testgeod """ self.geod = geodesic.Geodesic() # Fill a 10 by 7 numpy array with starting lons, lats, azimuths; ending # lons, lats and azimuths and distances to travel. data = np.array([(0.0000000000, 36.5300423550, 176.1258751622, 5.7623446947, -48.1642707791, 175.3343083163, 9398502.0434687007), (0.0000000000, 20.8766024619, 6.9012827094, 163.9792202999, 64.2764863397, 165.0440144913, 10462971.2273696996), (0.0000000000, 59.7405712203, 80.9569174535, 80.1969954660, 30.9857449391, 144.4488137288, 6549489.1863671001), (0.0000000000, 38.6508883588, 18.3455177945, 23.5931524958, 66.3457305181, 37.7145989984, 3425212.4767990001), (0.0000000000, 23.2214345509, 165.5720618611, 148.3625110902, -68.8453788967, 39.2692310682, 14506511.2971898001), (0.0000000000, 31.2989275984, 155.7723493796, 93.8764112107, -69.2776346668, 98.5250397385, 13370814.5013951007), (0.0000000000, 49.6823298563, 1.0175398481, 5.3554086646, 83.8681965431, 6.1667605618, 3815028.2543704999), (0.0000000000, 32.7651878215, 98.6494285944, 70.3527194957, 2.4777491770, 123.5999412794, 8030520.7178932996), (0.0000000000, 46.3648067071, 94.9148631993, 56.5676529172, 25.2581951337, 130.4405565458, 5485075.9286326999), (0.0000000000, 33.7321188396, 147.9041907517, 33.1346935645, -26.3211288531, 150.4502346224, 7512675.5414637001)], dtype=[('start_lon', np.float64), ('start_lat', np.float64), ('start_azi', np.float64), ('end_lon', np.float64), ('end_lat', np.float64), ('end_azi', np.float64), ('dist', np.float64)]) self.data = data.view(np.recarray) self.start_pts = np.array([self.data.start_lon, self.data.start_lat]).T self.end_pts = np.array([self.data.end_lon, self.data.end_lat]).T self.dir_soln = np.array([self.data.end_lon, self.data.end_lat, self.data.end_azi]).T self.inv_soln = np.array([self.data.dist, self.data.start_azi, self.data.end_azi]).T
def narrow(lon='auto', lat='auto', ax=None, lfactor=1, **kwargs): """ Plot north arrow. Parameters: lon: Starting longitude (decimal degrees) for arrow lat: Starting latitude (ecimal degrees) for arrow ax: Axes on which to plot arrow lfactor: Length factor to increase/decrease arrow length Returns: ax: Axes with arrow plotted """ if ax is None: ax = plt.gca() geodesic = cgeo.Geodesic() # Set up geodesic calculations # Get map projection from axes crs = ax.projection if (lon == 'auto') & (lat == 'auto'): trans = ax.transAxes + ax.transData.inverted() sx, sy = trans.transform((0.2, 0.2)) lon, lat = ccrs.Geodetic().transform_point(sx, sy, src_crs=crs) # Get geodetic projection for lat/lon - do not confuse with geodesic gdet = ccrs.Geodetic() # Get axes extent and convert to lat/lon x1, x2, y1, y2 = ax.get_extent() tlx, tly = gdet.transform_point(x1, y2, src_crs=crs) blx, bly = gdet.transform_point(x1, y1, src_crs=crs) diff = abs(bly - tly) # Get x coverage of plot in decimal degrees # Get arrow endpoint scaled by diff and lfactor end = geodesic.direct(points=[lon, lat], azimuths=0, distances=lfactor * diff * 2 * 10**4)[0] # Transform lat-lon into axes coordinates xstart, ystart = crs.transform_point(lon, lat, src_crs=ccrs.Geodetic()) # Get X-Y coordinates of endpoint xend, yend = crs.transform_point(end[0], end[1], src_crs=ccrs.Geodetic()) # Plot arrow as annotation ax.annotate("", xy=(xstart, ystart), xycoords='data', xytext=(xend, yend), textcoords='data', arrowprops=dict(arrowstyle="<|-", connectionstyle="arc3")) # Add N to arrow ax.text(xend, yend, 'N', fontsize=7, ha='center') return (ax)
def distance2coast(croco_ds, coast='coastline_rho', **kwargs): ''' Calculate the distance to coast from a coastline Parameters ---------- croco_ds: xr.Dataset Provides the variables coastline_rho, lon_rho, lat_rho condition: boolean array the same shape as mask_rho to mask the coastline and i.e. remove islands ''' # apply masking condition (i.e. remove islands) cline = croco_ds[coast] clon = croco_ds.lon_rho clat = croco_ds.lat_rho if kwargs and 'condition' in kwargs: cline = cline.where(kwargs['condition'], drop=True) clon = clon.where(kwargs['condition'], drop=True) clat = clat.where(kwargs['condition'], drop=True) # flatten coast to one dimension coast1D = cline.stack(points=(cline.dims)) lon1D = clon.stack(points=(cline.dims)).where(coast1D, drop=True) lat1D = clat.stack(points=(cline.dims)).where(coast1D, drop=True) #lonlim = (lon1D > -84) #lon1D = lon1D.where(lonlim,drop=True) #lat1D = lat1D.where(lonlim,drop=True) coastline_from_mask = np.stack([lon1D.values, lat1D.values]).T print('Calculating distances to {} coastal points...'.format( coastline_from_mask.shape[0])) croco_coords = [ np.array([lo, la]) for lo, la in zip( croco_ds.lon_rho.stack(n=(croco_ds.mask_rho.dims)).values, croco_ds.lat_rho.stack(n=(croco_ds.mask_rho.dims)).values) ] geo = cgeo.Geodesic() dist_list = [] update_progress(0) for ind, cro in enumerate(croco_coords): dists = geo.inverse(cro, coastline_from_mask).base[:, 0] / 1000 dist_list.append(np.min(dists)) if ind % 100 == 0: update_progress(ind / len(croco_coords)) distarray = np.array(dist_list) update_progress(1) dist2coast = xr.zeros_like( croco_ds.mask_rho).stack(n=croco_ds.mask_rho.dims) dist2coast.values = distarray.T dist2coast = dist2coast.unstack('n') return dist2coast
def _point_along_line(ax, start, distance, projected=False, verbose=False): """Point at a given distance from start at a given angle. Args: ax: CartoPy axes. start: Starting point for the line in data coordinates. distance: Positive physical distance to travel in meters. angle: Anti-clockwise angle for the bar, in degrees. Default: 0 Returns: (lon,lat) coords of a point (a (2, 1)-shaped NumPy array) """ # Direction vector of the line in axes coordinates. if not projected: geodesic = cgeo.Geodesic() Direct_R = geodesic.direct(start, 90, distance) target_longitude, target_latitude, forw_azi = Direct_R.base.T target_point = ([target_longitude[0], target_latitude[0]]) actual_dist = geodesic.inverse(start, target_point).base.ravel()[0] if verbose: print('Starting point', start) print('target point', target_point) print('Expected distance between points: ', distance) print('Actual distance between points: ', actual_dist) if projected: longitude, latitude = start target_longitude = longitude + distance target_point = (target_longitude, latitude) if verbose: print('Axes is projected? ', projected) print('Expected distance between points: ', distance) print('Actual distance between points: ', target_longitude - longitude) return start, target_point
def _point_along_line(ax, start, distance, angle=-90, projected=False): """Point at a given distance from start at a given angle. Args: ax: CartoPy axes. start: Starting point for the line in axes coordinates. distance: Positive physical distance to travel in meters. angle: Anti-clockwise angle for the bar, in degrees. Default: 0 Returns: (lon,lat) coords of a point (a (2, 1)-shaped NumPy array) """ start_coords = _axes_to_lonlat(ax, start, projected) # Direction vector of the line in axes coordinates. if not projected: geodesic = cgeo.Geodesic() Direct_R = geodesic.direct(start_coords, angle, distance) longitudes, latitudes, forw_azi = Direct_R.base.T # print('Distance', distance) # print('start_coords: ', start_coords) # print('longitudes: ', longitudes) # actual_dist = geodesic.inverse(start_coords, # target_point).base.ravel()[0] # print('Starting point', start_coords) # print('Ending point', target_point) # print('Expected distance between points: ', distance) # print('Actual distance between points: ', actual_dist) if projected: start_coords longitudes, latitudes = start_coords longitudes = longitudes + np.sin(np.deg2rad(angle)) * distance target_point = (longitudes, latitudes) return start_coords, target_point
def overplot_craters(): """Overplot a lunar map with known crater outlines""" imdata_small = mio.load_lola_downsampled() smalldata = downscale_local_mean(imdata_small, factors=(10, 10)) moon_globe = ccrs.Globe(ellipse=None, # can remove after #1588/#564 semimajor_axis=Constants.moon_radius, flattening=Constants.moon_flattening) moon_crs = ccrs.Robinson(globe=moon_globe) moon_transform = ccrs.PlateCarree(globe=moon_globe) plt.rc('text', usetex=True) fig = plt.figure(figsize=(12, 6), dpi=120) ax = plt.axes(projection=moon_crs) ax.gridlines(color='#252525', linestyle='dotted') im = ax.imshow(smalldata, origin="upper", transform=moon_transform) cbar = fig.colorbar(im, orientation='vertical', shrink=0.7) cbar.set_label(r'$\mathrm{LOLA~digital~elevation~model~(m)}$') ax.set_global() # Reference: International Astronomical Union (IAU) Planetary Gazetteer # CSV data downloaded from: https://planetarynames.wr.usgs.gov/ # Check the page here for all the history behind the moon feature naming: # https://the-moon.us/wiki/IAU_nomenclature iau_fname = os.path.join(Paths.table_dir, 'iau_approved_craters.csv') #iau_fname = os.path.join(Paths.table_dir, 'iau_approved_features.csv') iau_lunar_craters = pd.read_csv(iau_fname) lons = iau_lunar_craters.Center_Longitude lats = iau_lunar_craters.Center_Latitude radii_in_meters = iau_lunar_craters.Diameter * 500 # km to m moon_geodesic = geodesic.Geodesic(radius=Constants.moon_radius, flattening=Constants.moon_flattening) craters = [] crater_proj = ccrs.Geodetic(globe=moon_globe) for lon, lat, radius in zip(lons, lats, radii_in_meters): if not radius: continue crater = moon_geodesic.circle(lon=lon, lat=lat, radius=radius, n_samples=15) craters.append(crater) geom = shapely.geometry.Polygon(crater) ax.add_geometries((geom,), crs=crater_proj, alpha=0.6, facecolor='none', edgecolor='white', linewidth=0.5) plt.savefig(os.path.join(Paths.fig_dir, "lunar_craters.png"), dpi=120)
def _point_along_line(ax, start, distance, angle=0, tol=0.01): """Point at a given distance from start at a given angle. Parameters ---------- ax : :class:`cartopy.mpl.geoaxes.GeoAxes` Cartopy axes. start : tuple Starting point for the line in axes coordinates. distance : float Positive physical distance to travel. angle : float, optional Anti-clockwise angle for the bar, in radians. Default: 0 tol : float, optional Relative error in distance to allow. Default: 0.01 Returns ------- np.ndarray, shape (2, 1) Coordinates of a point """ # Direction vector of the line in axes coordinates. direction = np.array([np.cos(angle), np.sin(angle)]) geodesic = cgeo.Geodesic() # Physical distance between points. def dist_func(a_axes, b_axes): a_phys = _axes_to_lonlat(ax, a_axes) b_phys = _axes_to_lonlat(ax, b_axes) inv = geodesic.inverse(a_phys, b_phys) try: # Geodesic().inverse returns a NumPy MemoryView like [[distance, # start azimuth, end azimuth]]. return inv.base[0, 0] except TypeError: # In newer versions, it is a plain numpy array return inv[0, 0] end = _upper_bound(start, direction, distance, dist_func) return _distance_along_line(start, end, distance, dist_func, tol)
def makeErrCircle(self, lat, lon, radius): circle_points = cgeodesic.Geodesic().circle(lon=lon, lat=lat, \ radius=radius, n_samples=100, endpoint=False) return sgeometry.Polygon(circle_points)
from h3.h3 import H3Index as Index import networkx from database import db from ecoregions import ECOREGIONS, TC, RESOLUTION try: __file__ except NameError: __file__ = 'this' ArrayIndex = t.Tuple[int, int] # Define some constants GEODESIC: geodesic.Geodesic = geodesic.Geodesic() P = t.TypeVar("P", bound=sgeom.Point) class Point(sgeom.Point): # type: ignore @classmethod def from_h3(cls: t.Type[P], index: Index) -> P: lat, lon = h3.h3_to_geo(index) return cls(lon, lat) def __repr__(self) -> str: return f"Point(longitude={self.x:}, latitude={self.y:})" @property def longitude(self) -> float:
land_shp_fname = shpreader.natural_earth(resolution='50m', category='physical', name='land') land_geom = unary_union([ record.geometry for record in shpreader.Reader(land_shp_fname).records() if record.attributes.get('featurecla') != "Null island" ]) LAND = prep(land_geom) DEFINITELY_INLAND = prep(land_geom.buffer( -1 / 60., resolution=4)) # 1 arc minute – 2 km near the equator. BUFFER_NOT_SEA = prep(land_geom.buffer(1 / 60., resolution=4)) GEODESIC = geodesic.Geodesic() def is_landlocked(xy): return DEFINITELY_INLAND.contains(sgeom.Point(*xy)) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument("database") parser.add_argument("--start") args = parser.parse_args() engine, tables = db(args.database) t_hex = tables["hex"] t_dist = tables["dist"]
def test_geometry_length_point(): geod = geodesic.Geodesic() geom = sgeom.Point(lhr) with pytest.raises(ValueError): geod.geometry_length(geom)
def test_geometry_length_polygon(): geod = geodesic.Geodesic() geom = sgeom.Polygon(np.array([lhr, jfk, tul])) expected = pytest.approx(lhr_to_jfk + jfk_to_tul + tul_to_lhr, abs=1) assert geod.geometry_length(geom) == expected
ax.background_patch.set_facecolor('k') for aa in airports: # print('Processing '+aa+'...') row = np.where(dat['iata_code'] == aa)[0][0] lat = dat.loc[row, :]['latitude_deg'] lon = dat.loc[row, :]['longitude_deg'] ax.plot(lon, lat, 'o', color='xkcd:electric pink', transform=ccrs.Geodetic(), alpha=0.7, markersize=3) # Define the geometry for calculating cumulative distance myGeod = geodesic.Geodesic() totDist = 0 # Overlay the routes for tt in trips: source = tt[0] dest = tt[1] sourceInd = np.where(dat['iata_code'] == source)[0][0] destInd = np.where(dat['iata_code'] == dest)[0][0] sourceLat = dat.loc[sourceInd, :]['latitude_deg'] sourceLon = dat.loc[sourceInd, :]['longitude_deg'] destLat = dat.loc[destInd, :]['latitude_deg'] destLon = dat.loc[destInd, :]['longitude_deg'] ax.plot( [sourceLon, destLon], [sourceLat, destLat], color='xkcd:neon green',
def test_geometry_length_linestring(): geod = geodesic.Geodesic() geom = sgeom.LineString(np.array([lhr, jfk, lhr]).T) expected = pytest.approx(lhr_to_jfk * 2, abs=1) assert geod.geometry_length(geom) == expected
def find_duplicates(craters, radius=1737.4, k=10, rcd=5., ddiam=0.25, filter_pairs=False): """Finds duplicate pairs within crater catalog. Triples or more will show up as multiple pairs. Parameters ---------- craters : pandas.DataFrame Crater catalogue. radius : float, optional Radius of the world. k : int, optional Nearest neighbours to search for duplicates. Default is 10. rcd : float, optional Minimum value of min(crater_pair_diameters) / pair_distance to be considered a crater pair. Minimum rather than average is used to help weed out satellite craters. This criterion is asymmetric between pairs, and when filter_pairs=False may lead to single pair entries. ddiam : float, optional Maximum value of abs(diameter1 - diameter2) / avg(diameters) to be considered a crater pair. filter_pairs : bool, optional If `True`, filters data frame and keeps only one entry per crater pair. Returns ------- outframe : pandas.DataFrame Data frame of crater duplicate pairs. """ mgeod = geodesic.Geodesic(radius=radius, flattening=0.) # Convert to 3D (<https://en.wikipedia.org/wiki/ # Spherical_coordinate_system#Cartesian_coordinates>); phi = [-180, 180) is # equivalent to [0, 360). craters['phi'] = np.pi / 180. * craters['Long'] craters['theta'] = np.pi / 180. * (90 - craters['Lat']) craters['x'] = radius * np.sin(craters['theta']) * np.cos(craters['phi']) craters['y'] = radius * np.sin(craters['theta']) * np.sin(craters['phi']) craters['z'] = radius * np.cos(craters['theta']) # Create tree. kdt = kd(craters[["x", "y", "z"]].as_matrix(), leafsize=10) # Loop over all craters to find duplicates. First, find k + 1 nearest # neighbours (k + 1 because query will include self). Lnn, inn = kdt.query(craters[["x", "y", "z"]].as_matrix(), k=k + 1) # Remove crater matching with itself (by checking id). Lnn_remove_self = np.empty([Lnn.shape[0], Lnn.shape[1] - 1]) inn_remove_self = np.empty([Lnn.shape[0], Lnn.shape[1] - 1], dtype=int) for i in range(Lnn_remove_self.shape[0]): not_self = (inn[i] != i) inn_remove_self[i] = inn[i][not_self] Lnn_remove_self[i] = Lnn[i][not_self] craters['Lnn'] = list(Lnn_remove_self) craters['inn'] = list(inn_remove_self) # Get radii of nearest neighbors. inn_ravel = inn[:, 1:].ravel() craters['dnn'] = list( craters['Diameter (km)'].as_matrix()[inn_ravel].reshape(-1, 10)) craters['long_nn'] = list(craters['Long'].as_matrix()[inn_ravel].reshape( -1, 10)) craters['lat_nn'] = list(craters['Lat'].as_matrix()[inn_ravel].reshape( -1, 10)) craters['set_nn'] = list(craters['Dataset'].as_matrix()[inn_ravel].reshape( -1, 10)) # Prepare empty lists. dup_id1 = [] dup_id2 = [] dup_D1 = [] dup_D2 = [] dup_L = [] dup_LEuclid = [] dup_ll1 = [] dup_ll2 = [] dup_source1 = [] dup_source2 = [] # Iterate over craters to determine if any are duplicate pairs. for index, row in craters.iterrows(): # For each pair, record the smaller crater diameter. pair_diameter_min = np.array( [min(x, row['Diameter (km)']) for x in row['dnn']]) proper_dist = np.asarray( mgeod.inverse(np.array([row['Long'], row['Lat']]), np.vstack([row['long_nn'], row['lat_nn']]).T))[:, 0] # Duplicate pair criteria: 1). min(diameter) / distance > rcd; 2). # abs(diameter1 - diameter2) / average(diameter) < ddiam - i.e. the # separation distance of the centres must be much smaller than either # diameter, and the diameters should be very similar. rcd_crit = (pair_diameter_min / row['Lnn'] > rcd) diam_sim_crit = ((2. * abs(row['dnn'] - row['Diameter (km)']) / (row['dnn'] + row['Diameter (km)'])) < ddiam) dup_candidates, = np.where(rcd_crit & diam_sim_crit) if dup_candidates.size: for i in dup_candidates: if index == row['inn'][i]: raise AssertionError("Two craters with identical IDs.") dup_id1.append(index) dup_id2.append(row['inn'][i]) dup_D1.append(row['Diameter (km)']) dup_D2.append(row['dnn'][i]) dup_L.append(proper_dist[i]) dup_LEuclid.append(row['Lnn'][i]) dup_ll1.append((row['Long'], row['Lat'])) dup_ll2.append((row['long_nn'][i], row['lat_nn'][i])) dup_source1.append(row['Dataset']) dup_source2.append(row['set_nn'][i]) # Multi-index pandas table; see # <https://pandas.pydata.org/pandas-docs/stable/advanced.html>. outframe = pd.DataFrame( { 'ID1': dup_id1, 'ID2': dup_id2, 'Diameter1 (km)': dup_D1, 'Diameter2 (km)': dup_D2, 'Separation (km)': dup_L, 'Euclidean Separation (km)': dup_LEuclid, 'Lat/Long1': dup_ll1, 'Lat/Long2': dup_ll2, 'Dataset1': dup_source1, 'Dataset2': dup_source2 }, columns=('ID1', 'ID2', 'Diameter1 (km)', 'Diameter2 (km)', 'Separation (km)', 'Euclidean Separation (km)', 'Lat/Long1', 'Lat/Long2', 'Dataset1', 'Dataset2')) # Hacky, O(N^2) duplicate entry removal. if filter_pairs: osub = outframe[["ID1", "ID2"]].as_matrix() osub = np.array([set(x) for x in osub]) indices_to_remove = [] for i in range(osub.shape[0]): if i not in indices_to_remove: dups = np.where(osub[i + 1:] == osub[i])[0] + i + 1 indices_to_remove += list(dups) indices_to_remove = list(set(indices_to_remove)) outframe.drop(indices_to_remove, inplace=True) outframe.reset_index(inplace=True, drop=True) return outframe
def __init__( self, sites, subset=None, crs=None): """Convert a set of sites into a network. This function converts a set of language locations, with their attributes, into a network (graph). If a subset is defined, only those sites in the subset go into the network. Args: sites(dict): a dict of sites with keys "locations", "id" subset(list): boolean assignment of sites to subset Returns: dict: a network """ if crs is not None: try: from cartopy import crs as ccrs, geodesic except ImportError as e: print("Using a coordinate reference system (crs) requires the ´cartopy´ library:") print("pip install cartopy") raise e if subset is None: # Define vertices and edges vertices = sites['id'] locations = sites['locations'] # Distance matrix self.names = sites['names'] else: sub_idx = np.nonzero(subset)[0] vertices = list(range(len(sub_idx))) # Delaunay triangulation locations = sites['locations'][sub_idx, :] # Distance matrix self.names = [sites['names'][i] for i in sub_idx] # Delaunay triangulation delaunay = compute_delaunay(locations) v1, v2 = delaunay.toarray().nonzero() edges = np.column_stack((v1, v2)) # Adjacency Matrix adj_mat = delaunay.tocsr() if crs is None: loc = np.asarray(sites['locations']) diff = loc[:, None] - loc dist_mat = np.linalg.norm(diff, axis=-1) else: transformer = pyproj.transformer.Transformer.from_crs( crs_from=crs, crs_to=pyproj.crs.CRS("epsg:4326")) w_locations = np.vstack( transformer.transform(locations[:, 0], locations[:, 1]) ).T geod = geodesic.Geodesic() dist_mat = np.hstack([geod.inverse(location, w_locations)[:, :2] for location in w_locations]) self.vertices = vertices self.edges = edges self.locations = locations self.adj_mat = adj_mat self.n = len(vertices) self.m = edges.shape[0] self.dist_mat = dist_mat
def test_geometry_length_ndarray(): geod = geodesic.Geodesic() geom = np.array([lhr, jfk, lhr]) expected = pytest.approx(lhr_to_jfk * 2, abs=1) assert geod.geometry_length(geom) == expected
def scalebar(length, slon='auto', slat='auto', az=90, label=True, ax=None, **kwargs): """ Plot scalebar of given length in meters. Parameters: length: Length of scalebar in meters slon: Starting longitude (decimal degrees) for scalebar slat: Starting latitude (decimal degrees) for scalebar az = Azimuth of scalebar label: Boolean for whether to label length of scalebar in km ax: Axes on which to plot scalebar Return: ax: Axes with scalebar plotted """ if ax is None: ax = plt.gca() geodesic = cgeo.Geodesic() # Set up geodesic calculations # Get map projection from axes crs = ax.projection if (slon == 'auto') & (slat == 'auto'): trans = ax.transAxes + ax.transData.inverted() sx, sy = trans.transform((0.1, 0.1)) slon, slat = ccrs.Geodetic().transform_point(sx, sy, src_crs=crs) # Calculate endpoint for given distance end = geodesic.direct(points=[slon, slat], azimuths=az, distances=length)[0] elon = end[0] elat = end[1] mid = geodesic.direct(points=[slon, slat], azimuths=az, distances=length / 2)[0] clon = mid[0] clat = mid[1] # Plot line from start to end ax.plot([slon, elon], [slat, elat], transform=ccrs.Geodetic(), **kwargs, linewidth=3) # Add label with number of km if label == True: # Transform lat-lon into axes coordinates tlon, tlat = crs.transform_point(clon, clat, src_crs=ccrs.Geodetic()) # Add label as annotation ax.annotate(text=str(round(length / 1000, None)) + ' km', xy=(tlon, tlat), xytext=(0, 3), xycoords='data', textcoords='offset points', fontsize=7, ha='center') return (ax)