Esempio n. 1
0
    def from_geo_file(geo_file: str, res, buffer_accuracy=1, to_crs: str = ''):
        """ Get geo grid from existing geo file (such as shapefile)

        :param geo_file: path to geo file (e.g. shapefile)
        :param res: resolution of the grid
        :param buffer_accuracy: accuracy of the buffer surrounded the region (in degrees)
        :param to_crs: final coordinate reference system of the geo grid (empty string for keeping the original CRS,
        under the form 'epsg:number' or pyproj name otherwise)
        :return: GeoGrid
        """
        # Import locally (to avoid conflicts)
        from gistools.projections import proj4_from

        check_type(geo_file, str, res, (int, float), buffer_accuracy,
                   (int, float), to_crs, str)

        if os.path.isfile(geo_file):
            geo_ds = gpd.read_file(geo_file)
        else:
            raise GeoGridError("{} is not a valid geo file".format(geo_file))

        try:
            to_crs = pyproj.CRS(to_crs)
        except ValueError:
            warnings.warn(
                "Empty or invalid CRS/proj name. Geo grid matching geo file projection",
                GeoGridWarning)
        else:
            geo_ds = geo_ds.to_crs(to_crs)

        return GeoGrid.from_geopandas(geo_ds, res, buffer_accuracy)
Esempio n. 2
0
def check_string(string: str, list_of_strings: Collection) -> str:
    """ Check validity of and return string against list of valid strings

    :param string: searched string
    :type string: str
    :param list_of_strings: list/tuple/set of valid strings string is to be checked against
    :type list_of_strings: list, tuple, set
    :return: validate string from list of strings if match
    :rtype: str

    :Example:
        >>> check_string("well", ["Well Done", "no match", "etc."])
        "Well Done"
        >>> check_string("slow", ("slow", "slowdown"))
        ValueError: input match more than one valid value among ["slow", "slowdown"]
        >>> check_string("few", {"missed", "little"})
        ValueError: input must match one of those: ["missed", "little"]
    """
    check_type(string, str, list_of_strings, Collection)
    [check_type(x, str) for x in list_of_strings]

    output_string = []

    for item in list_of_strings:
        if item.lower().startswith(string.lower()):
            output_string.append(item)

    if len(output_string) == 1:
        return output_string[0]
    elif len(output_string) == 0:
        raise ValueError("input must match one of those: {}".format(list_of_strings))
    elif len(output_string) > 1:
        raise ValueError("input match more than one valid value among {}".format(list_of_strings))
Esempio n. 3
0
    def __init__(self, roads, nodes, *args, **kwargs):
        """

        :param roads: Road instance
        :param nodes: road nodes
        """
        check_type(roads, Road, nodes, RoadNode)

        super().__init__(roads, nodes, *args, **kwargs)
Esempio n. 4
0
    def polygonize(self, field_name, layer_name="layer", is_8_connected=False):
        """ Convert raster into vector polygon(s)

        :param field_name: name of the corresponding field in the final shape file
        :param layer_name: name of resulting layer
        :param is_8_connected: pixel connectivity used for polygon
        :return:
        """
        check_type(is_8_connected, bool)
        with ShapeTempFile() as out_shp:
            self._gdal_polygonize(out_shp, layer_name, field_name, is_8_connected)
            return PolygonLayer(out_shp, name=layer_name)
Esempio n. 5
0
    def __init__(self, raster, geo_grid: GeoGrid = None, no_data_value=None, crs=None):
        """ RasterMap constructor

        :param raster: raster file or numpy array
        :param geo_grid: GeoGrid instance
        :param no_data_value: data to be regarded as "no_data"
        :param crs: projection used for the raster

        :Example:
        >>>
        """
        check_type(raster, (str, np.ndarray))

        if type(raster) == np.ndarray:
            raster_file = None
            try:
                test_fit = geo_grid.data_fits(raster)
                crs = pyproj.CRS(crs)
                # crs = proj4_from(crs)
                if not test_fit:
                    raise RasterMapError("Input geo grid does not fit raster")
            except AttributeError:
                raise RasterMapError("Geo grid argument has not been set")
            except (ValueError, TypeError):
                raise RasterMapError("Invalid projection: crs='{}'".format(crs))
        else:
            raster_file = raster
            try:
                geo_grid = GeoGrid.from_raster_file(raster)
                crs = crs_from_raster(raster)
                raster = raster_to_array(raster)
            except RuntimeError:
                raise RasterMapError("Invalid/unknown file '%s'" % raster_file)

        # Set attributes
        self._raster_file = raster_file
        self._geo_grid = geo_grid
        self._raster_array = np.array(raster, dtype='float64')  # Ensure compatibility with NaNs
        self._crs = crs
        self._res = self._geo_grid.res
        self._x_origin = self._geo_grid.geo_transform[0]
        self._y_origin = self._geo_grid.geo_transform[3]
        self._x_size = self._geo_grid.num_x
        self._y_size = self._geo_grid.num_y
        self._shape = self._raster_array.shape
        self._no_data_value = no_data_value

        if no_data_value is not None:
            self._raster_array[self._raster_array == no_data_value] = np.nan  # Use attribute (raster_array) rather than
            # instance (self == ...) to avoid 'recursion' error with decorator above

        # Available filters
        self._filters = {"majority_filter": self._majority_filter, "sieve": self._gdal_sieve}
Esempio n. 6
0
def shape_to_raster(shapefile: str,
                    raster_file: str,
                    geo_grid: GeoGrid,
                    geo_crs: str = "WGS84",
                    burn_value=1,
                    no_data_value=0):
    """ Convert shape to raster

    Convert input file (e.g shapefile)
    geometry into raster
    (see https://pcjericks.github.io/py-gdalogr-cookbook/raster_layers.html)

    :param shapefile: path to shapefile to convert
    :param raster_file: path to raster file where to write
    :param geo_grid: geo grid which raster must rely on
    :param geo_crs: geographic coordinate system (see http://spatialreference.org/ref/epsg/wgs-84/
    for the WGS84 CRS for instance)
    :param burn_value: value to be burned from shape into raster
    :param no_data_value: value of raster cells where there are no data
    """
    check_type(shapefile, str, raster_file, str, geo_grid, GeoGrid, geo_crs,
               str)
    source_ds = ogr.Open(shapefile)
    if source_ds is None:
        raise RuntimeError("Unable to open {}".format(shapefile))

    # Retrieve bounding box of geometry
    source_layer = source_ds.GetLayer()

    # Create the destination data source
    driver = gdal.GetDriverByName('GTiff')

    # Be very careful here with geo_grid.num_x
    # and geo_grid.num_y : they are not the number
    # of pixels but the number of edges regarding
    # raster computation...
    target_ds = driver.Create(raster_file, geo_grid.num_x, geo_grid.num_y, 1,
                              gdal.GDT_Byte)
    target_ds.SetGeoTransform(geo_grid.geo_transform)
    band = target_ds.GetRasterBand(1)
    band.SetNoDataValue(no_data_value)

    # Set geographic coordinate system
    projection = osr.SpatialReference()
    projection.SetWellKnownGeogCS(geo_crs)
    target_ds.SetProjection(projection.ExportToWkt())

    # Rasterization
    gdal.RasterizeLayer(target_ds, [1], source_layer, burn_values=[burn_value])

    return 0
Esempio n. 7
0
def raster_to_array(raster_file: str, band=None):
    """ Get numpy array from raster file

    :param raster_file: path to raster file
    :param band: raster band number
    :return: numpy array

    :Example:
    >>> array = raster_to_array("/path/to/my/raster.tif")
    """
    check_type(raster_file, str)

    source_ds = gdal.Open(raster_file)
    if band:
        source_ds = source_ds.GetRasterBand(band)

    return source_ds.ReadAsArray()
Esempio n. 8
0
    def __init__(self, edges, nodes, match_edge_nodes=True, tolerance=1):
        """ Network class constructor

        :param edges: Edge instance
        :param nodes: Node instance
        :param match_edge_nodes: Boolean --> match edge nodes with respect to tolerance
        :param tolerance: distance tolerance for considering nodes and edge nodes the same (in m)
        """
        check_type(edges, Edge, nodes, Node)
        self._edges = edges
        self._nodes = nodes

        # Retrieve edge nodes corresponding to nodes
        if match_edge_nodes:
            edge_nodes = self._edges.get_nodes()
            distance, nn = nodes.distance_and_nearest_neighbor(edge_nodes)
            self._nodes["geometry"] = [edge_nodes.geometry[n] for n in nn]
            self._nodes = self._nodes[distance <= tolerance]
Esempio n. 9
0
def compute_surface(lb_pixel, rb_pixel, ub_pixel, bb_pixel, geo_type: str,
                    ellipsoid: Ellipsoid):
    """ Compute surface of earth pixels

    Combine surface element in spherical coordinates
    and local spherical approximation of the given
    ellipsoid to compute pixel surface. Or return
    surface from evenly spaced pixels from equiarea
    map projection.
    :param lb_pixel: left border pixel coordinate
    :param rb_pixel: right border pixel coordinate
    :param ub_pixel: upper border pixel coordinate
    :param bb_pixel: bottom border pixel coordinate
    :param geo_type: geo projection ("latlon" or "equal")
    :param ellipsoid: ellipsoid model
    :return:
    """

    latitude = (ub_pixel + bb_pixel) / 2

    def ellipsoid_normal():
        return ellipsoid.a / sqrt(1 - (ellipsoid.e**2) *
                                  (sin(latitude * pi / 180))**2)

    def local_sphere_radius():

        p = ellipsoid.a * (1 - ellipsoid.e**2) / (
            1 - ellipsoid.e**2 * sin(latitude * pi / 180)**2)**(3 / 2)
        return sqrt(ellipsoid_normal() * p) * 10**-3

    check_type(geo_type, str, ellipsoid, Ellipsoid)

    if geo_type == "latlon":
        surface = local_sphere_radius() ** 2 * (sin(ub_pixel * pi / 180) - sin(bb_pixel * pi / 180)) *\
                  (rb_pixel * pi / 180 - lb_pixel * pi / 180)
    elif geo_type == "equal":
        surface = abs(ub_pixel - bb_pixel) * abs(rb_pixel - lb_pixel)
    else:
        raise ValueError(
            "Projection {} has not been defined yet".format(geo_type))

    return surface
Esempio n. 10
0
    def from_raster_file(raster_file: str):
        """ Retrieve geo grid from raster file

        :param raster_file: path to raster file
        :return: GeoGrid
        """
        check_type(raster_file, str)

        try:
            source_ds = gdal.Open(raster_file)
        except RuntimeError as error:
            raise error

        geo_transform = source_ds.GetGeoTransform()

        # Warning: ll_corner and ur_corner correspond to grid corners
        # that is pixel centers, but gdal rasters are defined with respect
        # to pixel corners... That's why "+- res/2"
        ll_corner = (geo_transform[3] +
                     source_ds.RasterYSize * geo_transform[5] -
                     geo_transform[5] / 2,
                     geo_transform[0] + geo_transform[1] / 2)
        ur_corner = (geo_transform[3] + geo_transform[5] / 2,
                     geo_transform[0] +
                     source_ds.RasterXSize * geo_transform[1] -
                     geo_transform[1] / 2)

        # Geo type
        if crs_from_raster(raster_file).is_geographic:
            geo_type = "latlon"
        else:
            geo_type = "equal"

        if math.isclose(math.fabs(geo_transform[1]),
                        math.fabs(geo_transform[5])):
            geo_grid = GeoGrid(ll_corner, ur_corner,
                               math.fabs(geo_transform[1]), geo_type)
        else:
            geo_grid = None
            warnings.warn('No regular raster: no GeoGrid implemented', Warning)

        return geo_grid
