Renaming GreyLine to WorldMap, and migrating to a Cartopy-based grey line implementation based on the code from the Aurora Forecast example: http://scitools.org.uk/cartopy/docs/latest/gallery/aurora_forecast.html

pull/64/head
Christian Jacobs 2018-02-24 11:23:49 +00:00
rodzic f3ee36a10b
commit bb3b8094af
12 zmienionych plików z 89 dodań i 45 usunięć

Wyświetl plik

@ -12,7 +12,8 @@ virtualenv:
before_install:
- sudo apt-get update -qq
- sudo apt-get install -yq xvfb gir1.2-gtk-3.0 python3-gi-cairo python-mpltoolkits.basemap python3-numpy python3-matplotlib python3-sphinx python-libhamlib2 python3-flake8 python3-pip
- sudo apt-get install -yq xvfb python3 python3-pip gir1.2-gtk-3.0 python3-gi-cairo python3-flake8 python-libhamlib2
- sudo pip3 install -U -r requirements.txt
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"

Wyświetl plik

@ -1,5 +1,19 @@
# Change Log
## [UNRELEASED]
### Added
- Added support for the SAT_NAME, SAT_MODE, PROP_MODE, and GRIDSQUARE ADIF fields for the purposes of satellite QSO logging.
- Pinpointing of callsigns on the world map by looking up the latitude-longitude coordinates based on the value in the COUNTRY field. A new right-click popup menu has been created for this purpose.
- Added basic copy/paste functionality for individual records.
- Added a requirements.txt file for the purpose of installing dependencies.
### Changed
- Renamed the GreyLine class to WorldMap, since it now does more than just grey line plotting.
- Improved the section on dependencies in the README.
### Fixed
- Updated the list of supported ADIF fields.
## [1.0.0] - 2017-08-02
### Added
- Pin-pointing of QTH on grey line map.
@ -95,6 +109,7 @@
- QSO filtering and sorting.
- Duplicate record removal.
[UNRELEASED]: https://github.com/ctjacobs/pyqso/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/ctjacobs/pyqso/compare/v0.3...v1.0.0
[0.3]: https://github.com/ctjacobs/pyqso/compare/v0.2...v0.3
[0.2]: https://github.com/ctjacobs/pyqso/compare/v0.1...v0.2

Wyświetl plik

@ -30,17 +30,17 @@ As the name suggests, PyQSO is written primarily in the [Python](https://www.pyt
* gir1.2-gtk-3.0
* python3-gi-cairo
Several extra packages are necessary to enable the full functionality of PyQSO, such as the grey line tool. Many of these (specified in the `requirements.txt` file) can be readily installed system-wide using the Python package manager by issuing the following command in the terminal:
Several extra packages are necessary to enable the full functionality of PyQSO. Many of these (specified in the `requirements.txt` file) can be readily installed system-wide using the Python package manager by issuing the following command in the terminal:
sudo pip3 install -U -r requirements.txt
but the complete list is given below:
* python3-matplotlib (version 1.3.0 or later)
* python3-mpltoolkits.basemap
* python3-numpy
* libxcb-render0-dev
* python3-cairocffi
* [cartopy](http://scitools.org.uk/cartopy/) (for drawing the world map and grey line)
* [geocoder](https://pypi.python.org/pypi/geocoder) (for QTH lookups)
* python3-sphinx (for building the documentation)
* python3-hamlib (for Hamlib support)

Wyświetl plik

@ -24,7 +24,7 @@ include:
- Progress tracker for the `DXCC <http://www.arrl.org/dxcc/>`_ award.
- Grey line plotter.
- World map with grey line.
- Filter QSOs based on callsign (e.g. only display contacts with callsigns beginning with "M6").
@ -65,5 +65,5 @@ If you have any comments or questions about PyQSO, please send them via email to
Structure of this documentation
-------------------------------
The structure of this documentation is as follows. The section on `Getting Started <getting_started.html>`_ provides information on the PyQSO installation process through to creating a new logbook (or opening an existing one). The `Log Management <log_management.html>`_ section explains how to create a log in the logbook, as well as the basic operations that users can perform with existing logs, such as printing, importing/exporting logs, and sorting. The `Record Management <record_management.html>`_ section deals with the bottom layer of the three-tier model - the creation, deletion, and modification of QSO records in a log. The `Toolbox <toolbox.html>`_ section introduces the PyQSO toolbox which contains three tools that are useful to amateur radio operators: a DX cluster, a grey line plotter, and an awards progress tracker. Finally, the `Preferences <preferences.html>`_ section explains how users can set up Hamlib support and show/hide various fields in a log, along with several other user preferences that can be set via the Preferences dialog window. A `keyboard shortcuts list <shortcuts.html>`_ is also available for reference.
The structure of this documentation is as follows. The section on `Getting Started <getting_started.html>`_ provides information on the PyQSO installation process through to creating a new logbook (or opening an existing one). The `Log Management <log_management.html>`_ section explains how to create a log in the logbook, as well as the basic operations that users can perform with existing logs, such as printing, importing/exporting logs, and sorting. The `Record Management <record_management.html>`_ section deals with the bottom layer of the three-tier model - the creation, deletion, and modification of QSO records in a log. The `Toolbox <toolbox.html>`_ section introduces the PyQSO toolbox which contains three tools that are useful to amateur radio operators: a DX cluster, a world map, and an awards progress tracker. Finally, the `Preferences <preferences.html>`_ section explains how users can set up Hamlib support and show/hide various fields in a log, along with several other user preferences that can be set via the Preferences dialog window. A `keyboard shortcuts list <shortcuts.html>`_ is also available for reference.

Wyświetl plik

@ -17,7 +17,7 @@ Under the ``General`` tab, the user can choose to:
- Keep the ``Add Record`` dialog window open after a new QSO is added, in preparation for the next QSO.
- Pin-point the user's QTH on the grey line map by specifying the latitude-longitude coordinates (or looking them up based on the QTH's name, e.g. city name).
- Pin-point the user's QTH on the world map by specifying the latitude-longitude coordinates (or looking them up based on the QTH's name, e.g. city name).
.. _figure:summary:
.. figure:: images/summary.png

Wyświetl plik

@ -31,20 +31,18 @@ adjacent ``Send Command`` button (or pressing the Enter key).
The DX cluster frame.
Grey line
World map
---------
The grey line tool (see figure:grey_line_) can be used to
check which parts of the world are in darkness. The position of the grey
line is automatically updated every 30 minutes.
The world map tool (see figure:world_map_) can be used to plot the QTH of your station and stations that you have contacted. It also features a grey line to check which parts of the world are in darkness. The position of the grey line is automatically updated every 30 minutes.
The user's QTH can be pin-pointed on the map by specifying the QTH's location (e.g. city name) and latitude-longitude coordinates in the preferences. If the `geocoder <https://pypi.python.org/pypi/geocoder>`_ library is installed then these coordinates can be filled in for you by clicking the lookup button after entering the QTH's name, otherwise the coordinates will have to be entered manually.
.. _figure:grey_line:
.. figure:: images/grey_line.png
.. _figure:world_map:
.. figure:: images/world_map.png
:align: center
The grey line tool with the user's QTH (e.g. Southampton) pin-pointed on the map.
The world map tool with the user's QTH (e.g. Southampton) pin-pointed.
Awards
------

Wyświetl plik

@ -1053,7 +1053,7 @@ class Logbook:
return
def pinpoint_callback(self, widget=None, path=None):
""" A callback function used to pinpoint the callsign on the grey line map. """
""" A callback function used to pinpoint the callsign on the world map. """
try:
log_index = self.get_log_index()
@ -1065,7 +1065,7 @@ class Logbook:
logging.error(e)
return
self.application.toolbox.grey_line.pinpoint(r)
self.application.toolbox.world_map.pinpoint(r)
return

Wyświetl plik

@ -33,7 +33,7 @@ class Popup:
# Collect Gtk menu items and connect signals.
self.items = {}
# Plot selected QSO on the grey line map.
# Plot selected QSO on the world map.
self.items["PINPOINT"] = self.builder.get_object("mitem_pinpoint")
self.items["PINPOINT"].connect("activate", self.application.logbook.pinpoint_callback)

Wyświetl plik

@ -792,7 +792,7 @@
</packing>
</child>
<child>
<object class="GtkBox" id="greyline">
<object class="GtkBox" id="worldmap">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
@ -806,10 +806,10 @@
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="greyline_label">
<object class="GtkLabel" id="worldmap_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Grey Line</property>
<property name="label" translatable="yes">World Map</property>
</object>
<packing>
<property name="position">1</property>
@ -908,8 +908,8 @@
<property name="type_hint">dialog</property>
<property name="transient_for">pyqso</property>
<property name="program_name">PyQSO</property>
<property name="version">1.0.0</property>
<property name="copyright" translatable="yes">Copyright (C) 2012-2017 Christian Thomas Jacobs</property>
<property name="version">1.1.0-dev</property>
<property name="copyright" translatable="yes">Copyright (C) 2012-2018 Christian Thomas Jacobs</property>
<property name="comments" translatable="yes">A contact logging tool for amateur radio operators.</property>
<property name="website">http://christianjacobs.uk/pyqso</property>
<property name="license" translatable="yes">This program is free software: you can redistribute it and/or modify
@ -1460,7 +1460,7 @@ along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.</pro
<property name="spacing">2</property>
<child>
<object class="GtkCheckButton" id="general_show_qth_checkbutton">
<property name="label" translatable="yes">Pin-point QTH on grey line map</property>
<property name="label" translatable="yes">Pin-point QTH on world map</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>

Wyświetl plik

@ -1,6 +1,6 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2017 Christian Thomas Jacobs.
# Copyright (C) 2013-2018 Christian Thomas Jacobs.
# This file is part of PyQSO.
@ -18,7 +18,7 @@
# along with PyQSO. If not, see <http://www.gnu.org/licenses/>.
from pyqso.dx_cluster import DXCluster
from pyqso.grey_line import GreyLine
from pyqso.world_map import WorldMap
from pyqso.awards import Awards
@ -38,7 +38,7 @@ class Toolbox:
self.tools = self.builder.get_object("tools")
self.dx_cluster = DXCluster(self.application)
self.grey_line = GreyLine(self.application)
self.world_map = WorldMap(self.application)
self.awards = Awards(self.application)
self.tools.connect_after("switch-page", self.on_switch_page)
@ -52,7 +52,7 @@ class Toolbox:
return
def on_switch_page(self, widget, label, new_page):
""" Re-draw the Grey Line if the user switches to the grey line tab. """
if(widget.get_tab_label(label).get_text() == "Grey Line"):
self.grey_line.draw()
""" Re-draw the WorldMap if the user switches to the World Map tab. """
if(widget.get_tab_label(label).get_text() == "World Map"):
self.world_map.draw()
return

Wyświetl plik

@ -1,6 +1,6 @@
#!/usr/bin/env python3
# Copyright (C) 2013-2017 Christian Thomas Jacobs.
# Copyright (C) 2013-2018 Christian Thomas Jacobs.
# This file is part of PyQSO.
@ -20,6 +20,7 @@
from gi.repository import GObject
import logging
from os.path import expanduser
from datetime import datetime
try:
import configparser
except ImportError:
@ -35,7 +36,7 @@ try:
have_necessary_modules = True
except ImportError as e:
logging.warning(e)
logging.warning("Could not import a non-standard Python module needed by the GreyLine class, or the version of the non-standard module is too old. Check that all the PyQSO dependencies are satisfied.")
logging.warning("Could not import a non-standard Python module needed by the WorldMap class, or the version of the non-standard module is too old. Check that all the PyQSO dependencies are satisfied.")
have_necessary_modules = False
try:
import geocoder
@ -63,16 +64,16 @@ class Point:
return
class GreyLine:
class WorldMap:
""" A tool for visualising the grey line. """
""" A tool for visualising the world map. """
def __init__(self, application):
""" Set up the drawing canvas and the timer which will re-plot the grey line every 30 minutes.
""" Set up the drawing canvas and the timer which will re-plot the world map every 30 minutes.
:arg application: The PyQSO application containing the main Gtk window, etc.
"""
logging.debug("Setting up the grey line...")
logging.debug("Setting up the world map...")
self.application = application
self.builder = self.application.builder
@ -81,8 +82,8 @@ class GreyLine:
if(have_necessary_modules):
self.fig = matplotlib.figure.Figure()
self.canvas = FigureCanvas(self.fig) # For embedding in the Gtk application
self.builder.get_object("greyline").pack_start(self.canvas, True, True, 0)
self.refresh_event = GObject.timeout_add(1800000, self.draw) # Re-draw the grey line automatically after 30 minutes (if the grey line tool is visible).
self.builder.get_object("worldmap").pack_start(self.canvas, True, True, 0)
self.refresh_event = GObject.timeout_add(1800000, self.draw) # Re-draw the world map automatically after 30 minutes (if the world map tool is visible).
# Plot the QTH coordinates, if available.
config = configparser.ConfigParser()
@ -96,11 +97,11 @@ class GreyLine:
qth_longitude = float(config.get("general", "qth_longitude"))
self.add_point(qth_name, qth_latitude, qth_longitude, "ro")
except ValueError:
logging.warning("Unable to get the QTH name, latitude and/or longitude. The QTH will not be pinpointed on the grey line map. Check preferences?")
logging.warning("Unable to get the QTH name, latitude and/or longitude. The QTH will not be pinpointed on the world map. Check preferences?")
self.builder.get_object("greyline").show_all()
self.builder.get_object("worldmap").show_all()
logging.debug("Grey line ready!")
logging.debug("World map ready!")
return
@ -118,7 +119,7 @@ class GreyLine:
return
def pinpoint(self, r):
""" Pinpoint the location of a QSO on the grey line map based on the COUNTRY field.
""" Pinpoint the location of a QSO on the world map based on the COUNTRY field.
:arg r: The QSO record containing the location to pinpoint.
"""
@ -144,7 +145,7 @@ class GreyLine:
def draw(self):
""" Draw the world map and the grey line on top of it.
:returns: Always returns True to satisfy the GObject timer, unless the necessary GreyLine dependencies are not satisfied (in which case, the method returns False so as to not re-draw the canvas).
:returns: Always returns True to satisfy the GObject timer, unless the necessary WorldMap dependencies are not satisfied (in which case, the method returns False so as to not re-draw the canvas).
:rtype: bool
"""
@ -152,7 +153,7 @@ class GreyLine:
toolbox = self.builder.get_object("toolbox")
tools = self.builder.get_object("tools")
if(tools.get_current_page() != 1 or not toolbox.get_visible()):
# Don't re-draw if the grey line is not visible.
# Don't re-draw if the world map is not visible.
return True # We need to return True in case this is method was called by a timer event.
else:
# Set up the world map.
@ -165,14 +166,43 @@ class GreyLine:
ax.add_feature(cartopy.feature.COASTLINE)
ax.add_feature(cartopy.feature.BORDERS, alpha=0.25)
# TODO: Re-draw the grey line.
# Draw the grey line. This is based on the code from the Cartopy Aurora Forecast example (http://scitools.org.uk/cartopy/docs/latest/gallery/aurora_forecast.html) and used under the Open Government Licence (http://scitools.org.uk/cartopy/docs/v0.15/copyright.html).
dt = datetime.utcnow()
axial_tilt = 23.5
ref_solstice = datetime(2016, 6, 21, 22, 22)
days_per_year = 365.2425
seconds_per_day = 86400.0
days_since_ref = (dt - ref_solstice).total_seconds()/seconds_per_day
lat = axial_tilt*numpy.cos(2*numpy.pi*days_since_ref/days_per_year)
sec_since_midnight = (dt - datetime(dt.year, dt.month, dt.day)).seconds
lng = -(sec_since_midnight/seconds_per_day - 0.5)*360
pole_lng = lng
if lat > 0:
pole_lat = -90 + lat
central_rot_lng = 180
else:
pole_lat = 90 + lat
central_rot_lng = 0
rotated_pole = cartopy.crs.RotatedPole(pole_latitude=pole_lat, pole_longitude=pole_lng, central_rotated_longitude=central_rot_lng)
x = numpy.empty(360)
y = numpy.empty(360)
x[:180] = -90
y[:180] = numpy.arange(-90, 90.)
x[180:] = 90
y[180:] = numpy.arange(90, -90., -1)
ax.fill(x, y, transform=rotated_pole, color='grey', alpha=0.75)
# Plot points on the map.
if(self.points):
logging.debug("Plotting QTHs on the map...")
for p in self.points:
ax.plot(p.longitude, p.latitude, p.style, transform=cartopy.crs.PlateCarree())
ax.text(p.longitude+0.01*p.longitude, p.latitude+0.01*p.latitude, p.name, color="black", size="small", weight="bold")
ax.text(p.longitude+0.05*p.longitude, p.latitude+0.025*p.latitude, p.name, color="black", size="small", weight="bold")
return True
else:

Wyświetl plik

@ -1,6 +1,6 @@
numpy
matplotlib>=1.3.0
basemap
cartopy
cairocffi
sphinx
geocoder