aboutsummaryrefslogtreecommitdiffstats
path: root/maketiles/packtiles
blob: f14a151c4a7fdb4b461868d3ec2bd73328c2de72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/perl -w
#
# packtiles:
# Pack a tree of tiles etc. as generated by 10kfull etc. into the more efficient
# packed format which is read by the tile server.
#
# Copyright (c) 2006 UK Citizens Online Democracy. All rights reserved.
# Email: chris@mysociety.org; WWW: http://www.mysociety.org/
#

my $rcsid = ''; $rcsid .= '$Id: packtiles,v 1.2 2006-09-20 13:30:48 chris Exp $';

use strict;

use CDB_File;
use Errno;
use File::Slurp;
use File::stat;
use IO::Dir;
use IO::File;

use constant TILEID_LEN => 20;
use constant BLOCKING => 14;

sub debug (@) {
    print STDERR @_;
}

sub pack_tile_dir ($$) {
    my ($indir, $outdir) = @_;
    
}

# new_index_block
# 
sub new_index_block () {
    # Initially all tiles are null so all bits are set.
    my $bitmaplen = int((BLOCKING * BLOCKING + 8) / 8);
    my $buf = "\xff" x $bitmaplen;
    $buf .= "\00" x (BLOCKING * BLOCKING * TILEID_LEN);
}

# add_tile_to_index_block BLOCK X Y ID
#
sub add_tile_to_index_block ($$$$) {
    my $x = $_[1];
    my $y = $_[2];
    my $id = $_[3];

    # clear the nullness bit.
    my $off = $x + $y * BLOCKING;
    my $b = unpack('C', substr($_[0], $off >> 3, 1));
    $b &= ~(1 << ($off & 7));
    substr($_[0], $off >> 3, 1) = pack('C', $b);
    
    # stick the tile itself in.
    my $bitmaplen = int((BLOCKING * BLOCKING + 8) / 8);
    substr($_[0], $bitmaplen + $off * TILEID_LEN, TILEID_LEN) = $id;
}

die "arguments are input (unpacked) tile directory, and output (packed)\n"
    . "tileset directories\n"
    if (@ARGV != 2);

my ($inputdir, $outputdir) = @ARGV;
$inputdir =~ s#/$##;
$outputdir =~ s#/$##;
die "$inputdir: not a directory" if (!-d $inputdir);
die "$outputdir: not a directory" if (!-d $outputdir);

# first make the index of tile locations to IDs.
my %index;
debug("reading index of tile locations to IDs...\n");
my $f = new IO::File("$inputdir/index", O_RDONLY);
my $len = stat($f)->size();
my $n = 0;
while (defined(my $line = $f->getline())) {
    debug("\rreading index: ", $f->tell(), "/$len bytes")
        if (0 == ($n % 100));
    ++$n;
    
    next if ($line =~ /^#/);

    chomp($line);
    my ($x, $y, $hash) = split(/ /, $line);
    my $id = pack('H*', $hash);

    my $x2 = int($x / BLOCKING);
    my $y2 = int($y / BLOCKING);

    $index{"$x2,$y2"} ||= new_index_block();
    add_tile_to_index_block(
                    $index{"$x2,$y2"},
                    $x % BLOCKING, $y % BLOCKING,
                    $id);
}
debug("\rreading index: $len/$len bytes\n");

die "$inputdir/index: $!" if ($f->error());
$f->close();

debug("writing index blocks to index.cdb... ");
my $C = new CDB_File("$outputdir/index.cdb", "$outputdir/index.new");
$C->insert(blocking => BLOCKING);
foreach (keys(%index)) {
    $C->insert($_, $index{$_});
}
debug("done\n");
debug("finalising index.cdb... ");
$C->finish();
debug("done\n");

debug("packing individual tile directories...\n");
my $n = 0;
for (my $u = 0; $u < 4096; ++$u) {
    my $subpath = sprintf('%x/%x/%x', $u & 0xf, ($u >> 4) & 0xf, $u >> 8);

    next unless (-d "$inputdir/$subpath");

    # Ensure the path exists.
    foreach my $p (qw(1 3 5)) {
        my $d = "$outputdir/" . substr($subpath, 0, $p);
        die "$d: mkdir: $!" if (!mkdir($d) && !$!{EEXIST});
    }

    my $in = "$inputdir/$subpath";
    my $out = "$outputdir/$subpath";

    my $f = new IO::File("$out/tiles", O_WRONLY | O_CREAT | O_TRUNC, 0644)
        or die "$out/tiles: open: $!";
    $C = new CDB_File("$out/tiles.cdb", "$out/tiles.tmp");
    my $d = new IO::Dir($in) or die "$in: $!";
    while (my $name = $d->read()) {
        next unless ($name =~ /^([0-9a-f]+)\.png$/);
        my $id = pack('H*', $1);
        my $buf = read_file("$in/$name") or die "$in/$name: $!";

        die "$in/$name: length of file read by File::Slurp did not match stat"
            unless (stat("$in/$name")->size() == length($buf));

        my $rec = sprintf('%x:%x', $f->tell(), length($buf));
        $C->insert($id, $rec);

        die "$out/tiles: write: $!" if ($len != $f->write($buf, $len));

        ++$n;
    }

    $f->close();
    $C->finish();

    debug("\rpacking tiles: $u/4096");
}

debug("\rpacking tiles: 4096/4096\n");
debug("packed $n tiles total\n");