Beispiel #1
0
    def write_splitted_images(self, target_dir, filename, filter_fun=lambda x:True, idxs_to_be_kept=None):
        """Write all splitted images as tif file.

        Parameters
        ----------
        target_dir: str
            The directory to save the splitted images.
        filename: str
            The prefix od the filename. The index number will be followed by 
            the output filename you defined, e.g. <filename>_idx_idxh_idxw;.
        filter_fun: function, optional, default: lambda x:True
            Filter specific spllitted images and not save it. The input of the function is 
            a splitted image. If the function output is True, the splitted image will be 
            saved.
        idxs_to_be_kept: list, optional
            list of indexs of splitted image to save as file.
        """
        df_attribute = self.get_geo_attribute(return_geo_transform=True)
        splitted_images = self.get_splitted_images()
        idxs_to_be_kept = range(len(df_attribute)) if idxs_to_be_kept is None else idxs_to_be_kept
        for idx, row in df_attribute.iterrows():
            if idx in idxs_to_be_kept:
                target_img = splitted_images[idx].copy()
                idx_str = "_" + ("%3i"%idx).replace(" ", "0")
                idx_h_str = "_" + ("%3i"%row['idx_h']).replace(" ", "0")
                idx_w_str = "_" + ("%3i"%row['idx_w']).replace(" ", "0")
                path = os.path.join(target_dir, filename + idx_str + idx_h_str + idx_w_str + ".tif")
                gt = row['geo_transform']
                if filter_fun(target_img):
                    tgp.write_raster(path, target_img, gt, self.proj, self.gdaldtype, self.no_data_value)
Beispiel #2
0
def raster_pixel_to_polygon(src_tif_path,
                            dst_shp_path,
                            all_bands_as_feature=False,
                            crs=None,
                            return_gdf=False):
    """
    crs should be dict type 'epsg:<epsg_code>', e.g. 'epsg:4326'
    """
    rows, cols, bands, geo_transform, projection, dtype_gdal, no_data_value, metadata = tgp.get_raster_info(
        src_tif_path)
    X = tgp.get_raster_data(src_tif_path)
    idxs = np.where(np.ones_like(X[:, :, 0], dtype=bool))
    rows = []

    for row_idx, col_idx in zip(*idxs):
        row = {}
        npidx = (row_idx, col_idx)
        row['geometry'] = Polygon(
            tgp.npidxs_to_coord_polygons([npidx], geo_transform)[0])
        if all_bands_as_feature:
            for i in range(X.shape[2]):
                row['band' + str(i + 1)] = X[row_idx, col_idx, i]
        rows.append(row)
    df_shp = gpd.GeoDataFrame(rows, geometry='geometry')
    if crs:
        df_shp.crs = crs
    if return_gdf:
        return df_shp
    else:
        df_shp.to_file(dst_shp_path)
Beispiel #3
0
    def get_geo_attribute(self, return_geo_transform=False, crs=None):
        """Get geo_attributes (idx, idx_h, idx_w, geo_transform, geometry) 
        of all splitted images.

        Parameters
        ----------
        return_geo_transform: bool, optional, default: False
            Return gdal geo_transform for each geometry in the output GeoDataFrame.
        crs: str, optional
            The crs for the output GeoDataFrame e.g. 'epsg:4326'.

        Returns
        -------
        df_attribute: gpd.GeoDataFrame
            The geo_attributes of all splitted images, which can be output as a shapefile.
        """
        rows = []
        for i in range(self.n_splitted_images):
            idx_h , idx_w = self.convert_order_to_location_index(i)

            h_start_inner, h_stop_inner = self.__convert_to_inner_index_h(idx_h, idx_h)
            w_start_inner, w_stop_inner = self.__convert_to_inner_index_w(idx_w, idx_w)

            left_top_coord = tgp.npidxs_to_coords([(h_start_inner, w_start_inner)], self.src_gt)[0]
            left_buttom_coord = tgp.npidxs_to_coords([(h_start_inner, w_stop_inner)], self.src_gt)[0]
            right_buttom_coord = tgp.npidxs_to_coords([(h_stop_inner, w_stop_inner)], self.src_gt)[0]
            right_top_coord = tgp.npidxs_to_coords([(h_stop_inner, w_start_inner)], self.src_gt)[0]

            x_min, y_max = left_top_coord
            row = {
                "idx":i,
                "idx_h":idx_h,
                "idx_w":idx_w,
                "geo_transform":(x_min, self.src_gt[1], self.src_gt[2], y_max, self.src_gt[4], self.src_gt[5]),
                "geometry": Polygon([left_top_coord, 
                                    left_buttom_coord, 
                                    right_buttom_coord, 
                                    right_top_coord, 
                                    left_top_coord]),
            }
            rows.append(row)
        df_attribute = gpd.GeoDataFrame(rows, geometry='geometry')

        if crs is not None:
            df_attribute.crs = crs
        elif self.proj is not None: 
            try:
                df_attribute.crs = 'init:' + str(tgp.wkt_to_epsg(self.proj))
            except:
                df_attribute.crs = self.proj

        if not return_geo_transform:
            df_attribute.drop('geo_transform', axis=1, inplace=True)

        return df_attribute
Beispiel #4
0
    def to_file(self, fp):
        """Save the file. It is recommended to save the file using tif format, that is 
        use '.tif' as its extension.

        Parameters
        ----------
        fp: str
            File path.
        """
        tgp.write_raster(fp, self.data, self.geo_transform, self.projection,
                         self.gdaldtype, self.no_data_value, self.metadata)
