用可视化地图讲照片的故事(Python+Leaflet)

手机和数码相机拍的照片里除了我们能看到的RGB像元数据,还包含了拍摄时间、图像分辨率、感光值、GPS坐标等属性,记录在Exif(Exchangeable image file format)模块里。

随着手机像素越来越高,用手机记录身边的事(和自拍)已经变成很自然的动作,在一年里我们的手机肯定存了很多照片,照片和Exif数据块中的位置可以做哪些有趣的事情?一张图片和对应的拍摄位置如果没那么多可能性,那一系列照片和位置呢?

我们可以直观看近些年都去了哪里;可以制作和(男/女)朋友一起出去玩的地图故事;可以根据拍照时间和位置动态可视化游览路线;可以基于坐标的聚类整理照片,如拍了800张照片,把每个城市的照片批量整理到各自文件夹;……

地理位置属于个人隐私数据,相关应用需要注意隐私问题,之前挺火的一个谣言是可以根据别人朋友圈发的图知道别人的具体位置,但实际上微信会对朋友圈的图片进行压缩,Exif里的坐标数据是会删除掉的,所以朋友圈的图片是提取不了坐标的。以下实践基于部分自己这些年拍的照片,避免侵犯其他人隐私。

查看照片中的Exif信息

本文主要做的:批量提照片中的坐标->可视化照片位置->制作游历故事地图

所用到的工具:

  • Python和exifread库
  • Leaflet和两个插件

1,批量提照片中的坐标

照片中的地理坐标记录在Exif块里,Exif信息以0xFFE1作为开头标记,采用TIFF格式,可以自己解析或直接用轮子exifread库,exifread是一个很方便使用的读取tiff和jpeg格式图片的Python库,在pypi上的介绍是:

Easy to use Python module to extract Exif metadata from tiff and jpeg files.

通过 pip install exifread安装后就可以使用了,我们现在只关心照片的坐标和拍摄时间,根据其教程探索参数和用法。

exifread库的使用

写代码提取这部分数据:

def extractExif(fpath):#提取坐标
    try:
        with open(fpath,'rb') as rf:
            exif=exifread.process_file(rf)
        eDate=exif['EXIF DateTimeOriginal'].printable
        eLon=exif['GPS GPSLongitude'].printable
        eLat=exif['GPS GPSLatitude'].printable
        lon=eLon[1:-1].replace(' ','').replace('/',',').split(',')
        #'[116, 29, 10533/500]' to [116,29,10533,500]  type==(list)
        lon=float(lon[0])+float(lon[1])/60+float(lon[2])/float(lon[3])/3600
        lat=eLat[1:-1].replace(' ','').replace('/',',').split(',')
        lat=float(lat[0])+float(lat[1])/60+float(lat[2])/float(lat[3])/3600
        return [lon,lat,eDate]  #经度,纬度,拍摄时间
    except Exception as e:
        print(e,fpath)
        return None

注意的是如果拍照时没有读取地理位置权限那就不好记录拍照时的坐标了,所以使用时需要做一个判断。调用上面的函数批量取一个文件夹下照片的坐标:

wpt='J:/DS_refine/SQL-lyn/exifExtract/image'
latLons=[]
for root, dirs, files in os.walk(wpt):
    print(len(files))
    for f in files:
        exif=extractExif('{0}/{1}'.format(wpt,f))
        if exif:
            exif[2]=exif[2]+' '+f
            latLons.append(exif)
        else:
            print(f,'exif is None')

有了照片和对应的位置,可以做可视化讲故事了。下面的实践需要了解一些前端HTML和JavaScript知识。

2,在地图中展示坐标

直接展示地理点坐标有很多工具,百度/高德地图的API、Echarts、Leaflet、OpenLayers等。

这里用Leaflet框架和 marker-clustering.js 实现坐标点展示和缩小时点聚合的效果,这样能适应各种缩放层级。效果如下:

markerClustring-可视化效果

实现方式是在前端的html页面里引入 leaflet.jsleaflet.markercluster-src.js,对map元素进行配置和设置好坐标数据,把html文件配置好之后,把数据写入js文件再调用就好。基于1中提取的坐标,保存为js文件,然后在浏览器打开html文件,就是上图中的效果了。另外需要说明的是,这些标记点(marker)点击之后都是能看到具体的文本的,展示的文本就是title里的内容,是有交互效果的。

