等经纬度影像 转 墨卡托影像,等经纬度图片 转 墨卡托图片

该博客介绍了如何将等经纬度投影的卫星影像转换成适用于GIS地图的墨卡托投影,以便进行地图叠加。通过Java程序实现经纬度影像到墨卡托影像的转换,涉及关键步骤包括计算像素间距、纬度距离和转换矩阵。程序适用于处理气象数据,例如风云四A星真彩色云图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间做了气象方面的工作,要在gis地图上叠加卫星影像。

gis地图是墨卡托投影,卫星影像是等经纬度投影,不能直接叠加。

于是整出了这个等经纬度影像 转 墨卡托影像的程序。

上代码:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

/**
 * 等经纬度影像 转 墨卡托影像
 * 基本思路:
 *     1、赤道上经度跨越1度,其代表的距离恒定不变。
 *     2、(经纬度影像)由起止经纬度可以计算出径向距离,得到径向有多少像素,从而可以计算出一像素代表多少米;
 *     3、纬度到赤道的距离有公式可以计算出,再由第2步得到的一像素代表多少米,就可以知道纬度需要多少像素填充。
 * @author RainingTime
 *
 */
public class LatLonImage2MercatorImage {
	
	public static void main(String[] args) throws IOException {
		long start = System.currentTimeMillis();
        
		//原始数据起止经纬度{{开始纬度、开始经度}、{结束纬度、结束经度}},左上角开始,右下角结束
		Double[][] sourceRange = {{53.817,60.2}, {4.83,138.78}};
		
		//裁剪区域起止经纬度{{开始纬度、开始经度}、{结束纬度、结束经度}},左上角开始,右下角结束。
		//若不需要裁剪,则令cutRange = sourceRange
        Double[][] cutRange = {{53.817,60.2}, {4.83,138.78}};
        
        String imageType = "png";
        
        String source = "C:/Users/RainingTime/Desktop/FY4影像/FY4A_20210421074900000.jpg";
        String outPut = "C:/Users/RainingTime/Desktop/FY4影像/FY4A_20210421074900000-mkt." + imageType;
        BufferedImage sourceImage = ImageIO.read(new File(source));
        BufferedImage mercator = imageToMercator(sourceImage, sourceRange, cutRange, imageType);
		ImageIO.write(mercator, imageType, new File(outPut));
        
        long end = System.currentTimeMillis();
        System.out.println("转换完成,用时:"+(end-start)+"ms");
	}