Beispiel #5
0
    def reproject(self, dst_crs='EPSG:4326', src_crs=None):
        """Reproject the raster data.

        Parameters
        ----------
        dst_crs: str, optional, default: EPSG:4326
            The target crs to transform the raster to.
        src_crs: str, optional
            The source crs to transform the raster from. If None, 
            get the projection from src_raster.

        Returns
        -------
        dst_raster: Raster
            Reprojected result.

        Examples
        -------- 
        >>> import TronGisPy as tgp
        >>> src_raster_fp = tgp.get_testing_fp()
        >>> src_raster = tgp.read_raster(src_raster_fp)
        >>> print("project(before)", src_raster.projection)
        >>> dst_raster = src_raster.reproject(dst_crs='EPSG:4326', src_crs=None)
        >>> print("project(after)", tgp.wkt_to_epsg(dst_raster.projection))
        """
        src_ds = self.to_gdal_ds()
        if src_crs:
            dst_ds = gdal.Warp('',
                               src_ds,
                               srcSRS=src_crs,
                               dstSRS=dst_crs,
                               format='MEM')
        else:
            dst_ds = gdal.Warp('', src_ds, dstSRS=dst_crs, format='MEM')
            if dst_ds is None:
                try:
                    src_crs = "EPSG:" + str(tgp.wkt_to_epsg(self.projection))
                    dst_ds = gdal.Warp('',
                                       src_ds,
                                       srcSRS=src_crs,
                                       dstSRS=dst_crs,
                                       format='MEM')
                    assert dst_ds is not None, "Please provide src_crs since the raster does not contain valid crs information."
                    print("Please provide src_crs to accelerate the process.")
                except:
                    assert False, "Please provide src_crs since the raster does not contain crs information."

        dst_raster = tgp.read_gdal_ds(dst_ds)
        return dst_raster
Beispiel #6
0
    def __getitem__(self, slice_value):
        if type(slice_value) in [int, slice]:
            h_start, h_stop = self.__process_slice_value(slice_value)
            w_start, w_stop = 0, self.n_steps_w
        elif type(slice_value) == tuple:
            h_start, h_stop = self.__process_slice_value(slice_value[0])
            w_start, w_stop = self.__process_slice_value(slice_value[1])

        h_start_inner, h_stop_inner = self.__convert_to_inner_index_h(h_start, h_stop)
        w_start_inner, w_stop_inner = self.__convert_to_inner_index_w(w_start, w_stop)

        data = self.src_image[h_start_inner:h_stop_inner, w_start_inner:w_stop_inner]
        gt = np.array(self.src_gt).copy()
        gt[[0, 3]] = tgp.npidxs_to_coords([(h_start_inner, w_start_inner)], self.src_gt)[0]
        raster = tgp.Raster(data, gt, self.proj, self.gdaldtype, self.no_data_value)
        return raster
Beispiel #7
0
def dem_to_aspect(src_raster, band=0, alg='Horn', trigonometric=False):
    """Calculate the aspect for the DEM.

    Parameters
    ----------
    src_raster : Raster
        The dem used to calculate the aspect.
    band : int, optional, default: 0
        source band number to use.
    alg : {'ZevenbergenThorne' or 'Horn'}, optional, default: Horn
        The literature suggests Zevenbergen & Thorne to be more suited to smooth landscapes, 
        where Horn’s formula to perform better on rougher terrain.
    trigonometric: bool, optional, default: False
        whether to return trigonometric angle instead of azimuth. 
        Thus 0deg means East, 90deg North, 180deg West, 270deg South.

    Returns
    -------
    dst_raster: Raster
        Aspect calculated from the DEM.
    """
    options = dict(band=band + 1,
                   alg=alg,
                   trigonometric=trigonometric,
                   format='MEM')
    ds_src = src_raster.to_gdal_ds()
    ds = gdal.DEMProcessing('', ds_src, 'aspect', **options)
    dst_raster = tgp.read_gdal_ds(ds)
    return dst_raster
Beispiel #8
0
    def apply(self, apply_fun, return_raster=False, gdaldtype=None, no_data_value=None): # apply functino to all images:
        """Apply a function to all splitted images.

        Parameters
        ----------
        apply_fun: function
            The function used to apply to all splitted images.

        Returns
        -------
        return_objs: nparray
            splitted images that have applied the function.
        """
        return_objs = []
        padded_image = self.padded_image
        geo_transforms = self.get_geo_attribute(True)['geo_transform']
        for i in range(self.n_splitted_images):
            idx_h , idx_w = self.convert_order_to_location_index(i)
            h_start_inner, h_stop_inner = self.__convert_to_inner_index_h(idx_h, idx_h)
            w_start_inner, w_stop_inner = self.__convert_to_inner_index_w(idx_w, idx_w)
            splitted_img = padded_image[h_start_inner:h_stop_inner,w_start_inner:w_stop_inner].copy()
            data = apply_fun(splitted_img)
            if return_raster:
                gt = geo_transforms[i]
                pj = self.proj
                gdaldtype = self.gdaldtype if gdaldtype is None else gdaldtype
                no_data_value = self.no_data_value if no_data_value is None else no_data_value
                raster = tgp.Raster(data, gt, pj, gdaldtype, no_data_value)
                return_objs.append(raster)
            else:
                return_objs.append(data)
        return return_objs
Beispiel #9
0
    def remap_by_ref_raster(self, ref_raster):
        """remap the raster on to another canvas drew by referenced raster.

        Parameters
        ----------
        ref_raster: Raster
            The referenced raster. The extent, projection and resolution will be used.

        Returns
        -------
        dst_raster: Raster
            Remapped result.

        """
        src_ds = self.to_gdal_ds()
        src_projection, src_gdaldtype = self.projection, self.gdaldtype
        ref_geo_transform, ref_projection = ref_raster.geo_transform, ref_raster.projection
        output_bounds = ref_raster.extent_for_gdal  # minX, minY, maxX, maxY

        x_res, y_res = ref_geo_transform[1], ref_geo_transform[5]
        output_type = src_gdaldtype
        dst_ds = gdal.Warp('',
                           src_ds,
                           xRes=x_res,
                           yRes=y_res,
                           outputBounds=output_bounds,
                           format='MEM',
                           srcSRS=src_projection,
                           dstSRS=ref_projection,
                           outputType=output_type,
                           resampleAlg='bilinear')
        dst_raster = tgp.read_gdal_ds(dst_ds)
        return dst_raster
Beispiel #10
0
def dem_to_slope(src_raster, band=0, alg='Horn', slope_format='degree'):
    """Calculate the slope for the DEM.

    Parameters
    ----------
    src_raster : Raster
        The dem used to calculate the slope.
    band : int, optional, default: 0
        source band number to use.
    alg : {'ZevenbergenThorne' or 'Horn'}, optional, default: Horn
        The literature suggests Zevenbergen & Thorne to be more suited to smooth landscapes, 
        where Horn’s formula to perform better on rougher terrain.
    slope_format: {"degree" or "percent"}, optional, default degree
        The format of the slope.

    Returns
    -------
    dst_raster: Raster
        Slope calculated from the DEM.
    """
    options = dict(band=band + 1,
                   alg=alg,
                   slopeFormat=slope_format,
                   format='MEM')
    ds_src = src_raster.to_gdal_ds()
    ds = gdal.DEMProcessing('', ds_src, 'slope', **options)
    dst_raster = tgp.read_gdal_ds(ds)
    return dst_raster
Beispiel #11
0
 def update_gdaldtype_by_npdtype(self):
     """Update gdaldtype according to gdaldtype using `TronGisPy.npdtype_to_gdaldtype`.
     For memory operation, numpy dtype will be used. For saving the file,
     gdal dtype will be used. If the data of raster object have been
     changed, its recomended to update the dtype before saveing the file.
     """
     self.gdaldtype = tgp.npdtype_to_gdaldtype(self.data.dtype)
Beispiel #12
0
def dem_to_hillshade(src_raster, band=0, alg='Horn', azimuth=315, altitude=45):
    """Calculate the hillshade for the DEM.

    Parameters
    ----------
    src_raster : Raster
        The dem used to calculate the hillshade.
    band : int, optional, default: 0
        source band number to use.
    alg : {'ZevenbergenThorne' or 'Horn'}, optional, default: Horn
        The literature suggests Zevenbergen & Thorne to be more suited to smooth landscapes, 
        where Horn’s formula to perform better on rougher terrain.
    azimuth : int, optional, default 315
        Azimuth of the light, in degrees. 0 if it comes from the top of the raster, 90 from the east, ... 
        The default value, 315, should rarely be changed as it is the value generally used to generate shaded maps.
    altitude : int, optional, default 45
        Altitude of the light, in degrees. 90 if the light comes from above the DEM, 0 if it is raking light.

    Returns
    -------
    dst_raster: Raster
        Hillshade calculated from the DEM.
    """
    options = dict(band=band + 1,
                   alg=alg,
                   azimuth=azimuth,
                   altitude=altitude,
                   format='MEM')
    ds_src = src_raster.to_gdal_ds()
    ds = gdal.DEMProcessing('', ds_src, 'hillshade', **options)
    dst_raster = tgp.read_gdal_ds(ds)
    return dst_raster
Beispiel #13
0
def get_raster_extent(fp, return_type='poly'):
    """Get the boundary of the raster file.

    Parameters
    ----------
    fp: str
        File path of the raster file.
    return_type: {'poly', 'plot', 'gdal'}
        If 'poly', return four corner coordinates. If plot, return (xmin, xmax, ymin, ymax). If 'gdal', return (xmin, ymin, xmax, ymax). 

    Returns
    -------
    extent: ndarray or tuple
        Depends on return_type. If 'poly', return four corner coordinates. If plot, return (xmin, xmax, ymin, ymax). If 'gdal', return (xmin, ymin, xmax, ymax). 

    Examples
    --------
    >>> import TronGisPy as tgp 
    >>> from shapely.geometry import Polygon
    >>> raster_fp = tgp.get_testing_fp() 
    >>> extent = tgp.get_raster_extent(raster_fp) 
    >>> Polygon(extent).area
    570976.9697303267
    """
    rows, cols, geo_transform = get_raster_info(fp, ['rows', 'cols', 'geo_transform'])
    return tgp.get_extent(rows, cols, geo_transform, return_type)
Beispiel #14
0
def remap_tif(src_tif_path, dst_tif_path, ref_tif_path):
    src_projection, src_gdaldtype = tgp.get_raster_info(
        src_tif_path, ['projection', 'gdaldtype'])
    ref_geo_transform, ref_projection = tgp.get_raster_info(
        ref_tif_path, ['geo_transform', 'projection'])
    output_bounds = tgp.get_raster_extent(ref_tif_path,
                                          'gdal')  # (minX, minY, maxX, maxY)

    x_res, y_res = ref_geo_transform[1], ref_geo_transform[5]
    output_type = src_gdaldtype
    gdal.Warp(dst_tif_path,
              src_tif_path,
              outputBounds=output_bounds,
              xRes=x_res,
              yRes=y_res,
              outputType=output_type,
              srcSRS=src_projection,
              dstSRS=ref_projection)
