Skip to content

Instantly share code, notes, and snippets.

@monperrus
Created March 16, 2025 10:30
Show Gist options
  • Select an option

  • Save monperrus/842cb622894bca6974f9e5db2d6a4ac9 to your computer and use it in GitHub Desktop.

Select an option

Save monperrus/842cb622894bca6974f9e5db2d6a4ac9 to your computer and use it in GitHub Desktop.
write a transformer xml in osm format (openstreetmap) to svg which only keeps map lines
import xml.etree.ElementTree as ET
from typing import List, Dict, Tuple
import sys
class OSMtoSVGConverter:
def __init__(self):
self.nodes: Dict[str, Tuple] = {}
self.ways: List[List[Tuple]] = []
self.min_lat = float('inf')
self.max_lat = float('-inf')
self.min_lon = float('inf')
self.max_lon = float('-inf')
def parse_osm(self, osm_file: str):
"""Parse OSM XML file and extract nodes and ways"""
tree = ET.parse(osm_file)
root = tree.getroot()
# First pass: collect all nodes
for node in root.findall('.//node'):
node_id = node.get('id')
lat = float(node.get('lat'))
lon = float(node.get('lon'))
# Update bounds
self.min_lat = min(self.min_lat, lat)
self.max_lat = max(self.max_lat, lat)
self.min_lon = min(self.min_lon, lon)
self.max_lon = max(self.max_lon, lon)
self.nodes[node_id] = (lat, lon)
# Second pass: collect all ways
for way in root.findall('.//way'):
way_nodes = []
for nd in way.findall('nd'):
node_id = nd.get('ref')
if node_id in self.nodes:
way_nodes.append(self.nodes[node_id])
if way_nodes:
self.ways.append(way_nodes)
def convert_to_svg(self, width: int = 800, height: int = 600) -> str:
"""Convert parsed data to SVG format"""
# Calculate scaling factors
lat_range = self.max_lat - self.min_lat
lon_range = self.max_lon - self.min_lon
scale_x = width / lon_range if lon_range > 0 else 1
scale_y = height / lat_range if lat_range > 0 else 1
# Start SVG document
svg = f'''<?xml version="1.0" encoding="UTF-8"?>
<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
'''
# Add ways as paths
for way in self.ways:
if not way:
continue
path = "M "
first = True
for lat, lon in way:
# Convert coordinates to SVG space
x = (lon - self.min_lon) * scale_x
y = height - (lat - self.min_lat) * scale_y # Flip Y axis
if first:
path += f"{x:.1f},{y:.1f} "
first = False
else:
path += f"L {x:.1f},{y:.1f} "
svg += f' <path d="{path}" fill="none" stroke="black" stroke-width="1"/>\n'
# Close SVG document
svg += "</svg>"
return svg
def convert_file(self, input_file: str, output_file: str, width: int = 800, height: int =
600):
"""Convert OSM file to SVG file"""
self.parse_osm(input_file)
svg_content = self.convert_to_svg(width, height)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(svg_content)
def main():
if len(sys.argv) != 3:
print("Usage: python osm_to_svg.py input.osm output.svg")
sys.exit(1)
converter = OSMtoSVGConverter()
converter.convert_file(sys.argv[1], sys.argv[2])
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment