diff options
-rwxr-xr-x | maketiles/10kfull | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/maketiles/10kfull b/maketiles/10kfull new file mode 100755 index 000000000..cac0829f4 --- /dev/null +++ b/maketiles/10kfull @@ -0,0 +1,159 @@ +#!/usr/bin/perl -w +# +# 10kfull: +# Turn the 1:10,000 Ordnance Survey map tiles into 254-by-254 pixmaps at full +# resolution. +# +# Copyright (c) 2005 UK Citizens Online Democracy. All rights reserved. +# Email: chris@mysociety.org; WWW: http://www.mysociety.org/ +# + +my $rcsid = ''; $rcsid .= '$Id: 10kfull,v 1.1 2006-09-14 17:45:08 chris Exp $'; + +use strict; + +use Digest::SHA1; +use Errno; +use File::stat; +use Geography::NationalGrid; +use IO::Dir; +use IO::File; + +use constant TILE_SIZE_PX => 7874; +use constant TILE_SIZE_M => 5000; + +use constant SUBTILE_SIZE_PX => 254; +use constant SUBTILES_PER_TILE => (TILE_SIZE_PX / SUBTILE_SIZE_PX); + +sub debug (@) { + print STDERR @_; +} + +sub System (@) { + debug("executing command: ", @_, "\n"); + system(@_); + die "command failed with status $?" if ($?); +} + +die "arguments are directory containing input tiles in TIFF format and directory to which output tiles are to be written" + if (2 != @ARGV); + +my ($inputdir, $outputdir) = @ARGV; +$inputdir =~ s#/$##; +$outputdir =~ s#/$##; +die "$inputdir: not a directory" if (!-d $inputdir); +die "$outputdir: not a directory" if (!-d $outputdir); + +# +# Within the output directory we create a subdirectory tiles/ containing tile +# images named for their SHA1 checksums, and a text file mapping tile +# coordinates to the SHA1 sums. +# + +die "$outputdir/tiles: $!" + if (!mkdir("$outputdir/tiles", 0755) && !$!{EEXIST}); + +my $index = new IO::File("$outputdir/index", O_WRONLY | O_CREAT | O_TRUNC, 0644) + or die "$outputdir/index: $!"; + +$index->print(<<EOF); +# This index file connects individual tiles of the 1:10,000 Ordnance Survey +# maps at their full resolution (0.635m/px). Each such tile is 254 by 254 +# pixels. The tiles are indexed according to their coordinates within the full +# Ordnance Survey grid, with (0, 0) being the tile at the SW corner of the grid +# (immediately NE of the origin), (1, 0) being the tile to its east, and (0, 1) +# the tile immediately to its north. Note that not all tiles are present. For +# each such tile position this file lists the SHA1 message-digest of the image +# file which represents the tile. +EOF + +my $d = new IO::Dir($inputdir) + or die "$inputdir: $!"; + +my $total_input_size = 0; # KB +my $total_output_size = 0; # KB +while (my $name = $d->read()) { + next unless ($name =~ /\.tiff?$/i); + debug("have image file $inputdir/$name\n"); + + my ($gridsq, $offset, $quadrant) + = map { uc($_) } ($name =~ /^([A-Z]{2})(\d{2})([NS][EW])\./i); + + if (!$gridsq) { + debug(" -> doesn't seem to be a valid 1:10,000 image tile; ignoring\n"); + next; + } + + my $st = stat("$inputdir/$name"); + my $input_tile_size = $st->size(); + debug(" tile size = $input_tile_size bytes\n"); + $total_input_size += $input_tile_size / 1024.; + + debug(" -> grid square = $gridsq; offset = $offset; quadrant = $quadrant\n"); + my $p = new Geography::NationalGrid('GB', + GridReference => "$gridsq $offset"); + + my ($E, $N) = ($p->easting(), $p->northing()); + debug(" -> SW corner of grid square is at ($E, $N)\n"); + + $N += TILE_SIZE_M if ($quadrant =~ /^N/); + $E += TILE_SIZE_M if ($quadrant =~ /E$/); + debug(" => SW corner of tile is at ($E, $N)\n"); + + # Figure out the tile offsets. + my $x = ($E / TILE_SIZE_M) * SUBTILES_PER_TILE; + my $y = ($N / TILE_SIZE_M) * SUBTILES_PER_TILE; + debug(" => subtile col/row for SW-most subtile is ($x, $y)\n"); + + # Now split this tile into subtiles. + System("tifftopnm $inputdir/$name |" + . " pnmtilesplit -P -f '$outputdir/%d,%d.png'" + . " -p 'pnmtopng -compression 9 2>/dev/null' 254 254"); + + # Consider each of the subtiles. + debug("finding SHA1 digests of tiles and adding them to index...\n"); + my $nduplicates = 0; + my $nnew = 0; + my $size = 0; + for (my $j = 0; $j < SUBTILES_PER_TILE; ++$j) { + debug("\r$j/", SUBTILES_PER_TILE); + for (my $i = 0; $i < SUBTILES_PER_TILE; ++$i) { + my $fn = "$outputdir/$i,$j.png"; + my $f = new IO::File($fn, O_RDONLY) + or die "$fn: $!"; + my $sha = new Digest::SHA1(); + $sha->addfile($f); + $sha = $sha->hexdigest(); + $f->close(); + $index->printf("%d %d %s\n", $x + $i, $y + $j, $sha); + my ($n1, $n2, $n3) = ($sha =~ /^(.)(.)(.)/); + mkdir("$outputdir/tiles/$n1"); + mkdir("$outputdir/tiles/$n1/$n2"); + mkdir("$outputdir/tiles/$n1/$n2/$n3"); + my $fn2 = "$outputdir/tiles/$n1/$n2/$n3/$sha.png"; + if (-e $fn2) { + unlink($fn); + ++$nduplicates; + } else { + rename($fn, $fn2) or die "rename: $!"; + ++$nnew; + my $st = stat($fn2); + $size += $st->size(); + } + } + } + debug("\r", SUBTILES_PER_TILE, "/", SUBTILES_PER_TILE, "\n"); + debug("had $nduplicates duplicate tiles + $nnew new, $size bytes\n"); + debug(" output subtiles are ", + sprintf('%.1f%%', 100 * $size / $input_tile_size), + " of input tile size for this tile\n"); + $total_output_size += $size / 1024.; + debug(" so far total input size ", + sprintf('%.1fKB', $total_input_size), "\n"); + debug(" so far total output size ", + sprintf('%.1fKB', $total_output_size), "\n"); + debug(" output subtiles are ", + sprintf('%.1f%%', 100 * $total_output_size / $total_input_size), + " of input tile size so far overall\n"); + $index->flush(); +} |