Beispiel #15
0
    def write_combined_tif(self, X, dst_tif_path, gdaldtype=None, no_data_value=None):
        """Combine the model predict result on splitted images and write as tif file.

        Parameters
        ----------
        X: array_like
            The splitted image prediction result. should have the same shape with the 
            SplittedImage.get_splitted_images.
        dst_tif_path: str
            The location to save the tif file.
        gdaldtype: int, optional
            The type of the cell defined in gdal which will affect the information 
            to be stored when saving the file. This can be generate from `gdal.GDT_XXX` 
            such as `gdal.GDT_Int32` equals 5 and `gdal.GDT_Float32` equals 6.
        no_data_value: int or float, optional
            Define which value to replace nan in numpy array when saving a raster file.
            
        """
        X_combined_bands = self.get_combined_image(X)
        gdaldtype = gdaldtype if gdaldtype is not None else self.gdaldtype
        no_data_value = no_data_value if no_data_value is not None else self.no_data_value
        tgp.write_raster(dst_tif_path, X_combined_bands, geo_transform=self.src_gt, projection=self.proj, gdaldtype=gdaldtype, no_data_value=no_data_value)
Beispiel #16
0
def tif_composition(ref_tif_path,
                    src_tif_paths,
                    dst_tif_path,
                    dst_tif_dtype_gdal=None):
    """
    ref_tif_path: should be used to create the canvas with final coordinate system, geo_transform and projection, 
    src_tif_paths: should be in list type with elements with full path of tif images.
    dst_tif_path: output file path
    """
    # get geo info
    rows, cols, bands, geo_transform, projection, dtype_gdal, no_data_value, metadata = tgp.get_raster_info(
        ref_tif_path)
    if dst_tif_dtype_gdal:
        dtype_gdal = dst_tif_dtype_gdal

    # cal bands count
    bands_for_each_tif = [
        tgp.get_raster_info(tif_path)[2] for tif_path in src_tif_paths
    ]
    bands = sum(bands_for_each_tif)

    # bands compositions: create new tif
    dst_ds = gdal.GetDriverByName('GTiff').Create(dst_tif_path, cols, rows,
                                                  bands, dtype_gdal)
    dst_ds.SetGeoTransform(geo_transform)
    dst_ds.SetProjection(projection)

    # bands compositions: write bands
    band_num = 1
    for tif_path, bands_for_the_tif in zip(src_tif_paths, bands_for_each_tif):
        nparr = tgp.get_raster_data(tif_path)
        for band_num_for_the_tif in range(bands_for_the_tif):
            band = dst_ds.GetRasterBand(band_num)
            band.WriteArray(nparr[:, :, band_num_for_the_tif], 0, 0)
            band.FlushCache()
            if no_data_value:
                band.SetNoDataValue(no_data_value)
            band_num += 1
    dst_ds = None
Beispiel #17
0
 def __repr__(self):
     desc = ""
     desc += "shape: ({rows}, {cols}, {bands})\n".format(rows=self.rows,
                                                         cols=self.cols,
                                                         bands=self.bands)
     desc += "gdaldtype: {gdaldtype}\n".format(
         gdaldtype=tgp.get_gdaldtype_name(self.gdaldtype))
     desc += "geo_transform: {geo_transform}\n".format(
         geo_transform=self.geo_transform)
     desc += "projection: {projection}\n".format(projection=self.projection)
     desc += "no_data_value: {no_data_value}\n".format(
         no_data_value=self.no_data_value)
     desc += "metadata: {metadata}".format(metadata=self.metadata)
     return desc
Beispiel #18
0
    def to_gdal_ds(self):
        """Export raster object to `gdal.DataSource`.

        Returns
        -------
        ds: gdal.DataSource
            DataSource converted from the raster object.
        """
        ds = tgp.write_gdal_ds(self.data,
                               geo_transform=self.geo_transform,
                               projection=self.projection,
                               gdaldtype=self.gdaldtype,
                               no_data_value=self.no_data_value)
        return ds
Beispiel #19
0
    def get_values_by_coords(self, coords):
        """get the data digital values of the coordinates

        Parameters
        ----------
        coords: ndarray 
            The coordinates with shape (n_points, 2). The order of last dimension is (lng, lat).

        Returns
        -------
        coords: ndarray 
            The data digital values of the coordinates.
        """
        npidxs_row, npidxs_col = tgp.coords_to_npidxs(
            coords.astype(np.float32), self.geo_transform).T
        return self.data[npidxs_row, npidxs_col]
Beispiel #20
0
def clip_raster_with_extent(src_raster, extent):
    """Clip raster with extent.

    Parameters
    ----------
    src_raster: Raster
        Which raster data to be clipped.
    extent: tuple
        extent to clip the data with (xmin, ymin, xmax, ymax) format.

    Returns
    -------
    dst_raster: Raster. 
        Clipped result.

    Examples
    -------- 
    >>> import geopandas as gpd
    >>> import TronGisPy as tgp
    >>> from TronGisPy import ShapeGrid
    >>> from matplotlib import pyplot as plt
    >>> from shapely.geometry import Polygon
    >>> src_raster_fp = tgp.get_testing_fp('satellite_tif')
    >>> src_raster = tgp.read_raster(src_raster_fp)
    >>> ext = xmin, ymin, xmax, ymax = [329454.39272725, 2746809.43272727, 331715.57090906, 2748190.90181818]
    >>> dst_raster = ShapeGrid.clip_raster_with_extent(src_raster, ext)
    >>> fig, (ax1, ax2) = plt.subplots(1, 2) # plot the result
    >>> src_raster.plot(ax=ax1) 
    >>> ext_poly = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]
    >>> df_ext_poly = gpd.GeoDataFrame([Polygon(ext_poly)], columns=['geom'], geometry='geom')
    >>> df_ext_poly.boundary.plot(ax=ax1)
    >>> ax1.set_title('original image and clipper')
    >>> dst_raster.plot(ax=ax2)
    >>> ax2.set_title('clipped image')
    >>> plt.show()
    """
    assert src_raster.geo_transform is not None, "src_raster.geo_transform should not be None"
    src_ds = src_raster.to_gdal_ds()
    dst_ds = gdal.Warp('',
                       src_ds,
                       format='MEM',
                       outputBounds=extent,
                       cropToCutline=True)
    dst_raster = tgp.read_gdal_ds(dst_ds)
    return dst_raster
Beispiel #21
0
def dem_to_TPI(src_raster, band=0):
    """Calculate the topographic position index (TPI) for the DEM.

    Parameters
    ----------
    src_raster : Raster
        The dem used to calculate the TPI.
    band : int, optional, default: 0
        source band number to use.

    Returns
    -------
    dst_raster: Raster
        TPI calculated from the DEM.
    """
    options = dict(band=band + 1, format='MEM')
    ds_src = src_raster.to_gdal_ds()
    ds = gdal.DEMProcessing('', ds_src, 'TPI', **options)
    dst_raster = tgp.read_gdal_ds(ds)
    return dst_raster
Beispiel #22
0
def dem_to_roughness(src_raster, band=0):
    """Calculate the roughness for the DEM.

    Parameters
    ----------
    src_raster : Raster
        The dem used to calculate the roughness.
    band : int, optional, default: 0
        source band number to use.

    Returns
    -------
    dst_raster: Raster
        roughness calculated from the DEM.
    """
    options = dict(band=band + 1, format='MEM')
    ds_src = src_raster.to_gdal_ds()
    ds = gdal.DEMProcessing('', ds_src, 'Roughness', **options)
    dst_raster = tgp.read_gdal_ds(ds)
    return dst_raster
Beispiel #23
0
def write_raster(fp, data, geo_transform=None, projection=None, gdaldtype=None, no_data_value=None, metadata=None):
    """Write raster file.

    Parameters
    ----------
    fp: str
        File path of the raster file.
    geo_transform: tuple or list, optional
        Affine transform parameters (c, a, b, f, d, e = geo_transform). 
    projection: str, optional
        The well known text (WKT) of the raster which can be generate from `TronGisPy.epsg_to_wkt(<epsg_code>)`
    gdaldtype: int, optional
        The type of the cell defined in gdal which will affect the information 
        to be stored when saving the file. This can be generate from `gdal.GDT_XXX` 
        such as `gdal.GDT_Int32` equals 5 and `gdal.GDT_Float32` equals 6.
    no_data_value: int or float, optional
        Define which value to replace nan in numpy array when saving a raster file.
    metadata: dict, optional
        Define the metadata of the raster file.
    """
    if len(data.shape) == 2:
        data = np.expand_dims(data, axis=2)
    rows, cols, bands = data.shape
    gdaldtype = tgp.npdtype_to_gdaldtype(data.dtype) if gdaldtype is None else gdaldtype
    ds = gdal.GetDriverByName('GTiff').Create(fp, cols, rows, bands, gdaldtype) # dst_filename, xsize=512, ysize=512, bands=1, eType=gdal.GDT_Byte
    if geo_transform is not None:
        ds.SetGeoTransform(geo_transform)
    if projection is not None:
        ds.SetProjection(projection)
    if metadata is not None:
        ds.SetMetadata(metadata)

    for b in range(bands):
        band = ds.GetRasterBand(b+1)
        band.WriteArray(data[:, :, b], 0, 0)
        if no_data_value is not None:
            band.SetNoDataValue(no_data_value)
        band.FlushCache()
    ds = None
Beispiel #24
0
    def __init__(self,
                 data,
                 geo_transform=None,
                 projection=None,
                 gdaldtype=None,
                 no_data_value=None,
                 metadata=None):
        """Initializing Raster object.

        Parameters
        ----------
        data: array_like
            Digital number for each raster cell. Data is in (n_rows, n_cols, n_bands) shape.
        geo_transform: tuple or list, optional
            Affine transform parameters (c, a, b, f, d, e = geo_transform). 
        projection: str, optional
            The well known text (WKT) of the raster which can be generate 
            from `TronGisPy.epsg_to_wkt(<epsg_code>)`
        gdaldtype: int, optional
            The type of the cell defined in gdal which will affect the information 
            to be stored when saving the file. This can be generate from `gdal.GDT_XXX` 
            such as `gdal.GDT_Int32` equals 5 and `gdal.GDT_Float32` equals 6.
        no_data_value: int or float, optional
            Define which value to replace nan in numpy array when saving a raster file.
        metadata: dict, optional
            Define the metadata of the raster file.
        """
        if len(data.shape) == 2:
            data = np.expand_dims(data, axis=2)
        self.data = data
        self.geo_transform = geo_transform if geo_transform is not None else [
            0, 1, 0, 0, 0, -1
        ]
        self.gdaldtype = gdaldtype if gdaldtype is not None else tgp.npdtype_to_gdaldtype(
            data.dtype)
        self.projection = projection
        self.no_data_value = no_data_value
        self.metadata = metadata
        self.cache_data_for_plot = None
