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
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