The following Python script can stream through the data, requiring no memory. It converted my 3.1G OSM file from XML into GeoJSON in 81 seconds.
In my case, I had to convert a bunch of way
s into either LineString's or Polygons, so I used the existence of highway=*
to differentiate between the two. However, you may need to tweak the logic to fit your use case.
import json
import sys
sys.stdout.write('{"type":"FeatureCollection","features":[')
with open(sys.argv[1]) as f:
# Skip irrelevant metadata lines
while "<way" not in next(f):
pass
coords = "" # Coordinates of each geometry
is_highway = False # Tracked for each way
is_first_way = True # Tracked for printing commas
for line in f:
# If we close a way, flush it and reset
if "</way" in line:
coords = "[" + coords[:-1] + "]"
if not is_highway:
coords = "[" + coords + "]"
sys.stdout.write(
[",", ""][is_first_way]
+ '{"type":"Feature","properties":null,"geometry":{"type":'
+ ['"Polygon"', '"LineString"'][is_highway]
+ ',"coordinates":'
+ coords
+ "}}"
)
is_first_way = False
coords = ""
is_highway = False
# Add coordinates from node
elif "<nd" in line:
split = line.split('"')
coords += "[" + split[-2] + "," + split[-4] + "],"
# Check if highway, if not already
elif not is_highway and '<tag k="highway"' in line:
is_highway = True
sys.stdout.write("]}")
Sample Input
My input OSM looked like…
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="Overpass API 0.7.56.9 76e5016d">
<note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note>
<meta osm_base="2021-04-05T15:06:01Z"/>
<bounds minlat="33.4393033" minlon="-86.9466304" maxlat="33.6223902" maxlon="-86.6768410"/>
<way id="926253889">
<bounds minlat="33.4951768" minlon="-86.8805626" maxlat="33.4956986" maxlon="-86.8801360"/>
<nd ref="8595476500" lat="33.4956986" lon="-86.8805626"/>
<nd ref="8595476501" lat="33.4951768" lon="-86.8801360"/>
<tag k="highway" v="service"/>
</way>
...
</osm>
And the output is…
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": null,
"geometry": {
"type": "LineString",
"coordinates": [
[
-86.8805626,
33.4956986
],
[
-86.880136,
33.4951768
]
]
}
},
...
]
}