# # PERL mapweaver module by gary68 # # Copyright(C)2011, Gerhard Schwanz # # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 3 of the License, or(at your option)any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with this program; if not, see # package mwCoastLines; use strict; use warnings; use Math::Polygon; use List::Util qw[min max]; use mwMap; use mwFile; use mwConfig; use mwMisc; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); require Exporter; @ISA = qw(Exporter AutoLoader); @EXPORT = qw(processCoastLines ); sub nearestPoint{# # accepts x/y coordinates and returns nearest point on border of map to complete cut coast ways # my $ref = shift; my $x = $ref->[0]; my $y = $ref->[1]; my $xn; my $yn; my $min = 99999; # print " NP: initial $x $y\n"; my($xmax, $ymax)= getDimensions(); # print " NP: dimensions $xmax $ymax\n"; if(abs($xmax-$x)<$min){# right $xn = $xmax; $yn = $y; $min = abs($xmax-$x); } if(abs($ymax-$y)<$min){# bottom $xn = $x; $yn = $ymax; $min = abs($ymax-$y); } if(abs($x)<$min){# left $xn = 0; $yn = $y; $min = abs($x); } if(abs($y)< $min){# top $xn = $x; $yn = 0; } # print " NP: final $xn $yn\n"; my @a =($xn, $yn); return \@a; }sub nextPointOnBorder{# # accepts x/y coordinates and returns next point on border - to complete coast rings with other polygons and corner points # hints if returned point is a corner # # right turns my($x, $y)= @_; my($xn, $yn); my $corner = 0; my($xmax, $ymax)= getDimensions(); if($x == $xmax){# right border if($y < $ymax){ $xn = $xmax; $yn = $y + 1; } else{ $xn = $xmax - 1; $yn = $ymax; }} else{ if($x == 0){# left border if($y > 0){ $xn = 0; $yn = $y - 1; } else{ $xn = 1; $yn = 0; } } else{ if($y == $ymax){# bottom border if($x > 0){ $xn = $x - 1; $yn = $ymax; } else{ $xn = 0; $yn = $ymax - 1; } } else{ if($y == 0){# top border if($x < $xmax){ $xn = $x + 1; $yn = 0; } else{ $xn = $xmax; $yn = 1; } } } }} # print "NPOB: $x, $y --- finito $xn $yn\n"; if(($xn == 0)and($yn == 0)){$corner = 1;} if(($xn == 0)and($yn == $ymax)){$corner = 1;} if(($xn == $xmax)and($yn == 0)){$corner = 1;} if(($xn == $xmax)and($yn == $ymax)){$corner = 1;} return($xn, $yn, $corner); } # --------------------------------------------------------------------------------- sub processCoastLines{ print "check and process coastlines...\n"; my $ref = shift; # ref to all coast ways my @allWays = @$ref; if(cv('debug')){ print "COAST: " . scalar(@allWays). " coast ways initially found.\n"; print "COAST: ways: @allWays\n\n"; } my($lonRef, $latRef)= getNodePointers(); my($nodesRef, $tagRef)= getWayPointers(); # check coast ways. eliminate invisible ways. eliminate points outside map. my @newWays =(); foreach my $w(@allWays){ my @nodes = @{$$nodesRef{$w}}; my $allIn = 1; my $allOut = 1; foreach my $n(@nodes){ if(pointInMap($n)){ $allOut = 0; } else{ $allIn = 0; } } if($allIn){ # use way as it is push @newWays, $w; print "COAST: way $w will be used unmodified.\n" if cv('debug'); } elsif($allOut){ # do nothing print "COAST: way $w will NOT be used. outside map.\n" if cv('debug');} } else{ # eliminate all outside nodes at start and end of way, then use new way # eliminate outsides at start shift @nodes while((scalar(@nodes)>= 1)and not pointInMap($nodes[0])); # eliminate outsides at end while((scalar @nodes >= 1)and not pointInMap($nodes[-1])){ pop @nodes; } if(scalar @nodes >= 2){ @{$$nodesRef{$w}}= @nodes; push @newWays, $w; if(cv('debug')eq "1"){print "COAST: modified way $w will be used.\n";} } else{ if(cv('debug')eq "1"){print "COAST: way $w too short now.\n";} } } } @allWays = @newWays; if(cv('debug')eq "1"){ print "\nCOAST: " . scalar(@allWays). " coast ways will be used.\n"; print "COAST: ways: @allWays\n\n"; } if(scalar @allWays > 0){ # build rings my($refWays, $refNodes)= buildRings(\@allWays, 0); my @ringNodes = @$refNodes; # contains all nodes of rings // array of arrays ! print "COAST: " . scalar(@ringNodes). " rings found.\n" if cv('debug'); # convert rings to coordinate system my @ringCoordsOpen =(); my @ringCoordsClosed =(); for(my $i=0; $i<=$#ringNodes; $i++){ # print "COAST: initial ring $i\n"; my @actualCoords =(); foreach my $node(@{$ringNodes[$i]}){ push @actualCoords, [convert($$lonRef{$node}, $$latRef{$node})]; } if(${$ringNodes[$i]}[0] == ${$ringNodes[$i]}[-1]){ push @ringCoordsClosed, [@actualCoords]; # islands } else{ push @ringCoordsOpen, [@actualCoords]; } # printRingCoords(\@actualCoords); my $num = scalar @actualCoords; print "COAST: initial ring $i - $actualCoords[0]->[0],$actualCoords[0]->[1] -->> $actualCoords[-1]->[0],$actualCoords[-1]->[1] nodes: $num\n" if cv('debug'); } if(cv('debug')eq "1"){print "COAST: add points on border...\n";} foreach my $ring(@ringCoordsOpen){ # print "COAST: ring $ring with border nodes\n"; # add first point on border my $ref = nearestPoint($ring->[0]); my @a = @$ref; unshift @$ring, [@a]; # add last point on border $ref = nearestPoint($ring->[-1]); @a = @$ref; push @$ring, [@a]; # printRingCoords($ring); } my @islandRings = @ringCoordsClosed; print "COAST: " . scalar(@islandRings). " islands found.\n" if cv('debug'); @ringCoordsClosed =(); # process ringCoordsOpen # add other rings, corners... while(scalar @ringCoordsOpen > 0){# as long as there are open rings print "COAST: building ring...\n" if(cv('debug'); my $ref = shift @ringCoordsOpen; # get start ring my @actualRing = @$ref; my $closed = 0; # mark as not closed my $actualX = $actualRing[-1]->[0]; my $actualY = $actualRing[-1]->[1]; my $actualStartX = $actualRing[0]->[0]; my $actualStartY = $actualRing[0]->[1]; print "COAST: actual and actualStart $actualX, $actualY - $actualStartX, $actualStartY\n" if cv('debug'); my $corner; while(!$closed){# as long as this ring is not closed ($actualX, $actualY, $corner)= nextPointOnBorder($actualX, $actualY); # print " actual $actualX, $actualY\n"; my $startFromOtherPolygon = -1; # find matching ring if there is another ring if(scalar(@ringCoordsOpen)> 0){ for(my $i=0; $i <= $#ringCoordsOpen; $i++){ my @test = @{$ringCoordsOpen[$i]}; # print " test ring $i: ", $test[0]->[0], " " , $test[0]->[1] , "\n"; if(($actualX == $test[0]->[0])and($actualY == $test[0]->[1])){ $startFromOtherPolygon = $i; print "COAST: matching start other polygon found i= $i\n" if cv('debug'); } } } # process matching polygon, if present if($startFromOtherPolygon != -1){# start from other polygon{ # append nodes # print "ARRAY TO PUSH: @{$ringCoordsOpen[$startFromOtherPolygon]}\n"; push @actualRing, @{$ringCoordsOpen[$startFromOtherPolygon]}; # set actual $actualX = $actualRing[-1]->[0]; $actualY = $actualRing[-1]->[1]; # drop p2 from opens splice @ringCoordsOpen, $startFromOtherPolygon, 1; print "COAST: openring $startFromOtherPolygon added to actual ring\n" if cv('debug'); } else{ if($corner){# add corner to actual ring push @actualRing, [$actualX, $actualY]; print "COAST: corner $actualX, $actualY added to actual ring\n" if cv('debug'); } } # check if closed if(($actualX == $actualStartX)and($actualY == $actualStartY)){ $closed = 1; push @actualRing, [$actualX, $actualY]; push @ringCoordsClosed, [@actualRing]; print "COAST: ring now closed and moved to closed rings.\n" if cv('debug'); } }# !closed }# open rings my $color = cv('oceancolor'); # build islandRings polygons print "OCEAN: building island polygons\n" if cv('debug'); my @islandPolygons =(); if(scalar @islandRings > 0){ for(my $i=0; $i<=$#islandRings; $i++){ my @poly =(); foreach my $node(@{$islandRings[$i]}){ push @poly, [$node->[0], $node->[1]]; } my($p)= Math::Polygon->new(@poly); $islandPolygons[$i] = $p; } } # build ocean ring polygons if(cv('debug')eq "1"){print "OCEAN: building ocean polygons\n";} my @oceanPolygons =(); if(scalar(@ringCoordsClosed)> 0){ for(my $i=0; $i<=$#ringCoordsClosed; $i++){ my @poly =(); foreach my $node(@{$ringCoordsClosed[$i]}){ push @poly, [$node->[0], $node->[1]]; } my($p)= Math::Polygon->new(@poly); $oceanPolygons[$i] = $p; } } else{ if(scalar(@islandRings)> 0){ if(cv('debug')eq "1"){print "OCEAN: build ocean rect\n";} my @ocean =(); my($x, $y)= getDimensions(); push @ocean, [0,0], [$x,0], [$x,$y], [0,$y], [0,0]; push @ringCoordsClosed, [@ocean]; my($p)= Math::Polygon->new(@ocean); push @oceanPolygons, $p; } } # finally create pathes for SVG for(my $i=0; $i<=$#ringCoordsClosed; $i++){ # foreach my $ring(@ringCoordsClosed){ my @ring = @{$ringCoordsClosed[$i]}; my @array =(); my @coords =(); foreach my $c(@ring){ push @coords, $c->[0], $c->[1]; } push @array, [@coords]; if(scalar @islandRings > 0){ for(my $j=0; $j<=$#islandRings; $j++){ # island in ring? 1:1 and coast on border? # if(isIn($islandPolygons[$j], $oceanPolygons[$i])== 1){ if((isIn($islandPolygons[$j], $oceanPolygons[$i])== 1)or ((scalar @islandRings == 1)and(scalar @ringCoordsClosed == 1))){ if(cv('debug')eq "1"){print "OCEAN: island $j in ocean $i\n";} my @coords =(); foreach my $c(@{$islandRings[$j]}){ push @coords, $c->[0], $c->[1]; } push @array, [@coords]; } } } # drawAreaOcean($color, \@array); my $svgText = "fill=\"$color\" "; drawArea($svgText, "none", \@array, 0, "base"); }}} sub pointInMap{ my($n)= shift; my($sizeX, $sizeY)= getDimensions(); my($lonRef, $latRef)= getNodePointers(); my($x, $y)= convert($$lonRef{$n}, $$latRef{$n}); my $ok = 0; if(($x >= 0)and ($x <= $sizeX)and ($y >= 0)and ($y <= $sizeY)){ $ok = 1; } return $ok; } 1;