def test_h3_set_to_multi_polygon_contiguous(self): # the second hexagon shares v0 and v1 with the first h3_addresses = ['89283082837ffff', '89283082833ffff'] multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses) vertices0 = h3.h3_to_geo_boundary(h3_addresses[0]) vertices1 = h3.h3_to_geo_boundary(h3_addresses[1]) # We shift the expected circular list so that it starts from multi_polygon[0][0][0], # since output starting from any vertex would be correct as long as it's in order. expected_coords = self.shift_circular_list(multi_polygon[0][0][0], [ vertices1[0], vertices1[1], vertices1[2], vertices0[1], vertices0[2], vertices0[3], vertices0[4], vertices0[5], vertices1[4], vertices1[5], ]) expected = [[expected_coords]] self.assertEqual(len(multi_polygon), 1, 'polygon count matches expected') self.assertEqual(len(multi_polygon[0]), 1, 'loop count matches expected') self.assertEqual(len(multi_polygon[0][0]), 10, 'coord count 10 matches expected') self.assertTrue(multi_polygon == expected, 'outline matches expected')
def polyfill_from_geoJson_extent(self, df, parent_resolution, export_hex=False, geo_json_output_path=None, export_child=None, export_geopackage=False, layer_name=None): #todo: check the projection, the child projection seems to be right but not the parent, the parent hex are distorted. """ :param df: input geopandas Dataframe :param parent_resolution: H3 Hex resolution :param export_hex: boolean, if you want to export the hexs :param geo_json_output_path: output of h3 geojson :param export_child: boolean, if you want to export the child of resolution :param export_geopackage: boolean, if you want to export to geopackage :param layer_name: the name of the layer that you want to export """ bbox = box(*df.total_bounds) hexs = h3.polyfill(bbox.__geo_interface__, parent_resolution, geo_json_conformant=True) polygonise = lambda hex_id: Polygon( h3.h3_to_geo_boundary( hex_id, geo_json=True) ) all_polys = gp.GeoSeries(list(map(polygonise, hexs)), index=hexs, crs="EPSG:4326") #print(all_polys) if export_hex is True and geo_json_output_path is not None: all_polys.to_file(geo_json_output_path +"_{}.json".format(parent_resolution), driver='GeoJSON') if export_hex is True and export_geopackage is True and layer_name is not None: all_polys.to_file(os.path.join(self.out_gdb, '{}_H3_hexy.gpkg'.format(layer_name)), layer="{}_res_{}".format(layer_name, parent_resolution), driver='GPKG') if export_child: child_res = parent_resolution + 1 hexs = h3.polyfill(bbox.__geo_interface__, child_res , geo_json_conformant=True) polygonise = lambda hex_id: Polygon( h3.h3_to_geo_boundary( hex_id, geo_json=True) ) all_polys = gp.GeoSeries(list(map(polygonise, hexs)), index=hexs, crs="EPSG:4326") if export_hex is True and geo_json_output_path is not None: all_polys.to_file(geo_json_output_path + "_{}.json".format(child_res), driver='GeoJSON') if export_hex is True and export_geopackage is True and layer_name is not None: all_polys.to_file(os.path.join(self.out_gdb, '{}_H3_hexy.gpkg'.format(layer_name)), layer="{}_res_{}".format(layer_name,child_res), driver='GPKG')
def polyfill_from_polyon(self, polygon, parent_resolution, export_hex=False, geo_json_output_path=None, export_child=None, export_geopackage=False, layer_name=None): """ :param df: input geopandas Dataframe :param parent_resolution: H3 Hex resolution :param export_hex: boolean, if you want to export the hexs :param geo_json_output_path: output of h3 geojson :param export_child: boolean, if you want to export the child of resolution :param export_geopackage: boolean, if you want to export to geopackage :param layer_name: the name of the layer that you want to export """ polygon_buffer = polygon['geometry'].buffer(0.0000001)[0] #print(polygon_buffer) hexs = h3.polyfill(polygon_buffer.__geo_interface__, parent_resolution, geo_json_conformant=True) polygonise = lambda hex_id: Polygon( h3.h3_to_geo_boundary( hex_id, geo_json=True) ) all_polys = gp.GeoSeries(list(map(polygonise, hexs)), index=hexs, crs="EPSG:4326") # print(all_polys) if export_hex is True and geo_json_output_path is not None: all_polys.to_file(geo_json_output_path + "_{}.json".format(parent_resolution), driver='GeoJSON') if export_hex is True and export_geopackage is True and layer_name is not None: all_polys.to_file(os.path.join(self.out_gdb, 'gpkg', '{}_H3_hexy.gpkg'.format(layer_name)), layer="{}_res_{}".format(layer_name, parent_resolution), driver='GPKG') if export_child: child_res = parent_resolution + 1 hexs = h3.polyfill(polygon_buffer.__geo_interface__, child_res, geo_json_conformant=True) polygonise = lambda hex_id: Polygon( h3.h3_to_geo_boundary( hex_id, geo_json=True) ) all_polys = gp.GeoSeries(list(map(polygonise, hexs)), index=hexs, crs="EPSG:4326") if export_hex is True and geo_json_output_path is not None: all_polys.to_file(geo_json_output_path + "_{}.json".format(child_res), driver='GeoJSON') if export_hex is True and export_geopackage is True and layer_name is not None: all_polys.to_file(os.path.join(self.out_gdb, 'gpkg', '{}_H3_hexy.gpkg'.format(layer_name)), layer="{}_res_{}".format(layer_name, child_res), driver='GPKG')
def draw(df, id_col, val_col, extent, color_selector, figsize=(8, 8), dpi=100, width=600, alpha=0.8, axis_visible=False, **kwargs): fig, ax = plt.subplots(figsize=figsize, dpi=dpi) ax.xaxis.set_visible(axis_visible) ax.yaxis.set_visible(axis_visible) t = tmb.tiles.build_OSM() plotter = tmb.Plotter(extent, t, width=width) plotter.plot(ax, t) add_color_bar(df, val_col, fig, color_selector, **kwargs) n = len(df) for i in range(n): id = df[id_col].iloc[i] val = df[val_col].iloc[i] color = color_selector(val) vts = _swap(h3.h3_to_geo_boundary(id)) xys = [tmb.project(*x) for x in vts] poly = plt.Polygon(xys, fc=color, alpha=alpha) ax.add_patch(poly) fig.text(0.86, 0.125, '© DATAWISE', va='bottom', ha='right') return fig, ax
def counts_by_hexagon(df, resolution, latlong): '''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] print('1st') #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) df_aggreg = df.groupby(by="hex_id").size().reset_index() print(len(df_aggreg)) 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)] } ) """ return df_aggreg
def _df_to_h3(df, h3_level=8, aggfunc=np.sum): """ Aggregates point data to corresponding h3 polygons For more on h3 see https://uber.github.io/h3/#/ Parameters ---------- df : pd.DataFrame of lat/long data to be aggregated, or GeoDataFrame with valid point geometry h3_level : resolution of h3_tiles. Default is arbitrary aggfunc : function, str, list or dict to aggregate numeric cols to h3 tile as per pd.DataFrame.agg(aggfunc) Returns ------- H3DataFrame of the dataframe aggregated to h3 tiles, with index 'id' of h3 tile code """ df = _validate_point_data(df) if isinstance(df, GeoDataFrame): df = gpdf_to_latlong_df(df) # Utility for h3.geo_to_h3 to work on dataframe row lat_lng_to_h3 = lambda row, h3_level: h3.geo_to_h3(row['latitude'], row[ 'longitude'], h3_level) df['id'] = df.apply(lat_lng_to_h3, args=(h3_level, ), axis=1) df = df.drop(columns=['latitude', 'longitude']) df = df.groupby('id').agg(aggfunc).reset_index() # Utilty for for h3.h3_to_geo_boundary to return shapely.geometry.Polygon h3_to_polygon = lambda h3_address: Polygon( h3.h3_to_geo_boundary(h3_address, geo_json=True)) df['geometry'] = df.id.apply(h3_to_polygon) return PorygonDataFrame(df.set_index('id'))
def get_geos(self): h3s = [ col for col in self.as_df.columns if all(c in string.hexdigits for c in col) ] return [(h3.h3_to_geo_boundary(g), self.as_df[g].fillna(0).sum(), g) for g in h3s]
def test_h3_set_to_multi_polygon_single_geo_json(self): h3_addresses = ['89283082837ffff'] multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses, True) vertices = h3.h3_to_geo_boundary(h3_addresses[0], True) # We shift the expected circular list so that it starts from multi_polygon[0][0][0], # since output starting from any vertex would be correct as long as it's in order. expected_coords = self.shift_circular_list(multi_polygon[0][0][0], [ vertices[2], vertices[3], vertices[4], vertices[5], vertices[0], vertices[1] ]) expected = [[expected_coords]] self.assertEqual(len(multi_polygon), 1, 'polygon count matches expected') self.assertEqual(len(multi_polygon[0]), 1, 'loop count matches expected') self.assertEqual( len(multi_polygon[0][0]), 7, 'coord count 7 matches expected according to geojson format') self.assertEqual( multi_polygon[0], multi_polygon[-1], 'first coord should be the same as last coord according to geojson format' ) self.assertAlmostEqual( multi_polygon[0][0][0][0], -122.42778275313199, None, 'the coord should be (lng, lat) according to geojson format (1)') self.assertAlmostEqual( multi_polygon[0][0][0][1], 37.77598951883773, None, 'the coord should be (lng, lat) according to geojson format (2)') # Discard last coord for testing below, since last coord is the same as the first one multi_polygon[0][0].pop() self.assertEqual(multi_polygon, expected, 'outline matches expected')
def _polyfill(self, geometry: shapely.geometry.polygon.Polygon, resolution: int) -> List[Tile]: """Internal polyfill. Calls grids polyfills accordinly. Parameters ---------- geometry : shapely.geometry.polygon.Polygon resolution : int Returns ------- List[Tile] """ if self.grid_type == "s2": return [ Tile(geo["geometry"], geo["id"], "s2") for geo in s2.polyfill( geometry.geojson, resolution, True, with_id=True) ] elif self.grid_type == "h3": return [ Tile(h3.h3_to_geo_boundary(tile_id, True), tile_id, "h3") for tile_id in h3.polyfill_geojson( geometry.geojson, resolution) ] elif self.grid_type in ("bing", "quadtree"): return [ Tile(quadtree.tile_to_geo_boundary(tile_id), tile_id, self.grid_type) for tile_id in quadtree.polyfill(geometry.shapely, resolution) ]
def main(data_file, polygon_file, resolution, output_file): data = pd.read_csv(data_file).query("~latitude.isnull()") # Index the data by h3. Not the most efficient way to do it but it's fast # enough IMO. data.loc[:, "h3_index"] = [ h3.geo_to_h3(row.latitude, row.longitude, resolution) for _, row in data.iterrows() ] # Read in the US states polygons. us_states = shape(json.load(polygon_file)) state_hexes = set() # Polyfill each state and add it to the big list of h3 indexes. for geometry in us_states: state_hexes |= h3.polyfill(mapping(geometry), resolution, geo_json_conformant=True) all_hexes = state_hexes | set(data.h3_index) # Now reindex the counted sightings by hex address and fill the empties # with zeros. grouped_sightings = (data.groupby("h3_index").agg({ "date": "count" }).reindex(list(all_hexes), fill_value=0)) geo_json = {"type": "FeatureCollection", "features": []} for h3_address, row in grouped_sightings.iterrows(): hexagon = h3.h3_to_geo_boundary(h3_address, geo_json=True) geo_json["features"].append({ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [hexagon] }, "properties": { "hex_address": h3_address, "count": int(row.date), }, }) # Now it's map time. map_center = [data["latitude"].mean(), data["longitude"].mean()] colormap = branca.colormap.linear.YlOrRd_09.scale( grouped_sightings.date.min(), grouped_sightings.date.max()) m = folium.Map(location=map_center, zoom_start=5, tiles="cartodbpositron") folium.GeoJson( geo_json, tooltip=folium.GeoJsonTooltip(["hex_address", "count"]), style_function=lambda x: { "fillColor": colormap(x["properties"]["count"]), "color": "gray", "weight": 0.1, "fillOpacity": 0.5, }, ).add_to(m) colormap.add_to(m) m.save(output_file)
def main(polygon_file, data_file, resolution, output_file): logger.info("Reading 👣 sightings.") data = pd.read_csv(data_file).query("~latitude.isnull()") data.loc[:, "h3_index"] = [ h3.geo_to_h3(row.latitude, row.longitude, resolution) for _, row in data.iterrows() ] logger.info("Reading US polygon.") us_states = shape(json.load(polygon_file)) us_hexes = set(data.h3_index) logger.info("Polyfilling the USA.") for geometry in tqdm(us_states, total=len(us_states)): us_hexes |= h3.polyfill(mapping(geometry), resolution, geo_json_conformant=True) logger.info(f"Writing to {output_file.name}.") writer = csv.DictWriter(output_file, fieldnames=["hex_address", "hex_geojson"]) writer.writeheader() for us_hex in tqdm(us_hexes, total=len(us_hexes)): writer.writerow({ "hex_address": us_hex, "hex_geojson": json.dumps(h3.h3_to_geo_boundary(us_hex, geo_json=True)), })
def h3_geodataframe_with_values(h3_dataframe_with_values): """GeoDataFrame with resolution 9 H3 index, values, and Hexagon geometries""" geometry = [ Polygon(h3.h3_to_geo_boundary(h, True)) for h in h3_dataframe_with_values.index ] return gpd.GeoDataFrame( h3_dataframe_with_values, geometry=geometry, crs="epsg:4326" )
def __hex_boundary(self, hex_id: str) -> List[GeoLatLng]: h3_boundary = h3.h3_to_geo_boundary(hex_id) amap_boundary = [] for lat, lng in h3_boundary: h3_geo = GeoLatLng(lat, lng, using_rad=False) amap_geo = self.__h3Geo_to_amapGeo(h3_geo) amap_boundary.append(amap_geo) return amap_boundary
def display_hexagon(surf, x, y, l_color=RED): h3coord = h3.geo_to_h3(y, x, 8) bound_list = h3.h3_to_geo_boundary(h3coord) adj_bound_list = return_adj_coord(bound_list) pygame.draw.polygon(surf, l_color, adj_bound_list, 2)
def get_geos(self): try: from h3 import h3 except ImportError: # pragma: no cover logger.error('h3-py must be installed for geo hashing capabilities. Exiting.' 'Install it with: pip install scikit-hts[geo]') return h3s = [col for col in self.as_df.columns if all(c in string.hexdigits for c in col)] return [(h3.h3_to_geo_boundary(g), self.as_df[g].fillna(0).sum(), g) for g in h3s]
def __init__(self, id, hexagon, demand, destination): self.id = id self.polygon = shape( {"type": "Polygon", "coordinates": [h3.h3_to_geo_boundary(hexagon, geo_json=True)], "properties": ""}) self.centre = Location(self.polygon.centroid.y, self.polygon.centroid.x) self.hexagon = hexagon self.list_of_vehicles = [] self.demand = demand self.destination = destination
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_h3_to_parent_aggregate(h3_geodataframe_with_values): result = h3_geodataframe_with_values.h3.h3_to_parent_aggregate(8) # TODO: Why does Pandas not preserve the order of groups here? index = pd.Index(["881f1d4811fffff", "881f1d4817fffff"], name="h3_08") geometry = [Polygon(h3.h3_to_geo_boundary(h, True)) for h in index] expected = gpd.GeoDataFrame( {"val": [5, 3]}, geometry=geometry, index=index, crs="epsg:4326" ) assert_geodataframe_equal(expected, result)
def hex_to_polygon(hexid): """Transforms single hexid to shapely hexagonal polygon """ list_of_coords_list=h3.h3_to_geo_boundary(h3_address=hexid,geo_json=False) #return Polygon([tuple(i) for i in list_of_coords_list]) # Corrijo que las coordenadas que necesita geopandas tienen que estar invertidas con logitud primero y latitud despues return Polygon([tuple([pair[1],pair[0]]) for pair in list_of_coords_list])
def hexagon_from_location(lat, lon, resolution=3): """Get a geojson hexagon from a lat/lon pair""" return { "type": "Polygon", "coordinates": [ h3.h3_to_geo_boundary(encode(lat, lon, res=resolution), geo_json=True) ] }
def test_h3_set_to_multi_polygon_single_geo_json(self): h3_addresses = ['89283082837ffff'] multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses, True) vertices = h3.h3_to_geo_boundary(h3_addresses[0], True) # As above, could require an order update later on expected = [[[ vertices[2], vertices[3], vertices[4], vertices[5], vertices[0], vertices[1], vertices[2] ]]] self.assertEqual(multi_polygon, expected, 'outline matches expected')
def create_h3_geom_cells(extent, resolutions, table, export_type, db_engine): """Create geometry for h3 cells in given extent for given resolutions levels Parameters: extent (array): array of lat lon extent pairs for covering with h3 cells resolutions(array): array of integer h3 resolution levels table(string): table name for postgres database db_engine (sqlalchemy.engine): sqlalchemy database engine export_type(string): where to export 'geojson' or 'postgres' Returns: none """ extent = gpd.GeoSeries(box(extent[0], extent[1], extent[2], extent[3])).__geo_interface__ for res in resolutions: calc_time = time.time() print(f'start caclulating resolution {res}') set_hex = list(h3.polyfill(extent['features'][0]["geometry"], res=res)) print(f'finish caclulating resolution {res} in {str(round(time.time() - calc_time, 2))} seconds') if export_type == 'postgres': geom_time = time.time() gdf = gpd.GeoDataFrame({"cell_id": set_hex}) gdf['geometry'] = gdf["cell_id"].apply(lambda x: (Polygon(h3.h3_to_geo_boundary(x,geo_json=True)))) print(f'finish caclulating geometry fo res {res} in {str(round(time.time() - geom_time, 2))} seconds') import_time = time.time() gdf.to_postgis(table + str(res), db_engine, if_exists='replace') print(f'finish import to db {res} in {str(round(time.time() - import_time, 2))} seconds') elif export_type == 'geojson': transformer = Transformer.from_crs("epsg:4326", 'proj=isea') gdf = gpd.GeoDataFrame({"cell_id": set_hex}) gdf['geometry'] = gdf["cell_id"].apply(lambda x: Polygon(h3.h3_to_geo_boundary(x, True))) gdf['area'] = gdf["geometry"].apply(lambda x: transform(transformer.transform, x).area) gdf.to_file("{}{}.geojson".format(table, res), driver='GeoJSON') print('finish import to geojson {} {}'.format(res, time.asctime(time.localtime(time.time()))))
def shapely_from_h3(h3_index): """ This function takes a h3 index id and returns a shapely Polygon Parameters: h3_index (str): H3 index id Returns: shapely.Polygon : cell Polygon """ return Polygon([[i[1], i[0]] for i in h3.h3_to_geo_boundary(h3_index)])
def counts_by_hexagon(df, resolution, latlong, filter_variable=None): '''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] print('1st') #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) df.hex_no = pd.Categorical(df.hex_id) df['hex_no'] = df.hex_no.codes df['hex_no'] = df['hex_no'].astype(str) if filter_variable and hex_filter_select.value != 'All Hexes': if hex_filter_select.value == 'Filter by Number': hex_filter_list = hex_filter_no.value.split(',') df = df[df['hex_no'].isin(hex_filter_list)] #df_aggreg = df.groupby(by = ["hex_id","hex_no"]).size().reset_index() df_aggreg = df.groupby(by=["hex_id", "hex_no"]).agg({ 'vehicle_id': 'count', 'object_data-fare': 'mean' }).reset_index() print(len(df_aggreg)) df_aggreg.columns = ["hex_id", "hex_no", "value", "average_fare"] if filter_variable and hex_filter_select.value != 'All Hexes': if hex_filter_select.value == 'Filter by Threshold': hex_filter_threshold = int(hex_filter_no.value) df_aggreg = df_aggreg[df_aggreg['value'] > hex_filter_threshold] hex_th_filtered_list = list(set(df_aggreg['hex_no'])) df = df[df['hex_no'].isin(hex_th_filtered_list)] 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)] } ) """ return df, df_aggreg
def test_h3_set_to_multi_polygon_contiguous(self): # the second hexagon shares v0 and v1 with the first h3_addresses = ['89283082837ffff', '89283082833ffff'] multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses) vertices0 = h3.h3_to_geo_boundary(h3_addresses[0]) vertices1 = h3.h3_to_geo_boundary(h3_addresses[1]) # As above: This test is brittle but worthwhile; it's possible we'll # need to update to a new start vertex if the algo is changed expected = [[[ vertices1[0], vertices1[1], vertices1[2], vertices0[1], vertices0[2], vertices0[3], vertices0[4], vertices0[5], vertices1[4], vertices1[5], ]]] self.assertEqual(multi_polygon, expected, 'outline matches expected')
def test_h3_set_to_multi_polygon_single(self): h3_addresses = ['89283082837ffff'] multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses) vertices = h3.h3_to_geo_boundary(h3_addresses[0]) # This is tricky, because output in an order starting from any vertex # would also be correct, but that's difficult to assert and there's # value in being specific here expected = [[[ vertices[2], vertices[3], vertices[4], vertices[5], vertices[0], vertices[1] ]]] self.assertEqual(multi_polygon, expected, 'outline matches expected')
def test_h3_set_to_multi_polygon_single(self): h3_addresses = ['89283082837ffff'] multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses) vertices = h3.h3_to_geo_boundary(h3_addresses[0]) # We shift the expected circular list so that it starts from multi_polygon[0][0][0], # since output starting from any vertex would be correct as long as it's in order. expected_coords = self.shift_circular_list(multi_polygon[0][0][0], [ vertices[2], vertices[3], vertices[4], vertices[5], vertices[0], vertices[1] ]) expected = [[expected_coords]] self.assertEqual(multi_polygon, expected, 'outline matches expected')
def fill_shapefile_hexes(geojson: json, resolution: int): """ Fill a shapefile with hexes of a particular h3 resolution. parameters ---------- geojson:json - json with structure like: resolution:ind - h3 resolution returns ---------- hex_df:gpd.GeoDataFrame() - geo dataframe with hex_id and geometry """ # Generates hex_ids of filled polygon set_hexagons = h3.polyfill(geojson=geojson, res=resolution, geo_json_conformant=True) # Generate list of polygons list_hexagons = list(set_hexagons) # Reverse the latitude and longitude one_hex_of_fill = list_hexagons[0] one_hex_of_fill_coords_latlon = h3.h3_to_geo_boundary(h=one_hex_of_fill, geo_json=False) one_hex_of_fill_coords_lonlat = reverse_lat_lon(hex_coords=one_hex_of_fill_coords_latlon) # Create hex_id df, fill values with zero and assign the geometries df_fill_hex = pd.DataFrame({"hex_id": list_hexagons}) df_fill_hex["value"] = 0 df_fill_hex['geojson'] = df_fill_hex.hex_id.apply(lambda x: {"type": "Polygon", "coordinates": [reverse_lat_lon(h3.h3_to_geo_boundary(h=x, geo_json=False))] } ) # Fill the geometries and write out the final dataframe df_fill_hex['geometry'] = df_fill_hex['geojson'].apply(lambda x: Polygon(x['coordinates'][0])) df_fill_hex = gpd.GeoDataFrame(df_fill_hex, crs="EPSG:4326") return df_fill_hex
def create_h3_geom_cells_global(resolutions, table, export_type, db_engine=''): """Create geometry for h3 cells globally for given resolutions Parameters: db_engine (sqlalchemy.engine): sqlalchemy database engine resolutions(array): array of integer h3 resolution levels table(string): table name for postgres database export_type(string): where to export 'geojson' or 'postgres' Returns: none """ for res in resolutions: set_hex_0 = list(h3.get_res0_indexes()) set_hex = [] if res == 0: set_hex = set_hex_0 else: for i in set_hex_0: set_hex.extend(list(h3.h3_to_children(i, res))) if export_type == 'postgres': gdf = pd.GeoDataFrame({"cell_id": set_hex}) gdf['geometry'] = gdf["cell_id"].apply(lambda x:(Polygon(h3.h3_to_geo_boundary(x, geo_json=True)).wkb)) print('finish caclulating geometry {} {}'.format(res, time.asctime(time.localtime(time.time())))) gdf.to_postgis(table + str(res), db_engine, if_exists='replace') print('finish import to db {} {}'.format(res, time.asctime(time.localtime(time.time())))) elif export_type == 'geojson': transformer = Transformer.from_crs("epsg:4326", 'proj=isea') gdf = gpd.GeoDataFrame({"cell_id": set_hex}) gdf['geometry'] = gdf.cell_id.apply(lambda x: Polygon(h3.h3_to_geo_boundary(x, geo_json=True))) print('finish caclulating geometry {} {}'.format(res, time.asctime(time.localtime(time.time())))) gdf['area'] = gdf.geometry.apply(lambda x: transform(transformer.transform, x).area) gdf.to_file("{}{}.geojson".format(table, res), driver='GeoJSON') print('finish import to geojson {} {}'.format(res, time.asctime(time.localtime(time.time()))))
def test_h3_to_geo_boundary(self): latlngs = h3.h3_to_geo_boundary('85283473fffffff') expectedlatlngs = [[37.271355866731895, -121.91508032705622], [37.353926450852256, -121.86222328902491], [37.42834118609435, -121.9235499963016], [37.42012867767778, -122.0377349642703], [37.33755608435298, -122.09042892904395], [37.26319797461824, -122.02910130919]] self.assertEqual(len(latlngs), len(expectedlatlngs), 'got the expected number of vertices') for i in range(len(latlngs)): self.assertAlmostEqual(latlngs[i][0], expectedlatlngs[i][0], None, 'lat is ok') self.assertAlmostEqual(latlngs[i][1], expectedlatlngs[i][1], None, 'lng is ok')