Beispiel #25
0
    def plot(self,
             flush_cache=True,
             norm=True,
             clip_percentage=(0.02, 0.98),
             clip_min_max=None,
             log=False,
             rescale_percentage=None,
             bands=None,
             ax=None,
             title=None,
             cmap=None,
             figsize=None):
        """Plot the raster object.

        Parameters
        ----------
        flush_cache: bool. 
            Flush the cached processed result for quick plotting.
        norm: bool, optional, default: True
            Normalize the image for showing.
        clip_percentage: tuple of float, optional, default: (0.02, 0.98)
            The percentage to cut the data in head and tail e.g. (0.02, 0.98)
        log: bool, optional, default: False
            Get the log value of data to show the image.
        rescale_percentage: float, optional
            The percentage to recale (resize) the image for efficient showing.
        bands: list, optional
            Which bands to plot. Length of bands should be 1, 3 or 4.
            If 3 bands is used, each of them will be defined as rgb bands. 
            If the forth band is used, it will be the opacity value.
        ax: matplotlib.axes._subplots.AxesSubplot, optional
            On which ax the raster will be plot.
        title: str, optional
            The title of the histgram.
        cmap: str, optional
            See Colormaps in Matplotlib https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html.
        figsize: tuple, optional
            Width and height of the histgram e.g.(10, 10).
        """
        if bands is None:
            bands = [0, 1, 2] if self.bands >= 3 else [0]

        assert type(bands) is list, "type of bands should be list"
        assert len(bands) in [1, 3, 4], "length of bands should be 1, 3 or 4"

        if (self.cache_data_for_plot is None) or flush_cache:
            # reshape to valid shape for matplotlib
            if len(bands) == 1:
                data = self.data[:, :, bands[0]]
            else:
                data = self.data[:, :, bands]

            # deal with no data
            data = data.astype(np.float)
            data[data == self.no_data_value] = np.nan

            # detect single value
            if len(np.unique(data[~np.isnan(data)])) == 1:
                norm = False
                log = False
                clip_percentage = None

            # clip_percentage & clip_min_max
            if clip_percentage is not None:
                assert not (
                    clip_percentage is not None and clip_min_max is not None
                ), "should not set clip_percentage and clip_min_max at the same time!"
                assert len(
                    clip_percentage) == 2, "clip_percentage two element tuple"
                data = tgp.Normalizer().clip_by_percentage(
                    data, clip_percentage=clip_percentage)
            elif clip_min_max is not None:
                assert len(clip_min_max) == 2, "clip_min_max two element tuple"
                data = tgp.Normalizer().clip_by_min_max(data,
                                                        min_max=clip_min_max)

            # log
            if log:
                if data[~np.isnan(data)].min() < 0:
                    data -= data[~np.isnan(data)].min()
                data[~np.isnan(data)] = np.log(data[~np.isnan(data)] + 10**-6)

            # normalize
            if norm:
                data = tgp.Normalizer().fit_transform(data)

            if rescale_percentage is not None:
                import cv2
                data = cv2.resize(data, (int(self.cols * rescale_percentage),
                                         int(self.rows * rescale_percentage)))

            self.cache_data_for_plot = data
        else:
            data = self.cache_data_for_plot

        # flip xy
        c, a, b, f, d, e = self.geo_transform
        # lng = a * col + b * row + c
        # lat = d * col + e * row + f
        # a = d(lng) / d(col)
        # b = d(lng) / d(row)
        # d = d(lat) / d(col)
        # e = d(lat) / d(row)
        if (np.abs(a) > np.abs(d)) and (np.abs(e) > np.abs(b)):
            lng_res, lat_res = a, e
        elif (np.abs(d) > np.abs(a)) and (
                np.abs(b) > np.abs(e)
        ):  # if d > a, 1 col move contribute more on lat, less on lng
            data = np.flipud(np.rot90(data))
            lng_res, lat_res = d, b
        else:
            assert False, "not acceptable geotransform"
        if lng_res < 0:  # general lng_res > 0
            data = np.fliplr(data)
        if lat_res > 0:  # general lat_res < 0
            data = np.flipud(data)

        # plotting
        if ax is not None:
            img = ax.imshow(data, extent=self.extent_for_plot, cmap=cmap)
            ax.set_title(title)
        else:
            if figsize is not None:
                plt.figure(figsize=figsize)
            img = plt.imshow(data, extent=self.extent_for_plot, cmap=cmap)
            plt.title(title)
            plt.show()
        return img
