Trigpointing and point.py
=========================

One interesting email I received after posting `geolocation and path
cross`_ asked if the module could be used for quick visualisation when
trigpointing.  Now that I've found out what trigpointing is I believe it
can, and I've added a couple of extra features to make it easier for
trigpointers to use the old ``edist.py`` script.

Firstly, for those who don't know, trigpointing is the activity of
tracking down trigpoints and recording them.  I guess you could make
a parallel to trainspotting, but with a navigational slant.  Some people
apparently use GPS units to track down the trigpoints, that I'd
suggest makes it just hiking with trigpoints as waypoints.  And some
people, like Robert Johnson who mailed me, prefer to do the navigation
with just an ordnance survey map and a compass which in my eyes makes it
a little more interesting.  Also, a few sites I've found with Google_
seem to suggest that many trigpointers like to use triangulation,
although I suspect some mean trilateration, to travel between trigpoints
as a navigational challenge.

.. Note::
   Robert tells me that TrigpointingUK_ is a popular website among
   trigpointers in the UK.  It contains information about many of the
   trigpoints you can find, such as the one closest to me at Bygrave_

Anybody who knows me well will attest that that I'm quite the navigation
geek, mostly just as a curiosity being that what we're really talking
about is just applications of specific branches of math.  As such,
I actually find the concept of trigpointing by hand quite intriguing.
That being said technology is here to assist us, and with that let me
introduce ``trigpoints.py`` a simple extension over the original
``edist.py`` script.

.. code-block:: pycon

    >>> from upoints import trigpoints
    >>> database_location = urllib.urlopen("http://www.haroldstreet.org.uk/waypoints/alltrigs-wgs84.txt")
    >>> Trigpoints = trigpoints.Trigpoints(database_location)
    >>> print(len(Trigpoints))
    6557

Thanks to `the online database`_ we now have the locations of all the
Ordnance Survey trigpoints in an easy to use format -- a Python_
dictionary.

If I'd like to see trigpoints close to me, say within 20kM, and less
than 60m above sea level I could tap the following in to my IPython_
session:

.. code-block:: pycon

    >>> Home = trigpoints.point.Point(52.015, -0.221)
    >>> for identifier, trigpoint in sorted(Trigpoints.items()):
    ...     if Home.__eq__(trigpoint, 20) and trigpoint.altitude < 60:
    ...         print("%s - %s" % (identifier, trigpoint))
    500936 - Broom Farm (52°03'57"N, 000°16'53"W alt 37m)
    501822 - Crane Hill (52°11'10"N, 000°14'51"W alt 58m)
    503750 - Limlow Hill (52°03'31"N, 000°04'20"W alt 59m)
    505681 - Sutton (52°06'24"N, 000°11'57"W alt 55m)

Or we can display all the trigpoints within a given region. For example,
to show trigpoints within the region from 51°52'15"N, 000°28'29"W to
52°09'07"N, 000°01'52"W.

.. code-block:: pycon

    >>> latitude_min = trigpoints.utils.to_dd(51, 52, 15)
    >>> longitude_min = trigpoints.utils.to_dd(0, -28, -29)
    >>> latitude_max = trigpoints.utils.to_dd(52, 9, 7)
    >>> longitude_max = trigpoints.utils.to_dd(0, -1, -52)
    >>> for identifier, trigpoint in sorted(Trigpoints.items()):
    ...     if latitude_min < trigpoint.latitude < latitude_max \
    ...       and longitude_min < trigpoint.longitude < longitude_max:
    ...         print("%s - %s" % (identifier, trigpoint))
    500928 - Bromley Common (51°52'17"N, 000°06'14"W alt 118m)
    500936 - Broom Farm (52°03'57"N, 000°16'53"W alt 37m)
    501097 - Bygrave (52°00'38"N, 000°10'24"W alt 97m)
    501417 - Cherrys Green (51°55'13"N, 000°01'52"W alt 126m)
    501428 - Chicksands North Radio Mast (52°02'46"N, 000°22'17"W alt 62m)
    501928 - Croydon Hill (52°07'37"N, 000°05'26"W alt 78m)
    502034 - Deacon Hill (51°57'19"N, 000°21'46"W alt 173m)
    502908 - Hammer Hill Farm (52°04'32"N, 000°24'05"W alt 89m)
    503138 - Higham Gobion (51°58'48"N, 000°23'55"W alt 75m)
    503750 - Limlow Hill (52°03'31"N, 000°04'20"W alt 59m)
    503774 - Little Easthall Farm (51°53'23"N, 000°15'23"W alt 140m)
    504024 - Marsh Farm Mh (51°55'24"N, 000°27'39"W alt 152m)
    505392 - Sish Lane (51°54'39"N, 000°11'11"W alt 136m)
    505681 - Sutton (52°06'24"N, 000°11'57"W alt 55m)
    505852 - Therfield (52°01'03"N, 000°03'38"W alt 168m)
    506163 - Warden Hill (51°55'20"N, 000°24'53"W alt 195m)
    506165 - Warden Tunnel (52°05'15"N, 000°22'30"W alt 84m)


Or we could generate a file to use with xearth that contains all the
trigpoints above 1000m above sea level:

.. code-block:: pycon

    >>> from upoints.utils import dump_xearth_markers
    >>> high_markers = {}
    >>> for identifier, trigpoint in Trigpoints.items():
    ...     if trigpoint.altitude > 1000:
    ...         high_markers[identifier] = trigpoint
    >>> f = open("high_markers.txt", "w")
    >>> f.write("\n".join(dump_xearth_markers(high_markers)))
    >>> f.close()

.. figure:: http://www.jnrowe.ukfsn.org/graphics/Scotland_trigpoints_mini.png
   :alt: xearth showing trigpoints higher than 1000m in Scotland
   :width: 192
   :height: 192
   :target: http://www.jnrowe.ukfsn.org/graphics/Scotland_trigpoints.png

Now we can use xearth_, or xplanet_, to visualise the trigpoints that
are higher than 1000m.  If you start xearth with the command ``xearth
-pos "fixed 57 -4" -mag 25 -noroot -markerfile high_markers.txt`` you
will see an image similar to the one on the right.

You could of course use ``dump_xearth_markers()`` to dump the entire
trigpoint database, but with over 6000 locations the result is just
going to be a sea of blurred text when rendered.

And it is possible to fold the generation of the ``high_markers``
dictionary in to a single operation using lambda expressions and
``filter()`` such as:

.. code-block:: pycon

    >>> high_markers = dict(filter(lambda x: x[1].altitude > 1000,
    ...                            Trigpoints.items()))

However, you opinion on whether this is cleaner or not depends a lot on
your background.  If only you could run ``filter()`` on a dictionary
directly, this would definitely be the better solution.  I'm going to
continue using the unrolled version on this page because it seems more
people are comfortable with them in spite of me favouring the
``filter()`` and ``lambda`` version, but it is just a matter of taste
and yours may vary.

Using ``trigpoints.py`` you could generate marker file for locations
with an altitude of between 900m and 910m using their location names as
labels.

.. code-block:: pycon

    >>> display_markers = {}
    >>> for identifier, trigpoint in Trigpoints.items():
    ...     if 900 < trigpoint.altitude < 910:
    ...         display_markers[identifier] = trigpoint
    >>> f = open("display_markers.txt", "w")
    >>> f.write("\n".join(dump_xearth_markers(display_markers,
    ...                                            "name")))
    >>> f.close()

The result of how that query could be shown with xplanet can be found to the
right.

.. figure:: http://www.jnrowe.ukfsn.org/graphics/ranged_trigpoints_mini.png
   :alt: xplanet showing trigpoints with altitudes in the range 900-910m
   :width: 192
   :height: 192
   :target: http://www.jnrowe.ukfsn.org/graphics/ranged_trigpoints.png

The ``Trigpoint`` class inherits from the ``Point`` class in
``point.py``, and therefore has all the same methods it does.  You can
calculate distances and bearings between trigpoints.  I suggest reading
the HTML files generated by epydoc_ that are included in the
tarball to see how it all works, including some more examples.

.. Note::
   And on a slight tangent, in my mind one of the best reasons for using
   Python is now evident, Nokia_ provide Python builds for some of their
   "smartphone" handsets.  This means it is possible to use
   ``trigpoints.py`` on the move using only the mobile phone in your
   pocket, and it makes for a fun diversion from Snake 3D.  Even as
   a simple database it can be surprisingly useful, especially given the
   difficulty of finding the minuscule trigpoint symbol on Ordnance
   Survey's Explorer series maps.

We're on a journey now, so if you can think of any cool uses for any of
the classes and functions in the ``upoints`` tarball drop me a mail.

.. _geolocation and path cross: geolocation_and_pathcross.html
.. _Google: http://www.google.com/
.. _the online database: http://www.haroldstreet.org.uk/trigpoints.php
.. _xearth: http://www.cs.colorado.edu/~tuna/xearth/
.. _xplanet: http://xplanet.sourceforge.net/
.. _Mercurial: http://www.selenic.com/mercurial/
.. _Python: http://www.python.org/
.. _Nokia: http://www.nokia.com/
.. _epydoc: http://epydoc.sourceforge.net/
.. _IPython: http://ipython.scipy.org/
.. _Bygrave: http://www.trigpointinguk.com/trigs/trig-details.php?t=1830
.. _TrigpointingUK: http://www.trigpointinguk.com/

..
    :vim: set ft=rst ts=2 sw=2 et:

