Mercurial > repos > shellac > sam_consensus_v3
diff env/lib/python3.9/site-packages/networkx/readwrite/nx_shp.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/env/lib/python3.9/site-packages/networkx/readwrite/nx_shp.py Mon Mar 22 18:12:50 2021 +0000 @@ -0,0 +1,316 @@ +""" +********* +Shapefile +********* + +Generates a networkx.DiGraph from point and line shapefiles. + +"The Esri Shapefile or simply a shapefile is a popular geospatial vector +data format for geographic information systems software. It is developed +and regulated by Esri as a (mostly) open specification for data +interoperability among Esri and other software products." +See https://en.wikipedia.org/wiki/Shapefile for additional information. +""" +import networkx as nx + +__all__ = ["read_shp", "write_shp"] + + +def read_shp(path, simplify=True, geom_attrs=True, strict=True): + """Generates a networkx.DiGraph from shapefiles. Point geometries are + translated into nodes, lines into edges. Coordinate tuples are used as + keys. Attributes are preserved, line geometries are simplified into start + and end coordinates. Accepts a single shapefile or directory of many + shapefiles. + + "The Esri Shapefile or simply a shapefile is a popular geospatial vector + data format for geographic information systems software [1]_." + + Parameters + ---------- + path : file or string + File, directory, or filename to read. + + simplify: bool + If True, simplify line geometries to start and end coordinates. + If False, and line feature geometry has multiple segments, the + non-geometric attributes for that feature will be repeated for each + edge comprising that feature. + + geom_attrs: bool + If True, include the Wkb, Wkt and Json geometry attributes with + each edge. + + NOTE: if these attributes are available, write_shp will use them + to write the geometry. If nodes store the underlying coordinates for + the edge geometry as well (as they do when they are read via + this method) and they change, your geomety will be out of sync. + + strict: bool + If True, raise NetworkXError when feature geometry is missing or + GeometryType is not supported. + If False, silently ignore missing or unsupported geometry in features. + + Returns + ------- + G : NetworkX graph + + Raises + ------ + ImportError + If ogr module is not available. + + RuntimeError + If file cannot be open or read. + + NetworkXError + If strict=True and feature is missing geometry or GeometryType is + not supported. + + Examples + -------- + >>> G = nx.read_shp("test.shp") # doctest: +SKIP + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Shapefile + """ + try: + from osgeo import ogr + except ImportError as e: + raise ImportError("read_shp requires OGR: http://www.gdal.org/") from e + + if not isinstance(path, str): + return + + net = nx.DiGraph() + shp = ogr.Open(path) + if shp is None: + raise RuntimeError(f"Unable to open {path}") + for lyr in shp: + fields = [x.GetName() for x in lyr.schema] + for f in lyr: + g = f.geometry() + if g is None: + if strict: + raise nx.NetworkXError("Bad data: feature missing geometry") + else: + continue + flddata = [f.GetField(f.GetFieldIndex(x)) for x in fields] + attributes = dict(zip(fields, flddata)) + attributes["ShpName"] = lyr.GetName() + # Note: Using layer level geometry type + if g.GetGeometryType() == ogr.wkbPoint: + net.add_node((g.GetPoint_2D(0)), **attributes) + elif g.GetGeometryType() in (ogr.wkbLineString, ogr.wkbMultiLineString): + for edge in edges_from_line(g, attributes, simplify, geom_attrs): + e1, e2, attr = edge + net.add_edge(e1, e2) + net[e1][e2].update(attr) + else: + if strict: + raise nx.NetworkXError( + "GeometryType {} not supported".format(g.GetGeometryType()) + ) + + return net + + +def edges_from_line(geom, attrs, simplify=True, geom_attrs=True): + """ + Generate edges for each line in geom + Written as a helper for read_shp + + Parameters + ---------- + + geom: ogr line geometry + To be converted into an edge or edges + + attrs: dict + Attributes to be associated with all geoms + + simplify: bool + If True, simplify the line as in read_shp + + geom_attrs: bool + If True, add geom attributes to edge as in read_shp + + + Returns + ------- + edges: generator of edges + each edge is a tuple of form + (node1_coord, node2_coord, attribute_dict) + suitable for expanding into a networkx Graph add_edge call + """ + try: + from osgeo import ogr + except ImportError as e: + raise ImportError( + "edges_from_line requires OGR: " "http://www.gdal.org/" + ) from e + + if geom.GetGeometryType() == ogr.wkbLineString: + if simplify: + edge_attrs = attrs.copy() + last = geom.GetPointCount() - 1 + if geom_attrs: + edge_attrs["Wkb"] = geom.ExportToWkb() + edge_attrs["Wkt"] = geom.ExportToWkt() + edge_attrs["Json"] = geom.ExportToJson() + yield (geom.GetPoint_2D(0), geom.GetPoint_2D(last), edge_attrs) + else: + for i in range(0, geom.GetPointCount() - 1): + pt1 = geom.GetPoint_2D(i) + pt2 = geom.GetPoint_2D(i + 1) + edge_attrs = attrs.copy() + if geom_attrs: + segment = ogr.Geometry(ogr.wkbLineString) + segment.AddPoint_2D(pt1[0], pt1[1]) + segment.AddPoint_2D(pt2[0], pt2[1]) + edge_attrs["Wkb"] = segment.ExportToWkb() + edge_attrs["Wkt"] = segment.ExportToWkt() + edge_attrs["Json"] = segment.ExportToJson() + del segment + yield (pt1, pt2, edge_attrs) + + elif geom.GetGeometryType() == ogr.wkbMultiLineString: + for i in range(geom.GetGeometryCount()): + geom_i = geom.GetGeometryRef(i) + yield from edges_from_line(geom_i, attrs, simplify, geom_attrs) + + +def write_shp(G, outdir): + """Writes a networkx.DiGraph to two shapefiles, edges and nodes. + Nodes and edges are expected to have a Well Known Binary (Wkb) or + Well Known Text (Wkt) key in order to generate geometries. Also + acceptable are nodes with a numeric tuple key (x,y). + + "The Esri Shapefile or simply a shapefile is a popular geospatial vector + data format for geographic information systems software [1]_." + + Parameters + ---------- + outdir : directory path + Output directory for the two shapefiles. + + Returns + ------- + None + + Examples + -------- + nx.write_shp(digraph, '/shapefiles') # doctest +SKIP + + References + ---------- + .. [1] https://en.wikipedia.org/wiki/Shapefile + """ + try: + from osgeo import ogr + except ImportError as e: + raise ImportError("write_shp requires OGR: http://www.gdal.org/") from e + # easier to debug in python if ogr throws exceptions + ogr.UseExceptions() + + def netgeometry(key, data): + if "Wkb" in data: + geom = ogr.CreateGeometryFromWkb(data["Wkb"]) + elif "Wkt" in data: + geom = ogr.CreateGeometryFromWkt(data["Wkt"]) + elif type(key[0]).__name__ == "tuple": # edge keys are packed tuples + geom = ogr.Geometry(ogr.wkbLineString) + _from, _to = key[0], key[1] + try: + geom.SetPoint(0, *_from) + geom.SetPoint(1, *_to) + except TypeError: + # assume user used tuple of int and choked ogr + _ffrom = [float(x) for x in _from] + _fto = [float(x) for x in _to] + geom.SetPoint(0, *_ffrom) + geom.SetPoint(1, *_fto) + else: + geom = ogr.Geometry(ogr.wkbPoint) + try: + geom.SetPoint(0, *key) + except TypeError: + # assume user used tuple of int and choked ogr + fkey = [float(x) for x in key] + geom.SetPoint(0, *fkey) + + return geom + + # Create_feature with new optional attributes arg (should be dict type) + def create_feature(geometry, lyr, attributes=None): + feature = ogr.Feature(lyr.GetLayerDefn()) + feature.SetGeometry(g) + if attributes is not None: + # Loop through attributes, assigning data to each field + for field, data in attributes.items(): + feature.SetField(field, data) + lyr.CreateFeature(feature) + feature.Destroy() + + # Conversion dict between python and ogr types + OGRTypes = {int: ogr.OFTInteger, str: ogr.OFTString, float: ogr.OFTReal} + + # Check/add fields from attribute data to Shapefile layers + def add_fields_to_layer(key, value, fields, layer): + # Field not in previous edges so add to dict + if type(value) in OGRTypes: + fields[key] = OGRTypes[type(value)] + else: + # Data type not supported, default to string (char 80) + fields[key] = ogr.OFTString + # Create the new field + newfield = ogr.FieldDefn(key, fields[key]) + layer.CreateField(newfield) + + drv = ogr.GetDriverByName("ESRI Shapefile") + shpdir = drv.CreateDataSource(outdir) + # delete pre-existing output first otherwise ogr chokes + try: + shpdir.DeleteLayer("nodes") + except: + pass + nodes = shpdir.CreateLayer("nodes", None, ogr.wkbPoint) + + # Storage for node field names and their data types + node_fields = {} + + def create_attributes(data, fields, layer): + attributes = {} # storage for attribute data (indexed by field names) + for key, value in data.items(): + # Reject spatial data not required for attribute table + if key != "Json" and key != "Wkt" and key != "Wkb" and key != "ShpName": + # Check/add field and data type to fields dict + if key not in fields: + add_fields_to_layer(key, value, fields, layer) + # Store the data from new field to dict for CreateLayer() + attributes[key] = value + return attributes, layer + + for n in G: + data = G.nodes[n] + g = netgeometry(n, data) + attributes, nodes = create_attributes(data, node_fields, nodes) + create_feature(g, nodes, attributes) + + try: + shpdir.DeleteLayer("edges") + except: + pass + edges = shpdir.CreateLayer("edges", None, ogr.wkbLineString) + + # New edge attribute write support merged into edge loop + edge_fields = {} # storage for field names and their data types + + for e in G.edges(data=True): + data = G.get_edge_data(*e) + g = netgeometry(e, data) + attributes, edges = create_attributes(e[2], edge_fields, edges) + create_feature(g, edges, attributes) + + nodes, edges = None, None