Beispiel #26
0
    def refine_resolution(self,
                          dst_resolution,
                          resample_alg='near',
                          extent=None,
                          rotate=True):
        """Refine the resolution of the raster.

        Parameters
        ----------
        dst_resolution: int
            Target Resolution.
        resample_alg: {'near', 'bilinear', 'cubic', 'cubicspline', 'lanczos', 'average', 'mode'}.
            ``near``: nearest neighbour resampling (default, fastest algorithm, worst interpolation quality).
            ``bilinear``: bilinear resampling.
            ``cubic``: cubic resampling.
            ``cubicspline``: cubic spline resampling.
            ``lanczos``: Lanczos windowed sinc resampling.
            ``average``: average resampling, computes the weighted average of all non-NODATA contributing pixels.
            ``mode``: mode resampling, selects the value which appears most often of all the sampled points.
        extent: tuple
            extent to clip the data with (xmin, ymin, xmax, ymax) format.
        rotate: bool
            If True, the function will rotate the raster and adds noData values around it to make a new rectangular image 
            matrix if the rotation in Raster.geo_transform is not zero, else it will keep the original rotation angle of 
            Raster.geo_transform. Gdal will rotate the image by default . Please refer to the issue
            https://gis.stackexchange.com/questions/256081/why-does-gdalwarp-rotate-the-data and 
            https://github.com/OSGeo/gdal/issues/1601.

        Returns
        -------
        dst_raster: Raster
            Refined result.

        Examples
        -------- 
        >>> import numpy as np
        >>> import TronGisPy as tgp
        >>> from TronGisPy import ShapeGrid
        >>> from matplotlib import pyplot as plt
        >>> src_raster_fp = tgp.get_testing_fp('dem_process_path')
        >>> src_raster = tgp.read_raster(src_raster_fp)
        >>> src_raster.data[src_raster.data == -999] = np.nan
        >>> dst_raster = src_raster.refine_resolution(dst_resolution=10, resample_alg='bilinear')
        >>> fig, (ax1, ax2) = plt.subplots(1, 2) # plot the result
        >>> src_raster.plot(ax=ax1)
        >>> ax1.set_title('original dem ' + str(src_raster.shape))
        >>> dst_raster.plot(ax=ax2)
        >>> ax2.set_title('refined image ' + str(dst_raster.shape))
        >>> plt.show()
        """
        src_ds = self.to_gdal_ds()

        if rotate:
            dst_ds = gdal.Warp('',
                               src_ds,
                               xRes=dst_resolution,
                               yRes=dst_resolution,
                               outputBounds=extent,
                               format='MEM',
                               resampleAlg=resample_alg)
            dst_raster = tgp.read_gdal_ds(dst_ds)
        else:
            assert extent is None, "you cannot set the extent when rotate == False"
            zoom_in = dst_resolution / self.pixel_size[0]
            dst_ds = gdal.Warp('',
                               src_ds,
                               xRes=zoom_in,
                               yRes=zoom_in,
                               format='MEM',
                               resampleAlg=resample_alg,
                               transformerOptions=[
                                   'SRC_METHOD=NO_GEOTRANSFORM',
                                   'DST_METHOD=NO_GEOTRANSFORM'
                               ])
            dst_geo_transform = np.array(self.geo_transform)
            dst_geo_transform[[1, 2, 4, 5]] *= zoom_in
            dst_raster = tgp.read_gdal_ds(dst_ds)
            dst_raster.geo_transform = tuple(dst_geo_transform)
            dst_raster.projection = self.projection
        return dst_raster
Beispiel #27
0
    def hist(self,
             norm=False,
             clip_percentage=None,
             log=False,
             bands=None,
             ax=None,
             title=None,
             figsize=None):
        """plot digital value histgram (distribution) of raster object.

        Parameters
        ----------
        norm: bool, optional, default: False
            Normalize the image for showing.
        clip_percentage: tuple of float, optional
            The percentage to cut the data in head and tail e.g. (0.02, 0.98)
        log: bool, optional, default: False
            Use the log value to plot the histgram.
        bands: list, optional
            Which bands to plot. if bands==None, use values from all bands.
        ax: matplotlib.axes._subplots.AxesSubplot, optional
            On which ax the raster will be plot.
        title: str, optional
            The title of the histgram.
        figsize: tuple, optional
            Width and height of the histgram e.g.(10, 10).
        """
        if bands is None:
            data = self.data[self.data != self.no_data_value].flatten()
        else:
            data = self.data[:, :, bands]
            data = data[data != self.no_data_value].flatten()

        # clip_percentage
        if clip_percentage is not None:
            assert len(
                clip_percentage) == 2, "clip_percentage two element tuple"
            idx_st = int(len(data.flatten()) * clip_percentage[0])
            idx_end = int(len(data.flatten()) * clip_percentage[1])
            X_sorted = np.sort(data.flatten())
            data_min = X_sorted[idx_st]
            data_max = X_sorted[idx_end]
            data[data < data_min] = data_min
            data[data > data_max] = data_max

        # log
        if log:
            data = np.log(data)

        # normalize
        if norm:
            data = tgp.Normalizer().fit_transform(
                data, clip_percentage=clip_percentage)

        if ax is not None:
            ax.hist(data)
            ax.set_title(title)
        else:
            if figsize is not None:
                plt.figure(figsize=figsize)
            plt.hist(data)
            plt.title(title)
            plt.show()
Beispiel #28
0
 def extent_for_gdal(self):
     """(xmin, xmax, ymin, ymax) of the raster boundary."""
     return tgp.get_extent(self.rows,
                           self.cols,
                           self.geo_transform,
                           return_type='gdal')
Beispiel #29
0
def gdal_fillnodata(raster,
                    band=0,
                    no_data_value=999,
                    max_distance=100,
                    smoothing_iterations=0):
    """Interpolate values on specific cells (generally nan cell) using 
    `gdal.FillNodata`. To be mentioned, this cannot accept zero in its data
    except its no_data_value is zero.

    Parameters
    ----------
    raster: Raster
        The Raster object you want to fill the no_data_value.
    band: int, optional, default: 0
        The band numnber of Raster object you want to fill the no_data_value 
        which start from zero.
    no_data_value: int or float, optional, default: 999
        The value to be filled with interpolated value. If no_data_value == None, 
        use np.nan as no_data_value.
    max_distance: int, optional, default: 100
        The cells within the max distance from no_data_value location will be 
        calculated to fill the no_data_value.
    smoothing_iterations: int, optional, default: 0
        The maximum limitation on loop.

    Returns
    -------
    data_interp: ndarray. 
        Interpolation result.

    Examples
    -------- 
    >>> import numpy as np
    >>> import TronGisPy as tgp 
    >>> from TronGisPy import Interpolation
    >>> from matplotlib import pyplot as plt
    >>> raster_fp = tgp.get_testing_fp('tif_forinterpolation')
    >>> raster = tgp.read_raster(raster_fp)
    >>> raster.data[np.isnan(raster.data)] = 999
    >>> raster_interp = Interpolation.gdal_fillnodata(raster)
    >>> fig, (ax1, ax2) = plt.subplots(1,2)
    >>> raster.plot(ax=ax1)
    >>> raster_interp.plot(ax=ax2)
    >>> plt.show()
    """
    # make dst_band
    ds_dst = raster.to_gdal_ds()
    dstband = ds_dst.GetRasterBand(band + 1)

    # make maskband
    raster_mask = raster.copy()
    raster_mask.data[raster_mask.data == no_data_value] = 0
    raster_mask.data[raster_mask.data != 0] = 1
    raster_mask.astype(bool)
    raster_mask.no_data_value = 0
    ds_mask = raster_mask.to_gdal_ds()
    maskband = ds_mask.GetRasterBand(1)

    gdal.FillNodata(dstband, maskband, max_distance, smoothing_iterations)
    data_interp = tgp.read_gdal_ds(ds_dst)

    ds_dst = None
    ds_mask = None
    return data_interp
Beispiel #30
0
def write_gdal_ds(data=None, bands=None, cols=None, rows=None, geo_transform=None, projection=None, gdaldtype=None, no_data_value=None, metadata=None):
    """Build the gdal DataSource from geo-information attributes.

    Parameters
    ----------
    data: array_like, optional
        The digital number for each cell of the raster.  Data is in 
        (n_rows, n_cols, n_bands) shape.
    bands: int, optional
        Number of bands.
    cols: int, optional
        Number of cols.
    rows: int, optional
        Number of rows.
    geo_transform: tuple or list, optional
        Affine transform parameters (c, a, b, f, d, e = geo_transform). 
    projection: str, optional
        The well known text (WKT) of the raster which can be generate 
        from `TronGisPy.epsg_to_wkt(<epsg_code>)`
    gdaldtype: int, optional
        The type of the cell defined in gdal which will affect the information 
        to be stored when saving the file. This can be generate from `gdal.GDT_XXX` 
        such as `gdal.GDT_Int32` equals 5 and `gdal.GDT_Float32` equals 6.
    no_data_value: int or float, optional
        Define which value to replace nan in numpy array when saving a raster file.
    metadata: dict, optional
        Define the metadata of the raster file.

    Returns
    -------
    raster: Raster.
        output raster.

    Examples
    --------
    >>> import TronGisPy as tgp 
    >>> raster_fp = tgp.get_testing_fp() 
    >>> data = tgp.get_raster_data(raster_fp)
    >>> geo_transform, projection, gdaldtype, no_data_value = tgp.get_raster_info(raster_fp, ["geo_transform", "projection", "gdaldtype", "no_data_value"])
    >>> ds = tgp.write_gdal_ds(data, geo_transform=geo_transform, projection=projection, gdaldtype=gdaldtype, no_data_value=no_data_value)
    >>> raster = tgp.read_gdal_ds(ds)
    >>> raster
    shape: (677, 674, 3)
    geo_transform: (271982.8783, 1.1186219584569888, 0.0, 2769973.0653, 0.0, -1.1186305760705852)
    projection: PROJCS["TWD97 / TM2 zone 121",GEOGCS["TWD97",DATUM["Taiwan_Datum_1997",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","1026"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","3824"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",121],PARAMETER["scale_factor",0.9999],PARAMETER["false_easting",250000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","3826"]]
    no_data_value: -32768.0
    metadata: {}
    """
    if data is None:
        assert (bands is not None) and (cols is not None) and (rows is not None), "bands, cols, rows should not be None"
        assert (gdaldtype is not None), "gdaldtype should not be None"
    else:    
        if len(data.shape) == 2:
            data = np.expand_dims(data, axis=2)
        bands = data.shape[2] if bands is None else bands
        cols = data.shape[1] if cols is None else cols
        rows = data.shape[0] if rows is None else rows
        gdaldtype = tgp.npdtype_to_gdaldtype(data.dtype) if gdaldtype is None else gdaldtype

    ds = gdal.GetDriverByName('MEM').Create('', cols, rows, bands, gdaldtype) # dst_filename, xsize=512, ysize=512, bands=1, eType=gdal.GDT_Byte
    if geo_transform is not None:
        ds.SetGeoTransform(geo_transform)
    if projection is not None:
        ds.SetProjection(projection)
    if metadata is not None:
        ds.SetMetadata(metadata)

    if no_data_value is not None:
        for b in range(bands):
            band = ds.GetRasterBand(b+1)
            band.SetNoDataValue(no_data_value)
            band.WriteArray(np.full((rows, cols), no_data_value), 0, 0)
            band.FlushCache()
    
    if data is not None:
        for b in range(data.shape[2]):
            band = ds.GetRasterBand(b+1)
            band.WriteArray(data[:, :, b], 0, 0)
            band.FlushCache()
    return ds