def geographical_distance(index1: Index, index2: Index) -> float: """Calculate the geodesic distance between two hex centers, in meters. >>> i = h3.geo_to_h3( ... numpy.random.random() * 180 - 90, ... numpy.random.random() * 360 - 180, ... RESOLUTION ... ) >>> i_, neighbors = h3.k_ring_distances(i, 1) >>> i_ == {i} True >>> edge = h3.edge_length(RESOLUTION, 'm') >>> ex = 3**0.5 * edge >>> ex 14799.349254643997 >>> for n in neighbors: ... print(0.7*ex < geographical_distance(i, n) < 1.3*ex ... or geographical_distance(i, n)) True True True True True True """ lat1, lon1 = h3.h3_to_geo(index1) lat2, lon2 = h3.h3_to_geo(index2) return numpy.asarray(GEODESIC.inverse( (lon1, lat1), (lon2, lat2)))[0, 0]
def display_test(surf): ori = h3.h3_to_geo('882a1072cdfffff') des = h3.h3_to_geo('882a100d39fffff') y1, x1 = ori y2, x2 = des display_dot(surf, x1, y1, l_color=BLACK) display_dot(surf, x2, y2, l_color=BLACK) return 0
def kring_smoothing(df, hex_col, metric_col, k): dfk = df[[hex_col]] dfk.index = dfk[hex_col] dfs = (dfk[hex_col].apply(lambda x: pd.Series(list(h3.k_ring(x, k)))). stack().to_frame('hexk').reset_index( 1, drop=True).reset_index().merge(df[[ hex_col, metric_col ]]).fillna(0).groupby(['hexk'])[[metric_col]].sum().divide( (1 + 3 * k * (k + 1))).reset_index().rename( index=str, columns={"hexk": hex_col})) dfs['lat'] = dfs[hex_col].apply(lambda x: h3.h3_to_geo(x)[0]) dfs['lng'] = dfs[hex_col].apply(lambda x: h3.h3_to_geo(x)[1]) return dfs
def counts_by_hexagon(df, resolution): '''Use h3.geo_to_h3 to index each data point into the spatial index of the specified resolution. Use h3.h3_to_geo_boundary to obtain the geometries of these hexagons''' #df = df[["latitude","longitude"]] df = df[latlong] #df["hex_id"] = df.apply(lambda row: h3.geo_to_h3(row["latitude"], row["longitude"], resolution), axis = 1) df["hex_id"] = df.apply( lambda row: h3.geo_to_h3(row[latlong[0]], row[latlong[1]], resolution), axis=1, meta=('int')) df_aggreg = df.groupby(by="hex_id").size().reset_index() df_aggreg.columns = ["hex_id", "value"] """ df_aggreg["geometry"] = df_aggreg.hex_id.apply(lambda x: { "type" : "Polygon", "coordinates": [h3.h3_to_geo_boundary(h3_address=x,geo_json=True)] } ) """ df_aggreg["center"] = df_aggreg.hex_id.apply( lambda x: { "type": "Polygon", "coordinates": [h3.h3_to_geo(h3_address=x)] }, meta=('tuple')) return df_aggreg
def folium_static_hexagons(df, h3_index_str='h3_index', h3_hex_str='h3_hexagon'): feature_collection = [] for index, row in df.iterrows(): feature = { 'type': 'Feature', 'geometry': { 'type': 'Polygon', 'coordinates': [[[elem[1], elem[0]] for elem in row[h3_hex_str] + [row[h3_hex_str][0]]]] } } feature_collection.append(feature) f_c = FeatureCollection(feature_collection) initial_coords = h3.h3_to_geo(df[h3_index_str][0]) map_ = folium.Map(location=initial_coords, control_scale=True, zoom_start=9) f_json = folium.GeoJson(f_c) map_.add_child(f_json) return map_
def search_position(id): lat, lng = h3.h3_to_geo(id) xpos = int((lng - extent_.min_lng) // lng_width) ypos = int((lat - extent_.min_lat) // lat_width) # import pdb # pdb.set_trace() return n * xpos + ypos
def store_ecocount_in_db(ecoregions: numpy.array, transform: rasterio.Affine, engine: sqlalchemy.engine.Connectable, t_eco: sqlalchemy.Table, t_hex: sqlalchemy.Table) -> None: for h, eco_count in hex_ecoregions(ecoregions, transform).items(): lat, lon = h3.h3_to_geo(h) try: engine.execute( t_hex.insert({ "hexbin": h, "longitude": lon, "latitude": lat })) except sqlalchemy.exc.IntegrityError: pass for id, freq in eco_count.items(): try: engine.execute( t_eco.insert({ "hexbin": h, "ecoregion": id, "frequency": freq })) except sqlalchemy.exc.IntegrityError: elsewhere = engine.execute( sqlalchemy.select([ t_eco.c.frequency ]).where((t_eco.c.hexbin == h) & (t_eco.c.ecoregion == id))).fetchone()[0] engine.execute( t_eco.update().where((t_eco.c.hexbin == h) & (t_eco.c.ecoregion == id)).values( {"frequency": elsewhere + freq}))
def test_h3_to_geo(self): latlng = h3.h3_to_geo('85283473fffffff') self.assertAlmostEqual( latlng[0], 37.34579337536848, None, 'lat center is ok' ) self.assertAlmostEqual( latlng[1], -121.97637597255124, None, 'lng center is ok' )
def display_crt_taxi_status(surf, taxi_cls): fontObj = pygame.font.Font(TEXT_FONT, 16) font_score = pygame.font.Font(TEXT_FONT, 14) surf.blit(fontObj.render('Call on : ', False, BLACK), (20, 70)) surf.blit(fontObj.render('Crt Pos(H3) : ', False, BLACK), (20, 88)) surf.blit(fontObj.render('Des Pos(H3) : ', False, BLACK), (20, 106)) surf.blit(fontObj.render('Remain Time : ', False, BLACK), (20, 124)) surf.blit(fontObj.render('Taxi Status : ', False, BLACK), (20, 142)) surf.blit(fontObj.render('T-Wait Tm : ', False, BLACK), (20, 160)) surf.blit(fontObj.render('Driver Prone : ', False, BLACK), (20, 178)) if taxi_cls.crt_des != None : #taxi_cls.call_status == True: #bound_list = h3.h3_to_geo_boundary(taxi_cls.crt_des) #adj_bound_list = return_adj_coord(bound_list) lat, lon = h3.h3_to_geo(taxi_cls.crt_des) # array of [lat, lng] rtn_adj_coord = return_adj_coord([[lat, lon]]) #pygame.draw.polygon(surf, BRIGHTBLUE, adj_bound_list, 2) pygame.draw.circle(surf, BRIGHTBLUE, (int(rtn_adj_coord[0][0]), int(rtn_adj_coord[0][1])), 3) #if taxi_cls.out_cell_move == True: # str_taxi_status = 'Out-cell On' #else : # str_taxi_status = 'In-cell On' #else : if taxi_cls.call_status == True: str_calls = 'True' else : str_calls = 'False' #if taxi_cls.out_cell_move == True: # str_taxi_status = 'Out-cell off' #else : # str_taxi_status = 'In-cell off' str_des = str(taxi_cls.crt_des) str_time = str(taxi_cls.crt_move_remain_tm) surf.blit(font_score.render(str_calls, False, BLUE), (150, 70)) surf.blit(font_score.render(str(taxi_cls.crt_pos), False, BLUE), (150, 88)) surf.blit(font_score.render(str_des, False, BLUE), (150, 106)) surf.blit(font_score.render(str_time, False, BLUE), (150, 124)) #surf.blit(font_score.render(str_taxi_status, False, BLUE), (150, 124)) surf.blit(font_score.render(taxi_cls.str_taxi_status, False, BLUE), (150, 142)) surf.blit(font_score.render(str(taxi_cls.total_wait_tm), False, BLUE), (150, 160)) surf.blit(font_score.render(str(taxi_cls.taxi_attribute), False, BLUE), (150, 172)) return 0
def make_migration(ctx, filename): ''' ''' now = datetime.now() with open(filename, mode="rt") as f: while True: data = f.readline() if data: dt, h3_address_from, h3_address_to, people_count = data.split( ",") time_offset = datetime.strptime(dt, "%H:%M") time_offset = now.replace(hour=time_offset.hour, minute=time_offset.minute) timestamp = round(time_offset.timestamp()) coords_from = h3.h3_to_geo(h3_address_from) coords_to = h3.h3_to_geo(h3_address_to) fields = [timestamp, *coords_from, *coords_to, people_count] print(",".join([str(f) for f in fields])) else: break
def display_taxi_img(surf, img, h3_coord, car_on = False): lat, lon = h3.h3_to_geo(h3_coord) # array of [lat, lng] #rect = img.get_rect() rtn_adj_coord = return_adj_coord([[lat,lon]]) rtn_adj_coord[0][0] = rtn_adj_coord[0][0] - 4 rtn_adj_coord[0][1] = rtn_adj_coord[0][1] - 8 surf.blit(img, rtn_adj_coord[0])
def vector_to_h3(vector_path, value_name, resolution, extent=None, layer=None): """Load raster values into h3 dggs cells Parameters: vector_path (string): path to vector file compatible with Geopandas for uploading value_name (string): vector attribute name to load into dggs cells resolution (integer): h3 cell resolution to use extent (list): extent as array of 2 lon lat pairs to get raster values for layer (string): vector layer name if geopackage is used Returns: Pandas dataframe """ # Open vector to geodataframe gdf = gpd.read_file(vector_path, layer) # Get extent to fill with h3 hexes if extent: extent = gpd.GeoSeries(box(extent[0], extent[1], extent[2], extent[3])).__geo_interface__ else: extent = gpd.GeoSeries( box(gdf['geometry'].total_bounds[0], gdf['geometry'].total_bounds[1], gdf['geometry'].total_bounds[2], gdf['geometry'].total_bounds[3])).__geo_interface__ # Create dataframe with cell_ids from extent with given resolution print(f"Start filling raster extent with h3 indexes at resolution {resolution}") h3_gdf = gpd.GeoDataFrame({'cell_id': list(h3.polyfill_geojson(extent['features'][0]["geometry"], res=resolution))}) # Get hex centroids for points h3_gdf['geometry'] = h3_gdf['cell_id'].apply(lambda x: Point(h3.h3_to_geo(x)[1], h3.h3_to_geo(x)[0])) hex_gdf = h3_gdf.set_crs('epsg:4326') # Spatial join hex centroids with gdf vector_h3 = gpd.sjoin(hex_gdf, gdf) # Drop unnecessary fields vector_h3 = vector_h3[['cell_id', value_name]] return vector_h3
def h3_to_point(dataframe, h3_column='h3'): """Convert H3 index in pandas dataframe into longitude and latitude. :param: dataframe: pandas dataframe with a column of H3 indices :type dataframe: pandas.core.frame.DataFrame :param h3_column: column name of the column with the H3 indices, defaults to h3. :type h3_column: str, optional """ lat_lon = numpy.array( [h3.h3_to_geo(h3hexagon) for h3hexagon in dataframe['h3']]) dataframe['latitude'] = lat_lon[:, 0] dataframe['longitude'] = lat_lon[:, 1] return dataframe
def random_location(candidate_hex_addresses, resolution): root_hex = random.choice(candidate_hex_addresses) root_resolution = h3.h3_get_resolution(root_hex) current_resolution = root_resolution current_cell = root_hex while current_resolution < resolution: # Step up one resolution. current_resolution += 1 # Get all the children of the current cell. children = h3.h3_to_children(current_cell, current_resolution) # Draw a random child and set it to the current cell. current_cell = random.choice(list(children)) return h3.h3_to_geo(current_cell)
def decorated(*args, **kwargs): geoframe = kwargs.pop('geoframe', None) country = kwargs.pop('country', None) polygon = kwargs.pop('polygon', None) point = kwargs.pop('point', None) number_of_parameter = sum(x is not None for x in (geoframe, country, polygon, point)) if number_of_parameter > 1: return ("'geoframe', 'country', 'polygon' and " "'point' are mutually exclusive", 400) # Parse parameter geoframe if geoframe is not None: try: logger.debug('Try parsing geoframe') kwargs['wkt'] = bounding_box_to_wkt(*geoframe) except ValueError: return 'Invalid geoparam', 400 # parse parameter country elif country is not None: logger.debug('Try parsing country') try: kwargs['wkt'] = get_country_wkt(country.upper()) except CountryNotFound: return 'Unknown country code.', 400 # parse parameter polygon elif polygon is not None: try: logger.debug('Try parsing polygon') kwargs['wkt'] = polygon_to_wkt(polygon) except RESTParamError as err: return str(err), 400 # parse parameter point elif point is not None: try: logger.debug('Try to parse point') point = h3.h3_to_geo( h3.geo_to_h3(point[1], point[0], emissionsapi.db.resolution)) kwargs['wkt'] = f'POINT({point[1]} {point[0]})' # Take a radius from 0.01 decimal degree which are approx. # 1113 meter kwargs['distance'] = 0.01 except KeyError: return 'Invalid point', 400 return f(*args, **kwargs)
def data2goolemap(hex_res, data, t): cur_time_stamp = int(time.time()) tmp_dict = { 'o': b'o', 'oc': b'oc', 'd': b'd', } tmp_time_dict = { 'o': b'o:t', 'oc': b'oc:t', 'd': b'd:t', } tt = tmp_dict[t] ttime = tmp_time_dict[t] oc_tt = b'oc' oc_ttime = b'oc:t' tmp_zip_data = zip(hex_res, data) tmp_res = [] for x in tmp_zip_data: val = 0 if x[1] != {}: if tt in x[1] and ttime in x[1] and cur_time_stamp - int( x[1][ttime]) <= key_memory_time and int(x[1][tt]) > 0: val += int(x[1][tt]) # vals related to cancel orders give 5 times weight if oc_tt in x[1] and oc_ttime in x[1] and cur_time_stamp - int( x[1][oc_ttime]) <= key_memory_time and int( x[1][oc_tt]) > 0: val += int(x[1][oc_tt]) * 5 if val > 0: tmp_res.append([x[0], val]) print(len(tmp_res)) s = html_part1 + default_max_point for x in range(len(tmp_res)): tmp_loc = h3.h3_to_geo(tmp_res[x][0]) s += data_format % (tmp_loc[0], tmp_loc[1], tmp_res[x][1]) s += html_part2 return s
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 visualize_hexagons(hexagons_df, legends=True, join_muiltiindex_cols=True, polygon_conf_dict={}, folium_map=None, folium_map_config={}): """ Creates a Folium map with hexagons and pop-up legend. :param hexagons_df: DataFrame with the H3 index has index. :param legends: If 'True', each hexagon will have a pop-up with all the values of its row. :param join_muiltiindex_cols: Joins the 'hexagons_df' columns in a single columns, if MultiIndex :param polygon_conf_dict: a dict to config the folium.Polygon obj. Ex.: { # fill color is True by default, it has to be explicit turned off "color": {"col":"lost_tours", "big_is_better": False, "colormap_legend": "legend", "fill_color": True}, "color": {"val":"green"}, # Color can also just be a single value "weight": {"col":"n_bookings", "max": 6, "min":1}, "opacity": {"val":0.3}, "fill_opacity": {"val":0.15} } default: opacity: 0.3; fill_opacity: 0.15; weight: 0.5; color: green :param folium_map: The folium map obj to had the Hexagons to. If None a new map is created :param folium_map_config: default = {"zoom_start": 11,"tiles": 'cartodbpositron', "location": avg_hexagon_location} location: The initial lat, long to start the map at. If None tha Avg center of the hexagons will be used. zoom_start: the 'folium.Map()' zoom_start value. default 11 tiles: the 'folium.Map()' tiles value. default 'cartodbpositron' :return: Folium map obj """ hexagons_df = hexagons_df.copy() hexagons = hexagons_df.index.values n_hexs = len(hexagons) add_color_map_to_map = False # Hexagons popup Legends if legends is None or legends is False: legends = np.array(n_hexs * [None]) elif legends is True: if join_muiltiindex_cols and type( hexagons_df.columns) == pd.MultiIndex: hexagons_df.columns = ["-".join(c) for c in hexagons_df.columns] hexagons_dict = hexagons_df.to_dict("index") legends = [f"{idx}: {hexagons_dict[idx]}" for idx in hexagons] # processing Polygon Propreties # Adding default Polygon configs polygon_conf_dict.setdefault("opacity", {"val": 0.3}) polygon_conf_dict.setdefault("fill_opacity", {"val": 0.15}) polygon_conf_dict.setdefault("weight", {"val": 0.5}) polygon_conf_dict.setdefault("color", {"val": "green"}) all_poly_props_df = pd.DataFrame() for col_name, conf in polygon_conf_dict.items(): if "col" in conf: poly_prop_values = hexagons_df[conf["col"]] # Normalize if set(("min", "max")).issubset(conf): poly_prop_values = (conf["min"] + norm_col(poly_prop_values) * (conf["max"] - conf["min"])).values elif "val" in conf: poly_prop_values = len(hexagons_df) * [conf["val"]] # else: # raise Exception("No 'col' or 'val' key found! :(") # Processing colors if col_name == "color": if "col" in conf: big_is_better = conf[ "big_is_better"] if "big_is_better" in conf else True cm_legend = conf[ "colormap_legend"] if "colormap_legend" in conf else str( conf["col"]).replace("'", "") colormap = ColorMap(np.min(poly_prop_values), np.max(poly_prop_values), cm_legend, big_is_better) poly_prop_values = [colormap(ci) for ci in poly_prop_values] add_color_map_to_map = True # Adds fill color by default, it has to be explicit turned off if ("fill_color" in conf and conf["fill_color"]) or ("fill_color" not in conf): all_poly_props_df["fill_color"] = poly_prop_values all_poly_props_df[col_name] = poly_prop_values polys_config = list(all_poly_props_df.to_dict("index").values()) # Initial Location if "location" not in folium_map_config: location = np.mean([h3.h3_to_geo(h3_id) for h3_id in hexagons], axis=0) folium_map_config["location"] = location # Creates Folium map if folium_map is None: folium_map_config.setdefault("zoom_start", 11) folium_map_config.setdefault("tiles", 'cartodbpositron') m = folium.Map(**folium_map_config) else: m = folium_map # adding polygons for hex_id, leg, poly_conf in zip(hexagons, legends, polys_config): locations = h3.h3_to_geo_boundary(hex_id) folium.Polygon(locations, popup=leg, **poly_conf).add_to(m) # adds the colormap legend to the map if add_color_map_to_map: m.add_child(colormap.colormap) colormap.colormap.add_to(m) return m
def decode(h3_address): """Decode an h3 address to a lat/lon pair""" return h3.h3_to_geo(h3_address)
import pydeck as pdk from h3 import h3 #h3_key = h3.geo_to_h3(latitude, longitude, level) #h3.h3_to_geo_boundary(h3_address=h3_key) #h3.h3_to_geo_boundary(h3_address=h3_key) # 2014 locations of car accidents in the UK UK_ACCIDENTS_DATA = ( 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv' ) data = pd.read_csv(UK_ACCIDENTS_DATA) h3_address = h3.geo_to_h3(37.3615593, -122.0553238, 5) hex_center_coordinates = h3.h3_to_geo(h3_address) boundary = h3.h3_to_geo_boundary(h3_address) def lat_lng_to_h3(row): return h3.geo_to_h3(row['lat'], row['lng'], 11) data['h3'] = data.apply(lat_lng_to_h3, axis=1) def map(data, lat, lon, zoom): layer = pdk.Layer( "HexagonLayer", data=data, get_position='[lng, lat]',
unnested_lst = [] for col in df.columns: unnested_lst.append(df[col].apply(pd.Series).stack()) df = pd.concat(unnested_lst, axis=1, keys=df.columns) df = df.reset_index() df = df.drop('level_1', axis=1) # add in district/prov names df = pd.merge(df, matching_districts, how='left', left_on=['level_0'], right_on=['level_0']) # add lat & lng of center of hex df['centroid_lat'] = df['h3_hexs'].apply(lambda x: h3.h3_to_geo(x)[0]) df['centroid_long'] = df['h3_hexs'].apply(lambda x: h3.h3_to_geo(x)[1]) # turn h3 hexs into geo. boundary df['geometry'] = df["h3_hexs"].apply( lambda x: h3.h3_to_geo_boundary(h=x, geo_json=True)) # turn to Point df['geometry'] = df['geometry'].apply(lambda x: [Point(x, y) for [x, y] in x]) # turn to Polygon df['geometry'] = df['geometry'].apply( lambda x: Polygon([[poly.x, poly.y] for poly in x])) # turn to geoDF df_geo = gpd.GeoDataFrame(df, geometry="geometry") # plot to see
def main(): """ """ # APERTURE_SIZE = 7 filename = DATA_URL_DICT[3]['name'] DATA_URL = DATA_URL_DICT[3]['URL'] gdf = load_geopandas_dataset(DATA_URL=DATA_URL, dirpath='./data/', filename=filename) if gdf is not None: # assert gdf.crs is not None assert gdf.crs != "" st.write(f"gdf.crs: {gdf.crs}") gdf = gdf.to_crs(epsg='4326').copy() # h3_level = 8.5 gdf[f'H3_{h3_level}'] = gdf.apply( lambda row: h3.geo_to_h3(row.geometry.y, row.geometry.x, h3_level), axis=1) # st.write(gdf.plot()) # lat, lng, hex resolution h3_address = h3.geo_to_h3(58.426172, 17.3623063, h3_level) hex_center_coordinates = h3.h3_to_geo(h3_address) hex_boundary = h3.h3_to_geo_boundary(h3_address) m = visualize_hexagons([h3_address]) tooltip = "Hexagon center" folium.Marker(hex_center_coordinates, popup="Hexagon center", tooltip=tooltip).add_to(m) # hexagons = polygonize_hexagons(gdf=gdf, APERTURE_SIZE=8, crs='EPSG:3006') #st.write(hexagons) # st.write(gdf[f'H3_{h3_level}'].head(20)) # find all points that fall in the grid polygon counts = gdf.groupby([ f'H3_{h3_level}' ])[f'H3_{h3_level}'].agg('count').to_frame('count').reset_index() # https://spatialthoughts.com/2020/07/01/point-in-polygon-h3-geopandas/ # To visualize the results or export it to a GIS, we need to convert the H3 cell ids to a geometry. # The h3_to_geo_boundary function takes a H3 key and returns a list of coordinates that form the hexagonal cell. # Since GeoPandas uses shapely library for constructing geometries, we convert the list of coordinates # to a shapely Polygon object. Note the optional second argument to the h3_to_geo_boundary function which # we have set to True which returns the coordinates in the(x, y) order compared to default(lat, lon) def add_geometry(row): points = h3.h3_to_geo_boundary(row[f'H3_{h3_level}'], True) return Polygon(points) st.write(counts.head(20)) counts['geometry'] = counts.apply(add_geometry, axis=1) counts_gdf = gpd.GeoDataFrame(counts, crs='EPSG:4326') # We turn the dataframe to a GeoDataframe with the CRS EPSG:4326 # (WGS84 Latitude/Longitude) and write it to a geopackage. output_filename = f'./data/gridcounts_H3_{h3_level}.gpkg' counts_gdf.to_file(driver='GPKG', filename=output_filename) #hexs = h3.polyfill( # gdf.geometry[0].__geo_interface__, APERTURE_SIZE, geo_json_conformant=True) """ fig, ax = plt.subplots(figsize=(2, 4)) geojson_result = latlong_to_geojson_string_h3_geometry( latitude_dd=lat_centr_point, longitude_dd=lon_centr_point, resolution=APERTURE_SIZE) ax = show_map( geojson_result=geojson_result, center_location=[lat_centr_point, lon_centr_point], zoom_start=5.5, tiles="cartodbpositron") #ax = hexagons.plot(alpha=0.5, color="xkcd:pale yellow", figsize=(9, 9)) st.pyplot(fig) #fig, ax = plt.subplots(figsize=(2, 4)) #world = gpd.read_file( # gpd.datasets.get_path('naturalearth_lowres')) #sweden = world.query('name == "Sweden"') #sweden.plot(edgecolor='None', facecolor='lightgray', ax=ax) #gdf.plot(ax=ax) #ax.axis('off') #st.pyplot(fig) """ """ m = visualize_hexagons([h3_address]) m = visualize_hexagons(list(h3.k_ring_distances( h3_address, 4)[3]), color="purple") m = visualize_hexagons(list(h3.k_ring_distances( h3_address, 4)[2]), color="blue", folium_map=m) m = visualize_hexagons(list(h3.k_ring_distances( h3_address, 4)[1]), color="green", folium_map=m) m = visualize_hexagons(list(h3.k_ring_distances( h3_address, 4)[0]), color="red", folium_map=m) # add marker for center # add marker for Liberty Bell tooltip = "Hexagon center" folium.Marker( hex_center_coordinates, popup="Hexagon center", tooltip=tooltip ).add_to(m) """ #geoJson = gdf_to_h3_geojson(gdf=gdf) #st.write(geoJson) #polyline = geoJson['coordinates'][0] """
def selector(h3id): lat, lng = h3.h3_to_geo(h3id) return min_lat <= lat <= max_lat and min_lng <= lng <= max_lng
def draw_folium(df, id_col, val_col, zoom_start=13, control_scale=True, bins=None, fill_color='YlGn', fill_opacity=0.7, line_opacity=0.2, title=None, **kwargs): """ ヒートマップを描画する Attributes ---------- df : pandas.core.frame.DataFrame 対象のデータフレーム id_col : str 表示するデータのidのcolumn名 val_col : str 表示するデータのvalueのcloumn名 zoom_start : int foliumのズームの初期位置 control_scale : bool 縮尺を表示するかどうか bins : list choroplethの境界値。valueの最大値よりbinの最大値以上の必要があるので注意 max(df[val_col]) <= bins[-1] fill_color : str choroplethの色。以下から選択可能 ‘BuGn’, ‘BuPu’, ‘GnBu’, ‘OrRd’, ‘PuBu’, ‘PuBuGn’, ‘PuRd’, ‘RdPu’, ‘YlGn’, ‘YlGnBu’, ‘YlOrBr’, ‘YlOrRd’ fill_opacity : float [0,1], 透明度(色塗り) line_opacity : float [0,1], 透明度(境界) title : str legend title """ df["h3_lat"], df["h3_lng"] = zip(*df[id_col].apply(lambda x: h3.h3_to_geo( x))) # h3.h3_to_geo has a proper (lat, lng) output location = [df["h3_lat"].mean(), df["h3_lng"].mean()] #[lat,lng] fmap = folium.Map(location=location, zoom_start=zoom_start, control_scale=control_scale) copyright = ' <a href="https://www.datawise.co.jp/"> | © DATAWISE </a>,' folium.raster_layers.TileLayer( tiles='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', name='OpenStreetMap2', attr=copyright, overlay=True).add_to(fmap) df[id_col] = df[id_col].astype('str') geojson = {"type": "FeatureCollection", "features": []} df["h3_boundary"] = df[id_col].apply( lambda x: tuple((lat, lng) for lng, lat in h3.h3_to_geo_boundary(x)) ) # Switching lat, lng position because h3.h3_to_geo_boundary has a reverted output. def _process_tpl(id_col, h3_boundary): tpl = { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [] } } tpl["geometry"]["coordinates"].append(h3_boundary) tpl["id"] = id_col return tpl df["tpl"] = df[[id_col, "h3_boundary"]].apply(lambda x: _process_tpl(x[0], x[1]), axis=1) geojson["features"].extend(df["tpl"]) geojson = json.dumps(geojson) if bins is None: max_ = df.sort_values(val_col, ascending=False).reset_index()[val_col][0] bins = [max_ / 5 * i for i in range(6)] if title is None: title = val_col folium.Choropleth( geojson, # GeoJSONデータ name='choropleth', data=df, # DataFrameまたはSeriesを指定 columns=[id_col, val_col], # 行政区分コードと表示データ key_on='feature.id', # GeoJSONのキー(行政区分コード) fill_color=fill_color, # 色パレットを指定(※) bins=bins, # 境界値を指定 fill_opacity=fill_opacity, # 透明度(色塗り) line_opacity=line_opacity, # 透明度(境界) legend_name=title, # 凡例表示名 highlight=True, **kwargs).add_to(fmap) return fmap
def from_h3(cls: t.Type[P], index: Index) -> P: lat, lon = h3.h3_to_geo(index) return cls(lon, lat)
def decode(self, sId): """decode a geohash calling the original library""" return h3.h3_to_geo(sId)
def H3ToGeo(h3_id): return h3.h3_to_geo(h3_id)
def gen_hexagons(self, resolution, city): ''' Converts an input multipolygon layer to H3 hexagons given a resolution. Parameters ---------- resolution: int, 0:15 Hexagon resolution, higher values create smaller hexagons. city: GeoDataFrame Input city polygons to transform into hexagons. Returns ------- city_hexagons: GeoDataFrame Hexagon geometry GeoDataFrame (hex_id, geom). city_centroids: GeoDataFrame Hexagon centroids for the specified city (hex_id, geom). Example ------- >> lima = filter_population(pop_lima, poly_lima) >> lima_hex = gen_hexagons(8, lima) 0 | geometry 888e620e41fffff | POLYGON ((-76.80007 -12.46917, -76.80439 -12.4... 888e62c809fffff | POLYGON ((-77.22539 -12.08663, -77.22971 -12.0... 888e62c851fffff | POLYGON ((-77.20708 -12.08484, -77.21140 -12.0... 888e62c841fffff | POLYGON ((-77.22689 -12.07104, -77.23122 -12.0... 888e62c847fffff | POLYGON ((-77.23072 -12.07929, -77.23504 -12.0... 0 | geometry 888e620e41fffff | POINT (-76.79956 -12.47436) 888e62c809fffff | POINT (-77.22488 -12.09183) 888e62c851fffff | POINT (-77.20658 -12.09004) 888e62c841fffff | POINT (-77.22639 -12.07624) 888e62c847fffff | POINT (-77.23021 -12.08448) ''' # Polyfill the city boundaries h3_centroids = list() h3_polygons = list() h3_indexes = list() # Get every polygon in Multipolygon shape city_poly = city.explode().reset_index(drop=True) for ix, geo in city_poly.iterrows(): hexagons = h3.polyfill(geo['geometry'].__geo_interface__, res=resolution, \ geo_json_conformant=True) for hexagon in hexagons: centroid_lat, centroid_lon = h3.h3_to_geo( hexagon) # format as x,y (lon, lat) h3_centroids.append(Point(centroid_lon, centroid_lat)) h3_geo_boundary = h3.h3_to_geo_boundary(hexagon) [bound.reverse() for bound in h3_geo_boundary] # format as x,y (lon, lat) h3_polygons.append(Polygon(h3_geo_boundary)) h3_indexes.append(hexagon) # Create hexagon dataframe city_hexagons = gpd.GeoDataFrame( h3_indexes, geometry=h3_polygons).drop_duplicates() city_hexagons.crs = 'EPSG:4326' city_centroids = gpd.GeoDataFrame( h3_indexes, geometry=h3_centroids).drop_duplicates() city_centroids.crs = 'EPSG:4326' return city_hexagons, city_centroids
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. """ print("Working on hex around ({:}, {:}):".format(lon, lat)) 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) starts: t.List[h3.H3Index] = [] cs = sqlalchemy.select( [hex.c.hexbin, hex.c.longitude, hex.c.latitude, hex.c.habitable]) for h, lon, lat, habitable in db.execute(cs).fetchall(): if not habitable: continue row, col = rowcol((lat, lon)) if 500 <= col < width + 500 and 500 < row < height + 500: starts.append(h) 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) print("Computing central nodes…") center = {} partial = set() belongs = {} for row in range(ecoregions.shape[0]): incomplete = set() for col in range(ecoregions.shape[1]): lon, lat = transform * (col, row) hexbin = h3.geo_to_h3(lat, lon, RESOLUTION) incomplete.add(hexbin) if row == 0 or col < 10 or ecoregions.shape[1] - 10 <= col: partial.add(hexbin) try: del belongs[hexbin] except KeyError: pass elif hexbin not in partial: belongs.setdefault(hexbin, set()).add((row, col)) for hexbin in set(belongs) - incomplete: print(f"Checking {hexbin}…") points = belongs.pop(hexbin) if hexbin in partial: print("Not competely in this tile.") continue result = engine.execute( sqlalchemy.select([ hex.c.habitable, hex.c.vlongitude, hex.c.vlatitude, hex.c.longitude, hex.c.latitude ]).where(hex.c.hexbin == hexbin)).fetchone() if not result: center[hexbin] = rowcol(h3.h3_to_geo(hexbin)) print( "WHAT IS THIS??? THIS HEX ({:}, {:}) IS NOT IN THE DB!!!!". format(*center[hexbin])) continue h, vlon, vlat, lon, lat = result if vlon is not None and vlat is not None: print("Known in DB.") center[hexbin] = rowcol((vlat, vlon)) continue if lon and lat and not h: print("Uninhabitable.") center[hexbin] = rowcol((lat, lon)) continue print("Computing centralities…") engine.execute(hex.update().where(hex.c.hexbin == hexbin).values({ "vlatitude": 0.0, "vlongitude": 0.0 })) 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 dist = {(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()} border = [ (i - rmin, j - cmin) for (i, j) in points if (i - 1, j) not in points or (i + 1, j) not in points or ( i, j - 1) not in points or (i, j + 1) not in points ] c = t.Counter() max_dist = 0 for r0, c0 in border: pred = {(r0, c0): None} all_dist = distances_from_focus((r0, c0), set(border), dist, pred=pred) for b1 in border: n = b1 while pred[n]: n = pred[n] c[n] += 1 max_dist = max(max_dist, all_dist.max()) (r0, c0), centrality = c.most_common(1)[0] center[hexbin] = (r0 + rmin, c0 + cmin) print(hexbin, center[hexbin], centrality) lon, lat = transform * (c0 + cmin, r0 + rmin) rlat, rlon = h3.h3_to_geo(hexbin) print( f"Centalic node at ({lon}, {lat}). [Actual center at ({rlon}, {rlat}).]" ) engine.execute(hex.update().where(hex.c.hexbin == hexbin).values({ "vlatitude": lat, "vlongitude": lon })) try: db.execute( t_dist.insert({ "hexbin1": hexbin, "hexbin2": hexbin, "flat_distance": 0.0, "distance": max_dist, "source": 4 })) except sqlalchemy.exc.IntegrityError: pass
def h3_hex_centroid(hex): """ Get the centroid from a h3 hex location """ (lat, lon) = h3.h3_to_geo(hex) return {"lat": lat, "lon": lon}