U fb{@sddlZddlmZmZmZddlmZmZddlZddl Z ddl Z ddl mZddlZddlmZmZddlmZddlmZdd lmZdd lmZmZmZdd lmZmZmZmZm Z dd l!m"Z"dd l#m$Z$ddl%m&Z&m'Z'ddl(Z(ddl)m*Z*ddl+Z+dFddZ,dGddZ-dHddZ.dIddZ/ddZ0ddZ1dJddZ2d d!Z3d"d#Z4d$d%Z5d&d'Z6d(d)Z7d*d+Z8dKd-d.Z9d/d0Z:d1d2Z;d3d4ZdNdd?Z@d@dAZAdBdCZBdOdDdEZCdS)PN)_check_df_load_check_gdf_load_check_rasterio_im_load) _check_geom _check_crs)Affine)calculate_default_transform Resampling)transform_bounds)affine_transform)loads)PointPolygon LineString)MultiLineString MultiPolygonmappingboxshape)GeometryCollection)cascaded_union)gdalosr)warncubicc Cst|\}}|dkr"tt|}nt|}|dk r@t|\}} nd}|dkr\|dk r\t|}|dk rt|}t||||||} nt|||||} | S)a Reproject a dataset (df, gdf, or image) to a new coordinate system. This function takes a georegistered image or a dataset of vector geometries and converts them to a new coordinate reference system. If no target CRS is provided, the data will be converted to the appropriate UTM zone by default. To convert a pixel-coordinate dataset to geographic coordinates or vice versa, use :func:`solaris.vector.polygon.georegister_px_df` or :func:`solaris.vector.polygon.geojson_to_px_gdf` instead. Arguments --------- input_object : `str` or :class:`rasterio.DatasetReader` or :class:`gdal.Dataset` or :class:`geopandas.GeoDataFrame` An object to transform to a new CRS. If a string, it must be a path to a georegistered image or vector dataset (e.g. a .GeoJSON). If the object itself does not contain georeferencing information, the coordinate reference system can be provided with `input_crs`. input_crs : int, optional The EPSG code integer for the input data's CRS. If provided and a CRS is also associated with `input_object`, this argument's value has precedence. target_crs : int, optional The EPSG code for the output projection. If values are not provided for this argument or `target_object`, the input data will be re-projected into the appropriate UTM zone. If both `target_crs` and `target_object` are provided, `target_crs` takes precedence (and a warning is raised). target_object : str, optional An object in the desired destination CRS. If neither this argument nor `target_crs` is provided, the input will be projected into the appropriate UTM zone. `target_crs` takes precedence if both it and `target_object` are provided. dest_path : str, optional The path to save the output to (if desired). This argument is only required if the input is a :class:`gdal.Dataset`; otherwise, it is optional. resampling_method : str, optional The resampling method to use during reprojection of raster data. **Only has an effect if the input is a :class:`rasterio.DatasetReader` !** Possible values are ``['cubic' (default), 'bilinear', 'nearest', 'average']``. Returns ------- output : :class:`rasterio.DatasetReader` or :class:`gdal.Dataset` or :class:`geopandas.GeoDataFrame` An output in the same format as `input_object`, but reprojected into the destination CRS. N)_parse_geo_datarget_crs _reprojectreproject_to_utm) Z input_object input_crs target_crsZ target_object dest_pathresampling_method input_data input_typeZ target_data_outputr(g/home/ec2-user/SageMaker/vegetation-management-remars2022/remars2022-workshop/libs/solaris/utils/geo.py reprojects(2  r*bicubicc Cst|}t|}|dkr<||}|dk r8|j|ddn|dkrt|tjrt|d|d|j|j f|j \}}} |j } | |d||| d|dk r.tj|df| V} td|jdD]>} tjjt|| t| | |j|j||dtt|d qW5QRXt|}|nntj| ||jfd }td|jdD]H} tjjt|| |dddd| df|j|j||tt|d qRnHt|tjr|dk rtj||d t|d t |}nt!d |S)NvectorGeoJSON)driverraster WKT1_GDAL)crs transformwidthheightwr)source destination src_transformsrc_crs dst_transformdst_crs resampling)rzEPSG:)dstSRSz?An output path must be provided for reprojecting GDAL datasets.)"rto_crsto_file isinstancerasterio DatasetReaderr to_wktr3r4boundsmetacopyupdateopenrangecountwarpr*bandr2r1getattrr closenpzerosrDatasetWarpstrto_epsgOpen ValueError) r$r%r r!r"r#r'r2r3r4kwargsdstZband_idxr(r(r)rbsn              rc Cs|dkrt|}|dkr tdt|}t|tdd}|d|dd|d|d dg}t|}t||||||d }tjd rt d |S) zMConvert an input to a UTM CRS (after determining the correct UTM zone). Nz9An input CRS must be provided by input_data or input_crs.r1rg@r)r%r r!r"r#tmp) rrVr get_boundslatlon_to_utm_epsgrospathisfileremove) r$r%r r"r#rDmidpointZutm_epsgr'r(r(r)rs (  rc Cst|\}}|dkr"t|jj}nt|dkrt|tjrBt|j}nTt|tj r| }|d}||d|j }|d}||d|j } || ||g}|dk rt |}t|} t| d|df|}|S) a$Get the ``[left, bottom, right, top]`` bounds in any CRS. Arguments --------- geo_obj : a georeferenced raster or vector dataset. crs : int, optional The EPSG code (or other CRS format supported by rasterio.warp) for the CRS the bounds should be returned in. If not provided, the bounds will be returned in the same crs as `geo_obj`. Returns ------- bounds : list ``[left, bottom, right, top]`` bounds in the input crs (if `crs` is ``None``) or in `crs` if it was provided. r,r/rrr[Nr0)rlistgeometry total_boundsr@rArBrDrrQGetGeoTransform RasterXSize RasterYSizerrr rC) Zgeo_objr1r$r%rDZinput_gtmin_xmax_xmax_ymin_yr9r(r(r)r^s,      r^cCsnt|tjrt|jSt|tjr,t|jSt|tjrXtt t j | d ddStdt|dS)z@Get a coordinate reference system from any georegistered object.)wktZ AUTHORITYrzCsolaris doesn't know how to extract a crs from an object of type {}N)r@gpd GeoDataFramerr1rArBrrQintrSpatialReference GetProjection GetAttrValue TypeErrorformattype)objr(r(r)rs     rcCst|tr^|ds&|dr4d}t|}q|dsP|drd}t|}nF|}t|tjrtd}n0t|tj st|t j rd}nt d t|||fS)Njsoncsvr,tiftiffr/z3The input format {} is not compatible with solaris.)r@rSlowerendswithrrpd DataFramerArBrrQrVrxry)inputr%r$r(r(r)rs0    rcCst|}|dk rzt|}|dkrDt||tdd}t|jj|jj}t|}tj|g| d}| | j dd}n.|dkrt dnt |trt|}t||}|S)aReproject a geometry or coordinate into a new CRS. Arguments --------- input_geom : `str`, `list`, or `Shapely `_ geometry A geometry object to re-project. This can be a 2-member ``list``, in which case `input_geom` is assumed to coorespond to ``[x, y]`` coordinates in `input_crs`. It can also be a Shapely geometry object or a wkt string. input_crs : int, optional The coordinate reference system for `input_geom`'s coordinates, as an EPSG :class:`int`. Required unless `affine_transform` is provided. target_crs : int, optional The target coordinate reference system to re-project the geometry into. If not provided, the appropriate UTM zone will be selected by default, unless `affine_transform` is provided (and therefore CRSs are ignored.) affine_transform : :class:`affine.Affine`, optional An :class:`affine.Affine` object (or a ``[a, b, c, d, e, f]`` list to convert to that format) to use for transformation. Has no effect unless `input_crs` **and** `target_crs` are not provided. Returns ------- output_geom : Shapely geometry A shapely geometry object: - in `target_crs`, if one was provided; - in the appropriate UTM zone, if `input_crs` was provided and `target_crs` was not; - with `affine_transform` applied to it if neither `input_crs` nor `target_crs` were provided. NrY)r!)rgr1rrgz]If an input CRS is not provided, affine_transform is required to complete the transformation.)rrreproject_geometryr_centroidyxrqrrrCr>ilocrVr@raffine_to_listr )Z input_geomr r! affine_objgeomgdfZ output_geomr(r(r)r s"!   rcCst|j}t|S)aGet the projection unit for a vector_file or gdf. Arguments --------- vector_file : :py:class:`geopandas.GeoDataFrame` or geojson/shapefile A vector file or gdf with georeferencing Notes ----- If vector file is already in UTM coords, the projection WKT is complex: https://www.spatialreference.org/ref/epsg/wgs-84-utm-zone-11n/html/ In this case, return the second instance of 'UNIT'. Returns ------- unit : String The unit i.e. meter, metre, or degree, of the projection )rr1get_projection_unit)Z vector_filecr(r(r)gdf_get_projection_unitGs rcCst|j}t|S)aGet the projection unit for an image. Arguments --------- image : raster image, GeoTIFF or other format A raster file with georeferencing Notes ----- If raster is already in UTM coords, the projection WKT is complex: https://www.spatialreference.org/ref/epsg/wgs-84-utm-zone-11n/html/ In this case, return the second instance of 'UNIT'. Returns ------- unit : String The unit i.e. meters or degrees, of the projection )rr1r)imagerr(r(r)raster_get_projection_unit^s rcCst|}|jdj}|S)aGet the units of a specific SRS. Arguments --------- crs : :class:`pyproj.crs.CRS`, :class:`rasterio.crs.CRS`, `str`, or `int` The coordinate reference system to retrieve a unit for. Returns ------- unit : str The string-formatted unit. r)r axis_info unit_name)r1unitr(r(r)rus  rcCs:t|dkr|dd}tj|r.tj|St|SdS)a7Create an Affine from a list or array-formatted [a, b, d, e, xoff, yoff] Arguments --------- xform_mat : `list` or :class:`numpy.array` A `list` of values to convert to an affine object. Returns ------- aff : :class:`affine.Affine` An affine transformation object. rN)lenrAr2tastes_like_gdalr from_gdal)Z xform_matr(r(r)list_to_affines     rcCs|j|j|j|j|j|jgS)z@Convert a :class:`affine.Affine` instance to a list for Shapely.)abdexoffyoff)rr(r(r)rs rcst|tjr|nt|jddjdd}|fdd}|}||dd}t|dkrvtSd|_ d |j _ |}|jd dd d |d<g|jfd dd d }t S)aGet the intersection geometries between all geometries in a set. Arguments --------- polygons : `list`-like A `list`-like containing geometries. These will be placed in a :class:`geopandas.GeoSeries` object to take advantage of `rtree` spatial indexing. Returns ------- intersect_list A `list` of geometric intersections between polygons in `polygons`, in the same CRS as the input. T)dropcSs|jSN)rDrr(r(r)z2geometries_internal_intersection..cst|Sr)rf intersectionr)sindexr(r)rrcSs t|dkS)Nr)rrr(r(r)rrr intersectorsgs_idxcsfdddDS)Ncsg|]}|dkr|qS)rr().0irr(r) s zFgeometries_internal_intersection....rr(rr(rr)rrr)axiscs$|dt|dS)Nrr)appendrrr)gs output_polysr(r)rs) r@rq GeoSeries reset_indexrapplydropnarrnameindexr)polygonsZ gs_bboxesZintersect_listsr&r()rrrr) geometries_internal_intersections0    rrgcCs|r|stdt|}|jdd|jf}t|dkrB|St||jdtrh|| t ||<t |j t d|d}|j|jd}tjt j ||gdd|jd }|r||d||<|S) aSplit apart MultiPolygon or MultiLineString geometries. Arguments --------- gdf : :class:`geopandas.GeoDataFrame` or `str` A :class:`geopandas.GeoDataFrame` or path to a geojson containing geometries. obj_id_col : str, optional If one exists, the name of the column that uniquely identifies each geometry (e.g. the ``"BuildingId"`` column in many SpaceNet datasets). This will be tracked so multiple objects don't get produced with the same ID. Note that object ID column will be renumbered on output. If passed, `group_col` must also be provided. group_col : str, optional A column to identify groups for sequential numbering (for example, ``'ImageId'`` for sequential number of ``'BuildingId'``). Must be provided if `obj_id_col` is passed. geom_col : str, optional The name of the column in `gdf` that corresponds to geometry. Defaults to ``'geometry'``. Returns ------- :class:`geopandas.GeoDataFrame` A `geopandas.GeoDataFrame` that's identical to the input, except with the multipolygons split into separate rows, and the object ID column renumbered (if one exists). z1group_col must be provided if obj_id_col is used.Nrr)rgeom_col)rT) ignore_indexrZ)rVrloccolumns duplicatedrr@rrSrr rconcat_split_multigeom_rowtolistrruniquerqrrr1groupbycumcount)rZ obj_id_colZ group_colrZgdf2Zsplit_geoms_gdfr(r(r)split_multi_geometriess( rcs~t}|fddD|jrN|fddjDn|fddjD|jj|S)az Create a subgraph from G. Code almost directly copied from osmnx. Arguments --------- G : :class:`networkx.MultiDiGraph` A graph to be subsetted node_subset : `list`-like The subset of nodes to induce a subgraph of `G` Returns ------- G2 : :class:`networkx`.MultiDiGraph The subgraph of G that includes node_subset c3s|]}|j|fVqdSr)nodes)rn)Gr(r) +szget_subgraph..c3sR|]J\}}|kr|D]0\}}|kr|D]\}}||||fVq2qqdSritems)rrnbrsnbrZkeydictkeyr node_subsetr(r)r/s  c3s>|]6\}}|kr|D]\}}|kr|||fVqqdSrr)rrrrrrr(r)r5s  ) setZ fresh_copyZadd_nodes_fromZ is_multigraphZadd_edges_fromadjrgraphrG)rrG2r()rrr) get_subgraphs rcCsZg}t||ts t||trPt||}|D]}|}|||<||q0t|Sr)r@rr_split_multigeomrFrrr)Zgdf_rowrnew_rowsZ new_polyspolyZ row_w_polyr(r(r)r?s   rcCst|Sr)rf)Z multigeomr(r(r)rKsrr\cCs*t|}tt|d||d<t|S)N coordinates)rrOroundarrayr)r precisiongeojsonr(r(r)_reduce_geom_precisionOs rFcCsrt||\}}|r@|dkr d}n |dkr,d}d||}|d7}|dkrRd|}n|dkrbd|}|rn||fS|S) afGet the WGS84 UTM EPSG code based on a latitude and longitude value. Arguments --------- latitude : numeric The latitude value for the coordinate. longitude : numeric The longitude value for the coordinate. return_proj4 : bool, optional Should the proj4 string be returned as well as the EPSG code? Defaults to no (``False``)` Returns ------- epsg : int The integer corresponding to the EPSG code for the relevant UTM zone in WGS 84. proj4 : str The proj4 string for the CRS. Only returned if ``return_proj4=True``. Nz+northSz+southz+proj=utm +zone={} {}z, +ellps=WGS84 +datum=WGS84 +units=m +no_defsiXi)_latlon_to_utm_zonerx)latitude longitudeZ return_proj4Z zone_number zone_letterZdirection_indicatorprojepsgr(r(r)r_Vs r_TcCst|tjr|jd}t|tjr,|jd}d}d|krDdkrfnnd|kr\dkrfnnd}nVd|krzd krnn>|dkr|d krd }n(|d krd }n|d krd}n |dkrd}|dkrd}nd}d|krd ksntd||dkrt|ddd}||fS)a1Convert latitude and longitude to a UTM zone ID. This function modified from `the python utm library `_. Arguments --------- latitude : numeric or :class:`numpy.ndarray` The latitude value of a coordinate. longitude : numeric or :class:`numpy.ndarray` The longitude value of a coordinate. ns_only : bool, optional Should the full list of possible zone numbers be used or just the N/S options? Defaults to N/S only (``True``). Returns ------- zone_number : int The numeric portion of the UTM zone ID. zone_letter : str The string portion of the UTM zone ID. Note that by default this function uses only the N/S designation rather than the full range of possible letters. rN8@r[ HT !#*%rriz8Warning: UTM projections not recommended for latitude {}rr)r@rOndarrayflatrrxrs)rrns_onlyZutm_valrr(r(r)r~s4    0  rcCs4t|tst|tr|jjSt|tr0|jjjSdS)z4Get coordinates from various shapely geometry types.N)r@rrcoordsxyrexterior)rr(r(r) _get_coordss rcCs,|d|d|d|d|d|dgS)aoConvert bbox from ``[minx, miny, maxx, maxy]`` to coco format. COCO formats bounding boxes as ``[minx, miny, width, height]``. Arguments --------- bbox : :class:`list`-like of numerics A 4-element list of the form ``[minx, miny, maxx, maxy]``. Returns ------- coco_bbox : list ``[minx, miny, width, height]`` shape. rrr\r[r()bboxr(r(r)bbox_corners_to_cocosrcCstt|tr|jjj}n6t|tr0t|jjj}nt|trDtdntdt t |d|d}dd|D}|S)z*Convert a geometry to COCO polygon format.ziYou have MultiPolygon types in your label df. Remove, explode, or fix these to be Polygon geometry types.z*polygon must be a shapely geometry or WKT.rrcSsg|]}|D]}|q qSr(r()rZ coordinateitemr(r(r)rsz#polygon_to_coco..) r@rrrrrSr rrVrfzip)polygonrr(r(r)polygon_to_cocos     rcsttrTtt}|d}t|dksBtdt dt |ddn.tt sjtt j rtdksztt|dkr|dkrtd dSt|ttfr||f}|d|d|d|dgn||dk rt|}t|jj}nj}|d}|d } |d} |d } | |} | | } t | d}t | d}t ||d|d}t | | d|dfd d |D}|S)a'Splits a vector into approximately equal sized tiles. Adapted from @lossyrob's Gist__ .. Gist: https://gist.github.com/lossyrob/7b620e6d2193cb55fbd0bffacf27f7f2 The more complex the geometry, the slower this will run, but geometrys with around 10000 coordinates run in a few seconds time. You can simplify geometries with shapely.geometry.Polygon.simplify if necessary. Arguments --------- geometry : str, optional A shapely.geometry.Polygon, path to a single feature geojson, or list-like bounding box shaped like [left, bottom, right, top]. The geometry must be in the projection coordinates corresponding to the resolution units. tile_size : `tuple` of `int`s The size of the input tiles in ``(y, x)`` coordinates. By default, this is in pixel units; this can be changed to metric units using the `use_metric_size` argument. use_projection_units : bool, optional Is `tile_size` in pixel units (default) or distance units? To set to distance units use ``use_projection_units=True``. If False, resolution must be supplied. resolution: `tuple` of `float`s, optional (x resolution, y resolution). Used by default if use_metric_size is False. Can be acquired from rasterio dataset object's metadata. src_img: `str` or `raster`, optional A rasterio raster object or path to a geotiff. The bounds of this raster and the geometry will be intersected and the result of the intersection will be tiled. Useful in cases where the extent of collected labels and source imagery partially overlap. The src_img must have the same projection units as the geometry. Returns ------- tile_bounds : list (containing sublists like [left, bottom, right, top]) featuresrz0Feature collection must only contain one featurerrgFNz^Resolution must be specified if use_projection_units is False. Access it from src raster meta.r\r[c sZg|]R}D]H}t|||d|dfjs |||d|dfq qS)rr)rris_empty)rrjrgZ tmp_tile_sizeZy_minsr(r)rAszsplit_geom..)r@rSr{r rHreadrprintsysexitrrfrOrAssertionErrorrfloatrsrrrDceilarange)rg tile_size resolutionZuse_projection_unitsZsrc_imggjrrDxminxmaxyminymaxZx_extentZy_extentx_stepsy_stepsZx_minsZ tile_boundsr(rr) split_geomsV(    r)NNNNr)r+)NNr+)N)NNN)NNrg)r\)F)T)NFN)Dr`corerrrrrnumpyrOpandasr geopandasrqaffinerrAZ rasterio.warpr r r shapely.affinityr shapely.wktr shapely.geometryrrrrrrrrZshapely.geometry.collectionr shapely.opsrosgeorrr{warningsrrr*rrr^rrrrrrrrrrrrrrr_rrrrrr(r(r(r)st        J >  * <; 5)   ( >