As I continued to explore yesterday, I discovered the networkx
Python library, in particular its read_shp()
and write_shp()
functions.
import networkx
G = networkx.read_shp('linesfile.shp')
networkx.write_shp(G, './')
Got me a lines file with the original attributes and a points file with the nodes. I'm actually thrilled at the result, though there isn't a field for the node ID. Hopefully I can do this with just a spatial join.
The Solution
Well, I did it. Here's a reduced form of the python code I wrote. The full code with detailed comments is available in this gist
import networkx as nx
import lxml.etree as ET
G = nx.read_shp("fafnetworkLCC.shp")
G = nx.convert_node_labels_to_integers(G, first_label=0,
label_attribute = "coord")
# create element tree structure
network = ET.Element("network",
attrib={'name':"MATSim network exported from FAF shapefile."})
nodes = ET.SubElement(network, "nodes")
for i in range(len(G)):
ET.SubElement(nodes, "node",
attrib={'id': str(G.nodes()[i]),
'x':str(G.node[i]['coord'][0]),
'y':str(G.node[i]['coord'][1]),
'type':"2"})
links = ET.SubElement(network, "links",
attrib={'capperiod': "01:00:00",
'effectivecellsize': "7.5",
'effectivelanewidth': "3.75"})
length = nx.get_edge_attributes(G, "MILES")
idvar = nx.get_edge_attributes(G, "ID")
for i in range(len(G.edges())):
startnode = G.edges()[i][0]
endnode = G.edges()[i][1]
ET.SubElement(links, "link", attrib={
'id': str(idvar[(startnode, endnode)]),
'from': str(startnode),
'to': str(endnode),
'capacity': str(6000),
'modes': "car",
'oneway': str(1),
'type': str(10),
'length': str(length[(startnode, endnode)] * 1609.34)}) # convert to meters
tree = ET.ElementTree(network)
with open('network.xml', 'w') as f:
f.write("""<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE network SYSTEM "http://www.matsim.org/files/dtd/network_v1.dtd">
""")
tree.write(f, pretty_print = True)
Best Answer
If you are comfortable with Python, you could use ElementTree to parse the XML and pyshp to create the shapefile.
Here is something you can start with: