基于Python的Bing交通流地图转带属性Shapefile技术问询
Absolutely, this is totally feasible! You can extract those colored traffic flow lines, assign the corresponding congestion attributes, and convert them into a Shapefile that works seamlessly in QGIS. Below is a detailed breakdown of the approach, required tools, and a Python 2.7-compatible example code.
Required Python Modules
Since you're using Python 2.7, you'll need versions of these libraries that support it. Install them with this pip command:
pip install pillow numpy opencv-python==4.2.0.32 pyshp shapely==1.6.4.post2
Here's what each library does:
- Pillow: Load and manipulate the downloaded PNG images (you're already using this!)
- NumPy: Handle pixel data arrays for efficient color processing
- OpenCV: Perform color thresholding, noise cleanup, and contour detection to isolate traffic lines
- PyShp: Create Shapefiles directly from Python without needing heavy GIS tools like GDAL
- Shapely: Convert raw contour coordinates into valid geometric line features
Core Workflow
The process breaks down into 5 key steps:
- Convert the image to HSV color space: RGB is sensitive to lighting variations, but HSV lets you target specific color ranges more reliably.
- Color thresholding: Define HSV ranges for green, yellow, orange, and red to create masks that isolate each traffic line color.
- Clean up masks: Use morphological operations (like "opening") to remove small noise pixels that aren't part of actual traffic lines.
- Detect contours: Extract the shape of each traffic line from the cleaned masks.
- Build the Shapefile: Convert each contour into a LineString feature, assign the corresponding congestion attribute, and save everything to a Shapefile.
Example Code
Here's a Python 2.7 script that implements this workflow. Note: You'll need to adjust the HSV color ranges slightly if your Bing maps have subtle color variations.
from PIL import Image import numpy as np import cv2 import shapefile as shp from shapely.geometry import LineString import pyproj # Define color ranges (HSV) and corresponding attributes traffic_colors = [ {"name": "无交通", "value": 1, "lower": np.array([40, 40, 40]), "upper": np.array([80, 255, 255])}, # Green {"name": "轻度", "value": 2, "lower": np.array([20, 100, 100]), "upper": np.array([30, 255, 255])}, # Yellow {"name": "中度", "value": 3, "lower": np.array([10, 100, 100]), "upper": np.array([20, 255, 255])}, # Orange {"name": "重度", "value": 4, "lower": np.array([0, 100, 100]), "upper": np.array([10, 255, 255])} # Red ] # Bing Map uses Web Mercator (EPSG:3857) - define projection for Shapefile web_mercator = pyproj.Proj(init='epsg:3857') wgs84 = pyproj.Proj(init='epsg:4326') # Image metadata (from your Bing URL: center (45.8077453,15.963863), zoom 17, size 500x500) # Calculate pixel-to-coordinate conversion (simplified for this example) center_lat, center_lon = 45.8077453, 15.963863 zoom = 17 meters_per_pixel = 156543.03392 * np.cos(center_lat * np.pi / 180) / (2 ** zoom) image_width, image_height = 500, 500 # Convert center lat/lon to Web Mercator center_x, center_y = pyproj.transform(wgs84, web_mercator, center_lon, center_lat) # Initialize Shapefile writer w = shp.Writer(shp.POLYLINE) # Using POLYLINE for line features w.field("CONGESTION", "C", size=20) # Congestion name (e.g., "重度") w.field("LEVEL", "N") # Numeric value (1-4) # Load your downloaded image (replace with your file path) image_path = r'C:\Users\slika1.png' img = cv2.imread(image_path) hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) for color in traffic_colors: # Create mask for current color mask = cv2.inRange(hsv, color["lower"], color["upper"]) # Clean up mask: remove small noise with opening operation kernel = np.ones((3,3), np.uint8) mask_clean = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2) # Detect contours contours, _ = cv2.findContours(mask_clean.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: # Skip very small contours (noise) if cv2.contourArea(cnt) < 10: continue # Convert contour pixels to Web Mercator coordinates coords = [] for point in cnt: px, py = point[0] # Calculate offset from center in meters dx = (px - image_width/2) * meters_per_pixel dy = (image_height/2 - py) * meters_per_pixel # Y is inverted in image coordinates # Convert to Web Mercator x = center_x + dx y = center_y + dy coords.append((x, y)) # Create LineString and simplify if needed line = LineString(coords) simplified_line = line.simplify(1.0) # Simplify to reduce vertex count # Write to Shapefile w.line([list(simplified_line.coords)]) w.record(color["name"], color["value"]) # Save Shapefile w.save(r'C:\Users\traffic_flow') # Create PRJ file for projection info (required for QGIS) prj_content = 'PROJCS["WGS_1984_Web_Mercator_Auxiliary_Sphere",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator_Auxiliary_Sphere"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",0.0],PARAMETER["Standard_Parallel_1",0.0],PARAMETER["Auxiliary_Sphere_Type",0.0],UNIT["Meter",1.0]]' with open(r'C:\Users\traffic_flow.prj', 'w') as f: f.write(prj_content) print("Shapefile created successfully!")
Key Notes for QGIS Compatibility & Accuracy
- Projection: The script creates a PRJ file for Web Mercator (EPSG:3857), which matches Bing Maps. When you load the Shapefile into QGIS, it should align perfectly with the original map.
- Coordinate Precision: The pixel-to-coordinate conversion here is a simplified version. For absolute accuracy, you should implement the official Bing Maps tile coordinate math to calculate exact geographic positions for each pixel.
- Color Threshold Tuning: If the script isn't capturing all traffic lines or is picking up background noise, adjust the HSV lower/upper ranges. You can use an HSV color picker tool to get exact values from your images.
- Contour Simplification: The
simplify()method reduces the number of vertices in each line, which makes the Shapefile smaller and easier to work with in QGIS. Adjust the tolerance value (1.0 meters here) as needed.
内容的提问来源于stack exchange,提问作者spiro10

