zoukankan      html  css  js  c++  java
  • GDAL读取jpg文件中GPS坐标信息

    在目前很多相机都支持在拍照的时候保留GPS信息,这些信息一般都是存储在jpg图像的exif信息中。下面内容主要说明如何使用GDAL库来读取jpg图像中的GPS信息并解析经纬度坐标。

    首先,还是最常用的工具,gdalinfo,来查看这个GPS信息到底在什么地方。下面是使用gdalinfo输出的信息,图1为截图。

    Driver: JPEG/JPEG JFIF
    Files: C:\Users\LiMinlu\Desktop\DSCN8806.JPG
    Size is 4608, 3456
    Coordinate System is `'
    Metadata:
      EXIF_ColorSpace=1
      EXIF_ComponentsConfiguration=0x1 0x2 0x3 00
      EXIF_CompressedBitsPerPixel=(2)
      EXIF_Contrast=0
      EXIF_CustomRendered=0
      EXIF_DateTime=2013:03:18 16:06:49
      EXIF_DateTimeDigitized=2013:03:18 16:06:49
      EXIF_DateTimeOriginal=2013:03:18 16:06:49
      EXIF_DigitalZoomRatio=(0)
      EXIF_ExifVersion=0230
      EXIF_ExposureBiasValue=(0)
      EXIF_ExposureMode=0
      EXIF_ExposureProgram=2
      EXIF_ExposureTime=(0.00625)
      EXIF_FileSource=0x3
      EXIF_Flash=24
      EXIF_FlashpixVersion=0100
      EXIF_FNumber=(3.9)
      EXIF_FocalLength=(5)
      EXIF_FocalLengthIn35mmFilm=28
      EXIF_GainControl=4
      EXIF_GPSAltitude=(55.6)
      EXIF_GPSAltitudeRef=00
      EXIF_GPSDateStamp=2013:03:18
      EXIF_GPSImgDirection=(33.96)
      EXIF_GPSImgDirectionRef=T
      EXIF_GPSLatitude=(39) (53) (41.298)
      EXIF_GPSLatitudeRef=N
      EXIF_GPSLongitude=(116) (17) (28.344)
      EXIF_GPSLongitudeRef=E
      EXIF_GPSMapDatum=WGS-84   
      EXIF_GPSSatellites=03
      EXIF_GPSTimeStamp=(8) (5) (41.02)
      EXIF_GPSVersionID=0x2 0x3 00 00
      EXIF_ImageDescription=                               
      EXIF_Interoperability_Index=R98
      EXIF_Interoperability_Version=0x30 0x31 0x30 0x30
      EXIF_ISOSpeedRatings=125
      EXIF_LightSource=0
      EXIF_Make=NIKON
      EXIF_MakerNote=Nikon
      EXIF_MaxApertureValue=(3.9)
      EXIF_MeteringMode=5
      EXIF_Model=COOLPIX AW100s 
      EXIF_Orientation=1
      EXIF_PixelXDimension=4608
      EXIF_PixelYDimension=3456
      EXIF_ResolutionUnit=2
      EXIF_Saturation=0
      EXIF_SceneCaptureType=0
      EXIF_SceneType=0x1
      EXIF_Sharpness=0
      EXIF_Software=COOLPIX AW100sV1.0             
      EXIF_SubjectDistanceRange=2
      EXIF_UserComment=                                                                                                                       
      EXIF_WhiteBalance=0
      EXIF_XResolution=(300)
      EXIF_YCbCrPositioning=2
      EXIF_YResolution=(300)
    Image Structure Metadata:
      COMPRESSION=JPEG
      INTERLEAVE=PIXEL
      SOURCE_COLOR_SPACE=YCbCr
    Corner Coordinates:
    Upper Left  (    0.0,    0.0)
    Lower Left  (    0.0, 3456.0)
    Upper Right ( 4608.0,    0.0)
    Lower Right ( 4608.0, 3456.0)
    Center      ( 2304.0, 1728.0)
    Band 1 Block=4608x1 Type=Byte, ColorInterp=Red
      Image Structure Metadata:
        COMPRESSION=JPEG
    Band 2 Block=4608x1 Type=Byte, ColorInterp=Green
      Image Structure Metadata:
        COMPRESSION=JPEG
    Band 3 Block=4608x1 Type=Byte, ColorInterp=Blue
      Image Structure Metadata:
        COMPRESSION=JPEG
    图1 GDALINFO 输出的信息
    从上面的输出信息可以看出,jpg中存储GPS的信息是以EXIF_GPS***开头的元数据信息里面所存储。知道了存储位置我们就可以写程序来解析了,首先要做的是从这么多一大堆的EXIF信息中提取GPS的信息,代码如下:

    	char** papszMetadata = poDataset->GetMetadata( NULL ) ;
    	char** papszMetadataGPS = NULL;
    
    	if( CSLCount(papszMetadata) > 0 )
    	{
    		for(int i = 0; papszMetadata[i] != NULL; i++ )
    		{
    			if(EQUALN(papszMetadata[i], "EXIF_GPS", 8))
    			{
    				papszMetadataGPS = CSLAddString( papszMetadataGPS, papszMetadata[i]);
    				printf( "  %s\n", papszMetadata[i] );
    			}
    		}
    	}
    
    大概解释一下上面的代码,首先从poDataset 中获取元数据信息;然后判断元数据的个数是否大于0,也就是判断是否存在元数据;最后通过函数EQUALN来提取EXIF_GPS开头的元数据存放在新的字符串数组中。通过上面的代码提取之后的元数据如下:

      EXIF_GPSAltitude=(55.6)
      EXIF_GPSAltitudeRef=00
      EXIF_GPSDateStamp=2013:03:18
      EXIF_GPSImgDirection=(33.96)
      EXIF_GPSImgDirectionRef=T
      EXIF_GPSLatitude=(39) (53) (41.298)
      EXIF_GPSLatitudeRef=N
      EXIF_GPSLongitude=(116) (17) (28.344)
      EXIF_GPSLongitudeRef=E
      EXIF_GPSMapDatum=WGS-84   
      EXIF_GPSSatellites=03
      EXIF_GPSTimeStamp=(8) (5) (41.02)
      EXIF_GPSVersionID=0x2 0x3 00 00
    关于这些EXIF_GPS开头的含义可以参考这个页面http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps.html)。通过这个页面可以得知,我们要获取的坐标就是下面这几个元数据里面的值,标识的含义见后面。

      EXIF_GPSAltitude=(55.6)              ——海拔
      EXIF_GPSAltitudeRef=00               ——海拔参考值(应该就是水平面高程)
      EXIF_GPSLatitude=(39) (53) (41.298)  ——纬度信息
      EXIF_GPSLatitudeRef=N                ——纬度标识,N为北纬,S为南纬
      EXIF_GPSLongitude=(116) (17) (28.344)——经度信息
      EXIF_GPSLongitudeRef=E               ——经度标识,E为东经,W为西经
      EXIF_GPSMapDatum=WGS-84              ——参考椭球,这个应该都是WGS84吧
    其中经度和纬度信息是使用度分秒格式表示。知道了格式及其含义,就可以很方便的解析了。下面是一个解析的函数,使用了boost库中的split函数和lexical_cast函数。

    #include "gdal_priv.h"
    #include <vector>
    #include <string>
    using namespace std;
    
    #include "boost/lexical_cast.hpp"
    #include "boost/algorithm/string.hpp"
    using namespace boost;
    using namespace boost::algorithm;
    
    bool ExtractGPSInfo(char** papszMetadata, double &dLon, double &dLat, double &dHgt)
    {
    	if( CSLCount(papszMetadata) <= 0 )
    		return false;
    
    	char** papszMetadataGPS = NULL;
    	for(int i = 0; papszMetadata[i] != NULL; i++ )
    	{
    		if(EQUALN(papszMetadata[i], "EXIF_GPS", 8))
    			papszMetadataGPS = CSLAddString( papszMetadataGPS, papszMetadata[i]);
    	}
    
    	int iGPSCount = CSLCount(papszMetadataGPS);
    	if (iGPSCount <=0)
    	{
    		CSLDestroy( papszMetadataGPS );
    		return false;
    	}
    	
    	bool bIsNorth = true;
    	bool bIsEast = true;
    
    	for(int i = 0; papszMetadataGPS[i] != NULL; i++ )
    	{
    		vector<string> vSplitStr;
    		split(vSplitStr, papszMetadataGPS[i], is_any_of("="));	//使用=拆分字符串
    		if(vSplitStr.empty() || vSplitStr.size() != 2)
    			continue;
    
    		string strName = vSplitStr[0];	//取出标识符
    		string strValue = vSplitStr[1];	//取出值
    		if(strName.empty() || strValue.empty())
    			continue;
    
    		if(strName == "EXIF_GPSAltitude")	//获取海拔
    		{
    			vector<string> vSplitValue;
    			split(vSplitValue, strValue, is_any_of("( )"), token_compress_on);	//使用( )拆分字符串
    			if(vSplitValue.size() != 3)
    				dHgt = 0;
    			else
    				dHgt = lexical_cast<double>(vSplitValue[1]);
    		}
    		else if(strName == "EXIF_GPSLongitude")		//获取经度
    		{
    			vector<string> vSplitValue;
    			split(vSplitValue, strValue, is_any_of("( )"), token_compress_on);	//使用( )拆分字符串
    			if(vSplitValue.size() != 5)
    				dLon = 0;
    			else
    				dLon = lexical_cast<double>(vSplitValue[1]) + lexical_cast<double>(vSplitValue[2]) / 60.0 + lexical_cast<double>(vSplitValue[3]) / 3600.0;
    		}
    		else if(strName == "EXIF_GPSLatitude")	//获取纬度
    		{
    			vector<string> vSplitValue;
    			split(vSplitValue, strValue, is_any_of("( )"), token_compress_on);	//使用( )拆分字符串
    			if(vSplitValue.size() != 5)
    				dLat = 0;
    			else
    				dLat = lexical_cast<double>(vSplitValue[1]) + lexical_cast<double>(vSplitValue[2]) / 60.0 + lexical_cast<double>(vSplitValue[3]) / 3600.0;
    		}
    		else if(strName == "EXIF_GPSLongitude")		//获取经度
    		{
    			if (strValue == "E")
    				bIsEast = true;
    			else
    				bIsEast = false;
    		}
    		else if(strName == "EXIF_GPSLatitude")	//获取纬度
    		{
    			if (strValue == "N")
    				bIsNorth = true;
    			else
    				bIsNorth = false;
    		}
    	}
    
    	dLon = bIsEast ? dLon : -1.0*dLon;
    	dLat = bIsNorth ? dLat : -1.0*dLat;
    
    	return true;
    }
    需要注意的是,上面的代码中有句“split(vSplitValue, strValue, is_any_of("( )"), token_compress_on); //使用( )拆分字符串”,按理说这句应该会把括弧和空格去掉,我本机执行的时候确实去掉了,但是在解析后的字符串vector中的收尾增加了两个空字符串,所以下面只好从1开始了。解析后的输出如图2所示。


    图2 解析的坐标信息

  • 相关阅读:
    2015-01-21
    水文分析手册ArcHydro Tool 中文操作手册
    03018_监听器Listener
    元旦去峨眉山吧,人间值得
    常用Oracle SQL集锦
    结合公司现状浅谈CMDB
    CentOS7-Nginx编译安装
    Linux配置C++11编译环境
    Python实现通用web框架
    Python实现通用web框架
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6313959.html
Copyright © 2011-2022 走看看