def ring_in_order(center: h3.H3Index, k: int) -> t.Iterable[h3.H3Index]: """Return a hexagonal approximation of a circle. Give the elements of the k-ring around center in clockwise or anticlockwise order. """ points: t.Set[h3.H3Index] = h3.k_ring_distances(center, k)[k] start = points.pop() n = start yield start while points: n = (h3.k_ring_distances(n, 1)[1] & points).pop() points.remove(n) yield n
def users_in_hex_plus_neighbors_list(db, hexid, contiguity=1, resolution='9'): """Adding neigbors of specified contiguity using h3 ring functions # comment> Shouldnt be necessary to specify resolution once hexid is given> check h3 documentation to obtain resoltuion on the basis of hexid """ neighboring_hex_list = list(h3.k_ring_distances(hexid, ring_size=contiguity)[contiguity]) # funcion para graficar los poligonos # gdf=hexlist_to_geodataframe(neighboring_hex_list) # gdf.plot() users_in_hex_plus_neighbors_list = [] users_in_hex_list2 = users_in_hex_list(db, hexid, resolution=resolution) users_in_hex_plus_neighbors_list.extend(users_in_hex_list2) # adding first those living in hex hexfieldname_indb = 'hex' + resolution + "." + 'hex' + resolution for n_hexid in neighboring_hex_list: users_in_n_hex_cursor = db.users.find({hexfieldname_indb: n_hexid}) users_in_n_hex_df = pd.DataFrame(list(users_in_n_hex_cursor)) try: users_in_n_hex_list = list(users_in_n_hex_df['u_id']) except KeyError: pass else: users_in_hex_plus_neighbors_list.extend(users_in_n_hex_list) return users_in_hex_plus_neighbors_list
def create_city_hexagons(lat=49.83826, lon=24.02324): """ For Lviv, coordinates: 49.83826, 24.02324. Function, for deviding city into regions, and save coordinates for this boundaries to DF. Coordinates must be a city center from OpenStreetMap Takes: lat: float value of latitude lon: float value of longitude Return: df with coordinates """ h3_address = h3.geo_to_h3(lat, lon, 8) hexagons = h3.k_ring_distances(h3_address, 6) list_address = [hex for el in hexagons for hex in el] latitudes1, latitudes2, latitudes3, latitudes4, latitudes5, latitudes6 = \ list(), list(), list(), list(), list(), list() longitudes1, longitudes2, longitudes3, longitudes4, longitudes5, longitudes6 = \ list(), list(), list(), list(), list(), list() hexagon_id = list() for index, element in enumerate(list_address): hex_boundary = h3.h3_to_geo_boundary(element) hexagon_id.append(index + 1) latitudes1.append(hex_boundary[0][0]) longitudes1.append(hex_boundary[0][1]) latitudes2.append(hex_boundary[1][0]) longitudes2.append(hex_boundary[1][1]) latitudes3.append(hex_boundary[2][0]) longitudes3.append(hex_boundary[2][1]) latitudes4.append(hex_boundary[3][0]) longitudes4.append(hex_boundary[3][1]) latitudes5.append(hex_boundary[4][0]) longitudes5.append(hex_boundary[4][1]) latitudes6.append(hex_boundary[5][0]) longitudes6.append(hex_boundary[5][1]) df_address = pd.DataFrame({ "hexagon_id": hexagon_id, "latitude1": latitudes1, "latitude2": latitudes2, "latitude3": latitudes3, "latitude4": latitudes4, "latitude5": latitudes5, "latitude6": latitudes6, "longitude1": longitudes1, "longitude2": longitudes2, "longitude3": longitudes3, "longitude4": longitudes4, "longitude5": longitudes5, "longitude6": longitudes6 }) return df_address
def test_k_ring_distances(self): hexagons = h3.k_ring_distances('8928308280fffff', 1) self.assertEqual(2, len(hexagons)) self.assertEqual(1, len(hexagons[0])) self.assertEqual(6, len(hexagons[1])) self.assertTrue('8928308280fffff' in hexagons[0]) self.assertTrue('8928308280bffff' in hexagons[1]) self.assertTrue('89283082807ffff' in hexagons[1]) self.assertTrue('89283082877ffff' in hexagons[1]) self.assertTrue('89283082803ffff' in hexagons[1]) self.assertTrue('89283082873ffff' in hexagons[1]) self.assertTrue('8928308283bffff' in hexagons[1]) hexagons = h3.k_ring_distances('870800003ffffff', 2) self.assertEqual(3, len(hexagons)) self.assertEqual(1, len(hexagons[0])) self.assertEqual(6, len(hexagons[1])) self.assertEqual(11, len(hexagons[2]))
def run_on_one_tile( lon: float, lat: float, db: sqlalchemy.engine.Engine, hex: sqlalchemy.Table, t_dist: sqlalchemy.Table ) -> t.Set[ArrayIndex]: """Compute pairwise distances and ecoregion composition Given a digital elevation model as raster map and a matching ecoregions raster map, compute the pairwise distance to its 1-hex and 2-hex neighbors for every H3 address hex at standard resolution, as well as the approximate cover of that cell in terms of ecoregions, for all cells where that is possible. Distances are computed in gross hours of travel while navigating off-track, following [@irmischer2018measuring]. Returns ======= d: A mapping. d[h1][h2] is the distance, in hours, from the center of h1 to the center of h2. e: A mapping. d[h1][b] is the proportion of hex h1 covered by ecoregion b. """ elevation_file = gmted_tile_from_geocoordinates(lon, lat) m_trafo = elevation_file.transform height, width = elevation_file.shape elevation = numpy.full((height + 1000, width + 1000), -100, int) ecoregions = numpy.full((height + 1000, width + 1000), 999, int) elevation[500:-500, 500:-500] = elevation_file.read(1) ecoregions[500:-500, 500:-500] = ecoregion_tile_from_geocoordinates(lon, lat).read(1) print("Loading adjacent data…") try: elevation[:500, :500] = (gmted_tile_from_geocoordinates(lon - 30, lat + 20)).read(1)[-500:, -500:] ecoregions[:500, :500] = ecoregion_tile_from_geocoordinates(lon - 30, lat + 20).read(1)[-500:, -500:] except rasterio.RasterioIOError: pass try: elevation[:500, 500:-500] = (gmted_tile_from_geocoordinates(lon, lat + 20)).read(1)[-500:, :] ecoregions[:500, 500:-500] = ecoregion_tile_from_geocoordinates(lon, lat + 20).read(1)[-500:, :] except rasterio.RasterioIOError: pass try: elevation[:500, -500:] = (gmted_tile_from_geocoordinates(lon + 30, lat + 20)).read(1)[-500:, :500] ecoregions[:500, -500:] = ecoregion_tile_from_geocoordinates(lon + 30, lat + 20).read(1)[-500:, :500] except rasterio.RasterioIOError: pass try: elevation[500:-500, :500] = (gmted_tile_from_geocoordinates(lon - 30, lat)).read(1)[:, -500:] ecoregions[500:-500, :500] = ecoregion_tile_from_geocoordinates(lon - 30, lat).read(1)[:, -500:] except rasterio.RasterioIOError: pass try: elevation[500:-500, -500:] = (gmted_tile_from_geocoordinates(lon + 30, lat)).read(1)[:, :500] ecoregions[500:-500, -500:] = ecoregion_tile_from_geocoordinates(lon + 30, lat).read(1)[:, :500] except rasterio.RasterioIOError: pass try: elevation[-500:, :500] = (gmted_tile_from_geocoordinates(lon - 30, lat - 20)).read(1)[:500, -500:] ecoregions[-500:, :500] = ecoregion_tile_from_geocoordinates(lon - 30, lat - 20).read(1)[:500, -500:] except rasterio.RasterioIOError: pass try: elevation[-500:, 500:-500] = (gmted_tile_from_geocoordinates(lon, lat - 20)).read(1)[:500, :] ecoregions[-500:, 500:-500] = ecoregion_tile_from_geocoordinates(lon, lat - 20).read(1)[:500, :] except rasterio.RasterioIOError: pass try: elevation[-500:, -500:] = (gmted_tile_from_geocoordinates(lon + 30, lat - 20)).read(1)[:500, :500] ecoregions[-500:, -500:] = ecoregion_tile_from_geocoordinates(lon + 30, lat - 20).read(1)[:500, :500] except rasterio.RasterioIOError: pass print("Computing hex extents…") transform = rasterio.Affine(m_trafo.a, 0, m_trafo.c - 500 * m_trafo.a, 0, m_trafo.e, m_trafo.f - 500 * m_trafo.e) def rowcol(latlon): lat, lon = latlon if lon > 170: # FIXME: We can and need to do this because we are working on the # Americas and the Americas only. The generic solution is more # difficult. lon = lon - 360 col, row = ~transform * (lon, lat) return int(row), int(col) center: t.Dict[h3.H3Index, t.Tuple[int, int]] = {} cs = sqlalchemy.select([hex.c.hexbin, hex.c.vlongitude, hex.c.vlatitude]).where(hex.c.vlongitude != None) for h, lon, lat in db.execute(cs).fetchall(): row, col = rowcol((lat, lon)) if 500 <= col < width + 500 and 500 < row < height + 500: center[h] = (row, col) print("Computing terrain coefficients…") terrain_coefficient_raster = TC[ecoregions] print("Computing distances on the grid…") distance_by_direction = all_pairwise_distances( elevation, transform, terrain_coefficient_raster) failed: t.Set[ArrayIndex] = set() finished: t.Set[ArrayIndex] = set() for start, (row, col) in center.items(): print(f"Computing area around {start:}…") lon, lat = transform * (col, row) hexbin = h3.geo_to_h3(lat, lon, RESOLUTION) this, neighbors1, neighbors2, neighbors3 = list(h3.k_ring_distances(hexbin, 3)) neighbors: t.Set[h3.H3Index] = neighbors1 | neighbors2 distance_known = sqlalchemy.select([t_dist.c.hexbin2]).where( (t_dist.c.hexbin1 == start) & (t_dist.c.source == 0)) missing = (neighbors - {k[0] for k in db.execute(distance_known).fetchall()}) & set(center) if not missing: print("Already known.") finished.add(start) continue print("Not found:", missing) if start in failed or start in finished: continue points = [] for n in neighbors2 | neighbors3: try: points.append(center[n]) except KeyError: points.append(rowcol(h3.h3_to_geo(n))) rmin = min(p[0] for p in points) rmax = max(p[0] for p in points) cmin = min(p[1] for p in points) cmax = max(p[1] for p in points) assert rmin >= 0 assert cmin >= 0 def rel_rowcol(latlon): lat, lon = latlon if lon > 170: lon = lon - 360 col, row = ~transform * (lon, lat) return int(row) - rmin, int(col) - cmin destinations = [(center[n][0] - rmin, center[n][1] - cmin) for n in neighbors if n in center] print(f"Looking for {neighbors:}…") distances = distances_from_focus( (center[start][0] - rmin, center[start][1] - cmin), set(destinations), {(n, e): d[rmin-min(n, 0):rmax + 1 - max(0, n), cmin-min(e, 0):cmax + 1 - max(0, e)] for (n, e), d in distance_by_direction.items()}, pred=None ) # For debugging: Output the results distances_by_center = {} for t, d in center.items(): try: dist = distances[d[0] - rmin, d[1] - cmin] if numpy.isfinite(dist): distances_by_center[t] = dist except IndexError: continue print(distances_by_center) with db.begin() as conn: for n, d in distances_by_center.items(): try: conn.execute(t_dist.insert({ "hexbin1": start, "hexbin2": n, "distance": d, "source": 0}).prefix_with('OR IGNORE')) except sqlalchemy.exc.IntegrityError: pass finished.add(start) return failed
def k_ring_distances(self, hex_id: str, ring_size: int) -> set: """return indices for all hexagons within the range of `ring_size` hexagon from hex_id, hexagons are grouped by the distance. """ return h3.k_ring_distances(hex_id, ring_size)
def neighbors(hex: h3.H3Index) -> t.Set[h3.H3Index]: this, neighbors = h3.k_ring_distances(hex, 1) return neighbors