def test_linearrings_invalid_shape(shape): coords = np.ones(shape) with pytest.raises(ValueError): pygeos.linearrings(coords) # make sure the first coordinate != second coordinate coords[..., 1] += 1 with pytest.raises(ValueError): pygeos.linearrings(coords)
def test_polygon_with_none_hole(): actual = pygeos.polygons( pygeos.linearrings(box_tpl(0, 0, 10, 10)), [ pygeos.linearrings(box_tpl(1, 1, 2, 2)), None, pygeos.linearrings(box_tpl(3, 3, 4, 4)), ], ) assert pygeos.area(actual) == 98.0
import numpy as np import pygeos point_polygon_testdata = ( pygeos.points(np.arange(6), np.arange(6)), pygeos.box(2, 2, 4, 4), ) point = pygeos.points(2, 3) line_string = pygeos.linestrings([(0, 0), (1, 0), (1, 1)]) linear_ring = pygeos.linearrings([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]) polygon = pygeos.polygons([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)]) multi_point = pygeos.multipoints([(0, 0), (1, 2)]) multi_line_string = pygeos.multilinestrings([[(0, 0), (1, 2)]]) multi_polygon = pygeos.multipolygons([ [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)], [(2.1, 2.1), (2.2, 2.1), (2.2, 2.2), (2.1, 2.2), (2.1, 2.1)], ]) geometry_collection = pygeos.geometrycollections( [pygeos.points(51, -1), pygeos.linestrings([(52, -1), (49, 2)])]) point_z = pygeos.points(1.0, 1.0, 1.0) polygon_with_hole = pygeos.Geometry( "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0), (2 2, 2 4, 4 4, 4 2, 2 2))") all_types = ( point, line_string, linear_ring, polygon, multi_point, multi_line_string,
def test_linearrings_unclosed(): actual = pygeos.linearrings(box_tpl(0, 0, 1, 1)[:-1]) assert str(actual) == "LINEARRING (1 0, 1 1, 0 1, 0 0, 1 0)"
def test_linearrings_from_xy(): actual = pygeos.linearrings([0, 1, 2, 0], [3, 4, 5, 3]) assert str(actual) == "LINEARRING (0 3, 1 4, 2 5, 0 3)"
def test_polygon_from_linearring(): actual = pygeos.polygons(pygeos.linearrings(box_tpl(0, 0, 1, 1))) assert str(actual) == "POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))"
def test_linearrings(coordinates): actual = pygeos.linearrings(coordinates, indices=len(coordinates) * [0]) assert_geometries_equal(actual, pygeos.linearrings(coordinates))
def test_linearrings_invalid(coordinates): # attempt to construct linestrings with 1 coordinate with pytest.raises(pygeos.GEOSException): pygeos.linearrings(coordinates, indices=np.zeros(len(coordinates)))
@pytest.mark.parametrize("holes", [[1, 2], None, [linear_ring, point], "hello"]) def test_polygons_invalid_holes(holes): with pytest.raises((TypeError, ValueError, pygeos.GEOSException)): pygeos.polygons([linear_ring, linear_ring], holes, indices=[0, 1]) @pytest.mark.parametrize("indices", [[1, 2], [point], "hello", [1], [-1]]) def test_polygons_invalid_indices(indices): with pytest.raises((TypeError, ValueError)): pygeos.polygons([linear_ring], [linear_ring], indices=indices) hole_1 = pygeos.linearrings([(0.2, 0.2), (0.2, 0.4), (0.4, 0.4)]) hole_2 = pygeos.linearrings([(0.6, 0.6), (0.6, 0.8), (0.8, 0.8)]) poly = pygeos.polygons(linear_ring) poly_hole_1 = pygeos.polygons(linear_ring, holes=[hole_1]) poly_hole_2 = pygeos.polygons(linear_ring, holes=[hole_2]) poly_hole_1_2 = pygeos.polygons(linear_ring, holes=[hole_1, hole_2]) @pytest.mark.parametrize( "holes,indices,expected", [ ([None], [1], [poly, poly]), ([hole_1], [0], [poly_hole_1, poly]), ([hole_1], [1], [poly, poly_hole_1]), ([hole_1, hole_2], [0, 0], [poly_hole_1_2, poly]), ([hole_1, hole_2], [0, 1], [poly_hole_1, poly_hole_2]),
def test_linearrings(): actual = pygeos.linearrings(box_tpl(0, 0, 1, 1)) assert actual.to_wkt() == "LINEARRING (1 0, 1 1, 0 1, 0 0, 1 0)"
def fix(dem, coastline, **kwargs): # --------------------------------------------------------------------- logger.info("adjust dem\n") # --------------------------------------------------------------------- # define coastline try: shp = gp.GeoDataFrame.from_file(coastline) except: shp = gp.GeoDataFrame(coastline) if "ival" in dem.data_vars: xp = dem.ilons.values yp = dem.ilats.values else: xp = dem.longitude.values yp = dem.latitude.values minlon = xp.min() maxlon = xp.max() minlat = yp.min() maxlat = yp.max() if xp.mean() < 0 and xp.min() < -180.0: flag = -1 elif xp.mean() > 0 and xp.max() > 180.0: flag = 1 else: flag = 0 if flag == 1: block1 = shp.cx[minlon:180, minlat:maxlat].copy() block2 = shp.cx[-180:(maxlon - 360.0), minlat:maxlat].copy() for idx, poly in block2.iterrows(): block2.loc[idx, "geometry"] = shapely.ops.transform(lambda x, y, z=None: (x + 360.0, y), poly.geometry) block = pd.concat([block1, block2]) elif flag == -1: block1 = shp.cx[minlon + 360:180, minlat:maxlat].copy() block2 = shp.cx[-180:maxlon, minlat:maxlat].copy() for idx, poly in block1.iterrows(): block1.loc[idx, "geometry"] = shapely.ops.transform(lambda x, y, z=None: (x - 360.0, y), poly.geometry) block = pd.concat([block1, block2]) else: block = shp.cx[minlon:maxlon, minlat:maxlat] try: block = gp.GeoDataFrame(geometry=list(block.unary_union.geoms)) except: pass # --------------------------------------------------------------------- logger.debug("compute water and land\n") # --------------------------------------------------------------------- # create a polygon of the lat/lon window grp = shapely.geometry.Polygon([(minlon, minlat), (minlon, maxlat), (maxlon, maxlat), (maxlon, minlat)]) grp = grp.buffer(0.5) # buffer it to get also the boundary points g = block.unary_union.symmetric_difference(grp) # get the diff try: t = gp.GeoDataFrame({"geometry": g}) except: t = gp.GeoDataFrame({"geometry": [g]}) t["length"] = t["geometry"][:].length # optional t = t.sort_values(by="length", ascending=0) # use the length to list them t = t.reset_index(drop=True) t["in"] = gp.GeoDataFrame(geometry=[grp.buffer(0.001)] * t.shape[0]).contains( t) # find the largest of boundaries try: idx = np.where( t["in"] == True)[0][0] # first(largest) boundary within lat/lon b = t.iloc[idx].geometry # get the largest except: b = shapely.geometry.GeometryCollection() # define wet/dry water = b land = grp - b if (not land) | (not water): # --------------------------------------------------------------------- logger.debug("only water/land present...\n") # --------------------------------------------------------------------- if "ival" in dem.data_vars: dem = dem.assign(fval=dem.ival) else: dem = dem.assign(adjusted=dem.elevation) return dem if "ival" in dem.data_vars: df = pd.DataFrame({ "longitude": dem.ilons.values.flatten(), "latitude": dem.ilats.values.flatten(), "elevation": dem.ival.values.flatten(), }) else: df = dem.elevation.to_dataframe().reset_index() # --------------------------------------------------------------------- logger.debug("invoke pygeos\n") # --------------------------------------------------------------------- spoints_ = pygeos.points(list(df.loc[:, ["longitude", "latitude"]].values) ) # create pygeos objects for the points # Add land boundaries to a pygeos object try: lbs = [] for l in range(len(land.boundary.geoms)): z = pygeos.linearrings(land.boundary.geoms[l].coords[:]) lbs.append(z) except: lbs = pygeos.linearrings(land.boundary.coords[:]) bp = pygeos.polygons(lbs) # --------------------------------------------------------------------- logger.debug("find wet and dry masks\n") # --------------------------------------------------------------------- # find the points on land tree = pygeos.STRtree(spoints_) try: wl = [] for l in range(len(land.boundary.geoms)): wl.append(tree.query(bp[l], predicate="contains").tolist()) ns = [j for i in wl for j in i] except: wl = tree.query(bp, predicate="contains").tolist() ns = wl lmask = np.zeros(spoints_.shape, dtype=bool) lmask[ns] = True wmask = ~lmask # invert for wet mask # --------------------------------------------------------------------- logger.debug("fix wet points\n") # --------------------------------------------------------------------- # Now see if the wet points have indeed negative values pw_mask = df.loc[wmask, "elevation"] > 0 if pw_mask.sum() > 0: pw = df.loc[wmask][ pw_mask] # problematic points: bathymetry > 0 in wet area # Resample to fix that ... xw = pw.longitude.values yw = pw.latitude.values # Define points with positive bathymetry x, y = np.meshgrid(dem.longitude, dem.latitude) #!!!!!!!! if flag == 1: xw = xw - 180.0 x = x - 180.0 elif flag == -1: xw = xw + 180.0 x = x + 180.0 # wet.fill_value = 0. mx = np.ma.masked_array(x, dem.elevation.values > 0) my = np.ma.masked_array(y, dem.elevation.values > 0) # fill the nan, if present, with values in order to compute values there if needed. dem.elevation.data[np.isnan(dem.elevation.values)] = 9999.0 # mask positive bathymetry wet_dem = np.ma.masked_array(dem.elevation, dem.elevation.values > 0) orig = pyresample.geometry.SwathDefinition( lons=mx, lats=my) # original bathymetry points targ = pyresample.geometry.SwathDefinition(lons=xw, lats=yw) # wet points bw = pyresample.kd_tree.resample_nearest(orig, wet_dem, targ, radius_of_influence=100000, fill_value=np.nan) df.loc[pw.index, "elevation"] = bw # replace in original dataset # --------------------------------------------------------------------- logger.debug("fix dry points\n") # --------------------------------------------------------------------- # .. the same for dry points pl_mask = df.loc[lmask, "elevation"] < 0 if pl_mask.sum() > 0: pl = df.loc[lmask][ pl_mask] # problematic points: bathymetry <0 in dry area ## Resample to fix that xl = pl.longitude.values yl = pl.latitude.values x, y = np.meshgrid(dem.longitude, dem.latitude) if flag == 1: xl = xl - 180.0 x = x - 180.0 elif flag == -1: xl = xl + 180.0 x = x + 180.0 # wet.fill_value = 0. dx = np.ma.masked_array(x, dem.elevation.values < 0) dy = np.ma.masked_array(y, dem.elevation.values < 0) # fill the nan, if present, with values in order to compute values there if needed. dem.elevation.data[np.isnan(dem.elevation.values)] = 9999.0 # mask positive bathymetry dry_dem = np.ma.masked_array(dem.elevation, dem.elevation.values < 0) orig = pyresample.geometry.SwathDefinition( lons=dx, lats=dy) # original bathymetry points targ = pyresample.geometry.SwathDefinition(lons=xl, lats=yl) # wet points bd = pyresample.kd_tree.resample_nearest(orig, dry_dem, targ, radius_of_influence=100000, fill_value=np.nan) df.loc[pl.index, "elevation"] = bd # replace in original dataset # --------------------------------------------------------------------- logger.debug("assemble dataset \n") # --------------------------------------------------------------------- # reassemble dataset if "ival" in dem.data_vars: if len(dem.ival.shape) == 1: new_dem = df.elevation.to_xarray() new_dem = xr.merge([new_dem]) new_dem = new_dem.rename({"elevation": "fval"}) new_dem.fval.attrs = {"coastline": "based on coastline"} new_dem = new_dem.rename({"index": "k"}).drop_vars("k") else: new_dem = (df.set_index(["latitude", "longitude"]).to_xarray().rename({ "longitude": "l", "latitude": "k", "elevation": "fval" }).drop_vars(["k", "l"])) else: df_new = df.set_index(["latitude", "longitude"]) new_dem = df_new.to_xarray() new_dem = new_dem.rename({"elevation": "adjusted"}) new_dem.attrs = {"coastline": "based on coastline"} cdem = xr.merge([dem, new_dem]) return cdem
def _create_mosaic( cls, features: Sequence[Dict], minzoom: int, maxzoom: int, quadkey_zoom: Optional[int] = None, accessor: Callable[[Dict], str] = default_accessor, asset_filter: Callable = default_filter, version: str = "0.0.2", quiet: bool = True, **kwargs, ): """Create mosaic definition content. Attributes: features (list): List of GeoJSON features. minzoom (int): Force mosaic min-zoom. maxzoom (int): Force mosaic max-zoom. quadkey_zoom (int): Force mosaic quadkey zoom (optional). accessor (callable): Function called on each feature to get its identifier (default is feature["properties"]["path"]). asset_filter (callable): Function to filter features. version (str): mosaicJSON definition version (default: 0.0.2). quiet (bool): Mask processing steps (default is True). kwargs (any): Options forwarded to `asset_filter` Returns: mosaic_definition (MosaicJSON): Mosaic definition. Examples: >>> MosaicJSON._create_mosaic([], 12, 14) """ quadkey_zoom = quadkey_zoom or minzoom if not quiet: click.echo(f"Get quadkey list for zoom: {quadkey_zoom}", err=True) dataset_geoms = polygons([ linearrings(feat["geometry"]["coordinates"][0]) for feat in features ]) bounds = tuple(total_bounds(dataset_geoms)) tiles = burntiles.burn(features, quadkey_zoom) tiles = [mercantile.Tile(*tile) for tile in tiles] mosaic_definition: Dict[str, Any] = dict( mosaicjson=version, minzoom=minzoom, maxzoom=maxzoom, quadkey_zoom=quadkey_zoom, bounds=bounds, center=((bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, minzoom), tiles={}, version="1.0.0", ) if not quiet: click.echo("Feed Quadkey index", err=True) # Create tree and find assets that overlap each tile tree = STRtree(dataset_geoms) with ExitStack() as ctx: fout = ctx.enter_context(open(os.devnull, "w")) if quiet else sys.stderr with click.progressbar( # type: ignore tiles, file=fout, show_percent=True, label="Iterate over quadkeys") as bar: for tile in bar: quadkey = mercantile.quadkey(tile) tile_geom = polygons( mercantile.feature(tile)["geometry"]["coordinates"][0]) # Find intersections from rtree intersections_idx = sorted( tree.query(tile_geom, predicate="intersects")) if len(intersections_idx) == 0: continue intersect_dataset, intersect_geoms = zip( *[(features[idx], dataset_geoms[idx]) for idx in intersections_idx]) dataset = asset_filter(tile, intersect_dataset, intersect_geoms, **kwargs) if dataset: mosaic_definition["tiles"][quadkey] = [ accessor(f) for f in dataset ] return cls(**mosaic_definition)
def test_linearrings_all_nan(): coords = np.full((4, 2), np.nan) with pytest.raises(pygeos.GEOSException): pygeos.linearrings(coords)
def test_linearrings_invalid_shape_scalar(): with pytest.raises(ValueError): pygeos.linearrings((1, 1))
import numpy as np import pygeos point_polygon_testdata = ( pygeos.points(np.arange(6), np.arange(6)), pygeos.box(2, 2, 4, 4), ) point = pygeos.points(2, 2) line_string = pygeos.linestrings([[0, 0], [1, 0], [1, 1]]) linear_ring = pygeos.linearrings(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))) polygon = pygeos.polygons( ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0), (0.0, 0.0))) multi_point = pygeos.multipoints([[0.0, 0.0], [1.0, 2.0]]) multi_line_string = pygeos.multilinestrings([[[0.0, 0.0], [1.0, 2.0]]]) multi_polygon = pygeos.multipolygons([ ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)), ((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1)), ]) geometry_collection = pygeos.geometrycollections( [pygeos.points(51, -1), pygeos.linestrings([(52, -1), (49, 2)])]) point_z = pygeos.points(1.0, 1.0, 1.0) all_types = ( point, line_string, linear_ring, polygon, multi_point, multi_line_string, multi_polygon,
def test_linearrings(coordinates): actual = pygeos.linearrings( np.array(coordinates, dtype=np.float64), indices=np.zeros(len(coordinates), dtype=np.intp), ) assert_geometries_equal(actual, pygeos.linearrings(coordinates))