Exemple #1
0
    def ogr2shapely(ogr_obj: Union[ogr.Feature, ogr.Geometry],
                    transform: osr.CoordinateTransformation = None,
                    flatten_to_2D=True) -> BaseGeometry:
        """Retrieve the Shapely object from an ogr feature

        Args:
            ogr ([type]): [description]
            ogr_feature (ogr.Feature, transform, optional): [description]. Defaults to None)->(BaseGeometry.

        Returns:
            [type]: [description]
        """
        if type(ogr_obj) is ogr.Feature:
            geom = ogr_obj.GetGeometryRef()
        elif type(ogr_obj) is ogr.Geometry:
            geom = ogr_obj
        else:
            raise VectorBaseException(
                'Could not detect type of object: {}. Must be of type ogr.Feature or ogr.Geometry'
                .format(type(ogr_obj)))

        # Do the flatten first to speed up the potential transform
        if flatten_to_2D is True and (geom.IsMeasured() > 0
                                      or geom.Is3D() > 0):
            geom.FlattenTo2D()

        if transform:
            geom.Transform(transform)

        shapely_obj = wkbload(geom.ExportToWkb())
        return shapely_obj
Exemple #2
0
    def shapely2ogr(shapely_object: ogr.Feature,
                    transform: osr.CoordinateTransformation = None,
                    flatten_to_2D=True) -> ogr.Geometry:
        """Get the OGR Geometry object from the shapely object

        Args:
            ogr ([type]): [description]
            ogr_feature (ogr.Feature, transform, optional): [description]. Defaults to None)->(BaseGeometry.

        Returns:
            [type]: [description]
        """

        new_obj = shapely_object

        if flatten_to_2D is True and (shapely_object.has_z):
            # Shapely hack for flattening
            new_obj = wkbload(wkbdumps(new_obj, output_dimension=2))

        geom = ogr.CreateGeometryFromWkb(new_obj.wkb)

        if transform:
            geom.Transform(transform)

        return geom
def load_geometries(feature_class, id_field, epsg=None):
    log = Logger('Shapefile')
    # Get the input network
    driver = ogr.GetDriverByName('ESRI Shapefile')
    dataset = driver.Open(feature_class, 0)
    layer = dataset.GetLayer()
    in_spatial_ref = layer.GetSpatialRef()

    # Determine the transformation if user provides an EPSG
    transform = None
    if epsg:
        out_spatial_ref, transform = get_transform_from_epsg(
            in_spatial_ref, epsg)

    features = {}

    progbar = ProgressBar(layer.GetFeatureCount(), 50, "Loading features")
    counter = 0
    for inFeature in layer:
        counter += 1
        progbar.update(counter)

        reach = inFeature.GetField(id_field)
        geom = inFeature.GetGeometryRef()

        # Optional coordinate transformation
        if transform:
            geom.Transform(transform)

        new_geom = wkbload(geom.ExportToWkb())
        geo_type = new_geom.GetGeometryType()

        if new_geom.is_empty:
            progbar.erase()  # get around the progressbar
            log.warning(
                'Empty feature with FID={} cannot be unioned and will be ignored'
                .format(inFeature.GetFID()))
        elif not new_geom.is_valid:
            progbar.erase()  # get around the progressbar
            log.warning(
                'Invalid feature with FID={} cannot be unioned and will be ignored'
                .format(inFeature.GetFID()))
        # Filter out zero-length lines
        elif geo_type in LINE_TYPES and new_geom.Length() == 0:
            progbar.erase()  # get around the progressbar
            log.warning('Zero Length for feature with FID={}'.format(
                inFeature.GetFID()))
        # Filter out zero-area polys
        elif geo_type in POLY_TYPES and new_geom.Area() == 0:
            progbar.erase()  # get around the progressbar
            log.warning('Zero Area for feature with FID={}'.format(
                inFeature.GetFID()))
        else:
            features[reach] = new_geom

    progbar.finish()
    dataset = None
    return features
def get_geometry_union(inpath, epsg, attribute_filter=None):
    """
    TODO: Remove this method and replace all references to the get_geometry_unary_union method below
    Load all features from a ShapeFile and union them together into a single geometry
    :param inpath: Path to a ShapeFile
    :param epsg: Desired output spatial reference
    :return: Single Shapely geometry of all unioned features
    """

    log = Logger('Shapefile')

    driver = ogr.GetDriverByName("ESRI Shapefile")
    data_source = driver.Open(inpath, 0)
    layer = data_source.GetLayer()
    in_spatial_ref = layer.GetSpatialRef()

    if attribute_filter:
        layer.SetAttributeFilter(attribute_filter)

    _out_spatial_ref, transform = get_transform_from_epsg(in_spatial_ref, epsg)

    geom = None
    progbar = ProgressBar(layer.GetFeatureCount(), 50, "Unioning features")
    counter = 0
    for feature in layer:
        counter += 1
        progbar.update(counter)

        new_geom = feature.GetGeometryRef()

        if new_geom is None:
            progbar.erase()  # get around the progressbar
            log.warning('Feature with FID={} has no geometry. Skipping'.format(
                feature.GetFID()))
            continue

        new_geom.Transform(transform)
        new_shape = wkbload(new_geom.ExportToWkb())
        try:
            geom = geom.union(new_shape) if geom else new_shape
        except Exception as e:
            progbar.erase()  # get around the progressbar
            log.warning(
                'Union failed for shape with FID={} and will be ignored'.
                format(feature.GetFID()))

    progbar.finish()
    data_source = None

    return geom
