def create_s2_geom_cells(extent, resolutions): # Create s2 rectangle to fill with s2 cells region_rect = s2.S2LatLngRect( s2.S2LatLng.FromDegrees(extent.bounds[1], extent.bounds[0]), s2.S2LatLng.FromDegrees(extent.bounds[3], extent.bounds[2])) coverer = s2.S2RegionCoverer() # Projection for cell area calculation transformer = Transformer.from_crs("epsg:4326", 'proj=isea') # Iterate through given resolutions, create and populate geopandas for each for r in resolutions: coverer.min_level = r coverer.max_level = r covering = coverer.GetCovering(region_rect) geoms = gpd.GeoDataFrame() geoms['cell_id'] = None geoms['area'] = None geoms['geometry'] = None for cellid in covering: new_cell = s2.S2Cell(cellid) vertices = [] for i in range(0, 4): vertex = new_cell.GetS2LatLngVertex(i) vertices.append((vertex.lng().degrees, vertex.lat().degrees)) geom = Polygon(vertices) geoms.loc[len(geoms)] = [cellid.get, transform(transformer.transform, geom).area, geom] geoms.to_file("s2_level{}.geojson".format(r), driver='GeoJSON')
def get_s2_cells(res, extent=None): """Get s2 cells for given resolution Parameters: res (int): S2 resolution extent (list): Extent as array of 2 lon lat pairs to get raster values for Returns: Pandas dataframe """ coverer = s2.S2RegionCoverer() if extent: coverer.set_fixed_level(res) region_rect = s2.S2LatLngRect( s2.S2LatLng_FromDegrees(extent[1], extent[0]), s2.S2LatLng_FromDegrees(extent[3], extent[2])) set_hex = [x.ToToken() for x in coverer.GetCovering(region_rect)] else: coverer.set_fixed_level(res) region_rect = s2.S2LatLngRect( s2.S2LatLng_FromDegrees(-90, -180), s2.S2LatLng_FromDegrees(90, 180)) set_hex = [x.ToToken() for x in coverer.GetCovering(region_rect)] df = pd.DataFrame({"cell_id": set_hex}) return df
async def level_12_covering(self): cap = await self.s2_cap() if not cap: return None coverer = s2.S2RegionCoverer() coverer.set_fixed_level(12) covering = coverer.GetCovering(cap) id_list = [hex(x.id()) for x in covering] return id_list
def cover_region(self, feature): # Cover a feature's extent with S2 cells xcoords = [x[0] for x in feature['geometry']['coordinates'][0]] ycoords = [y[1] for y in feature['geometry']['coordinates'][0]] rect = s2.S2LatLngRect( s2.S2LatLng.FromDegrees(min(ycoords), min(xcoords)), s2.S2LatLng.FromDegrees(max(ycoords), max(xcoords))) coverer = s2.S2RegionCoverer() coverer.set_max_cells(self.config.limit) coverer.set_min_level(self.config.min_res) coverer.set_max_level(self.config.max_res) ids = coverer.GetCovering(rect) return ids
def cover_rect(lat1, long1, lat2, long2): # create a rect in !!!!! region_rect = s2.S2LatLngRect(s2.S2LatLng.FromDegrees(lat1, long1), s2.S2LatLng.FromDegrees(lat2, long2)) # ask s2 to create a cover of this rect coverer = s2.S2RegionCoverer() coverer.set_min_level(10) coverer.set_max_level(30) coverer.set_max_cells(60) covering = coverer.GetCovering(region_rect) res = [c.ToToken() for c in covering] return {"total": len(res), "cellIDs": res}
def testCovererIsWrappedCorrectly(self): london = s2.S2LatLngRect(s2.S2LatLng.FromDegrees(51.3368602, 0.4931979), s2.S2LatLng.FromDegrees(51.7323965, 0.1495211)) e14lj = s2.S2LatLngRect(s2.S2LatLng.FromDegrees(51.5213527, -0.0476026), s2.S2LatLng.FromDegrees(51.5213527, -0.0476026)) coverer = s2.S2RegionCoverer() coverer.set_max_cells(6) self.assertEqual(6, coverer.max_cells()) covering = coverer.GetCovering(e14lj) self.assertLessEqual(len(covering), 6) for cellid in covering: self.assertTrue(london.Contains(s2.S2Cell(cellid))) interior = coverer.GetInteriorCovering(e14lj) for cellid in interior: self.assertTrue(london.Contains(s2.S2Cell(cellid)))
def vector_to_s2(vector_path, value_name, resolution, extent=None, layer=None): """Load vector values into s2 dggs cells Parameters: vector_path (string): path to vector file for uploading value_name (string): name of a vector attribute to be uploaded resolution (integer): s2 resolution to load vector values into extent (list): Extent as array of 2 lat lon pairs to get vector values for Returns: Pandas dataframe """ # Open vector to geodataframe gdf = gpd.read_file(vector_path, layer) # Get extent to fill with s2 squares if extent: region_rect = s2.S2LatLngRect( s2.S2LatLng_FromDegrees(extent[1], extent[0]), s2.S2LatLng_FromDegrees(extent[3], extent[2])) else: region_rect = s2.S2LatLngRect( s2.S2LatLng_FromDegrees(gdf['geometry'].total_bounds[1], gdf['geometry'].total_bounds[0]), s2.S2LatLng_FromDegrees(gdf['geometry'].total_bounds[2], gdf['geometry'].total_bounds[3])) coverer = s2.S2RegionCoverer() coverer.set_fixed_level(resolution) # Create dataframe with cell_ids from cover with given resolution print(f"Start filling raster extent with s2 indexes at resolution {resolution}") s2_gdf = gpd.GeoDataFrame({'cell_id': [x.ToToken() for x in coverer.GetCovering(region_rect)]}) # Get hex centroids for points s2_gdf['geometry'] = s2_gdf['cell_id'].apply( lambda x: Point(s2.S2CellId.FromToken(x,len(x)).ToLatLng().lng().degrees(), s2.S2CellId.FromToken(x,len(x)).ToLatLng().lat().degrees())) s2_gdf = s2_gdf.set_crs('epsg:4326') # Spatial join hex centroids with gdf vector_s2 = gpd.sjoin(s2_gdf, gdf) # Drop unnecessary fields vector_s2 = vector_s2[['cell_id', value_name]] return vector_s2
def _randomCaps(self, query_type, **indexer_options): # This function creates an index consisting either of points (if # options.index_contains_points_only() is true) or S2Caps of random size. # It then executes queries consisting of points (if query_type == POINT) # or S2Caps of random size (if query_type == CAP). # # indexer_options are set on both the indexer & coverer (if relevant) # eg. _randomCaps('cap', min_level=0) calls indexer.set_min_level(0) ITERATIONS = 400 indexer = s2.S2RegionTermIndexer() coverer = s2.S2RegionCoverer() # set indexer options for opt_key, opt_value in indexer_options.items(): setter = "set_%s" % opt_key getattr(indexer, setter)(opt_value) if hasattr(coverer, setter): getattr(coverer, setter)(opt_value) caps = [] coverings = [] index = defaultdict(set) index_terms = 0 query_terms = 0 for i in range(ITERATIONS): # Choose the region to be indexed: either a single point or a cap # of random size (up to a full sphere). terms = [] if indexer.index_contains_points_only(): cap = s2.S2Cap.FromPoint(s2.S2Testing.RandomPoint()) terms = indexer.GetIndexTerms(cap.center(), "") else: cap = s2.S2Testing.GetRandomCap( 0.3 * s2.S2Cell.AverageArea(indexer.max_level()), 4.0 * s2.S2Cell.AverageArea(indexer.min_level())) terms = indexer.GetIndexTerms(cap, "") caps.append(cap) coverings.append(s2.S2CellUnion(coverer.GetCovering(cap))) for term in terms: index[term].add(i) index_terms += len(terms) for i in range(ITERATIONS): # Choose the region to be queried: either a random point or a cap of # random size. terms = [] if query_type == 'cap': cap = s2.S2Cap.FromPoint(s2.S2Testing.RandomPoint()) terms = indexer.GetQueryTerms(cap.center(), "") else: cap = s2.S2Testing.GetRandomCap( 0.3 * s2.S2Cell.AverageArea(indexer.max_level()), 4.0 * s2.S2Cell.AverageArea(indexer.min_level())) terms = indexer.GetQueryTerms(cap, "") # Compute the expected results of the S2Cell query by brute force. covering = s2.S2CellUnion(coverer.GetCovering(cap)) expected, actual = set(), set() for j in range(len(caps)): if covering.Intersects(coverings[j]): expected.add(j) for term in terms: actual |= index[term] self.assertEqual(expected, actual) query_terms += len(terms) print("Index terms/doc: %0.2f, Query terms/doc: %0.2f" % (float(index_terms) / ITERATIONS, float(query_terms) / ITERATIONS))
def raster_to_s2(raster_path, value_name, cell_min_res, cell_max_res, extent=None, pix_size_factor=3): """Load raster values into s2 dggs cells Parameters: raster (string): path to raster file for uploading value_name (string): name of a value to be uploaded cell_min_res (integer): min h3 resolution to look for based on raster cell size cell_max_res (integer): max h3 resolution to look for based on raster cell size extent (list): Extent as array of 2 lon lat pairs to get raster values for pix_size_factor (pinteger): how times smaller h3 hex size should be comparing with raster cell size Returns: Pandas dataframe """ # Open raster rs = rasterio.open(raster_path) # Get extent to fill with s2 squares if extent: region_rect = s2.S2LatLngRect( s2.S2LatLng_FromDegrees(extent[1], extent[0]), s2.S2LatLng_FromDegrees(extent[3], extent[2])) else: region_rect = s2.S2LatLngRect( s2.S2LatLng_FromDegrees(rs.bounds.bottom, rs.bounds.left), s2.S2LatLng_FromDegrees(rs.bounds.top, rs.bounds.right)) # Get resolution dict resolutions = {} coverer = s2.S2RegionCoverer() # transformer = Transformer.from_crs("epsg:4326", 'proj=isea') for i in range(cell_min_res, cell_max_res, 1): # get s2 cell at level i coverer.set_fixed_level(i) cell = s2.S2Cell(coverer.GetCovering(region_rect)[0]) # get s2 edge size at resolution i p1 = cell.GetS2LatLngVertex(0) p2 = cell.GetS2LatLngVertex(1) # edge = Point(transformer.transform(p2.lat().degrees(), p2.lng().degrees())).distance(Point(transformer.transform(p1.lat().degrees(), p2.lng().degrees()))) edge = __haversine(p2.lat().degrees(), p2.lng().degrees(), p1.lat().degrees(), p2.lng().degrees()) resolutions[i] = edge # Get two neighbour pixels in raster x1 = rs.transform[2] y1 = rs.transform[5] x2 = rs.transform[2] + rs.transform[0] y2 = rs.transform[5] - rs.transform[4] # Get pixel size from projected src size = __haversine(x1, y1, x1, y2) print(f"Raster pixel size {size}") # Get raster band as np array raster_band_array = rs.read(1) # Get h3 resolution for raster pixel size for key, value in resolutions.items(): print(value) if value < size / pix_size_factor: resolution = key break print(resolution) coverer.set_fixed_level(resolution) # Create dataframe with cell_ids from cover with given resolution print(f"Start filling raster extent with s2 indexes at resolution {resolution}") df = pd.DataFrame({'cell_id': [x.ToToken() for x in coverer.GetCovering(region_rect)]}) # Get raster values for each hex_id print(f"Start getting raster values for s2 cells at resolution {resolution}") df[value_name] = df['cell_id'].apply(lambda x: raster_band_array[ rs.index(s2.S2CellId.FromToken(x,len(x)).ToLatLng().lng().degrees(), s2.S2CellId.FromToken(x,len(x)).ToLatLng().lat().degrees())]) # Drop nodata df = df[df[value_name] != rs.nodata] return df