Cypher(7) OSM to Neo4j

Working With .osm Road Network Data

Posting information

  • Posting Date : 29-07-2024
  • Last Edit : 29-07-2024
  • Writer : KWON Bongjae

Environment information

  • Hardware : MacBookPro8,2(late 11) / 8 × Intel® Core™ i7-2675QM CPU @ 2.20GHz / RAM 15.5 GiB / Mesa Intel® HD Graphics 3000
  • OS : Linux(openSUSE Tumbleweed 20240716)
  • DB : ArcadeDB, Neo4j Community 5.21.2 (using Cypher / openCypher)

    Posting detail

  • basic
import neo4j
import osmnx as ox

NEO4J_URI = "neo4j://localhost:"
NEO4J_USER = "neo4j"
NEO4J_PASSWORD = ""

driver = neo4j.GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))



  • Search OpenStreetMap and create a OSMNx graph
G = ox.graph_from_place("Busanjin-gu", network_type="drive")
fig, ax = ox.plot_graph(G)

gdf_nodes, gdf_relationships = ox.graph_to_gdfs(G)
gdf_nodes.reset_index(inplace=True)
gdf_relationships.reset_index(inplace=True)

gdf_nodes.plot(markersize=0.1)
gdf_nodes

gdf_relationships.plot(markersize=0.01, linewidth=0.5)
gdf_relationships



  • define Cypher queries to create constraints and indexes
constraint_query = "CREATE CONSTRAINT IF NOT EXISTS FOR (i:Intersection) REQUIRE i.osmid IS UNIQUE"

rel_index_query = "CREATE INDEX IF NOT EXISTS FOR ()-[r:ROAD_SEGMENT]-() ON r.osmids"

address_constraint_query = "CREATE CONSTRAINT IF NOT EXISTS FOR (a:Address) REQUIRE a.id IS UNIQUE"

point_index_query = "CREATE POINT INDEX IF NOT EXISTS FOR (i:Intersection) ON i.location"



  • Query to import our road network nodes GeoDataFrame
node_query = '''
	UNWIND $rows AS row
	WITH row WHERE row.osmid IS NOT NULL
	MERGE (i:Intersection {osmid: row.osmid})
		SET i.location =
			point({latitude: row.y, longitude: row.x }),
				i.ref = row.ref,
				i.highway = row.highway,
				i.street_count = toInteger(row.street_count)
	RETURN COUNT(*) as total

'''



  • Query to import our road network relationships GeoDataFrame
rels_query = '''
UNWIND $rows AS road
MATCH (u:Intersection {osmid: road.u})
MATCH (v:Intersection {osmid: road.v})
MERGE (u)-[r:ROAD_SEGMENT {osmid: road.osmid}]->(v)
SET r.oneway = road.oneway,
r.lanes = road.lanes,
r.ref = road.ref,
r.name = road.name,
r.highway = road.highway,
r.max_speed = road.maxspeed,
r.length = toFloat(road.length)
RETURN COUNT(*) AS total
'''
 
def create_constraints(tx):
results = tx.run(constraint_query)
results = tx.run(rel_index_query)
results = tx.run(address_constraint_query)
results = tx.run(point_index_query)

def insert_data(tx, query, rows, batch_size=10000):
total = 0
batch = 0

while batch * batch_size < len(rows):
results = tx.run(query, parameters = {'rows': rows[batch*batch_size:(batch+1)*batch_size].to_dict('records')}).data()

print(results)
total += results[0]['total']
batch += 1

with driver.session() as session:
session.execute_write(create_constraints)
session.execute_write(insert_data, node_query, gdf_nodes.drop(columns=['geometry'])) #FIXME: handle



  • Run our relationships GeoDataFrame import
with driver.session() as session:

session.execute_write(insert_data, rels_query, gdf_relationships.drop(columns=['geometry'])) #FIXME: handle geometry