Esempio n. 11
0
    def from_geopandas(geopandas_series, res, buffer_accuracy=1):
        """ Get geo grid from specific geopandas database

        :param geopandas_series:
        :param res:
        :param buffer_accuracy:
        :return:
        """
        check_type(geopandas_series, (gpd.GeoDataFrame, gpd.GeoSeries), res,
                   (int, float), buffer_accuracy, (int, float))
        # Default type for geo grid
        geo_type = 'latlon'

        try:
            # If resolution in km/m, type = 'equal'
            if not pyproj.Proj(geopandas_series.crs).crs.is_geographic:
                geo_type = 'equal'
        except AttributeError:
            raise ValueError("Input GeoSeries does not seem to be valid")

        try:
            bounds = geopandas_series.bounds
        except ValueError as e:
            raise GeoGridError("GeoSeries has invalid bounds:\n {}".format(e))

        try:
            ll_corner = (buffer_accuracy * math.floor(
                (bounds.miny.min() + res / 2) / buffer_accuracy),
                         buffer_accuracy * math.floor(
                             (bounds.minx.min() + res / 2) / buffer_accuracy))
            ur_corner = (buffer_accuracy * math.ceil(
                (bounds.maxy.max() - res / 2) / buffer_accuracy),
                         buffer_accuracy * math.ceil(
                             (bounds.maxx.max() - res / 2) / buffer_accuracy))
        except (ValueError, ZeroDivisionError, FloatingPointError):
            raise ValueError(
                "Value of buffer accuracy ({}) is not valid".format(
                    buffer_accuracy))
        else:
            return GeoGrid(ll_corner, ur_corner, res, geo_type)
Esempio n. 12
0
def plot_geolayer(gl,
                  ax=None,
                  attribute=None,
                  layer_label: str = None,
                  layer_color=None,
                  labels: str = None,
                  marker=None,
                  **kwargs):
    """ Plot geo layer

    :param gl:
    :param ax:
    :param attribute: attribute of the geo layer
    :param layer_label:
    :param layer_color:
    :param labels:
    :param marker:
    :param kwargs:
    :return:
    """
    def patch_point():
        return mlines.Line2D([0], [0],
                             linestyle="none",
                             marker=marker,
                             color=layer_color,
                             label=layer_label)

    def patch_line():
        return mlines.Line2D([], [], color=layer_color, label=layer_label)

    def patch_polygon():
        return mpatches.Patch(color=layer_color, label=layer_label)

    # Marker type for point geometry
    if marker is None:
        marker = "o"
    handles = []

    if ax is not None:
        try:
            handles = ax.handles
        except AttributeError:
            pass
    else:
        ax = plt.gca()

    patch_options = {
        'Line': patch_line,
        'Polygon': patch_polygon,
        'Point': patch_point
    }

    if layer_color is not None:
        try:
            color_patch = patch_options[gl.geom_type]()
        except ValueError:
            raise ValueError("'{}' is not a valid color".format(layer_color))
        handles.append(color_patch)

    # Use geopandas plotting function
    if gl.geom_type == "Point":
        ax = gl._gpd_df.plot(ax=ax,
                             column=attribute,
                             color=layer_color,
                             marker=marker,
                             label=layer_label,
                             **kwargs)
    else:
        ax = gl._gpd_df.plot(ax=ax,
                             column=attribute,
                             color=layer_color,
                             label=layer_label,
                             **kwargs)

    if labels is not None:
        check_type(labels, str)
        # label_list = []
        new_layer = gl.shallow_copy()
        new_layer['label_point'] = new_layer.geometry.apply(
            lambda x: x.representative_point().coords[:])
        new_layer['label_point'] = [lb[0] for lb in new_layer["label_point"]]

        for _, row in new_layer.iterrows():
            xy = row["label_point"]
            s = row["name"]
            ax.annotate(s=s, xy=xy)
            # label_list.append(pyplot.text(xy[0], xy[1], s))

        # adjust_text(label_list, only_move={'text': 'xy'}, force_points=0.15)

    # This function return a new attribute to Axes object: handles
    ax.handles = handles

    return ax