如何将经纬度下载的谷歌地图.TIF图像转换为GeoTIFF以保留空间信息
Absolutely! You can absolutely convert your downloaded map image into a GeoTIFF with spatial reference information retained. The core idea is to add geographic metadata (like projection, coordinate bounds) to the regular image file, since PIL (which you're using now) doesn't handle geospatial data natively. Here's a step-by-step solution using rasterio—a Python library designed for working with geospatial raster data:
Step 1: Install Required Libraries
First, install rasterio and numpy (we'll use numpy to convert PIL images to arrays for rasterio):
pip install rasterio numpy
Step 2: Fix Redundant Tile Downloads (Optional but Important)
Looking at your generateTiles method, you're downloading each tile twice and pasting it twice—this is redundant and wastes bandwidth/time. Let's clean that up first:
def generateTiles(self, **kwargs): start_x = kwargs.get('start_x', None) start_y = kwargs.get('start_y', None) tile_width = kwargs.get('tile_width', 5) tile_height = kwargs.get('tile_height', 5) if start_x == None or start_y == None: start_x, start_y = self.getXY() width, height = 256 * tile_width, 256 * tile_height map_img = Image.new('RGB', (width, height)) for x in range(0, tile_width): for y in range(0, tile_height): url = f'https://mt0.google.com/vt?lyrs={self._layer}&x={start_x + x}&y={start_y + y}&z={self._zoom}' current_tile = f'{x}-{y}' urllib.request.urlretrieve(url, current_tile) im = Image.open(current_tile) map_img.paste(im, (x * 256, y * 256)) os.remove(current_tile) return map_img
Step 3: Add Geospatial Metadata & Save as GeoTIFF
Next, modify your script to calculate the Web Mercator (EPSG:3857) bounds of your image (Google Maps uses this projection) and write the GeoTIFF using rasterio.
First, add these imports at the top of your script:
import rasterio import numpy as np
Then update the main function's save logic:
def main(): # Replace with your actual lat/lon values lat = 40.7128 lon = -74.0060 gmd = ImagesDownloader(lat, lon, 15, layer='s') print("瓦片坐标为{}".format(gmd.getXY())) try: # 获取高分辨率图像 img = gmd.generateTiles(tile_width=5, tile_height=5) # Explicitly set tile dimensions here except IOError: print("无法生成图像 - 请尝试调整缩放级别并检查坐标") else: # Convert PIL Image to numpy array img_array = np.array(img) # Calculate geospatial parameters (Web Mercator EPSG:3857) zoom = gmd._zoom start_x, start_y = gmd.getXY() tile_width = 5 tile_height = 5 # Web Mercator projection bounds mercator_extent = 20037508.34 * 2 # Total width/height of Web Mercator in meters tile_size_meters = mercator_extent / (2 ** zoom) pixel_size_meters = tile_size_meters / 256 # Size of each pixel in meters # Top-left corner coordinates (Web Mercator) top_left_x = -20037508.34 + start_x * tile_size_meters top_left_y = 20037508.34 - start_y * tile_size_meters # Create affine transform for the image transform = rasterio.transform.from_origin( top_left_x, top_left_y, pixel_size_meters, pixel_size_meters ) # Write the GeoTIFF file with rasterio.open( "high_resolution_geotiff.tif", "w", driver="GTiff", height=img_array.shape[0], width=img_array.shape[1], count=3, # 3 bands for RGB dtype=img_array.dtype, crs="EPSG:3857", transform=transform, ) as dst: # Transpose array from (H, W, 3) to (3, H, W) as required by rasterio dst.write(img_array.transpose(2, 0, 1)) print("GeoTIFF地图已成功生成")
How This Works
- Coordinate Conversion: We calculate the exact Web Mercator bounds of your tiled image using the tile coordinates (
start_x,start_y) and zoom level. - Affine Transform: This defines how pixel coordinates map to real-world geographic coordinates.
- Rasterio Writing: Rasterio handles embedding the CRS (EPSG:3857) and transform into the TIFF file, making it a valid GeoTIFF that GIS tools (like QGIS, ArcGIS) can recognize and display in the correct location.
Alternative: Using GDAL
If you prefer using GDAL (the underlying library rasterio uses), you could use gdal.Translate or build the dataset manually, but rasterio provides a much cleaner Pythonic API for this use case.
内容的提问来源于stack exchange,提问作者Skarl001