def network_statistics(label, shapefile_path):

    log = Logger('shapefile')
    log.info('Network ShapeFile Summary: {}'.format(shapefile_path))

    driver = ogr.GetDriverByName("ESRI Shapefile")
    dataset = driver.Open(shapefile_path, 1)
    layer = dataset.GetLayer()

    results = {}
    total_length = 0.0
    min_length = None
    max_length = None
    invalid_features = 0
    no_geometry = 0

    # Delete output column from network ShapeFile if it exists and then recreate it
    networkDef = layer.GetLayerDefn()
    for fieldidx in range(0, networkDef.GetFieldCount()):
        results[networkDef.GetFieldDefn(fieldidx).GetName()] = 0

    progbar = ProgressBar(layer.GetFeatureCount(), 50, "Calculating Stats")
    counter = 0
    for feature in layer:
        counter += 1
        progbar.update(counter)

        geom = feature.GetGeometryRef()

        if geom is None:
            no_geometry += 1
            continue

        shapely_obj = wkbload(geom.ExportToWkb())
        length = shapely_obj.length

        if shapely_obj.is_empty or shapely_obj.is_valid is False:
            invalid_features += 1

        total_length += length
        min_length = length if not min_length or min_length > length else min_length
        max_length = length if not max_length or max_length < length else max_length

        for fieldidx in range(0, networkDef.GetFieldCount()):
            field = networkDef.GetFieldDefn(fieldidx).GetName()
            if field not in results:
                results[field] = 0

            results[field] += 0 if feature.GetField(field) else 1

    progbar.finish()

    features = layer.GetFeatureCount()
    results['Feature Count'] = features
    results['Invalid Features'] = invalid_features
    results['Features without geometry'] = no_geometry
    results['Min Length'] = min_length
    results['Max Length'] = max_length
    results['Avg Length'] = (
        total_length / features) if features > 0 and total_length != 0 else 0.0
    results['Total Length'] = total_length

    for key, value in results.items():
        if value > 0:
            log.info('{}, {} with {:,} NULL values'.format(label, key, value))

    dataset = None
    return results
 def unionize(wkb_lst):
     return unary_union([wkbload(g) for g in wkb_lst]).wkb
def get_geometry_unary_union_from_wkt(inpath, to_sr_wkt):
    """
    Load all features from a ShapeFile and union them together into a single geometry
    :param inpath: Path to a ShapeFile
    :param epsg: Desired output spatial reference
    :return: Single Shapely geometry of all unioned features
    """

    log = Logger('Unary Union')

    driver = ogr.GetDriverByName("ESRI Shapefile")
    data_source = driver.Open(inpath, 0)
    layer = data_source.GetLayer()
    in_spatial_ref = layer.GetSpatialRef()

    out_spatial_ref, transform = get_transform_from_wkt(
        in_spatial_ref, to_sr_wkt)

    fcount = layer.GetFeatureCount()
    progbar = ProgressBar(fcount, 50, "Unary Unioning features")
    counter = 0

    def unionize(wkb_lst):
        return unary_union([wkbload(g) for g in wkb_lst]).wkb

    geom_list = []
    for feature in layer:
        counter += 1
        progbar.update(counter)
        new_geom = feature.GetGeometryRef()
        geo_type = new_geom.GetGeometryType()

        # We can't union non-valid shapes but sometimes a buffer by 0 can help
        if not new_geom.IsValid():
            progbar.erase()  # get around the progressbar
            log.warning(
                'Invalid shape with FID={} trying the Buffer0 technique...'.
                format(feature.GetFID()))
            try:
                new_geom = new_geom.Buffer(0)
                if not new_geom.IsValid():
                    progbar.erase()  # get around the progressbar
                    log.warning('   Still invalid. Skipping this geometry')
                    continue
            except Exception as e:
                progbar.erase()  # get around the progressbar
                log.warning(
                    'Exception raised during buffer 0 technique. skipping this file'
                )
                continue

        if new_geom is None:
            progbar.erase()  # get around the progressbar
            log.warning('Feature with FID={} has no geoemtry. Skipping'.format(
                feature.GetFID()))
        # Filter out zero-length lines
        elif geo_type in LINE_TYPES and new_geom.Length() == 0:
            progbar.erase()  # get around the progressbar
            log.warning('Zero Length for shape with FID={}'.format(
                feature.GetFID()))
        # Filter out zero-area polys
        elif geo_type in POLY_TYPES and new_geom.Area() == 0:
            progbar.erase()  # get around the progressbar
            log.warning('Zero Area for shape with FID={}'.format(
                feature.GetFID()))
        else:
            new_geom.Transform(transform)
            geom_list.append(new_geom.ExportToWkb())

            # IF we get past a certain size then run the union
            if len(geom_list) >= 500:
                geom_list = [unionize(geom_list)]
        new_geom = None

    log.debug('finished iterating with list of size: {}'.format(
        len(geom_list)))
    progbar.finish()

    if len(geom_list) > 1:
        log.debug('Starting final union of geom_list of size: {}'.format(
            len(geom_list)))
        # Do a final union to clean up anything that might still be in the list
        geom_union = wkbload(unionize(geom_list))
    elif len(geom_list) == 0:
        log.warning('No geometry found to union')
        return None
    else:
        log.debug('FINAL Unioning geom_list of size {}'.format(len(geom_list)))
        geom_union = wkbload(geom_list[0])
        log.debug('   done')

    print_geom_size(log, geom_union)
    log.debug('Complete')
    data_source = None
    return geom_union
