根据GPS坐标更新谷歌地图位置
我有一个带GPS芯片的Arduino,正在用Python处理NMEA字符串。我有一个HTML文件,设置了每隔几秒自动刷新一次,这样标记就会更新,但我希望能在不刷新页面的情况下更新位置信息。我知道可以通过拖拽来实现这个功能,像这里演示的那样:http://gmaps-samples-v3.googlecode.com/svn/trunk/draggable-markers/draggable-markers.html,但我需要知道如何替换这个拖拽事件,而是通过Python推送新的坐标。我需要一种方法把我的新坐标信息放到网页里。任何帮助或建议都非常感谢。这个问题唯一相关的类是GoogleMap。我对网页的东西不太熟悉,所以越简单越好。
我现在有的:
Python -> 打开网页并自动刷新
Python -> 在map.html中写入新的坐标
map.html 刷新并显示新的位置
我想要的:
Python -> 新位置 {{ }} 标记移动到新坐标
import re
import sys
import copy
import time
import threading
import Queue
import serial
import webbrowser
import traceback
import random
import math
import turtle
from pprint import pprint
from collections import OrderedDict
class MockIo(object):
def __init__(self):
pass
def read(self,buff):
lat = str(random.random())[2:6]
lon = str(random.random())[2:6]
return "$GPGGA,172307.000,3913.%s,N,07716.%s,W,2,10,0.8,199.9,M,-33.4,M,3.8,0000*46\r\n" % (lat,lon)
def write(self,buff):
pass
class GPSTurtle(object):
def __init__(self, new_x = 0, new_y = 0):
self.t = turtle.Turtle()
self.x_coord = new_x
self.y_coord = new_y
self.diff_x = 0
self.diff_y = 0
self.heading = 0
self.origin_x = 0
self.origin_y = 0
def initialize_origin(self, new_x, new_y):
self.origin_x = self.origin_x - new_x
self.origin_y = self.origin_y - new_y
def __update_pos(self, new_x, new_y):
new_x += self.origin_x
new_y += self.origin_y
new_x *= 20
new_y *= 20
self.diff_x = new_x - self.x_coord
self.diff_y = new_y - self.y_coord
if 0 == self.diff_x:
if self.diff_y > 0:
self.heading = 90
elif self.diff_y < 0:
self.heading = 270
elif 0 == self.diff_y:
if self.diff_x > 0:
self.heading = 0
elif self.diff_x < 0:
self.heading = 180
else:
self.heading = math.degrees(math.atan(float(self.diff_y)/float(self.diff_x)))
if self.diff_x < 0:
self.heading += 180
elif self.diff_y < 0:
self.heading += 360
self.set_pos(new_x, new_y)
print self.diff_x,self.diff_y,self.heading,self.x_coord,self.y_coord
def set_pos(self, new_x, new_y):
self.x_coord = new_x
self.y_coord = new_y
def __draw(self):
self.t.setheading(self.heading)
self.t.pendown()
self.t.goto(self.x_coord, self.y_coord)
self.t.penup()
def ungps(self, new_x, new_y):
new_x = int(1000.0 * new_x)
new_y = int(1000.0 * new_y)
return (new_x, new_y)
def update_and_draw(self, new_x, new_y):
self.__update_pos(new_x, new_y)
self.__draw()
class GPS(threading.Thread):
def __init__(self, comport = 'COM15', baud = 4800):
super(GPS, self).__init__()
self.GOOD = True
self.gpgga_keys = [
'message_id',
'utc_time',
'lattitude',
'n_s_ind',
'longitude',
'e_w_ind',
'pos_fix_ind',
'satellites',
'hdop',
'msl_altitude',
'units_1',
'geoid_sep',
'units_2',
# 'age_of_diff_corr', gps does not have this field by default
'diff_ref_station_id',
'checksum',
]
self.PSRF103 = {
'name':'$PSRF103',
'msg':{'GGA':'00','GLL':'01','GSA':'02','GSV':'03','RMC':'04','VTG':'05'},
'mode':{'SetRate':'00','Query':'01'},
'rate':{'off':'00','min':'01','max':'255'},
'cksumEnable':{'disable':'00','enable':'01'},
}
self.gps_msg_q = Queue.Queue()
self.gps_buff = ""
try:
self.gps_com = serial.Serial(
comport,
baud,
timeout = 1,
parity = serial.PARITY_NONE,
rtscts = 0,
xonxoff = 0
)
except serial.serialutil.SerialException:
print "Could not open com port, assuming simulation mode and setting"
print "com object to MockIo"
self.gps_com = MockIo()
def enable_all(self):
m = self.PSRF103
for msg in m['msg'].values():
st = ','.join([m['name'],msg,m['mode']['Query'],m['rate']['on'],m['cksumEnable']['enable']])
st = self.append_crc(st)
self.send_msg(st)
self.gps_com.read(4028)
def disable_all(self):
m = self.PSRF103
for msg in m['msg'].values():
st = ','.join([m['name'],msg,m['mode']['Query'],m['rate']['off'],m['cksumEnable']['enable']])
st = self.append_crc(st)
self.send_msg(st)
self.gps_com.read(4028)
def append_crc(self,st):
match = re.compile("\$(.*)")
crc = 0
if match.search(st):
st = match.search(st).group(1)
for letter in st:
crc = crc ^ ord(letter)
return "$%s*%0.2x\r\n" % (st,crc)
def run(self):
self.disable_all()
while self.GOOD:
self.send_GPGGA_req()
time.sleep(2)
def send_GPGGA_req(self):
m = self.PSRF103
st = ','.join([m['name'],m['msg']['GGA'],m['mode']['Query'],m['rate']['off'],m['cksumEnable']['enable']])
st = self.append_crc(st)
self.send_msg(st)
def parse_msg(self,st):
'''
SAMPLE GPGGA MSG
"$GPGGA,172307.000,3913.7428,N,07716.7474,W,2,10,0.8,199.9,M,-33.4,M,3.8,0000*46\r\n"
'''
retVal = (False,None)
st = st.rstrip('\r\n')
parse = st.split(',')
if st.startswith('$GPGGA') and len(self.gpgga_keys) == len(parse):
retVal = (True, OrderedDict(zip(self.gpgga_keys,parse)))
else:
pass
return retVal
def send_msg(self, st):
self.gps_com.write(st)
self.gps_buff = ''.join([self.gps_buff,self.gps_com.read(1024)])
buffsplit = re.compile(r'.*?\r\n|.+')
splt = buffsplit.findall(self.gps_buff)
if 0 < len(splt):
if splt[-1].endswith('\r\n'):
self.add_list_to_q(splt)
self.gps_buff = ""
else:
self.add_list_to_q(splt[:-1])
self.gps_buff = splt[-1]
def add_list_to_q(self,list_):
for item in list_:
self.gps_msg_q.put(item,False)
def get_item_from_q(self, block = True, timeout = 10):
return self.gps_msg_q.get(block, timeout)
def convert_lat_lon(self, lat, lon,ns,ew):
lat = "%f" % (float(lat[:-7]) + (float(lat[-7:])/60.0))
lon = "%f" % (float(lon[:-7]) + (float(lon[-7:])/60.0))
if 'S' == ns:
lat = str(float(lat) * -1.0)
if 'W' == ew:
lon = str(float(lon) * -1.0)
return (lat,lon)
class GoogleMap(object):
def __init__(self, path = 'map.html'):
self.path = path
self.map_html = '''
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="5" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html { height: 100% } body { height: 100%; margin: 0px; padding: 0px } #map_canvas { height: 100% } </style>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=true">
</script>
<script type="text/javascript">
function initialize() {
var lat = %s
var lng = %s
var latlng = new google.maps.LatLng(lat,lng);
var myOptions = {
zoom: 13,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var marker = new google.maps.Marker({position: latlng, map: map, title: %s });
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:100%; height:100%"></div>
</body>
</html>
'''
def write_map(self,lat = '39.229013',long = '-77.445735',marker = '""'):
new_map_html = self.map_html % ( str(lat), str(long), str(marker).replace('"','') )
with open(self.path, 'w') as f:
f.write(new_map_html)
def launch_browser(self):
webbrowser.open_new_tab(self.path)
if __name__ == "__main__":
map = GoogleMap('map.html')
map.write_map()
map.launch_browser()
gps = GPS('COM15',4800)
gps.start()
t = GPSTurtle()
first_update = True
try:
while True:
try:
st = gps.get_item_from_q(True,2)
success,gpgga = gps.parse_msg(st)
if success:
lat, lon = gps.convert_lat_lon( gpgga['lattitude'],
gpgga['longitude'],
gpgga['n_s_ind'],
gpgga['e_w_ind'])
la,ln = t.ungps(float(lat),float(lon))
if first_update:
t.initialize_origin(la,ln)
first_update = False
else:
t.update_and_draw(la,ln)
map.write_map(lat,lon,'ME!')
time.sleep(5)
except Queue.Empty:
# pass
print "Q-Empty"
except:
gps.GOOD = False
gps.join()
print "\n\nEXITING PROGRAM\n\n"
traceback.print_exc(file=sys.stdout)
1 个回答
与其频繁刷新页面,不如设置一个ajax短轮询。简单来说,就是让你的页面每隔几秒去检查一下某个接口,看看标记的位置。这里有一篇文章讨论这个方法,并链接到一个教程:使用jQuery在网页上获取实时数据
另一种选择是设置长轮询,也就是让页面与服务器保持一个开放的连接,等待更新。这样好处是,一旦坐标有变化,你的界面就会立刻更新。但缺点是,这样会给服务器带来很大的负担,因为它需要一直保持这个连接。而且,长轮询的扩展性比较差。
这篇文章很好地总结了这两种方法:短轮询与长轮询在实时网页应用中的比较
编辑:
这里有一个比较简单的例子,虽然有点粗糙,但应该能给你一个持续更新经纬度的基本框架。提前道歉,因为我没有使用过网络服务器,经验有限,所以我用的是我能想到的最简单的设置——谷歌应用引擎SDK。它自带一个非常容易安装和运行的开发服务器。这里是SDK的链接:http://code.google.com/appengine/downloads.html
main.py:
#!/usr/bin/env python
import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp import template
def doRender(handler, page, templatevalues=None):
path = os.path.join(os.path.dirname(__file__), page)
handler.response.out.write(template.render(path, templatevalues))
class MainHandler(webapp.RequestHandler):
def get(self):
doRender(self, 'template/main.html')
class AjaxHandler(webapp.RequestHandler):
def get(self):
self.response.out.write('{ "lat": "1", "long": "1"}')
def main():
application = webapp.WSGIApplication([('/', MainHandler),
('/data.js', AjaxHandler)],
debug=True)
util.run_wsgi_app(application)
if __name__ == '__main__':
main()
app.yaml:
application: ajaxtest
version: 1
runtime: python
api_version: 1
handlers:
- url: /favicon\.ico
static_files: favicon.ico
upload: favicon\.ico
- url: .*
script: main.py
main.html(把这个放在项目根目录下的“template”文件夹里):
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<body>
<button>Get JSON data</button>
<div></div>
<script type="text/javascript">
var test = 1;
function update(){
$.getJSON("/data.js",function(data){
var items = [];
$.each(data, function(key, val){
$("div").append(key + ":" + val + " ");
});
});
}
var t=setInterval("update()",1000);
</script>
</body>
</html>
根据你的需求,修改AjaxHandler以重新查询GPS坐标。同时修改$("div").append(key + ":" + val + " ");,以便将标记的坐标更新到谷歌地图上。
如果这对你没有帮助,或者你在使用twisted时遇到问题,请告诉我。今天我休假,所以应该能花更多时间来帮你。祝你好运!