[OSM-dev] Hierachical search of sea tiles
Frederik Ramm
frederik at remote.org
Fri May 4 02:20:35 BST 2007
Hi,
> I would like to propose this, if it hasn't been already. If a tile is marked
> "all-sea", then that information applies to all subtiles, and the handler
> does a search 'upwards' through the zoom levels.
I would like to weaken this a bit. I would say: If a tile is marked
"all-sea", then that information *is the default* for all sub-tiles. If
a sub-tile is provided in spite of that, then it takes precedence.
This rule makes insertion of new tiles a bit easier. Consider a
situation where you have a level-7 tile marked all-sea, and someone
uploads a little rock in the open sea on level-12. You would then have
to remove the all-sea marking for the level-7 tile, and replace it by: 3
all-sea markers on level-8, 3 all-sea markers on level 9, 3 all-sea
markers on level-10, 3 all-sea markers on level 11, 3 all-sea markers on
level 12 plus the one new tile.
If you say that the all-sea marker is just a default for searching
"upwards" but never a rule for the other direction, then you would not
need that complexity.
Attached is a perl script that uses the "sea tile" PNG file form
tiles at home SVN to generate database insert statements for the tile_meta
table with an "ocean" flag (yet to be introduced by way of ALTER TABLE) set.
It uses exactly this "default" method suggested by you; first computing
"downwards" from level-12 to find the low zoom level tiles that are all
water, then generate insert statements for those, and only generate
statements for the higher-level tiles if the tile above is not a sea
tile. (Martijn said he can also provide the same for higher zoom levels
but I think we do not need to seed the database with anything more
precise; level 12 is just fine, and the modified upload code will
gradually fill out the lower zoom levels.)
The theoretical number of sea tiles in level 12 and lower is about 15
million (assuming 70% ocean cover). This method boils those down to an
easily handleable 108,000.
I'd suggest inserting these values into the database. The next steps
would then be
* delete all-blue tiles from the filesystem and set the ocean flag for
these tiles in the meta table
* add the 404 handler to return the sea tile where appropriate
* modify the upload code to actually set the "ocean" flag for only-blue
tiles instead of storing a tile
* modify the t at h client to communicate the fact that something is
only-blue accordingly.
Bye
Frederik
(Script follows. The insert statement generation will probably have to
be changed depending on how we flag the ocean info in the database. And
I forgot, didn't we have an empty flag in the table as well? This could
be combined with my "ocean" flag to form one property that can take the
values empty-sea,empty-land or "proper tile".)
#!/usr/bin/perl -w
use strict;
use GD::Image;
use constant TILETYPE_UNKNOWN => 0;
use constant TILETYPE_LAND => 1;
use constant TILETYPE_SEA => 2;
use constant TILETYPE_TILE => 3;
my ($world_fh, $tileinfo_fh);
our $world_im;
my $seatiles;
open $world_fh, "<oceantiles_12.png" or die;
$world_im = GD::Image->newFromPng( $world_fh, 1 );
print STDERR "reading level 12 data\n";
# read image. for every level-12 sea tile, increase the
# "sea counter" of the level-11 tile it lies in.
for (my $x=0; $x<4096; $x++)
{
for (my $y=0; $y<4096; $y++)
{
$seatiles->[11]->[$x/2]->[$y/2]+=0;
if (get_type($world_im, $x, $y) == TILETYPE_SEA)
{
$seatiles->[11]->[$x/2]->[$y/2]++;
}
}
printf STDERR "\r%d%%...", $x*100/4096;
}
# now do this for other levels - i.e. whenever you have
# a level-11 tile with a sea counter of 4 (i.e. fully sea),
# increase level-10 counter etc.
for (my $lev=11; $lev>3; $lev--)
{
print STDERR "\rworking down zoom levels... $lev ";
for (my $x=0; $x<2**$lev; $x++)
{
for (my $y=0; $y<2**$lev; $y++)
{
$seatiles->[$lev-1]->[$x/2]->[$y/2]+=0;
if ($seatiles->[$lev]->[$x]->[$y] == 4)
{
$seatiles->[$lev-1]->[$x/2]->[$y/2]++;
}
}
}
}
# now work our way back. if a level-n tile is a sea tile,
# and the level-(n-1) tile wasn't, then write a database
# record.
for (my $lev=4; $lev<12; $lev++)
{
print STDERR "\rworking up zoom levels... $lev ";
for (my $x=0; $x<2**$lev; $x++)
{
for (my $y=0; $y<2**$lev; $y++)
{
if (($seatiles->[$lev]->[$x]->[$y] == 4) &&
($seatiles->[$lev-1]->[$x/2]->[$y/2]<4))
{
write_seatile_record($lev,$x,$y);
}
}
}
}
# special case level 12:
print STDERR "\nfinal level-12 processing\n";
for (my $x=0; $x<4096; $x++)
{
for (my $y=0; $y<4096; $y++)
{
if ((get_type($world_im, $x, $y) == TILETYPE_SEA) &&
($seatiles->[11]->[$x/2]->[$y/2]<4))
{
write_seatile_record(12,$x,$y);
}
}
printf STDERR "\r%d%%...", $x*100/4096;
}
print STDERR "\rcompleted\n";
sub clear_seatiles
{
my ($z,$x,$y) = @_;
$seatiles->[$z]->[$x]->[$y]=0;
return if ($z==11);
clear_seatiles($z+1,$x*2,$y*2);
clear_seatiles($z+1,$x*2+1,$y*2);
clear_seatiles($z+1,$x*2,$y*2+1);
clear_seatiles($z+1,$x*2+1,$y*2+1);
}
sub get_type
{
my($image, $x, $y) = @_;
my($r,$g,$b) = $image->rgb( $image->getPixel( $x,$y ) );
return TILETYPE_LAND if $r == 0 && $g == 255 && $b == 0;
return TILETYPE_SEA if $r == 0 && $g == 0 && $b == 255;
return TILETYPE_TILE if $r == 255 && $g == 255 && $b == 255;
return TILETYPE_UNKNOWN if $r == 0 && $g == 0 && $b == 0;
die "Wierd tiletype at [$x,$y]: ($r,$g,$b)\n";
}
sub write_seatile_record
{
my ($z,$x,$y)=@_;
printf "replace into tiles_meta ".
"(x, y, z, type, size, date, user, version, ocean) ".
"values(%d,%d,%d,%d,%d,%d,%d,%d,1);\n",
$x,
$y,
$z,
0,
0,
0,
0,
0;
}
--
Frederik Ramm ## eMail frederik at remote.org ## N49°00.09' E008°23.33'
More information about the dev
mailing list