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
def testContainsIsWrappedCorrectly(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)) self.assertTrue(london.Contains(e14lj))
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 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 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 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 testS2LatLngRectRegion(self): rect = s2.S2LatLngRect(s2.S2LatLng.FromDegrees(1.0, 2.0), s2.S2LatLng.FromDegrees(3.0, 4.0)) inside = s2.S2LatLng.FromDegrees(2.0, 3.0).ToPoint() outside = s2.S2LatLng.FromDegrees(0.0, 0.0).ToPoint() self.assertTrue(rect.Contains(inside)) self.assertFalse(rect.Contains(outside)) self.assertTrue(rect.Contains(s2.S2Cell(inside))) self.assertFalse(rect.Contains(s2.S2Cell(outside))) self.assertTrue(rect.MayIntersect(s2.S2Cell(inside))) self.assertFalse(rect.MayIntersect(s2.S2Cell(outside))) cap_bound = rect.GetCapBound() self.assertTrue(cap_bound.Contains(inside)) self.assertFalse(cap_bound.Contains(outside)) self.assertTrue(rect.ApproxEquals(rect.GetRectBound()))
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