Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
316
venv/Lib/site-packages/networkx/readwrite/nx_shp.py
Normal file
316
venv/Lib/site-packages/networkx/readwrite/nx_shp.py
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue