Categories
Uncategorized

Updating maps … to outdated data

I caved, and bought a Garmin nüvi 1490. Not that my GPSMap 60CSx wasn’t great at routing, but remembering to update maps before travel and its fiddly mounting requirements were a pain.

So, two hours of downloading map updates, and I fire it up … to find that the three year old hotel in Dartmouth, NS we were staying in was in the wrong place on the map:

Compare with OpenStreetMap:

So now, back near home, I ask it to find a post office. Here’s me parked outside one; do you see it on the screenshots?

Again, compare with OpenStreetMap:

So I thought I’d help Garmin out, but their Report a Map Error page needs me to know the type of my GPS, its serial number, the type and revision of my map, my name and e-mail address, and the coordinates of the error. OSM has me spoiled: at best, I can go in and edit; at second best, I can drop markers on OpenStreetBugs to flag errors for others to fix.

Garmin already has my name, e-mail address, GPS type, serial number and map revision through myGarmin™. The company could just as easily have a web-based map correction system that would be point-and-click. Follow the Leader is one of Garmin’s mottoes. In terms of user correctability of maps, however, they’re only the leader because they don’t know they’ve been lapped.

Categories
GIS

Toronto Data really open

Looks like the data sets at toronto.ca/open might finally actually be open; that is, usable in a way that doesn’t bind subsequent users to impossible terms. The new licence (which unfortunately is behind a squirrelly link) basically just requires you to put a reference to this paragraph somewhere near your data/application/whatever:

Contains public sector Datasets made available under the City of Toronto’s Open Data Licence v2.0.

and a link to the licence, where possible.

Gone are the revocation clauses, which really prevented any open use before, because they would require you to track down all the subsequent users of the data and get them to stop. Good. I think we can now use the data in OpenStreetMap.

While commenting on the licence’s squirrelly URL — I mean, could you remember http://www1.toronto.ca/wps/portal/open_data/open_data_fact_sheet_details?vgnextoid=59986aa8cc819210VgnVCM10000067d60f89RCRD? — I stumbled upon the comedy gold that is the City of Toronto Comments Wall log. There goes my planned reading for the day.

Categories
GIS

we can has street names

Pierre Béland reminded the Canadian OSM group of the GeoBase WMS, which has all the NRN road names in it. Here’s my neighbourhood’s OpenStreetMap data with the road names overlaid.

Categories
GIS

quite a bit of work to do in Toronto

Looking at the OSM Inspector for Toronto, there are still a lot of nodes that need to be remapped (or OpenStreetMap users that need to be advised) when the big licence change comes.

(and yes, I blogged this just so I’d remember the link for OSM Inspector.)

Categories
GIS

Not really getting the Azimuthal Equidistant projection right

Update: thanks to André, this works! See the updated ogr2ogr line.

All I really wanted to do is make a map like this:

This is an Azimuthal Equidistant projection, with me at the centre (of course) and the rest of the world spread out in a fan by distance and bearing. It’s somewhat surprising to find that South Africa is almost directly east of Toronto, and New Zealand to the southwest.

If I had a directional antenna and a rotor, this map would show me where I would have to point the antenna to contact that part of the world. I can’t rotate my dipole (unless I commit some unauthorized local plate tectonics) so I’m stuck with where my antenna transmits and receives best.

The above map was made with AZ_PROJ, a PostScript program of some complexity for plotting world maps for radio use. The instructions for installing and running AZ_PROJ are complex and slightly dated. I got the above output running it through Ghostscript like this:

gs -sDEVICE=pdfwrite -sOutputFile=va3pid_world.pdf az_ini.ps -- az_proj.ps world.wdb

The format of the az_ini.ps file is complex, and I’m glad I’m an old PS hacker to be able to make head or tail of it.

For all its user-hostility, AZ_PROJ is powerful. Here’s a version of the map I wanted all along:

This shows my furthest QSO in each of the 16 compass directions. (You might note that North is empty: my furthest contact in that direction is some 13km away, whether by lack of folks in that sector or dodginess of my antenna.) Contrast that with my Mercator QSO map, and you’ll see that Azimuthal Equidistant is a much better projection for this application.

To show how radically different the world looks to different people, here’s the world according to my mate Rob in Hamilton, NZ:

I’d been trying to use OGR to transform arbitrary shapefiles into this projection. For maps entirely contained within the same hemisphere (so having extent less than ±90° in any cardinal direction), this works:

ogr2ogr -t_srs "+proj=aeqd  +lat_0=43.7308 +lon_0=-79.2647" out.shp in.shp

The lat_0 and lon_0 parameters are just where you want the centre of the map to be. Things get a bit odd if you try to plot the whole world:

The antipodes get plotted underneath, and everything looks messed up. I may have to take my question to GIS – Stack Exchange to see if I can find an answer. Still, for all its wrongness, you can make something pretty, like my whole world Maidenhead locator grid projected this way turns into a rose:

Update: André advised on Stack Exchange that you need to specify the correct radius:

ogr2ogr -t_srs "+proj=aeqd  +lat_0=43.7308 +lon_0=-79.2647 +R=6371000" va3pid-world.shp TM_WORLD_BORDERS_SIMPL-0.3.shp

This works rather well:

aeqd-qgis

 

 

Categories
GIS

binning by angle

I was trying to sort a series of polar coordinates by compass direction: everything in the N sector, then NNE, NE, ENE, E, … This is trickier than it looks, since the North sector includes angles between 348.75° and 11.25°.

It’s easier to think of the problem instead of being one of N bins centred on 0°, but rather one of 2N bins starting at 0°. Then, all one needs to do is shift everything round by one of these half bins (so +1, modulo 2N), then divide the number of bins back to the number we want. Done!

Here’s some code to do it. It may look like C-like pseudocode, but it’s real live AWK:

function bin(angle,nbins){
  return int(((int(angle/(360.0/(nbins*2)))+1)%(nbins*2))/2);
}

You probably want the angles to be between 0..360°. But you knew that.

Categories
Uncategorized

APRS is go!

Automatic Packet Reporting System — APRS — is rather clever. It’s a way of reporting position, status or messages via the amateur radio 2m band. Data is relayed via digipeaters, and routed to/from the internet APRS-IS system to any user worldwide.

It’s a little fiddly to set up, even with a very polished (read: $$) handheld radio like the Kenwood TH-D72A. I’m a bit disappointed that the purported SiRFstar III GPS in this radio takes forever to get a lock, but it’s a nice radio despite this.

The screenshot above shows aprs.fi‘s tracking of my handheld (VA3PID-7) last night as I walked to Toronto Mappy Hour.

Categories
GIS

Drawing Eggs

When designing wind farms, you need to keep the turbines a certain distance apart from one another. If you don’t, the wakes from the turbines reduce efficiency, and the turbulence can reduce the warranted life of the machine. Typically, a manufacturer might specify a minimum downwind separation of 5 diameters, and a crosswind separation of 3 diameters. It’s an easy check with a buffer overlap, but these buffers are elliptical, which not all GIS packages can draw.

Take, for example, the following three points designated as (completely made-up)  wind turbine locations:

name    XCOORD           YCOORD
1    557186.675000    4757125.590000
2    557447.931000    4756968.690000
3    557664.999000    4756817.810000

These look quite far apart, even if you were using large, 100+m diameter wind turbines:

But if we have a wind direction of 210°, downwind/crosswind separation of 5D & 3D respectively, and a 101m diameter rotor, it’s not so good:

Turbines 2 & 3 are too close together; the ellipses shouldn’t touch.

As awk is the only scripting language I have on my work computer, I wrote the script that generates the buffer shapefile in awk. The script calls Frank’s Shapefile C Library utilities to actually make the shapefile. Here’s the code:

#!/bin/awk -f
# draw an ellipse based on turbine location to generate
#  for WTG separation buffer
# scruss - 2011-09-27

# assumes that stdin has three columns:
#  1 - label
#  2 - x coordinate
#  3 - y coordinate

# variables:
#  diameter = rotor diameter
#  cross = crosswind separation, diameters
#  down = downwind separation, diameters
#  wind = prevailing wind direction
#  base = base for shape file name

BEGIN {
	OFMT="%.1f";
	CONVFMT="%.1f";
	OFS=" ";

	if (diameter < 0) {
		print "diameter must be set";
		exit;
	}

	if (cross < 0) {
		print "cross must be set";
		exit;
	}

	if (down < 0) {
		print "down must be set";
		exit;
	}

	if (down < cross) {
		print "down must be greater than cross";
		exit;
	}

	if (wind < 0) {
		print "wind must be set";
		exit;
	}

	if (base ~ /^$/) {
		print "base must be a string";
		exit;
	}

	pi = 3.141592654; # I know, I know ...
	# calc cartesian angle from wind bearing, in radians
	beta = ((450 - wind)%360) * pi/180;

	# output shapelib tools init commands
	print "dbfcreate " base " -s name 40";
	print "shpcreate " base " polygon";
}

# for every line
{
	name=$1;
	x=$2;
	y=$3;

	major = diameter * down/2;
	minor = diameter * cross/2;
	first="";
	points="";
	maxn=36;
	for (i=0; i<maxn; i++) {
	    alpha = (i * (360/maxn)) * pi/180;
	    x1 = x + major * cos(alpha) * cos(beta) - minor * sin(alpha) * sin(beta);
	    y1 = y +  major * cos(alpha) * sin(beta) + minor * sin(alpha) * cos(beta);
	    if (i == 0) { # store the first point
		first= x1 " " y1;
	    }
	    points = points  " " x1 " " y1;
	}
	points = points  " " first;
	print "dbfadd " base ".dbf " name;
	print "shpadd " base, points;
}

awk is charmingly odd in that you can specify variable on the command line. Here’s how I called it, with the above coordinates as input:

awk -v diameter=101 -v cross=3 -v down=5 -v wind=210 -v base="fakewtg-ellipse" -f separation.awk

Pipe the output through a shell, and there are your ellipses.

Categories
Uncategorized

#mapfail

… or “トロント動物園“, as we locals purportedly call it. The This place has unverified edits legend is a bit of a giveaway. Looks like there’s been some messing about with Google Map Maker, which isn’t always the best tool for the job.

Categories
GIS

Ham Radio log to interactive OpenStreetMap

You might notice that there’s now a Ham Radio QSO Map lurking on the front page. Thanks to the WordPress OpenStreetMap plugin (which I’ve slightly abused before). Here’s a small piece of Perl which will take your ADIF log and convert it to a WP-OSM marker file.

Note that this program assumes you’ve downloaded your log from QRZ.com, as it requires the locator field for both inbound and outbound stations.

#!/usr/bin/perl -w
# adif2osm - convert ADIF log to OSM map file
# scruss.com / VA3PID - 2011/06/19

use strict;
use constant MARKERDIR =>
  'https://glaikit.org/wp-content/plugins/osm/icons/';
use constant QRZURL => 'http://qrz.com/db/';
sub maidenhead2latlong;

my ( $temp, @results ) = '';

### Fast forward past header
while (<>) {
  last if m/<eoh>\s+$/i;
}

### While there are records remaining...
while (<>) {
  $temp .= $_;

  ### Process if end of record tag reached
  if (m/<eor>\s+$/i) {
    my %hash;
    $temp =~ s/\n//g;
    $temp =~ s/<eoh>.*//i;
    $temp =~ s/<eor>.*//i;
    my @arr = split( '<', $temp );
    foreach (@arr) {
      next if (/^$/);
      my ( $key, $val ) = split( '>', $_ );
      $key =~ s/:.*$//;
      $hash{ lc($key) } = $val unless ( $key eq '' );
    }
    push @results, \%hash;
    $temp = '';
  }
}

# generate OSM plugin file
my @data = ();
my ( $mygrid, $station_callsign ) = '';

# output header
print
  join( "\t", qw/lat lon title description icon iconSize iconOffset/ ),
  "\n";
foreach (@results) {
  next unless ( exists( $_->{gridsquare} ) && exists( $_->{call} ) );
  $mygrid = $_->{my_gridsquare}
    if ( exists( $_->{my_gridsquare} ) );
  $station_callsign = $_->{station_callsign}
    if ( exists( $_->{station_callsign} ) );

  push @data, $_->{freq} . ' MHz' if ( exists( $_->{freq} ) );
  $data[$#data] .= ' (' . $_->{band} . ')' if ( exists( $_->{band} ) );
  push @data, $_->{mode} if ( exists( $_->{mode} ) );
  push @data, $_->{qso_date} . ' ' . $_->{time_on} . 'Z'
    if ( exists( $_->{qso_date} ) && exists( $_->{time_on} ) );
  my ( $lat, $long ) = maidenhead2latlong( $_->{gridsquare} );
  print join( "\t",
    $lat,
    $long,
    '<a href="' . QRZURL . $_->{call} . '">' . $_->{call} . '</a>',
    join( ' - ', @data ),
    MARKERDIR . 'wpttemp-green.png',
    '0,-24' ),
    "\n";

  @data = ();
}

# show home station last, so it's on top
my ( $lat, $long ) = maidenhead2latlong($mygrid);
print join( "\t",
  $lat,
  $long,
  '<a href="'
    . QRZURL
    . $station_callsign . '">'
    . $station_callsign . '</a>',
  'Home Station',
  MARKERDIR . 'wpttemp-red.png',
  '0,-24' ),
  "\n";

exit;

sub maidenhead2latlong {

  # convert a Maidenhead Grid location (eg FN03ir)
  #  to decimal degrees
  # this code could be cleaner/shorter/clearer
  my @locator =
    split( //, uc(shift) );    # convert arg to upper case array
  my $lat      = 0;
  my $long     = 0;
  my $latdiv   = 0;
  my $longdiv  = 0;
  my @divisors = ( 72000, 36000, 7200, 3600, 300, 150 )
    ;                          # long,lat field size in seconds
  my $max = ( $#locator > $#divisors ) ? $#divisors : $#locator;

  for ( my $i = 0 ; $i <= $max ; $i++ ) {
    if ( int( $i / 2 ) % 2 ) {    # numeric
      if ( $i % 2 ) {             # lat
        $latdiv = $divisors[$i];    # save for later
        $lat += $locator[$i] * $latdiv;
      }
      else {                        # long
        $longdiv = $divisors[$i];
        $long += $locator[$i] * $longdiv;
      }
    }
    else {                          # alpha
      my $val = ord( $locator[$i] ) - ord('A');
      if ( $i % 2 ) {               # lat
        $latdiv = $divisors[$i];    # save for later
        $lat += $val * $latdiv;
      }
      else {                        # long
        $longdiv = $divisors[$i];
        $long += $val * $longdiv;
      }
    }
  }
  $lat  += ( $latdiv / 2 );         # location of centre of square
  $long += ( $longdiv / 2 );
  return ( ( $lat / 3600 ) - 90, ( $long / 3600 ) - 180 );
}

You’ll need to update MARKERDIR to reflect your own WP-OSM installation. Mine might move, so if you don’t change it, and you don’t get markers, please don’t blame me.

The basic code to include a map is like this:

You’ll need to change the marker_file URL, too.

Note that, while this script generates links into the QRZ callsign database, it doesn’t hit that site unless you click a link.