You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

347 lines
12 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Johannes 'josch' Schauer <j.schauer@email.de>, M. Dietrich <mdt@pyneo.org>, F. Gau <fgau@pyneo.org>"
__version__ = "prototype"
__copyright__ = "Copyright (c) 2008 J. Schauer, 2008 M. Dietrich"
__license__ = "GPLv3"
from pyneo.codings import decode_json
import re
import evas
from edje import decorators
import math
from epydial import *
class GpsMapScreen():
def register_pyneo_callbacks(self):
PyneoController.register_callback("gps_position_change", self.on_gps_position_change)
PyneoController.register_callback("on_map_new_file", self.on_map_new_file)
PyneoController.register_callback("on_get_geocoding", self.on_get_geocoding)
class Tile(evas.Image):
def __init__(self, canvas):
evas.Image.__init__(self, canvas)
self.pass_events = True
#we need this to store the original position while the zoom animations
self.position = (0, 0)
self.size = 256, 256
self.fill = 0, 0, 256, 256
def set_position(self, x, y):
self.position = (x, y)
self.move(x, y)
def __init__(self, screen_manager, latitude=0.0, longitude=0.0, zoom=0.0, map_type="OsmStreet"):
self.canvas = screen_manager.get_evas()
# self.pass_events = True
# self.position = (0, 0)
self.size = 480, 640
# self.fill = 0, 0, 256, 256
# def set_position(self, x, y):
# self.position = (x, y)
# self.move(x, y)
self.buttons = {}
self.config = ConfigParser(INI_PATH).get_section_config('map')
self.map_type = self.config.get("map_type") or map_type
self.pymapd_cache = ConfigParser('/etc/pyneod.ini').get_section_config('map').get('cache_directory')
self.cache_directory = '/'.join([self.pymapd_cache, self.map_type, "%d/%d/%d"])
#mouse position
self.x_pos, self.y_pos = (0, 0)
#global variable for zooming
self.zoom_step = 0.0
#initial latitude, longitude, zoom
self.latitude = latitude or float(self.config.get('latitude'))
self.longitude = longitude or float(self.config.get('longitude'))
self.altitude = 0.0
self.kph = 0.0
self.course = 0.0
self.x = 0
self.y = 0
self.zoom = zoom or int(self.config.get('zoom'))
self.offset_x = 0
self.offset_y = 0
self.overlay = evas.Text(self.canvas, font=("Sans:style=Bold, Edje-Vera", 24), color="#808080")
self.overlay_text = "lat:%0.4f lon:%0.4f a:%0.f v:%0.f"
self.overlay.layer = 2
self.overlay.on_mouse_up_add(self.on_edje_signal_dialer_status_triggered)
self.geo_text = evas.Text(self.canvas, font=("Sans:style=Bold, Edje-Vera", 18), color="#808080")
self.geo_text.layer = 2
self.geo_text.pos = (0, 30)
self.pinpoint = evas.Rectangle(self.canvas, pos=(238, 318), size=(4, 4), color="#ff0000")
self.pinpoint.layer = 2
self.arrow = evas.Image(self.canvas)
self.arrow.geometry = 190, 270, 100, 100
self.arrow.fill = 0, 0, 100, 100
self.arrow.layer = 2
self.arrow.pass_events = True
for pos, text in enumerate(["back", "player-minus", "player-plus", "info"]):
self.buttons[text] = self.init_button(text, (pos+1)*16+pos*100, 524, 100, 100)
#calculate size of tile raster
self.border_x = int(math.ceil(self.size[0]/256.0))
self.border_y = int(math.ceil(self.size[1]/256.0))
self.mouse_down = False
self.animate = False
self.fix = False
self.icons = []
self.init_icons()
self.set_current_tile(self.latitude, self.longitude, self.zoom)
def init_button(self, name, x, y, dx, dy):
def button_callback(source, event):
if name == 'back':
PyneoController.show_gps_status_screen()
elif name == "player-minus" and not self.animate:
ecore.timer_add(0.05, self.animate_zoom_out)
elif name == "player-plus" and not self.animate:
ecore.timer_add(0.05, self.animate_zoom_in)
elif name == "info":
PyneoController.get_geocoding(self.latitude, self.longitude)
print '---', name
button = evas.Image(self.canvas, pos=(x,y), size=(dx,dy), file="%s%s.png" % (THEME_IMAGES, name))
button.fill = 0, 0, dx, dy
button.layer = 2
button.on_mouse_up_add(button_callback)
return button
def on_gps_position_change(self, status):
if status:
self.fix = True
self.altitude = float(status.get('altitude', self.altitude))
self.kph = float(status.get('speed', self.kph))
if status.get('course', 0):
self.course = float(status.get('course', self.course))
self.arrow.show()
self.arrow.file_set('/'.join([IMAGE_FILES_PATH, "map", "arrow%02d.png"%(round(self.course/15)%24)]))
else:
self.course = 0.0
if not self.animate:
self.latitude = float(status.get('latitude', self.latitude))
self.longitude = float(status.get('longitude', self.longitude))
self.arrow.file_set('/'.join([IMAGE_FILES_PATH, "map", "arrow%02d.png"%int(self.course/15)]))
self.set_current_tile(self.latitude, self.longitude, self.zoom)
else:
self.fix = False
self.altitude = 0.0
self.kph = 0.0
self.course = 0.0
self.arrow.hide()
if not self.animate:
self.overlay.text = self.overlay_text%(self.latitude, self.longitude, self.altitude, self.kph)
def on_get_geocoding(self, status):
address_text = re.findall(r'address.*?\n', status['geo'].encode('utf-8'))
self.geo_text.text = address_text[0][11:]
# m = decode_json(status['geo'])
# for item in m('Placemark'):
# print 'MMMMMM:', item
def on_edje_signal_dialer_status_triggered(self, edje, emission, source):
if source == "label" and not self.animate:
maps_avail = PyneoController.map_tiles.keys()
self.map_type = maps_avail[(maps_avail.index(self.map_type)+1)%len(maps_avail)]
self.cache_directory = '/'.join([self.pymapd_cache, self.map_type, "%d/%d/%d"])
self.init_redraw()
print '--- map type: ', self.map_type
def on_map_new_file(self, d):
for filename, info in d.items():
if info.get("state") != "DOWNLOADED":
print "ERROR", filename, info
else:
assert filename.startswith('file://')
filename = filename[7:]
parts = filename.split("/")
x = int(parts[-2])
y = int(parts[-1])
for icon in self.not_downloaded_tiles:
if icon.x == x and icon.y == y:
icon.file_set(filename)
def hide(self):
for icon in self.icons:
icon.hide()
self.overlay.hide()
self.geo_text.hide()
self.arrow.hide()
self.pinpoint.hide()
for text in ["back", "player-plus", "player-minus", "info"]:
self.buttons[text].hide()
# when to store current settings permanently?
# self.config.set('map_type', self.map_type)
# self.config.set('longitude', self.longitude)
# self.config.set('latitude', self.latitude)
# self.config.set('zoom', self.zoom)
# self.config.config.save()
def show(self):
for icon in self.icons:
icon.show()
self.overlay.show()
self.geo_text.show()
self.pinpoint.show()
if self.fix:
self.arrow.show()
for text in ["back", "player-plus", "player-minus", "info"]:
self.buttons[text].show()
#jump to coordinates
def set_current_tile(self, latitude, longitude, zoom):
#update shown coordinates everytime they change
self.overlay.text = self.overlay_text%(latitude, longitude, self.altitude, self.kph)
# self.overlay.part_text_set("zoom", str(zoom))
x = (longitude+180)/360 * 2**zoom
y = (1-math.log(math.tan(latitude*math.pi/180) + 1/math.cos(latitude*math.pi/180))/math.pi)/2 * 2**zoom
offset_x, offset_y = int((x-int(x))*256), int((y-int(y))*256)
#only redraw if x, y, zoom, offset_x or offset_y differ from before
if int(x) != int(self.x) \
or int(y) != int(self.y) \
or zoom != self.zoom \
or offset_x != self.offset_x \
or offset_y != self.offset_y:
self.zoom = zoom
self.x = x
self.y = y
self.offset_x, self.offset_y = offset_x, offset_y
self.init_redraw()
def init_icons(self):
#clean up
for icon in self.icons:
icon.delete()
self.icons = []
#fill
for i in xrange((2*self.border_x+1)*(2*self.border_y+1)):
self.icons.append(GpsMapScreen.Tile(self.canvas))
def init_redraw(self):
self.not_downloaded_tiles = []
for i in xrange(2*self.border_x+1):
for j in xrange(2*self.border_y+1):
k = (2*self.border_y+1)*i+j
x = int(self.x)+i-self.border_x
y = int(self.y)+j-self.border_y
self.icons[k].x = x
self.icons[k].y = y
self.icons[k].set_position((i-self.border_x)*256+self.size[0]/2-self.offset_x, (j-self.border_y)*256+self.size[1]/2-self.offset_y)
filename = self.cache_directory%(self.zoom, self.x+i-self.border_x, self.y+j-self.border_y)
if exists(filename):
self.icons[k].file_set(filename)
else:
self.icons[k].file_set('/'.join([IMAGE_FILES_PATH, "map", "404.png"]))
self.not_downloaded_tiles.append(self.icons[k])
self.current_pos = (0, 0)
if self.not_downloaded_tiles:
PyneoController.map_request_tiles(self.latitude, self.longitude, self.zoom, self.map_type)
def update_coordinates(self):
x = int(self.x) + (self.offset_x-self.current_pos[0])/256.0
y = int(self.y) + (self.offset_y-self.current_pos[1])/256.0
self.longitude = (x*360)/2**self.zoom-180
n = math.pi*(1-2*y/2**self.zoom)
self.latitude = 180/math.pi*math.atan(0.5*(math.exp(n)-math.exp(-n)))
self.overlay.text = self.overlay_text%(self.latitude, self.longitude, self.altitude, self.kph)
# self.overlay.part_text_set("zoom", str(self.zoom))
def zoom_in(self, zoom):
for icon in self.icons:
x = (1+zoom)*(icon.position[0]-self.size[0]/2)+self.size[0]/2
y = (1+zoom)*(icon.position[1]-self.size[1]/2)+self.size[1]/2
icon.geometry = int(x), int(y), 256+int(256*zoom), 256+int(256*zoom)
icon.fill = 0, 0, 256+int(256*zoom), 256+int(256*zoom)
def zoom_out(self, zoom):
for icon in self.icons:
x = (1-zoom*0.5)*(icon.position[0]-self.size[0]/2)+self.size[0]/2
y = (1-zoom*0.5)*(icon.position[1]-self.size[1]/2)+self.size[1]/2
icon.geometry = int(x), int(y), 256-int(256*zoom*0.5), 256-int(256*zoom*0.5)
icon.fill = 0, 0, 256-int(256*zoom*0.5), 256-int(256*zoom*0.5)
def animate_zoom_in(self):
if self.zoom < 18:
self.animate = True
if self.zoom_step < 1.0:
self.zoom_in(self.zoom_step)
self.zoom_step+=0.125
return True
self.zoom_step = 0.0
self.set_current_tile(self.latitude, self.longitude, self.zoom+1)
for icon in self.icons:
icon.size = 256, 256
icon.fill = 0, 0, 256, 256
self.animate = False
return False
def animate_zoom_out(self):
if self.zoom > 5:
self.animate = True
if self.zoom_step < 1.0:
self.zoom_out(self.zoom_step)
self.zoom_step+=0.125
return True
self.zoom_step = 0.0
self.set_current_tile(self.latitude, self.longitude, self.zoom-1)
for icon in self.icons:
icon.size = 256, 256
icon.fill = 0, 0, 256, 256
self.animate = False
return False
@decorators.signal_callback("mouse,down,1", "*")
def on_mouse_down(self, emission, source):
if not self.animate:
self.x_pos, self.y_pos = self.canvas.pointer_canvas_xy
self.mouse_down = True
@decorators.signal_callback("mouse,up,1", "*")
def on_mouse_up(self, emission, source):
self.mouse_down = False
if not self.animate:
#redraw if moved further than one tile in each direction 'cause the preoload will only download one tile further than requested
if abs(self.current_pos[0]) > 256 or abs(self.current_pos[1]) > 256:
self.x = int(self.x) + (self.offset_x-self.current_pos[0])/256.0
self.y = int(self.y) + (self.offset_y-self.current_pos[1])/256.0
self.offset_x, self.offset_y = int((self.x-int(self.x))*256), int((self.y-int(self.y))*256)
self.update_coordinates()
self.init_redraw()
if abs(self.current_pos[0]) > 0 or abs(self.current_pos[1]) > 0:
#on mouse up + move: update current coordinates
self.update_coordinates()
if self.not_downloaded_tiles:
PyneoController.map_request_tiles(self.latitude, self.longitude, self.zoom, self.map_type)
@decorators.signal_callback("mouse,move", "*")
def on_mouse_move(self, emission, source):
if self.mouse_down and not self.animate and not self.fix:
x_pos, y_pos = self.canvas.pointer_canvas_xy
delta_x = self.x_pos - x_pos
delta_y = self.y_pos - y_pos
self.x_pos, self.y_pos = x_pos, y_pos
for icon in self.icons:
icon.set_position(icon.pos[0]-delta_x, icon.pos[1]-delta_y)
self.current_pos = (self.current_pos[0]-delta_x, self.current_pos[1]-delta_y)
# vim:tw=0:nowrap