def _rough_convert_metres_to_dataset_units(in_spatial_ref, extent, distance):
    """DO NOT USE THIS FOR ACCURATE DISTANCES. IT'S GOOD FOR A QUICK CALCULATION
    WHEN DISTANCE PRECISION ISN'T THAT IMPORTANT

    Arguments:
        shapefile_path {[type]} -- [description]
        distance {[type]} -- [description]

    Returns:
        [type] -- [description]
    """
    log = Logger('_rough_convert_metres_to_dataset_units')
    # If the ShapeFile uses a projected coordinate system in meters then simply return the distance.
    # If it's projected but in some other units then throw an exception.
    # If it's in degrees then continue with the code below to convert it to metres.
    if in_spatial_ref.IsProjected() == 1:
        if in_spatial_ref.GetAttrValue('unit').lower() in [
                'meter', 'metre', 'm'
        ]:
            return distance
        else:
            raise Exception(
                'Unhandled projected coordinate system linear units: {}'.
                format(in_spatial_ref.GetAttrValue('unit')))

    # Get the centroid of the Shapefile spatial extent
    extent_ring = ogr.Geometry(ogr.wkbLinearRing)
    extent_ring.AddPoint(extent[0], extent[2])
    extent_ring.AddPoint(extent[1], extent[2])
    extent_ring.AddPoint(extent[1], extent[3])
    extent_ring.AddPoint(extent[0], extent[3])
    extent_ring.AddPoint(extent[0], extent[2])
    extent_poly = ogr.Geometry(ogr.wkbPolygon)
    extent_poly.AddGeometry(extent_ring)
    extent_centroid = extent_poly.Centroid()

    # Go diagonally on the extent rectangle
    pt1_orig = Point(extent[0], extent[2])
    pt2_orig = Point(extent[1], extent[3])
    orig_dist = pt1_orig.distance(pt2_orig)

    # Determine the UTM zone by locating the centroid of the shapefile extent
    # Then get the transformation required to convert to the Shapefile to this UTM zone
    utm_epsg = get_utm_zone_epsg(extent_centroid.GetX())
    in_spatial_ref.SetAxisMappingStrategy(osr.OAMS_TRADITIONAL_GIS_ORDER)

    out_spatial_ref = osr.SpatialReference()
    out_spatial_ref.ImportFromEPSG(int(utm_epsg))
    out_spatial_ref.SetAxisMappingStrategy(osr.OAMS_TRADITIONAL_GIS_ORDER)

    log.debug(
        'Original spatial reference is : \n       {0} (AxisMappingStrategy:{1})'
        .format(*get_srs_debug(in_spatial_ref)))
    log.debug(
        'Transform spatial reference is : \n       {0} (AxisMappingStrategy:{1})'
        .format(*get_srs_debug(out_spatial_ref)))

    transformFwd = osr.CoordinateTransformation(in_spatial_ref,
                                                out_spatial_ref)

    pt1_ogr = ogr.CreateGeometryFromWkb(pt1_orig.wkb)
    pt2_ogr = ogr.CreateGeometryFromWkb(pt2_orig.wkb)
    pt1_ogr.Transform(transformFwd)
    pt2_ogr.Transform(transformFwd)

    pt1_proj = wkbload(pt1_ogr.ExportToWkb())
    pt2_proj = wkbload(pt2_ogr.ExportToWkb())

    proj_dist = pt1_proj.distance(pt2_proj)

    output_distance = (orig_dist / proj_dist) * distance

    log.info('{}m distance converts to {:.10f} using UTM EPSG {}'.format(
        distance, output_distance, utm_epsg))

    if output_distance > 360:
        raise Exception(
            'Projection Error: \'{:,}\' is larger than the maximum allowed value'
            .format(output_distance))

    return output_distance