<script type="text/javascript">
    var tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }),
    latlng = L.latLng(37.552897,115.60571); //设置地图的坐标中心点
    var map = L.map('map', {center: latlng, zoom: 5, layers: [tiles]});
    var markers = L.markerClusterGroup();

    for (var i = 0; i < addressPoints.length; i++) {
        var a = addressPoints[i];
        var title = a[2];
        var marker = L.marker(new L.LatLng(a[0], a[1]), { title: title });
        marker.bindPopup(title);
        markers.addLayer(marker);
    }
    map.addLayer(markers);
</script>
所调用文件及结构展示

而把这些坐标放到百度地图的效果如下:

基于百度地图的点坐标可视化

坐标多的话就是密密麻麻的红点。

:百度地图中采用的坐标需要是百度坐标系(bd-09),而我们提取的坐标是GPS坐标,用的是WGS84坐标系,需要做转换,可以调用coordTransform_py进行转换,高德地图采用的是火星坐标系,也需要进行转换。

只是展示坐标不怎么有趣,下面做一个左侧图文描述右侧可视化坐标的效果。

3,游历故事地图

给那些年去过的地方写一个地图游记。示例效果如下:

那些年去过的地方

还是用之前提取的坐标和Leaflet框架。用到的插件是storymap.js,同样引用js之后,改变其中的坐标数据,因为是讲一个故事,具体内容当然按自己想讲述的写,将 <sectiondata-place="bodo">中的bodo和js代码中markers里的bodo对应好就好,例如bodo改为beijing。

var markers = {
    beijing: {lat:39.886426, lon: 116.404762, zoom: 6},
    tianjin: {lat:39.134594, lon: 117.191961, zoom: 7},
    fuyang: {lat:32.645140, lon: 116.268333, zoom: 7},
    ningbo: {lat:29.763531, lon: 121.898233, zoom: 8},
    liuzhou: {lat:24.313703, lon: 109.406884, zoom: 7}
};

4,整合聚类点到游历地图里

在我们做的游历地图里增加点聚类的效果,一个简单做法是在storymap.js里增加对markercluster.js的调用,从而可以用markerClusterGroup() 重写基本的marker标记点类型。效果如下:

地图故事效果图

在html里可以根据自己的想法增加更多的内容,例如具体的地址文本,只需要调用百度/高德地图的Web服务 API中的逆地理编码服务就可以实现,逆地理编码就是指将经纬度转换为详细结构化的地址,如把WGS84坐标系的坐标[116.421046,39.903004]逆地理编码对应北京市东城区北京站。

也可以继续探索更多的Leaflet插件。

另外可以换底图,例如换成Satellite卫星底图,改map初始化时地图瓦片图层的调用url就行 L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',效果如下,是不是也很生动呢?

用Mapbox的Satellite底图的效果1
用Mapbox的Satellite底图的效果2

空间位置可以做很多分析和很多有趣的事情,Python也是很强大的工具,仅需要发挥想象力。

参考资料

以上的一些代码同步 更新于QLWeilcf/VisualizedLyn

蛰虫始航

(因简书最近发文功能受限,打算把公众号变成主力分享平台,感兴趣欢迎关注,在这个人均关注公众号越来越多的阶段,自知做新增很难,希望做一个尽量有干货有成长的公众号,site:蛰虫始航)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,864评论 6 494
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,175评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,401评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,170评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,276评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,364评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,401评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,179评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,604评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,902评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,070评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,751评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,380评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,077评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,312评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,924评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,957评论 2 351

推荐阅读更多精彩内容

  • JavaScript 高级程序设计(特别是对浏览器差异的复杂处理),通常很困难也很耗时。为了应对这些调整,许多的 ...
    FiringAce阅读 185评论 0 0
  • 不讲历史,只说故事。大家好,我是讲故事的吕秀才。 我们今天讲意外的贤君——刘恒。为什么说刘恒当皇帝是个意外,按照正...
    探心猫家庭教育阅读 358评论 0 0
  • 你是亲自指导合成的父母称谓,父母真为什么这样的父母这么一来,唉,这个还是关乎到很多方面的,就是我们这家庭当中很多的...
    新愉阅读 297评论 0 0
  • 现在同事之间相处都需要以金钱来衡量了吗? 活了这么些年,我一直觉得只要你真心对待别人,别人亦会真心对待你,这些无关...
    以安云深阅读 178评论 2 1
  • 我身体里的细胞 每个在对你说着 我心里的那三个字
    印刷阅读 321评论 3 11