[OSM-dev] Hierachical search of sea tiles

Frederik Ramm frederik at remote.org
Fri May 4 02:20:35 BST 2007


> 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.


(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++)
	    if (get_type($world_im, $x, $y) == TILETYPE_SEA)
     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++)
             if ($seatiles->[$lev]->[$x]->[$y] == 4)

# 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) && 

# 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) && 
     printf STDERR "\r%d%%...", $x*100/4096;

print STDERR "\rcompleted\n";

sub clear_seatiles
     my ($z,$x,$y) = @_;

     return if ($z==11);

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) ".

Frederik Ramm  ##  eMail frederik at remote.org  ##  N49°00.09' E008°23.33'

More information about the dev mailing list