【代码】从照片中提取GPS信息并创建Shapefile
【代码】从照片中提取GPS信息并创建Shapefile
ytkz在地理信息系统中,获取精确的地理位置数据对于各种分析至关重要。随着数码摄影的普及,越来越多的照片包含了EXIF元数据,其中就包含了拍摄地点的GPS信息。本文将介绍如何使用Python编程语言从照片中提取这些GPS信息,并将其转换为Shapefile文件,以便在GIS软件中进行进一步的分析和处理。
一、准备工作
在开始之前,需要确保已经安装了以下Python库:
- PIL(或Pillow):用于处理图像文件。
- pyshp:用于读写Shapefile文件。
可以使用pip来安装这些库:
pip install pillow pyshp
二、准备工作
首先,我们需要编写一个函数来从照片中提取EXIF元数据中的GPS信息。这个函数将打开照片文件,解析EXIF标签,并提取出经度和纬度信息。
from PIL import Image
def exif(img):
"""
从图片中返回EXIF元数据
"""
exif_data = {}
try:
i = Image.open(img) # 使用PIL库打开图片
tags = i._getexif() # 获取图片的EXIF标签
for tag, value in tags.items():
decoded = TAGS.get(tag, tag) # 尝试从预定义的TAGS字典中获取标签的中文描述,否则使用标签ID
exif_data[decoded] = value # 将标签及其值存储到exif_data字典中
except:
pass # 捕获所有异常并忽略,这通常不是一个好的做法,应该明确指定要捕获的异常
return exif_data
三、处理GPS信息
由于EXIF中的GPS信息是以度、分、秒(DMS)的格式存储的,并且可能包含方向信息(东、西、南、北),我们需要编写一个函数dms2dd()
来将这些信息转换为十进制度(DD)的格式。
def dms2dd(d, m, s, direction):
"""
将度分秒格式转换为十进制度格式
"""
sec = float((m * 60) + s)
dec = float(sec / 3600)
deg = float(d + dec)
if direction.upper() == 'W' or direction.upper() == 'S':
deg = deg * -1
return float(deg)
接下来,我们需要编写一个函数gps()
来解析EXIF元数据中的GPS信息,并返回经度和纬度值。这个函数将首先检查是否存在GPSInfo标签,然后提取度、分、秒和方向信息,并使用dms2dd()
函数进行转换。
def gps(exif_data):
"""
从EXIF数据中提取GPS信息并转换为十进制度格式
"""
lat = None
lon = None
if exif_data and 'GPSInfo' in exif_data:
# 这里省略了详细的GPSInfo解析过程,因为它涉及多个EXIF标签的组合
# ...
# 假设我们已经从exif_data中提取了经纬度信息(度、分、秒和方向)
# 这里只是模拟赋值
lat_d, lat_m, lat_s, lat_dir = (30, 15, 30, 'N')
lon_d, lon_m, lon_s, lon_dir = (120, 20, 45, 'E')
lat = dms2dd(lat_d, lat_m, lat_s, lat_dir)
lon = dms2dd(lon_d, lon_m, lon_s, lon_dir)
return lat, lon
四、将GPS信息转换为Shapefile格式
一旦我们从照片中提取了GPS信息,就可以将其转换为Shapefile文件,这是一种常用于地理信息系统中的矢量数据格式。我们将使用Python的pyshp
库来创建Shapefile文件。
首先,我们需要编写一个函数gps_to_shapefile()
来遍历指定目录下的所有照片文件,提取GPS信息,并创建一个包含这些信息的Shapefile文件。
photos = {}
photo_dir = ".\photos"
# 查找指定目录下的所有JPG照片
files = glob.glob(os.path.join(photo_dir, "*.jpg"))
# 从文件中提取GPS元数据
for f in files:
e = exif(f)
lat, lon = gps(e)
photos[f] = [lon, lat] # 注意:这里通常经度在前,纬度在后,但此处按照您的代码保持原样
# 构建一个包含照片文件名作为属性的点shapefile
with shapefile.Writer("photos1", shapefile.POINT) as w:
w.field("NAME", "C", 80) # 创建一个名为NAME的字符型字段,最大长度为80
for f, coords in photos.items():
w.point(*coords) # 使用经度和纬度(注意顺序)创建一个点要素
w.record(f) # 为点要素添加文件名属性
五、完整代码
import glob
import os
try:
import Image
import ImageDraw
except:
from PIL import Image
from PIL.ExifTags import TAGS
import shapefile
def exif(img):
"""
从图片中返回EXIF元数据
"""
exif_data = {}
try:
i = Image.open(img) # 使用PIL库打开图片
tags = i._getexif() # 获取图片的EXIF标签
for tag, value in tags.items():
decoded = TAGS.get(tag, tag) # 尝试从预定义的TAGS字典中获取标签的中文描述,否则使用标签ID
exif_data[decoded] = value # 将标签及其值存储到exif_data字典中
except:
pass # 捕获所有异常并忽略,这通常不是一个好的做法,应该明确指定要捕获的异常
return exif_data
def dms2dd(d, m, s, i):
"""
将度/分/秒转换为十进制度
"""
sec = float((m * 60) + s) # 将分和秒转换为秒
dec = float(sec / 3600) # 将秒转换为小数度
deg = float(d + dec) # 将度和小数度相加
if i.upper() == 'W': # 如果方向是西
deg = deg * -1 # 将度数变为负数
elif i.upper() == 'S': # 如果方向是南
deg = deg * -1 # 将度数变为负数
return float(deg)
def gps(exif):
"""
从EXIF元数据中提取GPS信息
"""
lat = None # 纬度
lon = None # 经度
if exif.get('GPSInfo'): # 如果EXIF中包含GPS信息
# 纬度
coords = exif['GPSInfo']
i = coords[1] # 纬度方向(N/S)
d = coords[2][0] # 纬度度数
m = coords[2][1] # 纬度分钟
s = coords[2][2] # 纬度秒
lat = dms2dd(d, m, s, i) # 将纬度转换为十进制度
# 经度
i = coords[3] # 经度方向(E/W)
d = coords[4][0] # 经度度数
m = coords[4][1] # 经度分钟
s = coords[4][2] # 经度秒
lon = dms2dd(d, m, s, i) # 将经度转换为十进制度
return lat, lon
if __name__ == '__main__':
# 存储照片文件名和GPS坐标的字典
photos = {}
photo_dir = ".\photos"
# 查找指定目录下的所有JPG照片
files = glob.glob(os.path.join(photo_dir, "*.jpg"))
# 从文件中提取GPS元数据
for f in files:
e = exif(f)
lat, lon = gps(e)
photos[f] = [lon, lat] # 注意:这里通常经度在前,纬度在后,但此处按照您的代码保持原样
# 构建一个包含照片文件名作为属性的点shapefile
with shapefile.Writer("photos1", shapefile.POINT) as w:
w.field("NAME", "C", 80) # 创建一个名为NAME的字符型字段,最大长度为80
for f, coords in photos.items():
w.point(*coords) # 使用经度和纬度(注意顺序)创建一个点要素
w.record(f) # 为点要素添加文件名属性
结果在arcgis打开,如下: