Table of Contents
本篇笔记使用的坐标以及来源
为了方便二次验证(比如用其他在线工具看看自己的代码是不是真的有问题),这里贴一下本篇笔记涉及的坐标:
坐标来自这张图片:🔗 [gpsimage/gpsimage/images/android_samsung_galaxy.jpg at master · DenisCarriere/gpsimage] https://github.com/DenisCarriere/gpsimage/blob/master/gpsimage/images/android_samsung_galaxy.jpg
exiftool GPS position: 44 deg 14' 42.34" N, 76 deg 27' 5.50" W
坐标(Lat, Long格式): 44.2450944,-76.4515278
用🔗 [Lat Long to UTM Converter] https://www.latlong.net/lat-long-utm.html 转换为UTM以及在地图上定位:
UTM: (384104.82, 4900119.88), utm zone: 18, hemisphere: North
经纬度的写法顺序
带有N/S后缀:纬度, Latitude
带有E/W后缀:经度,Longitude
google map的表示顺序(无论是url还是复制到剪贴板的结果):(Latitude, Longitude)
本篇笔记的代码和笔记大部分情况下尽量使用(Lat, Long)的顺序,但由于习惯问题,有些地方可能还是Long, Lat的写法
UTM坐标系,easting: x, northing: y
本篇笔记表示的UTM坐标顺序:(x, y)
常见坐标写法
Lat, Long: 44.2450944,-76.4515278
google map url: https://www.google.com/maps/place/44°14'42.34"N,76°27'5.50"W
exiftool, GPS Position: 44 deg 14' 42.34" N, 76 deg 27' 5.50" W
google map对这一点右键复制过来的结果:44.2450944,-76.4515278
经纬度和UTM的转换
基本逻辑
简单来说就是:
- 类似(44, -76)这样的经纬度就可以代表地球上的唯一一点;N/S/E/W后缀可以用经纬度的数值推算,本身没有携带额外信息
- 类似(44, -76)这样的经纬度可以转换为UTM (x, y)的格式
- 类似(25000, 35000)这样的UTM (x, y)无法代表地球上的唯一一点
- 类似(25000, 35000)这样的UTM (x, y)需要加上utm zone,south/north信息才可以转换为经纬度的格式(也就是映射地球上唯一一点)
经度和纬度的正负号表示,以及N/S/E/W后缀表示:
转换流程如下:
以美国为例,UTM zone的划分(这就是为什么单凭utm的(x, y)数字无法定位唯一一点):
代码
用到了pyproj. 如果某个时候真的需要看具体实现的代码,可以用这种单文件的、结构简单、功能单一的代码学习:
🔗 [utm/index.es.js at master · TimothyGu/utm] https://github.com/TimothyGu/utm/blob/master/index.es.js
相关资源:
🔗 [ccgalberta.com/ccgresources/report11/2009-410_converting_latlon_to_utm.pdf] https://www.ccgalberta.com/ccgresources/report11/2009-410_converting_latlon_to_utm.pdf
🔗 [Albers Equal Area - EPSG:9822] https://epsg.io/9822-method
from pyproj import Proj
def latlong_to_utm(latitude, longitude):
utm_zone = int((longitude + 180) // 6) + 1
hemisphere = 'north' if latitude >= 0 else 'south'
proj_string = f"+proj=utm +zone={utm_zone} +{hemisphere} +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
proj_func = Proj(proj_string)
easting, northing = proj_func(longitude, latitude)
return easting, northing, utm_zone, hemisphere
def utm_to_latlong(easting, northing, utm_zone, hemisphere):
# hemisphere接受以下输入:north/south/N/S/North/South/n/s
proj_string = f"+proj=utm +zone={utm_zone} +{hemisphere} +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
proj_func = Proj(proj_string)
longitude, latitude = proj_func(easting, northing, inverse=True)
return latitude, longitude
以及测试代码:
easting, northing, utm_zone, hemisphere = latlong_to_utm(44.245194, -76.451528)
print(f"(44.245194, -76.451528) -> Easting: {easting}, Northing: {northing}, UTM Zone: {utm_zone}, Hemisphere: {hemisphere}")
lat, long = utm_to_latlong(384105.01791471586, 4900130.991667096, 18, 'north')
print(f'{lat}, {long}')
使用方法:
经纬度转UTM(顺带输出一些额外信息):
easting, northing, utm_zone, hemisphere = latlong_to_utm(44.245194, -76.451528)
UTM转经纬度(需要一些额外信息:utm zone number以及north/south hemisphere)
lat, long = utm_to_latlong(384105.01791471586, 4900130.991667096, 18, 'north')
运行结果:
(44.245194, -76.451528) -> Easting: 384105.01791471586, Northing: 4900130.991667096, UTM Zone: 18, Hemisphere: north
44.245194, -76.451528
(代码)图片Exif, google map url的相互转换
不涉及longlat-UTM的转换,大部分代码还是角度/正负号/字符串处理
浮点数与度数的转换可以用下面这个例子说明:
def float_to_degrees(float_value):
# 输入: -76.451528
# 返回: -76°-27'-5.50"
# 返回的格式(-76°-27'-5.50")距离google map url的后缀格式(-76°-27'-5.50"W)已经很接近了,但还要进一步处理一点东西(见function longlat_to_GmapUrl)
degrees = int(float_value)
float_value = (float_value - degrees) * 60
minutes = int(float_value)
seconds = (float_value - minutes) * 60
return "{:d}°{:02d}'{:05.2f}\"".format(degrees, minutes, seconds)
def exif_degree_to_googleMap(exif_degree_str):
# 用exiftool查看照片时会出现的信息:GPS Position: 44 deg 14' 42.34" N, 76 deg 27' 5.50" W
# 由于这个信息已经非常接近google map的url格式了,所以直接进行简单的字符串替换就行
# 输入:44 deg 14' 42.34" N, 76 deg 27' 5.50" W
# 输出:https://www.google.com/maps/place/44°14'42.34"N,76°27'5.50"W
suffix = exif_degree_str.replace(' ', '').replace('deg', '°')
return 'https://www.google.com/maps/place/' + suffix
def longlat_to_GmapUrl(input):
# input format: (string) lat, long (注意是lat在前long在后)
# input example: "44.24538645689406, -76.45154925766154"
# output example: https://www.google.com/maps/place/44°14'43.39"N,76°27'05.58"W
lat = float(input.split(',')[0].strip()) # 44.24538645689406
long = float(input.split(',')[1].strip()) # -76.45154925766154
lat_suffix = "N" if lat > 0 else "S"
long_suffix = "E" if long > 0 else "W"
# long和lat之间用逗号或者加号都可以
# google map用后缀(N, S, E, W)表示半球,不使用正负号,所以要用abs(long)和abs(lat)
url_siffix = float_to_degrees(abs(lat)) + lat_suffix + ',' + float_to_degrees(abs(long)) + long_suffix # example: 44°14'43.39"N,76°27'05.58"W
return 'https://www.google.com/maps/place/' + url_siffix
用法以及输出结果:
常见需求
给定照片文件,在google map上查看定位
比如:https://github.com/DenisCarriere/gpsimage/blob/master/gpsimage/images/android_samsung_galaxy.jpg
先用exiftool查看,定位到最后几行:
复制GPS Position并调用 exif_degree_to_googleMap("""44 deg 14' 42.34" N, 76 deg 27' 5.50" W""") 得到google map url:
https://www.google.com/maps/place/44°14'42.34"N,76°27'5.50"W
google map任意一点制作对应google map url
先google map任意一点右键复制(Lat, Long)信息:
复制到剪贴板的格式:
44.24538645689406, -76.45154925766154
这个格式是纯数字的(Latitude, Longitude),要转换为google map url格式还要进行一点处理,所以直接调用 longlat_to_GmapUrl('44.24538645689406, -76.45154925766154') 就可以得到对应的google map url:
https://www.google.com/maps/place/44°14'43.39"N,76°27'05.58"W
注意:上面的google map url需要print,也就是print(longlat_to_GmapUrl('44.24538645689406, -76.45154925766154')),不能使用jupyter notebook那种“直接写个变量就能输出结果”的方式查看(否则url格式会不正确)