	/**
	 * png、jpg影像转墨卡托投影
	 * @param sourcePath	原始影像
	 * @param outPutPath	输出影像
	 * @param sourceRange	原始影像经纬度范围,要求左上角开始,右下角结束:{{开始纬度、开始经度}、{结束纬度、结束经度}}
	 * @param cutRange		裁剪区域经纬度范围,要求左上角开始,右下角结束:{{开始纬度、开始经度}、{结束纬度、结束经度}},为null时默认不裁剪
	 * @param imageType		输出影像格式,png、jpg两种
	 * @throws IOException
	 */
	public static BufferedImage imageToMercator(BufferedImage sourceImage, Double[][] sourceRange, Double[][] cutRange, String imageType) throws IOException {
		double sourceStartLat = sourceRange[0][0];
		double sourceStartLon = sourceRange[0][1];
		double sourceEndLat = sourceRange[1][0];
		double sourceEndLon = sourceRange[1][1];
		double cutStartLat = sourceStartLat;
		double cutStartLon = sourceStartLon;
		double cutEndLat = sourceEndLat;
		double cutEndLon = sourceEndLon;
		if(cutRange != null){
			cutStartLat = cutRange[0][0];
			cutStartLon = cutRange[0][1];
			cutEndLat = cutRange[1][0];
			cutEndLon = cutRange[1][1];
		}
		
		if(sourceEndLat>sourceStartLat || sourceStartLon>sourceEndLon){
			System.out.println("数据源经纬度坐标从左上角开始,右下角结束!");
			return null;
		}
		if(cutEndLat>cutStartLat || cutStartLon>cutEndLon){
			System.out.println("裁剪区域经纬度坐标从左上角开始,右下角结束!");
			return null;
		}
		/**
		 * earthRadius = 6378137;//地球赤道半径6378137米
		 * 20037508.3427892 = earthRadius * (math.pi - 0);//赤道周长的一半
		 * 85.05112877980659 = (math.atan(math.exp(aa / earthRadius))-math.pi/4)*2 * 180 / math.pi;//墨卡托最大有效纬度
		 */
		if(cutStartLat > 85.051128){
			System.out.println("墨卡托投影起始纬度最大为:85.051128,裁剪区域将被限定。");
			cutStartLat = 85.051128;
		}
		if(cutEndLat <- 85.051128){
			System.out.println("墨卡托投影终止纬度最小为:-85.051128,裁剪区域将被限定。");
			cutEndLat = -85.051128;
		}
		
        int sourceWidth = sourceImage.getWidth();
		int sourceHeight = sourceImage.getHeight();
		double lonStep = (sourceEndLon-sourceStartLon)/(sourceWidth-1);
		double latStep = (sourceEndLat-sourceStartLat)/(sourceHeight-1);
		System.out.println("sourceWidth: " + sourceWidth + ", sourceHeight: " + sourceHeight + ", latStep: " + latStep + ", lonStep: " + lonStep);

		int cutWidth = (int)((cutEndLon - cutStartLon)/lonStep + 0.5) + 1;
		int cutHeight = (int)((cutEndLat - cutStartLat)/latStep + 0.5) + 1;
		System.out.println("cutWidth: "+cutWidth+", cutHeight: "+cutHeight);
		
		//截止纬度与赤道之间有多少个点,北半球为正值
		int start = (int)((Math.log(Math.tan((90 + cutStartLat) * Math.PI / 360)) / (Math.PI / 180))/ lonStep + 0.5);
		//起始纬度与赤道之间有多少个点,南半球为负值
		int end = (int)((Math.log(Math.tan((90 + cutEndLat) * Math.PI / 360)) / (Math.PI / 180))/ lonStep + 0.5);
		
		int mwidth = cutWidth;//墨卡托投影宽度
		int mheight = start - end + 1;//墨卡托投影高度
		System.out.println("mwidth: "+mwidth+", mheight: "+mheight);

		//建立预设
		List<List<Integer>> list = new ArrayList<List<Integer>>();
		List<Integer> innerList = null;
		for(int y=0; y<mheight; y++){
			innerList = new ArrayList<Integer>();
			for(int x=0; x<mwidth; x++){
				innerList.add(0);//填充0,透明图层
			}
			list.add(innerList);
			innerList = null;
		}
		
		//填充真实数据
		int equator = -1;//赤道的像素y轴坐标
		BufferedImage outImage = null;
		if("png".equals(imageType)){
			outImage = new BufferedImage(mwidth, mheight, BufferedImage.TYPE_INT_ARGB);
		}else{
			outImage = new BufferedImage(mwidth, mheight, BufferedImage.TYPE_INT_RGB);
		}
		List<Integer> yIndexList = new ArrayList<Integer>(); 
		for(int y=0; y<cutHeight; y++){
			double lat = cutStartLat + (y * latStep);//截止纬度 减 当前纬度,得到纬度差
			if(-85.051128 <= lat && lat <= 85.051128){
				int oy = start - (int)((Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180)) / lonStep + 0.5);
				
				if(oy<mheight){
					yIndexList.add(oy);
					if(lat == 0.0 || (lat>0.0 && (lat+0.5*latStep)<0.0) || (lat<0.0 && (lat-0.5*latStep)>0.0)){//赤道
						equator = oy;
					}
					for(int x=0; x<cutWidth; x++){
		        		int px = (int)((cutStartLon+x*lonStep-sourceStartLon)/lonStep + 0.5);
		        		int py = (int)((cutStartLat+y*latStep-sourceStartLat)/latStep + 0.5);
						int rgb = sourceImage.getRGB(px, py);
						list.get(oy).set(x, rgb);
					}
				}
			}
		}
		System.out.println("赤道的像素y轴坐标:"+equator);
		
		for(int y=0; y<mheight; y++){
			if(y <= equator){
				for(int i=0; i<yIndexList.size(); i++){
					if(y == yIndexList.get(i)){
						for(int x=0; x<cutWidth; x++){
							outImage.setRGB(x, y, list.get(y).get(x));
						}
						break;
					} else if(y > yIndexList.get(i) && (i+1)<yIndexList.size() && y < yIndexList.get(i+1)){
						for(int x=0; x<cutWidth; x++){
							outImage.setRGB(x, y, list.get(yIndexList.get(i+1)).get(x));
						}
						break;
					}
				}
			} else {
				for(int i=0; i<yIndexList.size(); i++){
					if(y == yIndexList.get(i)){
						for(int x=0; x<cutWidth; x++){
							outImage.setRGB(x, y, list.get(y).get(x));
						}
						break;
					} else if(y > yIndexList.get(i) && (i+1)<yIndexList.size() && y < yIndexList.get(i+1)){
						for(int x=0; x<cutWidth; x++){
							outImage.setRGB(x, y, list.get(yIndexList.get(i)).get(x));
						}
						break;
					}
				}
			}
		}
		list = null;
		return outImage;
	}

}

原图:风云四A星真彩色云图,来源:http://www.nmc.cn/publish/satellite/FY4A-true-color.htm

输出:因为不能上传大于5M的图片,这里将图片压缩了下才上传的。

传送门:墨卡托影像 转 等经纬度影像,墨卡托图片 转 等经纬度图片

<think>好的,我现在需要帮用户解决如何从图片中提取经纬度数据的问题。首先,我得回忆一下之前看到的参考内容。用户提到了引用[1]和引用[2],这两个都是关于使用Python和OCR技术来提取地理信息的。那用户的需求很明确,就是从图片中获取经纬度信息,可能用户手头有一些地理相关的图片,比如地图或卫星图像,需要从中提取坐标数据。 首先,我得考虑用户可能的技术背景。用户可能对编程有一定了解,但可能不熟悉具体的OCR工具或正则表达式处理。所以,在回答时,步骤需要详细但不过于复杂。用户需要的是一个清晰的步骤指南,包括使用的工具和代码示例。 第一步应该是选择OCR工具。引用[1]提到了百度OCR API,但用户可能需要免费的开源方案。Tesseract是一个不错的选择,但可能需要配置环境。或者,如果用户想用在线API,百度OCR确实有效,但需要API key。我需要把这两种方法都列出来,供用户选择。 接下来是图像预处理。考虑到图片可能有噪声或者文字方向问题,预处理步骤很重要。比如调整对比度、二值化、旋等,这些能提高OCR的识别率。用户可能没有处理过类似的问题,所以需要给出具体的代码示例,比如用OpenCV或PIL库处理。 然后是调用OCR识别文本。这里需要分两种情况:使用Tesseract和百度OCR。代码示例要分别展示,特别是百度OCR需要处理API请求和响应,这部分需要详细说明,比如如何发送图片数据,解析返回的JSON结果。 提取经纬度信息的关键是正则表达式。经纬度的格式有很多种,比如度分秒格式、十进制格式,或者带有方向的字母(N/S/E/W)。需要编写一个全面的正则表达式来覆盖这些情况。用户可能对正则表达式不太熟悉,所以需要给出具体的例子,并解释每个部分的作用。 最后是结果输出,将提取到的经纬度保存到文件,比如CSV或文本文件。这部分代码相对简单,但需要确保用户知道如何调整路径和格式。 此外,用户可能遇到的问题,比如图片质量差导致识别错误,或者经纬度格式特殊,需要提醒用户检查正则表达式和处理多结果的情况。应用场景方面,可以提到GIS数据处理、遥感分析等,让用户了解这个方法的实际用途。 最后,生成的相关问题需要覆盖用户可能进一步的需求,比如提高OCR准确率、处理多语言图片、批量处理等,这样用户可以根据自己的情况继续探索。</think>### 从图片中提取经纬度数据的步骤方法 #### 1. **使用OCR识别图片文本** 通过光学字符识别(OCR)技术提取图片中的文本内容,推荐使用以下工具: - **Python库**: `pytesseract`(基于Google Tesseract引擎)[^1] - **云服务API**: 百度OCR、Google Vision API(支持高精度识别) ```python # 示例:使用pytesseract识别图片文本 from PIL import Image import pytesseract image = Image.open("map_image.jpg") text = pytesseract.image_to_string(image, lang="chi_sim+eng") # 支持中英文混合识别 print(text) ``` #### 2. **正则表达式提取经纬度** 通过正则表达式从OCR结果中匹配经纬度格式: - **常见格式**: - 十进制格式:$40.7128°N, 74.0060°W$ - 度分秒格式:$40°42'46"N, 74°00'22"W$ ```python import re pattern = r''' (\d{1,3}°\d{1,2}[\'\′]?\d{1,2}(?:\.\d+)?[\"″]?[NSEW]?) # 纬度部分 [,\s]+ (\d{1,3}°\d{1,2}[\'\′]?\d{1,2}(?:\.\d+)?[\"″]?[NSEW]?) # 经度部分 ''' coordinates = re.findall(pattern, text, re.VERBOSE) ``` #### 3. **坐标格式换(可选)** 将度分秒格式换为十进制格式: $$ \text{十进制} = \text{度} + \frac{\text{分}}{60} + \frac{\text{秒}}{3600} $$ ```python def dms_to_decimal(dms): parts = re.split('[°\'″"]+', dms) degrees = float(parts[0]) minutes = float(parts[1]) if len(parts)>1 else 0 seconds = float(parts[2]) if len(parts)>2 else 0 return degrees + minutes/60 + seconds/3600 ``` #### 4. **结果验证与输出** - 验证经纬度范围:纬度$[-90, 90]$,经度$[-180, 180]$ - 输出为CSV或GeoJSON格式[^2] #### 关键注意事项 - 图片分辨率需≥300dpi以保证OCR精度 - 复杂背景图片需先进行灰度化、二值化预处理 - 百度OCR API对中文坐标标注(如"北纬30°")识别效果更佳
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RainingTime

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值