Created
March 16, 2025 10:30
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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