summaryrefslogtreecommitdiff
path: root/script/osm2navit_sea/osm2navit_sea.pl
diff options
context:
space:
mode:
Diffstat (limited to 'script/osm2navit_sea/osm2navit_sea.pl')
-rw-r--r--script/osm2navit_sea/osm2navit_sea.pl1010
1 files changed, 1010 insertions, 0 deletions
diff --git a/script/osm2navit_sea/osm2navit_sea.pl b/script/osm2navit_sea/osm2navit_sea.pl
new file mode 100644
index 00000000..a65c74d7
--- /dev/null
+++ b/script/osm2navit_sea/osm2navit_sea.pl
@@ -0,0 +1,1010 @@
+#!/usr/bin/perl -w
+#
+# Vincent "latouche" Touchard, vtouchar@latouche.info
+# 2008/09/10
+#
+# Transform a .osm file adding polygons representing the sea and islands.
+# The sea is a way with the tag natural=coastline_outer and the islands are
+# ways with the tag natural=coastline_inner.
+# Adding those tags to osm2navit allow the see to be viewed by navit
+#
+# To avoid big polygons, the map is divided into small tiles (0.5°x0.5°)
+#
+# usage:
+# osm2navit_sea.pl minlat minlon maxlat maxlon source_file output_file
+#
+# license: GPL
+#
+# history:
+# v1: first release
+# v2: 2008/09/06
+# - map split into tiles of 0.5x0.5°
+# v3: 2008/09/10
+# - detects islands vs lakes
+# takes each polygon and try to find is it is inside an other one. If true,
+# it is an island, if not, it is the sea. Should be a little more clever to detect
+# lake (tagged as natural=coastline instead of natural=water) inside islands.
+# This algo is not perfect but works in most cases
+# - sea tile with no way crossing are now drawn in a more clever way:
+# a node for each corner is added to nodes arrays and a way linking those
+# node is added. Those components are then added to the output file during
+# the last section of this script. This allow to detects islands in such tiles
+# as islands and not sea
+# v4: 2009/03/14
+# - new way to compute the intersection of a segment and the border of a tile
+# - don't use nodes that are out of the bbox specified as argument
+# - use lazy regex to parse the .osm file
+#
+# TODO:
+# - better lookup_handler (cf this fuction for more info)
+# - detect clockwise/anticlockwise ways => sea or land
+# for now -> every closed area is considered as land which is false.
+# some lakes are tags as natural=coastline instead of natural=water
+# ==> done, the algo should be improved for better results
+
+use strict;
+use warnings;
+
+use Math::Trig;
+use POSIX qw(ceil);
+use way;
+
+# lon € [-180, 180]
+# lat € [-85.0511, 85.0511]
+if ( @ARGV ne 6 ||
+ abs($ARGV[0]) > 85.0511 || abs($ARGV[2]) > 85.0511 ||
+ abs($ARGV[1]) > 180 || abs($ARGV[3]) > 180) {
+ print "Usage: osm2navit_sea.pl minlat minlon maxlat maxlon source_file output_file\n";
+ exit(1);
+}
+
+
+my $source=$ARGV[4];
+my $destination=$ARGV[5];
+
+my ($minlat, $minlong, $maxlat, $maxlong);
+my ($minlat2, $minlong2, $maxlat2, $maxlong2);
+
+$minlat=$ARGV[0];
+$minlong=$ARGV[1];
+$maxlat=$ARGV[2];
+$maxlong=$ARGV[3];
+
+#$minlat2=int($minlat*2)/2;
+#$minlong2=int($minlong*2)/2;
+#$maxlat2=ceil($maxlat*2)/2;
+#$maxlong2=ceil($maxlong*2)/2;
+
+$minlat2=$minlat;
+$minlong2=$minlong;
+$maxlat2=$maxlat;
+$maxlong2=$maxlong;
+
+
+#$rect_length=2*abs($maxlong-$minlong) + 2*abs($maxlat-$minlat);
+#
+#push (@corner_rect2lin, 0);
+#push (@corner_rect2lin, abs($maxlong-$minlong));
+#push (@corner_rect2lin, abs($maxlong-$minlong) + abs($maxlat-$minlat));
+#push (@corner_rect2lin, $rect_length - abs($maxlat-$minlat));
+
+my $osm_id=-1;
+
+my (%new_ways, %nodes, %ways, %tiles, @new_nodes);
+
+my ($i, $j, $way, $node, $tile);
+
+my $debug=0;
+
+sub debug {
+ print shift if ( $debug );
+}
+
+# project a node on a bounding box
+sub get_projection {
+ my ($lat, $lon, $minlat, $minlon, $maxlat, $maxlon) = @_;
+
+ my ($a, $b, $c, $d);
+
+ $a = abs( $lat - $minlat);
+ $b = abs( $lat - $maxlat);
+ $c = abs( $lon - $minlon);
+ $d = abs( $lon - $maxlon);
+
+
+ if ( $a <= $b ) {
+ if ( $a <= $c ) {
+ if ( $a <= $d ) {
+ # a
+ $lat=$minlat;
+ #$lon=$node->{"lon"};
+ } else {
+ # d
+ #$lat=$node->{"lat"};
+ $lon=$maxlon;
+ }
+ } else {
+ if ( $c <= $d ) {
+ # c
+ #$lat=$node->{"lat"};
+ $lon=$minlon;
+ } else {
+ #d
+ #$lat=$node->{"lat"};
+ $lon=$maxlon;
+ }
+ }
+ } else {
+ if ( $b <= $c ) {
+ if ( $b <= $d ) {
+ #b
+ $lat=$maxlat;
+ #$lon=$node->{"lon"};
+ } else {
+ # d
+ #$lat=$node->{"lat"};
+ $lon=$maxlon;
+ }
+ } else {
+ if ( $c <= $d ) {
+ # c
+ #$lat=$node->{"lat"};
+ $lon=$minlon;
+ } else {
+ #d
+ #$lat=$node->{"lat};
+ $lon=$maxlon;
+ }
+ }
+ }
+
+ return (($lat, $lon));
+}
+
+# Projet a node on a rectangle to a segment
+sub get_proj_rect2lin {
+ my ($lat, $lon, $minlat, $minlon, $maxlat, $maxlon) = @_;
+
+
+ if ($lat eq $maxlat) {
+ return abs($minlon - $lon);
+ } else {
+ if ($lon eq $maxlon) {
+ return abs($maxlon-$minlon) + abs($maxlat - $lat);
+ } else {
+ if ($lat eq $minlat) {
+ return abs($maxlon-$minlon) + abs($maxlat-$minlat) + abs($maxlon-$lon);
+ } else {
+ return 2*abs($maxlon-$minlon) + abs($maxlat-$minlat) + abs($minlat - $lat);
+ }
+ }
+ }
+}
+
+# projet a node from a segment to a rectangle
+sub getnode_proj_lin2rect {
+ my ($length, $minlat, $minlon, $maxlat, $maxlon) = @_;
+ my ($lat, $lon);
+
+ if ( $length > 2*abs($maxlon-$minlon) + abs($maxlat-$minlat)) {
+ $lat = ($minlat + $length - 2*abs($maxlon-$minlon) - abs($maxlat-$minlat));
+ $lon = ($minlon);
+ } else {
+ if ( $length > abs($maxlon-$minlon) + abs($maxlat-$minlat)) {
+ $lat = ($minlat);
+ $lon = ($maxlon - $length + abs($maxlon-$minlon) + abs($maxlat-$minlat));
+ } else {
+ if ( $length > abs($maxlon-$minlon)) {
+ $lat = ($maxlat - $length + abs($maxlon-$minlon));
+ $lon = ($maxlon);
+ } else {
+ $lat = ($maxlat);
+ $lon = ($minlon + $length);
+ }
+ }
+ }
+
+ return (($lat, $lon));
+}
+
+
+
+# get the distance between to nodes on the same rectangle
+sub get_path_length {
+ my ($lat1, $lon1, $lat2, $lon2, @box) = @_;
+
+ my ($a, $b, $rect_length, $dist);
+
+
+ $a = get_proj_rect2lin ( $lat1, $lon1, @box);
+ $b = get_proj_rect2lin ( $lat2, $lon2, @box);
+
+ $dist = $b - $a;
+ $rect_length = 2*abs( $box[2] - $box[0]) + 2*abs( $box[3] - $box[1]);
+ return $dist > 0 ? $dist : $dist + $rect_length;
+}
+
+# get the nodes to go through to join an other node over a rectangle
+sub get_path {
+ my ($lat1, $lon1, $lat2, $lon2, @box) = @_;
+
+ my ($a, $b, $rect_length, $dist, @corner_rect2lin);
+ my @path=();
+
+ $a = get_proj_rect2lin ( $lat1, $lon1, @box);
+ $b = get_proj_rect2lin ( $lat2, $lon2, @box);
+
+ $rect_length = 2*abs( $box[2] - $box[0]) + 2*abs( $box[3] - $box[1]);
+
+ $dist = $b - $a;
+ if ($dist <0) {
+ $b+=$rect_length;
+ }
+
+ @corner_rect2lin = ( 0,
+ abs( $box[3] - $box[1]),
+ abs( $box[3] - $box[1]) + abs( $box[2] - $box[0]),
+ $rect_length - abs( $box[2] - $box[0]));
+
+ foreach (@corner_rect2lin) {
+ if ($_ > $a && $_ < $b) {
+ my ($lat, $lon) = getnode_proj_lin2rect ($_, @box);
+ push (@path, {"lat" => $lat, "lon" => $lon});
+ }
+ }
+
+ foreach (@corner_rect2lin) {
+ if ($_ + $rect_length > $a && $_ + $rect_length < $b) {
+ my ($lat, $lon) = getnode_proj_lin2rect ($_, @box);
+ push (@path, {"lat" => $lat, "lon" => $lon});
+ }
+ }
+
+
+ return @path;
+}
+
+# type of tile at zoom12 (land, sea, both, unknown.
+# Uses oceantiles_12.dat for tiles@home project
+# it checks the tile at level 12 containing the point ($lat, $lon)
+# dx and dy are used to specify an offset: read tile (x+dx, y+dy)
+# A more clever way whould be to check all the tile contained in and area
+# and guess the final type
+# one sea or more => sea
+# one land or more => land
+# only both or unknown => since tiles are quite big, should be land
+#
+sub lookup_handler {
+ my ($lat, $lon, $dx, $dy) = @_;
+
+ # http://wiki.openstreetmap.org/index.php/Slippy_map_tilenames
+ # http://almien.co.uk/OSM/Tools/Coord
+ my $zoom=12;
+ # $tilex=int( ( $minlon +180)/360 *2**$zoom ) ;
+ my $tilex=int( ( $lon +180)/360 *2**$zoom ) ;
+ # $tiley=int( (1 - log(tan( $maxlat *pi/180) + sec( $maxlat *pi/180))/pi)/2 *2**$zoom ) ;
+ my $tiley=int( (1 - log(tan( $lat *pi/180) + sec( $lat *pi/180))/pi)/2 *2**$zoom ) ;
+
+
+ my $tileoffset = (($tiley+$dy) * (2**12)) + ($tilex+$dx);
+
+ my $fh;
+ open($fh, "<", "data/oceantiles_12.dat") or die ("Cannot open oceantiles_12.dat: $!");
+
+
+ seek $fh, int($tileoffset / 4), 0;
+ my $buffer;
+ read $fh, $buffer, 1;
+ $buffer = substr( $buffer."\0", 0, 1 );
+ $buffer = unpack "B*", $buffer;
+ my $str = substr( $buffer, 2*($tileoffset % 4), 2 );
+
+ close($fh);
+
+ #print "S" if $str eq "10";
+ #print "L" if $str eq "01";
+ #print "B" if $str eq "11";
+ #print "?" if $str eq "00";
+
+ return $str;
+}
+
+
+# return the intersection of a segment with the border of a tile
+sub intersec_way_tiles {
+ my ($node_in_lat, $node_in_lon,
+ $node_ext_lat,$node_ext_lon,
+ $tile_minlat, $tile_minlon, $tile_maxlat, $tile_maxlon) = @_;
+ my ($lat, $lon);
+
+ debug "intersec_way_tiles - $node_in_lat, $node_in_lon && $node_ext_lat,$node_ext_lon && $tile_minlat, $tile_minlon, $tile_maxlat, $tile_maxlon\n";
+
+ # case #1: node_ext above tile - try intersec with the top of the tile
+ if ($node_ext_lat > $tile_maxlat || $node_ext_lat < $tile_minlat) {
+ $lat = ($node_ext_lat > $tile_maxlat) ? $tile_maxlat : $tile_minlat;
+ if ($node_in_lat != $node_ext_lat) {
+ $lon = $node_in_lon + ($lat - $node_in_lat) * ($node_ext_lon - $node_in_lon) / ($node_ext_lat - $node_in_lat);
+ } else { # node_ext == node
+ $lon = $node_in_lon;
+ }
+ if ($lon > $tile_minlon && $lon < $tile_maxlon) {
+ return ($lat, $lon);
+ }
+ }
+ # case #2: node ext on the left or on the right of the tile
+ if ($node_ext_lon > $tile_maxlon || $node_ext_lon < $tile_minlon) {
+ $lon = ($node_ext_lon > $tile_maxlon) ? $tile_maxlon : $tile_minlon;
+ if ($node_in_lon != $node_ext_lon) {
+ $lat = $node_in_lat + ($lon - $node_in_lon) * ($node_ext_lat - $node_in_lat) / ($node_ext_lon - $node_in_lon); # thales
+ } else { # node_ext == node
+ $lat = $node_in_lat;
+ }
+ if ($lat > $tile_minlat && $lat < $tile_maxlat) {
+ return ($lat, $lon);
+ }
+ }
+
+ return ($node_ext_lat, $node_ext_lon);
+}
+
+# tells if a node is inside a polygon or not
+# not efficient a 100%, but quite good results
+# widely inspired from close-areas.pl from the T@h project
+sub polygon_contains_node {
+ my ($way, $node) = @_;
+
+
+ my $counter = 0;
+
+ my @way_nodes = $way->nodes();
+ my $p1 = $nodes{$way->first_node()};
+ my $n = scalar(@way_nodes);
+
+ for (my $i=1; $i < $n; $i++) {
+ my $p2 = $nodes{$way_nodes[$i]};
+
+ if ($node->{"lat"} > $p1->{"lat"} || $node->{"lat"} > $p2->{"lat"}) {
+ if ($node->{"lat"} < $p1->{"lat"} || $node->{"lat"} < $p2->{"lat"}) {
+ if ($node->{"lon"} < $p1->{"lon"} || $node->{"lon"} < $p2->{"lon"}) {
+ if ($p1->{"lat"} != $p2->{"lat"}) {
+ my $xint = ($node->{"lat"}-$p1->{"lat"})*($p2->{"lon"}-$p1->{"lon"})/($p2->{"lat"}-$p1->{"lat"})+$p1->{"lon"};
+ if ($p1->{"lon"} == $p2->{"lon"} || $node->{"lon"} <= $xint) {
+ $counter++;
+ }
+ }
+ }
+ }
+ }
+ $p1 = $p2;
+ }
+
+ return ($counter%2);
+}
+
+#################################
+print "initialising tiles\n";
+
+
+print "There will be ".ceil(abs($maxlong2-$minlong2)/0.5)."x".ceil(abs($maxlat2-$minlat2)/0.5)." tiles\n";
+
+my $nb_tiles_x = ceil(abs($maxlat2-$minlat2)/0.5);
+my $nb_tiles_y = ceil(abs($maxlong2-$minlong2)/0.5);
+
+for ($j=0; $j<$nb_tiles_x; $j++) {
+ for ($i=0; $i<$nb_tiles_y; $i++) {
+
+
+ my $type = lookup_handler ( $maxlat2-0.5*$j, $minlong2+0.5*$i, 0, 0);
+
+ print "S" if $type eq "10";
+ print "L" if $type eq "01";
+ print "B" if $type eq "11";
+ print "?" if $type eq "00";
+ my $tile_maxlon = $minlong2+0.5*$i + 0.5 > $maxlong2 ? $maxlong2 : $minlong2+0.5*$i + 0.5;
+ my $tile_minlat = $maxlat2-0.5*$j-0.5 < $minlat2 ? $minlat2 : $maxlat2-0.5*$j-0.5;
+ $tiles{$nb_tiles_x * $i+$j} = { "minlon" => $minlong2+0.5*$i, "maxlat" => $maxlat2-0.5*$j,
+ "maxlon" => $tile_maxlon, "minlat" => $tile_minlat,
+ "type" => $type, "id" => ($nb_tiles_x * $i+$j), "crossed" => 0};
+ }
+ print "\n";
+}
+
+print "\n";
+
+
+
+################################
+# find coastlines and store the nodes id
+print "Reading osm source file\n";
+
+my $input;
+my $output;
+open($input, "<$source") or die "Can't open $source: $!";
+
+my $way_id=0;
+my $is_coastline=0;
+my @nodes;
+while (<$input>) {
+
+ if ( /<way id=["'](\d+?)["']/) {
+ $way_id=$1;
+ $is_coastline=0;
+ @nodes=();
+ }
+ if ($way_id != 0) {
+ if ( /<nd ref=["'](\d+?)["']/) {
+ push(@nodes, $1);
+ }
+ if ( /<tag k=["']natural["'] v=["']coastline["']/) {
+ $is_coastline=1;
+ }
+ if ( /<\/way/ ) {
+ # forget node with only one element -> disturb the algo
+ if ( $is_coastline && scalar(@nodes) != 1 ) {
+ my $way=way->new($way_id);
+ $way->nodes(@nodes);
+ debug( "found coastline ".$way->id()." from ".$way->first_node()." to ".$way->last_node()." with ".$way->size()." nodes\n");
+ $ways{$way->id()}=$way;
+ }
+
+ $way_id=0;
+ }
+ }
+}
+
+close($input);
+
+
+
+
+###########################
+# to save memory, only retrieve usefull nodes
+print "Retrieving nodes:\n";
+
+$i=0;
+foreach (values %ways) {
+ foreach ($_->nodes()) {
+ $nodes{$_}={};
+ $i++;
+ }
+}
+debug("Created $i nodes\n");
+
+open($input, "<$source") or die "Can't open $source: $!";
+open($output, ">$destination") or die "Can't open $destination: $!";
+
+$i=0;
+while (<$input>) {
+ if ( /<node id=['"](\d+?)['"].* lat=['"](.+?)['"] lon=['"](.+?)['"]/ ) {
+ if ( exists($nodes{$1}) ) {
+ $nodes{$1}->{"id"}=$1;
+ $nodes{$1}->{"lat"}=$2;
+ $nodes{$1}->{"lon"}=$3;
+ $i++;
+ }
+ }
+ print $output $_ unless ( /<\/osm>/ );
+}
+close($input);
+
+debug("Retrieved $i nodes\n");
+
+
+
+############################
+# If the bounding box given as arguments to the program is smaller than the one used to generate
+# the input file, some nodes may be outside -> check for this before adding them to a tile
+print "Spliting data into tiles:\n";
+
+
+foreach $way (values %ways) {
+ #print "following new way\n";
+
+ my $new_way = way->new($osm_id--);
+ my @way_nodes = ();
+ my ($tile_id, $new_tile_id);
+ $tile_id = -1;
+ my $prev_node;
+
+ foreach $node ($way->nodes()) {
+
+ # node out of bbox -> skip the nodes until we reenter the bbox
+ if ($nodes{$node}->{"lat"} < $minlat2 || $nodes{$node}->{"lat"} > $maxlat2
+ || $nodes{$node}->{"lon"} < $minlong2 || $nodes{$node}->{"lon"} > $maxlong2) {
+
+ # previous node already skiped or 1st node out of the bbox
+ if ( $tile_id == -1 ) {
+ $prev_node = $node;
+
+
+ } else { # previous node in bbox, we add a node at the intersection of the current tile border and the segment [$node, $prev_node]
+
+ # find the intersection of the current way with the tiles
+ my $tile = $tiles{$tile_id};
+ my ($lat, $lon) = intersec_way_tiles (
+ $nodes{$prev_node}->{"lat"}, $nodes{$prev_node}->{"lon"},
+ $nodes{$node}->{"lat"}, $nodes{$node}->{"lon"},
+ $tile->{"minlat"}, $tile->{"minlon"}, $tile->{"maxlat"}, $tile->{"maxlon"});
+ my $mid_node = {"lat" => $lat, "lon" => $lon, "id" => $osm_id-- };
+
+ $nodes{ $mid_node->{"id"} } = $mid_node;
+ push( @new_nodes, $mid_node );
+
+ push ( @way_nodes, $mid_node->{"id"} );
+
+ # end way here
+ $new_way->nodes(@way_nodes);
+ $new_ways{$new_way->id()} = $new_way;
+ $ways{$new_way->id()} = $new_way;
+ push( @{$tiles{$tile_id}->{"ways"}}, $new_way->id());
+ $tiles{$tile_id}->{"crossed"}=1;
+
+ # begin new way
+ $new_way = way->new($osm_id--);
+ @way_nodes = ();
+
+ # reset title_id
+ $tile_id = -1;
+
+ }
+ } else {
+
+ my $tile_lat = int( ($maxlat2 - $nodes{$node}->{"lat"} ) / 0.5);
+ my $tile_lon = int( ($nodes{$node}->{"lon"} - $minlong2) / 0.5);
+ $new_tile_id = $nb_tiles_x * $tile_lon + $tile_lat;
+
+ if ( $tile_id == -1 ) {
+ $tile_id = $new_tile_id;
+
+ # we have reentered a tile -> add a node on its border at the intersec of the segment [$prev_node, $node]
+ if (defined $prev_node) {
+ # find the intersection of the current way with the tiles
+ my $tile = $tiles{$tile_id};
+ my ($lat, $lon) = intersec_way_tiles (
+ $nodes{$node}->{"lat"}, $nodes{$node}->{"lon"},
+ $nodes{$prev_node}->{"lat"}, $nodes{$prev_node}->{"lon"},
+ $tile->{"minlat"}, $tile->{"minlon"}, $tile->{"maxlat"}, $tile->{"maxlon"});
+ my $mid_node = {"lat" => $lat, "lon" => $lon, "id" => $osm_id-- };
+
+ $nodes{ $mid_node->{"id"} } = $mid_node;
+ push( @new_nodes, $mid_node );
+
+ push ( @way_nodes, $mid_node->{"id"} );
+
+ $tiles{$tile_id}->{"crossed"}=1;
+ }
+ }
+ # we have reach an other tile
+ if ( $new_tile_id != $tile_id ) {
+
+ my $tile = $tiles{$tile_id};
+ my $new_tile = $tiles{$new_tile_id};
+
+ # find the intersection of the way with the tiles
+ my ($lat, $lon) = intersec_way_tiles (
+ $nodes{$prev_node}->{"lat"}, $nodes{$prev_node}->{"lon"},
+ $nodes{$node}->{"lat"}, $nodes{$node}->{"lon"},
+ $tile->{"minlat"}, $tile->{"minlon"}, $tile->{"maxlat"}, $tile->{"maxlon"});
+ my $mid_node = {"lat" => $lat, "lon" => $lon, "id" => $osm_id-- };
+
+ $nodes{ $mid_node->{"id"} } = $mid_node;
+ push( @new_nodes, $mid_node );
+
+ push ( @way_nodes, $mid_node->{"id"} );
+
+ $new_way->nodes(@way_nodes);
+ $new_ways{$new_way->id()} = $new_way;
+ $ways{$new_way->id()} = $new_way;
+ push( @{$tiles{$tile_id}->{"ways"}}, $new_way->id());
+
+ # set both new and old tile as crossed by a way
+ # the new one is important because when we rich the end
+ # of the coast, there will be no "next way" to set it as
+ # crossed
+ $tiles{$tile_id}->{"crossed"}=1;
+ $tiles{$new_tile_id}->{"crossed"}=1;
+
+ $new_way = way->new($osm_id--);
+
+ @way_nodes=();
+
+
+ # first node on tile border
+ ($lat, $lon) = intersec_way_tiles (
+ $nodes{$node}->{"lat"}, $nodes{$node}->{"lon"},
+ $nodes{$prev_node}->{"lat"}, $nodes{$prev_node}->{"lon"},
+ $new_tile->{"minlat"}, $new_tile->{"minlon"}, $new_tile->{"maxlat"}, $new_tile->{"maxlon"});
+ my $mid_node2 = {"lat" => $lat, "lon" => $lon, "id" => $osm_id-- };
+
+ $nodes{ $mid_node2->{"id"} } = $mid_node2;
+ push( @new_nodes, $mid_node2 );
+
+ push ( @way_nodes, $mid_node2->{"id"} );
+
+ $tile_id = $new_tile_id;
+ }
+
+ push (@way_nodes, $node);
+ #print "Node $node added to tile $tile_id\n";
+ $prev_node=$node;
+ }
+ }
+
+ # if new way not empty, add it
+ if ( scalar(@way_nodes) > 1 && $tile_id != -1) {
+ $new_way->nodes(@way_nodes);
+ $new_ways{$new_way->id()} = $new_way;
+ $ways{$new_way->id()} = $new_way;
+ push( @{$tiles{$tile_id}->{"ways"}}, $new_way->id());
+ }
+
+ $way->distroy();
+
+}
+
+
+
+
+
+
+
+##############################################
+print "\n\nWorking on tiles:\n";
+
+
+#$tile = $tiles{54};
+foreach $tile (values %tiles) {
+
+ print( "->tile: ".$tile->{"id"}."\n");
+
+ my @box = ($tile->{"minlat"}, $tile->{"minlon"}, $tile->{"maxlat"}, $tile->{"maxlon"});
+
+ # No way going through the tile -> guess if it is sea or lanf
+ # SHould check if it containds nodes -> type unknown + nodes should mean sea + islands
+ unless ($tile->{"crossed"}) {
+ my $tile_is_sea=0;
+
+ # 00 => unknown
+ # 01 => land
+ # 10 => sea
+ # 11 => coast
+
+ # water
+ if ($tile->{"type"} eq "10") {
+ $tile_is_sea=1;
+
+ } elsif ($tile->{"type"} ne "01") { # not land
+
+ my %temp = ("00"=>0, "10"=>0, "01"=>0, "11"=>0);
+ $temp{lookup_handler($tile->{"maxlat"}, $tile->{"minlon"}, -1, 0)}++;
+ $temp{lookup_handler($tile->{"maxlat"}, $tile->{"minlon"}, +1, 0)}++;
+ $temp{lookup_handler($tile->{"maxlat"}, $tile->{"minlon"}, 0, -1)}++;
+ $temp{lookup_handler($tile->{"maxlat"}, $tile->{"minlon"}, 0, +1)}++;
+
+ if( $temp{"10"} > $temp{"01"} ) {
+ $tile_is_sea=1;
+ } elsif ( ($tile->{"type"} eq "11") and ( $temp{"01"} == 0 ) ) {
+ # if the tile is marked coast but no land near, assume it's a group of islands instead of lakes.
+ $tile_is_sea=1;
+ } # else = land
+
+ }
+
+ # add nodes and a way to draw the sea all over the tile
+ if ($tile_is_sea) {
+ my @way_nodes_id = ( $osm_id, $osm_id-1, $osm_id-2, $osm_id-3);
+ my @way_nodes = (
+ { "lat" => $tile->{"maxlat"}, "lon" => $tile->{"minlon"}, "id" => $osm_id--},
+ { "lat" => $tile->{"maxlat"}, "lon" => $tile->{"maxlon"}, "id" => $osm_id--},
+ { "lat" => $tile->{"minlat"}, "lon" => $tile->{"maxlon"}, "id" => $osm_id--},
+ { "lat" => $tile->{"minlat"}, "lon" => $tile->{"minlon"}, "id" => $osm_id--});
+
+ my $way = way->new($osm_id--);
+ $way->nodes(@way_nodes_id);
+
+ push( @new_nodes, @way_nodes );
+ foreach (@way_nodes) {
+ $nodes{$_->{"id"}} = $_;
+ }
+
+ $new_ways{$way->id()} = $way;
+ $ways{$way->id()} = $way;
+ push( @{$tile->{"ways"}}, $way->id());
+ }
+ }
+
+ # next tile if there is nothing in this one
+ next if ( ! $tile->{"ways"} );
+
+ #========================================
+ # merge ways that can be merged
+ debug "\tBuilding tree:\n";
+
+ foreach (@{$tile->{"ways"}}) {
+ $way = $new_ways{$_};
+ debug( "node ".$way->id()." (size: ".$way->size().")\n" );
+ # only for not yet complete polygons and ways without prev and next
+ if ($way->next() == 0 || $way->prev() == 0) {
+ foreach (@{$tile->{"ways"}}) {
+ my $way2=$new_ways{$_};
+ if ($way->last_node() == $way2->first_node() ) {
+ if ($way->next() == 0) {
+ debug( "\tnext = ".$way2->id()."\n");
+ $way->next($way2->id());
+ } else {
+ debug( "\t error: next way duplicated: old:".$way->next()." new:".$way2->id()." - node: ".$way->last_node()."\n");
+ }
+ }
+ if ($way->first_node() == $way2->last_node() ) {
+ if ($way->prev() == 0) {
+ debug( "\tprev = ".$way2->id()."\n");
+ $way->prev($way2->id());
+ } else {
+ debug( "\t error: prev way duplicated: old:".$way->prev()." new:".$way2->id()." - node: ".$way->first_node()."\n");
+ }
+ }
+ }
+ } else {
+ debug( "\tAlready ok\n" );
+ }
+ }
+ #=============================
+ # if some ways are not closed, we need to close them.
+ # It is done by adding new nodes on the border of the tile
+ # and joining them
+ debug ("\n\nClosing loops:\n");
+
+ my (@first_nodes, @last_nodes);
+
+ # project nodes on the border of the tile
+ # Some nodes are already on the border of the tile (mainly because of
+ # intersec_way_tiles)
+ foreach (@{$tile->{"ways"}}) {
+ my $way = $new_ways{$_};
+
+ next if ( $way->first_node() == $way->last_node()); # already a loop
+
+ if ( $way->prev() == 0) {
+ my @way_nodes = $way->nodes();
+
+ my $lat = $nodes{$way_nodes[0]}->{"lat"};
+ my $lon = $nodes{$way_nodes[0]}->{"lon"};
+ ($lat, $lon) = get_projection( $lat, $lon, $tile->{"minlat"}, $tile->{"minlon"}, $tile->{"maxlat"}, $tile->{"maxlon"} );
+ my $node = { "lat" => $lat, "lon" => $lon, "id" => ($osm_id--)};
+ unshift( @way_nodes, $node->{"id"} );
+ push( @new_nodes, $node );
+ $nodes{$node->{"id"}}=$node;
+ push( @first_nodes, $node->{"id"});
+
+ $way->nodes(@way_nodes);
+ }
+ if ( $way->next() == 0) {
+
+ my @way_nodes = $way->nodes();
+
+ my $lat = $nodes{$way_nodes[ @way_nodes - 1 ]}->{"lat"};
+ my $lon = $nodes{$way_nodes[ @way_nodes - 1 ]}->{"lon"};
+ ($lat, $lon) = get_projection( $lat, $lon, $tile->{"minlat"}, $tile->{"minlon"}, $tile->{"maxlat"}, $tile->{"maxlon"} );
+ my $node = { "lat" => $lat, "lon" => $lon, "id" => $osm_id--};
+ push( @way_nodes, $node->{"id"} );
+ push( @new_nodes, $node );
+ $nodes{$node->{"id"}}=$node;
+ push( @last_nodes, $node->{"id"});
+
+ $way->nodes(@way_nodes);
+
+ }
+
+ }
+
+ # link nodes -> find a way around the tile
+
+
+ my $last;
+ foreach $last (@last_nodes) {
+ debug "Looking for path ...\n";
+ my ($path1, $path2);
+ $path1=0;
+
+ # find the sortest path between nodes
+ # -> will give the next node around the tile
+ my $first;
+ my $first_elected;
+ foreach $first (@first_nodes) {
+ $path2=get_path_length($nodes{$last}->{"lat"},
+ $nodes{$last}->{"lon"},
+ $nodes{$first}->{"lat"},
+ $nodes{$first}->{"lon"},
+ @box);
+ if ($path1 == 0 || $path1 > $path2) {
+ $path1=$path2;
+ $first_elected=$first;
+ }
+ }
+
+ # get the path around the tile between the two nodes
+ my @path = get_path ( $nodes{$last}->{"lat"},
+ $nodes{$last}->{"lon"},
+ $nodes{$first_elected}->{"lat"},
+ $nodes{$first_elected}->{"lon"},
+ @box);
+
+ foreach (@path) {
+ $_->{"id"} = $osm_id--;
+ push(@new_nodes, $_);
+ $nodes{$_->{"id"}} = $_;
+ }
+
+
+ unshift(@path, $nodes{$last});
+ push(@path, $nodes{$first_elected});
+
+ my $way = way->new($osm_id--);
+ my @way_nodes=();
+ debug( "new way ".$way->id().":\n");
+ foreach (@path) {
+ push(@way_nodes, $_->{"id"});
+ debug( "nodes id: ".$_->{"id"}."\tlat: ".$_->{"lat"}." - long: ".$_->{"lon"}."\n");
+ }
+ $way->nodes(@way_nodes);
+
+ push( @{$tiles{$tile->{"id"}}->{"ways"}}, $way->id());
+ $new_ways{$way->id()} = $way;
+ $ways{$way->id()} = $way;
+
+
+
+ }
+
+
+ # linking the ways
+ # This time all ways should be loops because of the ways we added before
+ debug "\n\nBuilding tree with new ways:\n";
+
+ foreach (@{$tile->{"ways"}}) {
+ my $way = $new_ways{$_};
+ debug( "node ".$way->id()." (size: ".$way->size().")\n" );
+ # only for not yet complete polygons and ways without prev and next
+ if ($way->next() == 0 || $way->prev() == 0) {
+ foreach (@{$tile->{"ways"}}) {
+ my $way2=$new_ways{$_};
+ if ($way->last_node() == $way2->first_node() ) {
+ if ($way->next() == 0) {
+ debug( "\tnext = ".$way2->id()."\n");
+ $way->next($way2->id());
+ } else {
+ debug( "\t error: next way duplicated: old:".$way->next()." new:".$way2->id()." - node: ".$way->last_node()."\n");
+ }
+ }
+ if ($way->first_node() == $way2->last_node() ) {
+ if ($way->prev() == 0) {
+ debug( "\tprev = ".$way2->id()."\n");
+ $way->prev($way2->id());
+ } else {
+ debug( "\t error: prev way duplicated: old:".$way->prev()." new:".$way2->id()." - node: ".$way->first_node()."\n");
+ }
+ }
+ }
+ } else {
+ debug( "\tAlready ok\n" );
+ }
+ }
+
+
+ #merge ways to have only simple ways that are circular
+ debug "merge ways\n";
+
+ # copy into @a because we add new ways to the array inside the loop
+ # -> otherwise it creates an infinite loop
+ my @a = @{$tile->{"ways"}};
+ foreach (@a) {
+# print "way $_\n";
+ my $way = $new_ways{$_};
+ next unless ($way->is_alive());
+
+ my @way_nodes;
+ while ($way->is_alive()) {
+
+ push (@way_nodes, $way->nodes());
+
+ # Distroy the way -> it won't be counted twince thus this will stop the loop
+ # and it will free some memory
+ $way->distroy();
+ last if ($way->next() == 0);
+ $way=$new_ways{ $way->next() };
+ }
+ my $new_way = way->new($osm_id--);
+ $new_way->nodes(@way_nodes);
+
+ push( @{$tiles{$tile->{"id"}}->{"ways"}}, $new_way->id());
+ $new_ways{$new_way->id()} = $new_way;
+ $ways{$new_way->id()} = $new_way;
+
+ }
+
+
+ ## sort the ways between sea and land
+ foreach (@{$tile->{"ways"}}) {
+ my $way = $new_ways{$_};
+ next unless ($way->is_alive());
+# print "---- ".$way->id().": ".$way->first_node()." -> ".$way->last_node()."\n";
+ foreach (@{$tile->{"ways"}}) {
+ my $way2 = $new_ways{$_};
+ # - don't check if a way is included in itself
+ # - check only for ways of type "outer" which is the default type
+ # => actualy should ckeck it. if a not is inside a "inner" way then it belongs
+ # to a lake that is in an island
+ if ($way2->is_alive() && $way2->id() != $way->id() && $way2->type() eq "outer") {
+ if (polygon_contains_node($way2, $nodes{$way->first_node()})) {
+ $way->type("inner");
+ last;
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+########################################
+print "\n\nGenerating new data:\n";
+
+# add new nodes to output file
+foreach $node (@new_nodes) {
+ debug( "adding node ".$node->{"id"}." to osm file\n");
+ print $output " <node id=\"".$node->{"id"}."\" timestamp=\"2008-07-02T22:57:53Z\" user=\"latouche\" lat=\"".$node->{"lat"}."\" lon=\"".$node->{"lon"}."\"/>\n";
+}
+
+# add new ways (highway=motorway for debug)
+#foreach $way (values %new_ways) {
+# print $output " <way id=\"".$way->id()."\" timestamp=\"2008-07-02T22:57:53Z\" user=\"latouche\">\n";
+# foreach $node ($way->nodes()) {
+# print $output " <nd ref=\"".$node."\"/>\n" if defined $node;
+# }
+#
+# print $output " <tag k=\"highway\" v=\"motorway\"/>\n";
+# print $output " </way>\n";
+#
+#}
+
+
+# add new ways to output file
+foreach $tile (values %tiles) {
+ print( "Writing tile: ".$tile->{"id"}."\n");
+ foreach (@{$tile->{"ways"}}) {
+ my $way = $new_ways{$_};
+ my $type = "inner";
+ next unless ($way->is_alive());
+ print $output " <way id=\"".($osm_id--)."\" timestamp=\"2008-07-02T22:57:53Z\" user=\"latouche\">\n";
+ while ($way->is_alive()) {
+
+ $type = ($way->type() eq "outer") ? "water" : "land";
+
+ foreach $node ($way->nodes()) {
+ print "undef node in ".$way->id()."\n" unless defined $node;
+ print $output " <nd ref=\"".$node."\"/>\n" if defined $node;
+ }
+ $way->distroy();
+ last if ($way->next() == 0);
+ $way=$new_ways{ $way->next() };
+ }
+ print $output " <tag k=\"natural\" v=\"".$type."\"/>\n";
+ print $output " </way>\n";
+ }
+}
+
+
+print $output "</osm>\n";
+
+close($output);
+
+exit(0);