Python 做手机GPS实时跟踪网站(源码)
前言
GPS在物流领域广泛应用,平时我们接触最多的应该是出租车服务和快递服务。一般企业级的管理平台会跟踪每个运输工具的状态,方便进行一些分析和优化运算。
每个GPS终端一般都会连接到服务器,实时发送数据。当然也有终端本身就可以实时进行处理数据,不用实时发回服务器。
今天我们可以开发一个GPS跟踪系统,用于实时跟踪我们的手机GPS信号,并且使用Python的在地图上(大屏)显示历史轨迹以及当前位置,并且实时更新。
为了完成手机实时跟踪系统,我们需要分解步骤:
- 获取手机GPS信息,并且实时传给Python程序
- Python程序实时更新
- 数据显示在地图上,画出轨迹图
我们分步介绍思路和技术栈。
先看动图

实时上传手机GPS
安卓和iOS阵营的实现方法不同,安卓的方法很多,主要原因是安卓系统开放,接口容易暴露。iOS相对保守,这里我演示iOS方法,采用iPhone,iPad都可以。
iOS的实时传GPS的信息基本有两个思路。
- 找到一些app,可以把GPS信息暴露成一个网址,之后可以从这个网址解析GPS信息。这这个方法有缺点,如果网址是开放的,那无疑信息是不安全的。如果网址是需要授权的,那我们还需要登录到某些网站去获取授权,这样也是很麻烦。
- 采用iOS自带的app获取信息,这里我采用的iCloud账号。因为iCloud账户可以获取几乎所有的信息,包括相片,通讯类,设备信息,实时GPS数据等。所以我们只需要用Python登录iCloud就可以获取对应设备的GPS信息了。
这里推荐的库就是pyicloud,我们只需要调用PyiCloudService来创建API,就可以获取device的location 信息了。以下就是代码,由于我的iCloud绑定了多个设备,这里我跟踪device1的信息。
from pyicloud import PyiCloudService
import pandas as pd
import sys
import dash
from dash import Dash
import dash_leaflet as dl
from jupyter_dash import JupyterDash
from dash import html,dcc
from dash.dependencies import Input, Output,State
from datetime import datetime
import random
import numpy as np
api = PyiCloudService('[email protected]')
iOS的安全机制还是很复杂的,如果你的设备设置了Two-factor authentication,也就是还要验证码,我们也可以判断是否需要添加验证码,并提示用户输入。
通过上面的代码,我们就可以获得iCloud的所有信息了,包括GPS。
PHONE = api.devices[1]
print(PHONE.location())
搞定了 GPS的信息,我们可以写一个函数,用于更新数据,并保存在一个DataFrame里面。
这个DataFrame包含时间戳和对应的GPS 经纬度信息。
def update_gps_from_phone(phone_device,df):
loc = phone_device.location()
now = datetime.now()
df.loc[now] = [loc['latitude'],loc['longitude']]
return df
做个实时更新网站
做个网站,我首选的是plotly Dash,为什么?
因为我只想用Python搞定所有前端和后端。Dash我之前文章已经介绍过了,这里需要Interval 插件来更新页面。Interval就是一个定时组件,用于隔一段时间处理事件,比如更新画面,更新数据等。
dash的文章可以参考:
注意这个网站,可以运行在任何电脑,只需联网可以,并且对应的网址可以不用公网IP,只用127.0.01 就可以。因为我们不需远程访问该网站,iCloud自动会同步GPS信息。
实时更新地图
地图,首选的是plotly,因为适配Dash最佳,但是Plotly画轨迹不是很友好。
这里我采用Leaflet 来画轨迹地图,leaflet 号称开源第一js地图库,主要是它的设计思路较为合理,采用图层(layer)的形式来叠加,使得每个layer之间耦合度较低,控制起来更加灵活。
map 底图(tile)自身就是一个layer,你可以在tile layer 之上叠加任何layer。对于我们这个项目,我们需要叠加一个曲线layer (Polyline)以及marker layer。
Leaflet 是一个JavaScript的库,所以在python里面调用需要wrap 一下。
很多大神们已经开源了python库,Folium就是一个,但是这里我要用是另一个,Dash-leaflet。
为什么是后者,因为它的设计规范是参考Dash组件的。 我们可以调用Dash-leaflet的组件,比如Polyline,放在网页的layout里面。并且可以采用callback函数对数据进行实时更新。

总结一下,我们设计完整的网页代码如下:
- 创建Dash 程序
- 创建Dash的组件,包括以下:
- Interval:用于实时更新dash界面
- Input:用于控制更新周期,比如3s
- Leaflet Map,需要包含以下图层
- tile图层,是地图的背景层
- PolylineDecorator 图层,是Polyline的升级版,方便我们快速画出曲线,以及marker,这里我们marker我采用的自定义的icon
- 创建Dash Callback 函数
- callback函数用于对某些事件进行响应,这里我们的事件就是Interval的n_intervals值发生变化,触发GPS更新数据。

#app = Dash(__name__)
app = JupyterDash(__name__)
input_v = dcc.Input(
id="input_range", type="number", placeholder="input with interval",value=3,
min=2, max=120, step=1,
)
# interval
interval_c = dcc.Interval(id='interval_component',interval=3000,n_intervals=0)
# tile_layer
url = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'
attribution = '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
tile_layer = dl.TileLayer(url=url, maxZoom=20, attribution=attribution)
# marker_layer
iconUrl = "assets/person-running-solid.svg"
marker = dict(rotate=False, markerOptions=dict(icon=dict(iconUrl=iconUrl, iconAnchor=[16, 16])))
patterns = [dict(repeat='10', dash=dict(pixelSize=5, pathOptions=dict(color='#3388ff', weight=2, opacity=0.8))),
dict(offset='100%', repeat='50%', marker=marker)]
polyline = dl.PolylineDecorator(positions=LOC_DF.values.tolist(), patterns=patterns,id = 'polyline_component')
map_fig = dl.Map([tile_layer,polyline], style={'width': '1000px', 'height': '500px'},center=SIM_POS,zoom=15,id = 'map_component')
app.layout = html.Div([input_v,interval_c,map_fig])
@app.callback(
Output(component_id='polyline_component', component_property='positions'),
Input(component_id='interval_component', component_property='n_intervals'),
)
def update_output(n_clicks):
global LOC_DF
LOC_DF = update_gps_from_phone(PHONE,LOC_DF)
return LOC_DF.values.tolist()
@app.callback(
Output(component_id='interval_component', component_property='interval'),
Input(component_id='input_range', component_property='value'),
)
def update_output(value):
return value*1000
app.run_server(debug=False)
总结
以上就是个人手机的实时跟踪系统的设计,相当于Python版本的find my phone,不过因为我们可以保存所有的历史轨迹,所以用途还是很广泛的。
另外,代码包含leaflet的地图设计,还是具有参考价值的。