Package: gpsd-clients Version: 2.94-2 Severity: wishlist Tags: patch Hi Bernd,
I've sent the attached patch to gpsd-dev, but the list rejects posts from non-subscribers, and I don't really want to subscribe to yet another list just to post a patch. So I hope you'll forward this upstream :) The sky view in xgps is drawn using GDK primitives that lack antialiasing support, resulting in a somewhat ugly display. It's pretty easy to replace that with Cairo to have a better-looking sky view, so here's a patch to do just that (ported from some C code I've been toying with). Thanks, JB. -- System Information: Debian Release: squeeze/sid APT prefers unstable APT policy: (500, 'unstable') Architecture: amd64 (x86_64) Kernel: Linux 2.6.34 Locale: LANG=C, lc_ctype=fr...@euro (charmap=ISO-8859-15) Shell: /bin/sh linked to /bin/bash Versions of packages gpsd-clients depends on: ii libc6 2.11.1-2 Embedded GNU C Library: Shared lib ii libdbus-1-3 1.2.24-1 simple interprocess messaging syst ii libdbus-glib-1-2 0.86-1 simple interprocess messaging syst ii libglib2.0-0 2.24.1-1 The GLib library of C routines ii libgps19 2.94-2 Global Positioning System - librar ii libncurses5 5.7+20100313-2 shared libraries for terminal hand ii python-gps 2.94-2 Global Positioning System - Python ii python-gtk2 2.17.0-2 Python bindings for the GTK+ widge ii python2.6 2.6.5+20100529-1 An interactive high-level object-o gpsd-clients recommends no packages. Versions of packages gpsd-clients suggests: ii gpsd 2.94-2 Global Positioning System - daemon -- no debconf information
>From c38da21bb37364f9bd684acd94719dbfaf864df0 Mon Sep 17 00:00:00 2001 From: Julien BLACHE <j...@jblache.org> Date: Sat, 5 Jun 2010 14:16:18 +0200 Subject: [PATCH] Draw the sky view using Cairo instead of GDK GDK drawing primitives do not support antialiasing and their output doesn't look very nice in this day and age. Rewrite the sky view code to use Cairo instead. --- xgps | 143 ++++++++++++++++++++++++++++++++++++++++------------------------- 1 files changed, 88 insertions(+), 55 deletions(-) diff --git a/xgps b/xgps index 79902c2..3ad136f 100755 --- a/xgps +++ b/xgps @@ -19,6 +19,7 @@ import sys, os, re, math, time, exceptions, getopt, socket import gobject, pygtk pygtk.require('2.0') import gtk +import cairo import gps, gps.clienthelpers @@ -49,59 +50,83 @@ class unit_adjustments: class SkyView(gtk.DrawingArea): "Satellite skyview, encapsulates pygtk's draw-on-expose behavior." # See <http://faq.pygtk.org/index.py?req=show&file=faq18.008.htp> - HORIZON_PAD = 20 # How much whitespace to leave around horizon + HORIZON_PAD = 40 # How much whitespace to leave around horizon SAT_RADIUS = 5 # Diameter of satellite circle GPS_PRNMAX = 32 # above this number are SBAS satellites def __init__(self): gtk.DrawingArea.__init__(self) self.set_size_request(400, 400) - self.gc = None # initialized in realize-event handler + self.cr = None # New cairo context for each expose event self.width = 0 # updated in size-allocate handler self.height = 0 # updated in size-allocate handler self.connect('size-allocate', self.on_size_allocate) self.connect('expose-event', self.on_expose_event) - self.connect('realize', self.on_realize) - self.pangolayout = self.create_pango_layout("") self.satellites = [] - def on_realize(self, widget): - self.gc = widget.window.new_gc() - self.gc.set_line_attributes(1, gtk.gdk.LINE_SOLID, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - def on_size_allocate(self, widget, allocation): self.width = allocation.width self.height = allocation.height - self.diameter = min(self.width, self.height) - SkyView.HORIZON_PAD + self.radius = int((min(self.width, self.height) - SkyView.HORIZON_PAD) / 2) def set_color(self, spec): "Set foreground color for draweing." - self.gc.set_rgb_fg_color(gtk.gdk.color_parse(spec)) + self.cr.set_source_color(gtk.gdk.color_parse(spec)) - def draw_circle(self, widget, x, y, diam, filled=False): + def draw_circle(self, x, y, radius, filled=False): "Draw a circle centered on the specified midpoint." - widget.window.draw_arc(self.gc, filled, - x - diam / 2, y - diam / 2, - diam, diam, 0, 360 * 64) + self.cr.save() + + self.cr.arc(x, y, radius, 0, math.pi * 2.0) + + if filled: + self.cr.fill() + else: + self.cr.stroke() - def draw_line(self, widget, x1, y1, x2, y2): + self.cr.restore() + + def draw_line(self, x1, y1, x2, y2): "Draw a line between specified points." - widget.window.draw_lines(self.gc, [(x1, y1), (x2, y2)]) + self.cr.save() + + self.cr.move_to(int(x1), int(y1)) + self.cr.line_to(int(x2), int(y2)) + self.cr.stroke() + + self.cr.restore() - def draw_square(self, widget, x, y, diam, filled=False): + def draw_square(self, x, y, radius, filled=False): "Draw a square centered on the specified midpoint." - widget.window.draw_rectangle(self.gc, filled, - x - diam / 2, y - diam / 2, - diam, diam) + self.cr.save() - def draw_string(self, widget, x, y, letter, centered=True): + self.cr.rectangle(x - radius, y - radius, radius * 2, radius * 2) + + if filled: + self.cr.fill() + else: + self.cr.stroke() + + self.cr.restore() + + def draw_string(self, x, y, letter, centered=True): "Draw a letter on the skyview." - self.pangolayout.set_text(letter) + self.cr.save() + + self.cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) + self.cr.set_font_size(10) + if centered: - (w, h) = self.pangolayout.get_pixel_size() - x -= w/2 - y -= h/2 - self.window.draw_layout(self.gc, x, y, self.pangolayout) + extents = self.cr.text_extents(letter) + # width / 2 + x_bearing + x -= extents[2] / 2 + extents[0] + # height / 2 + y_bearing + y -= extents[3] / 2 + extents[1] + + self.cr.move_to(x, y) + self.cr.show_text(letter) + self.cr.new_path() + + self.cr.restore() def pol2cart(self, az, el): "Polar to Cartesian coordinates within the horizon circle." @@ -109,49 +134,57 @@ class SkyView(gtk.DrawingArea): # Exact spherical projection would be like this: # el = sin((90.0 - el) * DEG_2_RAD); el = ((90.0 - el) / 90.0); - xout = int((self.width / 2) + math.sin(az) * el * (self.diameter / 2)) - yout = int((self.height / 2) - math.cos(az) * el * (self.diameter / 2)) + xout = int((self.width / 2) + math.sin(az) * el * (self.radius)) + yout = int((self.height / 2) - math.cos(az) * el * (self.radius)) return (xout, yout) def on_expose_event(self, widget, event): - self.set_color("white") - widget.window.draw_rectangle(self.gc, True, 0,0, self.width,self.height) + self.cr = widget.window.cairo_create() + + self.cr.set_line_width(1) + + self.cr.rectangle(0, 0, self.width, self.height) + self.cr.set_source_rgb(0, 0, 0) + self.cr.fill() + + self.cr.set_source_rgb(255, 255, 255) # The zenith marker - self.set_color("gray") - self.draw_circle(widget, self.width / 2, self.height / 2, 6) + self.draw_circle(int(self.width / 2), int(self.height / 2), 6, False) + # The circle corresponding to 45 degrees elevation. # There are two ways we could plot this. Projecting the sphere # on the display plane, the circle would have a diameter of # sin(45) ~ 0.7. But the naive linear mapping, just splitting # the horizon diameter in half, seems to work better visually. - self.draw_circle(widget, self.width / 2, self.height / 2, - int(self.diameter * 0.5)) - self.set_color("black") + self.draw_circle(int(self.width / 2), int(self.height / 2), int(self.radius / 2), False) + # The horizon circle - self.draw_circle(widget, self.width / 2, self.height / 2, - self.diameter) - self.set_color("gray") + self.draw_circle(int(self.width / 2), int(self.height / 2), self.radius, False) + (x1, y1) = self.pol2cart(0, 0) (x2, y2) = self.pol2cart(180, 0) - self.draw_line(widget, x1, y1, x2, y2) + self.draw_line(x1, y1, x2, y2) + (x1, y1) = self.pol2cart(90, 0) (x2, y2) = self.pol2cart(270, 0) - self.draw_line(widget, x1, y1, x2, y2) + self.draw_line(x1, y1, x2, y2) + # The compass-point letters - self.set_color("black") (x, y) = self.pol2cart(0, 0) - self.draw_string(widget, x, y+10, "N") + self.draw_string(x, y-10, "N") (x, y) = self.pol2cart(90, 0) - self.draw_string(widget, x-10, y, "E") + self.draw_string(x+10, y, "E") (x, y) = self.pol2cart(180, 0) - self.draw_string(widget, x, y-10, "S") + self.draw_string(x, y+10, "S") (x, y) = self.pol2cart(270, 0) - self.draw_string(widget, x+10, y, "W") + self.draw_string(x-10, y, "W") + # The satellites + self.cr.set_line_width(2) for sat in self.satellites: (x, y) = self.pol2cart(sat.az, sat.el) if sat.ss < 10: - self.set_color("Black") + self.set_color("Gray") elif sat.ss < 30: self.set_color("Red") elif sat.ss < 35: @@ -161,15 +194,15 @@ class SkyView(gtk.DrawingArea): else: self.set_color("Green1"); if sat.PRN > SkyView.GPS_PRNMAX: - self.draw_square(widget, - x-SkyView.SAT_RADIUS, y-SkyView.SAT_RADIUS, - 2 * SkyView.SAT_RADIUS + 1, sat.used); + self.draw_square(x, y, SkyView.SAT_RADIUS, sat.used); else: - self.draw_circle(widget, - x-SkyView.SAT_RADIUS, y-SkyView.SAT_RADIUS, - 2 * SkyView.SAT_RADIUS + 1, sat.used); - self.set_color("Black") - self.draw_string(widget, x, y, str(sat.PRN), centered=False) + self.draw_circle(x, y, SkyView.SAT_RADIUS, sat.used); + + self.cr.set_source_rgb(255, 255, 255) + self.draw_string(x + SkyView.SAT_RADIUS, y + (SkyView.SAT_RADIUS * 2), str(sat.PRN), centered=False) + + self.cr = None + def redraw(self, satellites): "Redraw the skyview." self.satellites = satellites -- 1.7.1