From cc3d03f9ed29138b1c1741980e2a791eb379ecdd Mon Sep 17 00:00:00 2001 From: Atlas Cove Date: Thu, 28 Jul 2022 10:06:48 +0100 Subject: [PATCH] Initial re-commit --- OSM/QuadTree.pm | 365 +++++ OSM/gpx.pm | 199 +++ OSM/mapgen.pm | 2291 ++++++++++++++++++++++++++++++ OSM/mapgenRules.pm | 180 +++ OSM/osm.pm | 1760 +++++++++++++++++++++++ OSM/osmDB.pm | 839 +++++++++++ OSM/osmgraph.pm | 751 ++++++++++ icons-topo/.directory | 3 + icons-topo/arch-64.png | Bin 0 -> 2572 bytes icons-topo/arch.svg | 170 +++ icons-topo/aussicht-64.png | Bin 0 -> 3262 bytes icons-topo/aussicht.svg | 210 +++ icons-topo/bank-64.png | Bin 0 -> 1762 bytes icons-topo/bank.svg | 186 +++ icons-topo/baum-64.png | Bin 0 -> 1967 bytes icons-topo/baum.svg | 153 ++ icons-topo/biergarten-64.png | Bin 0 -> 2046 bytes icons-topo/biergarten.svg | 213 +++ icons-topo/burg-64.png | Bin 0 -> 1893 bytes icons-topo/burg-ruine-64.png | Bin 0 -> 2633 bytes icons-topo/burg.svg | 292 ++++ icons-topo/cafe-64.png | Bin 0 -> 2224 bytes icons-topo/cafe.svg | 201 +++ icons-topo/cemetery-64.png | Bin 0 -> 439 bytes icons-topo/cemetery.svg | 540 +++++++ icons-topo/denkmal-64.png | Bin 0 -> 1667 bytes icons-topo/denkmal.svg | 207 +++ icons-topo/forest-64.png | Bin 0 -> 1175 bytes icons-topo/forest-64.svg | 65 + icons-topo/forest-laub-64.png | Bin 0 -> 1005 bytes icons-topo/forest-nadel-64.png | Bin 0 -> 848 bytes icons-topo/forest.svg | 169 +++ icons-topo/funkturm-64.png | Bin 0 -> 2891 bytes icons-topo/funkturm.svg | 297 ++++ icons-topo/gaststaette-64.png | Bin 0 -> 2189 bytes icons-topo/gaststaette.svg | 217 +++ icons-topo/gipfel-20.png | Bin 0 -> 462 bytes icons-topo/gipfel.svg | 136 ++ icons-topo/grass-64.png | Bin 0 -> 374 bytes icons-topo/grass.svg | 1330 +++++++++++++++++ icons-topo/haltestelle-64.png | Bin 0 -> 1830 bytes icons-topo/haltestelle.svg | 181 +++ icons-topo/heath-64.png | Bin 0 -> 822 bytes icons-topo/heath.svg | 581 ++++++++ icons-topo/hgg-40.png | Bin 0 -> 1680 bytes icons-topo/hgg.svg | 286 ++++ icons-topo/hoehle-64.png | Bin 0 -> 1232 bytes icons-topo/hoehle.svg | 104 ++ icons-topo/hotel-68.png | Bin 0 -> 1887 bytes icons-topo/hotel.svg | 193 +++ icons-topo/imbiss-64.png | Bin 0 -> 2060 bytes icons-topo/imbiss.svg | 213 +++ icons-topo/information-48.png | Bin 0 -> 946 bytes icons-topo/information.svg | 123 ++ icons-topo/jugendherberge-64.png | Bin 0 -> 2192 bytes icons-topo/jugendherberge.svg | 211 +++ icons-topo/kirche-64.png | Bin 0 -> 1147 bytes icons-topo/kirche.svg | 187 +++ icons-topo/kreuz-40.png | Bin 0 -> 556 bytes icons-topo/kreuz.svg | 156 ++ icons-topo/mast-40.png | Bin 0 -> 1086 bytes icons-topo/mast.svg | 197 +++ icons-topo/military-64.png | Bin 0 -> 456 bytes icons-topo/orchard-64.png | Bin 0 -> 1037 bytes icons-topo/orchard.svg | 1253 ++++++++++++++++ icons-topo/ort-0-32.png | Bin 0 -> 627 bytes icons-topo/ort-1-32.png | Bin 0 -> 297 bytes icons-topo/ort-2-64.png | Bin 0 -> 965 bytes icons-topo/ort-3-64.png | Bin 0 -> 1913 bytes icons-topo/ort-4-64.png | Bin 0 -> 2679 bytes icons-topo/ort.svg | 375 +++++ icons-topo/parkplatz-40.png | Bin 0 -> 920 bytes icons-topo/parkplatz.svg | 145 ++ icons-topo/quarry-64.png | Bin 0 -> 293 bytes icons-topo/quelle-64.png | Bin 0 -> 1189 bytes icons-topo/quelle.svg | 118 ++ icons-topo/reserve-64.png | Bin 0 -> 462 bytes icons-topo/restaurant-64.png | Bin 0 -> 1845 bytes icons-topo/restaurant.svg | 167 +++ icons-topo/rock-64.png | Bin 0 -> 7134 bytes icons-topo/rock.svg | 1026 +++++++++++++ icons-topo/sand.png | Bin 0 -> 146 bytes icons-topo/schraffur.svg | 130 ++ icons-topo/schrein-40.png | Bin 0 -> 876 bytes icons-topo/schrein.svg | 168 +++ icons-topo/scree-64.png | Bin 0 -> 4429 bytes icons-topo/scree.svg | 292 ++++ icons-topo/scrub-64.png | Bin 0 -> 1637 bytes icons-topo/scrub.svg | 448 ++++++ icons-topo/shelter-64.png | Bin 0 -> 1518 bytes icons-topo/shelter-fire-64.png | Bin 0 -> 2638 bytes icons-topo/shelter.svg | 214 +++ icons-topo/swamp.png | Bin 0 -> 169 bytes icons-topo/tankstelle-64.png | Bin 0 -> 1624 bytes icons-topo/tankstelle.svg | 198 +++ icons-topo/telefon-64.png | Bin 0 -> 2075 bytes icons-topo/telefon.svg | 119 ++ icons-topo/topo_shelter.svg | 102 ++ icons-topo/topo_shelter_fire.svg | 115 ++ icons-topo/trinkwasser-56.png | Bin 0 -> 1855 bytes icons-topo/trinkwasser.svg | 131 ++ icons-topo/turm-64.png | Bin 0 -> 1168 bytes icons-topo/turm.svg | 220 +++ icons-topo/vineyard-64.png | Bin 0 -> 621 bytes icons-topo/vineyard.svg | 633 +++++++++ icons/alpineHut.svg | 94 ++ icons/areaBeach.svg | 98 ++ icons/areaCemetery.svg | 78 + icons/areaFieldHospital.svg | 80 ++ icons/areaGolf.svg | 92 ++ icons/areaHeath.svg | 86 ++ icons/areaHospital.svg | 72 + icons/areaMarsh.svg | 82 ++ icons/areaMilitary.png | Bin 0 -> 166 bytes icons/areaParking.svg | 98 ++ icons/areaPlayground.svg | 101 ++ icons/areaRefugees.svg | 156 ++ icons/areaScree.svg | 123 ++ icons/areaSport.png | Bin 0 -> 247 bytes icons/atm.svg | 179 +++ icons/bank.png | Bin 0 -> 590 bytes icons/bench.svg | 96 ++ icons/binoculars.svg | 114 ++ icons/blitzer.svg | 173 +++ icons/buildingCollapsed.svg | 130 ++ icons/buoy.svg | 88 ++ icons/bus.svg | 85 ++ icons/busstation.svg | 126 ++ icons/cafe.svg | 123 ++ icons/church.svg | 96 ++ icons/citylimit.svg | 89 ++ icons/cliff.svg | 84 ++ icons/fastfood.svg | 108 ++ icons/fieldHospital.svg | 96 ++ icons/firestation.svg | 137 ++ icons/fountain.svg | 100 ++ icons/fuel.png | Bin 0 -> 290 bytes icons/fuel.svg | 103 ++ icons/gate.svg | 145 ++ icons/glasses.svg | 116 ++ icons/guidepost.svg | 98 ++ icons/hospital.svg | 87 ++ icons/hospital2.png | Bin 0 -> 430 bytes icons/hotel.svg | 82 ++ icons/info.svg | 81 ++ icons/kindergarten.png | Bin 0 -> 247 bytes icons/kindergarten.svg | 144 ++ icons/landslide.svg | 96 ++ icons/lighthouse.svg | 132 ++ icons/mountainPass.svg | 79 ++ icons/parking.png | Bin 0 -> 381 bytes icons/peak.png | Bin 0 -> 152 bytes icons/peak.svg | 69 + icons/pharmacy.svg | 80 ++ icons/picnic.svg | 156 ++ icons/pier.svg | 110 ++ icons/playground.png | Bin 0 -> 764 bytes icons/police.png | Bin 0 -> 196 bytes icons/postbox.svg | 78 + icons/powerPole.svg | 69 + icons/powerSubStation.svg | 115 ++ icons/powerTower.svg | 84 ++ icons/powerWind.svg | 98 ++ icons/pub.svg | 82 ++ icons/radar.svg | 121 ++ icons/railwaystation.svg | 139 ++ icons/recycling.png | Bin 0 -> 483 bytes icons/reservoir.svg | 87 ++ icons/restaurant.svg | 155 ++ icons/restaurant1.svg | 148 ++ icons/restaurant2.svg | 149 ++ icons/school.svg | 96 ++ icons/shelter.svg | 79 ++ icons/shield_ger_bab.def | 16 + icons/shield_ger_bs.def | 16 + icons/shield_ger_ls.def | 16 + icons/soccer.svg | 120 ++ icons/speed_camera.png | Bin 0 -> 389 bytes icons/spring.svg | 78 + icons/supermarket.svg | 157 ++ icons/swimming.svg | 100 ++ icons/telephone.svg | 69 + icons/toilets.svg | 150 ++ icons/tower.svg | 76 + icons/towerRadio.svg | 124 ++ icons/viewpoint.svg | 151 ++ icons/wall.svg | 190 +++ icons/waterpark.svg | 88 ++ mw.pl | 184 +++ mwCoastLines.pm | 417 ++++++ mwConfig.pm | 404 ++++++ mwDir.pl | 119 ++ mwFile.pm | 434 ++++++ mwGPX.pm | 123 ++ mwInteractive.pl | 127 ++ mwLabel.pm | 328 +++++ mwMap.pm | 1241 ++++++++++++++++ mwMisc.pm | 801 +++++++++++ mwMulti.pm | 420 ++++++ mwNodes.pm | 187 +++ mwOccupy.pm | 332 +++++ mwRelations.pm | 326 +++++ mwRules.pm | 909 ++++++++++++ mwStandardRules.txt | 319 +++++ mwTopoRules.txt | 576 ++++++++ mwWayLabel.pm | 578 ++++++++ mwWays.pm | 394 +++++ 207 files changed, 34692 insertions(+) create mode 100644 OSM/QuadTree.pm create mode 100755 OSM/gpx.pm create mode 100755 OSM/mapgen.pm create mode 100755 OSM/mapgenRules.pm create mode 100755 OSM/osm.pm create mode 100755 OSM/osmDB.pm create mode 100755 OSM/osmgraph.pm create mode 100644 icons-topo/.directory create mode 100644 icons-topo/arch-64.png create mode 100644 icons-topo/arch.svg create mode 100644 icons-topo/aussicht-64.png create mode 100644 icons-topo/aussicht.svg create mode 100644 icons-topo/bank-64.png create mode 100644 icons-topo/bank.svg create mode 100644 icons-topo/baum-64.png create mode 100644 icons-topo/baum.svg create mode 100644 icons-topo/biergarten-64.png create mode 100644 icons-topo/biergarten.svg create mode 100644 icons-topo/burg-64.png create mode 100644 icons-topo/burg-ruine-64.png create mode 100644 icons-topo/burg.svg create mode 100644 icons-topo/cafe-64.png create mode 100644 icons-topo/cafe.svg create mode 100644 icons-topo/cemetery-64.png create mode 100644 icons-topo/cemetery.svg create mode 100644 icons-topo/denkmal-64.png create mode 100644 icons-topo/denkmal.svg create mode 100644 icons-topo/forest-64.png create mode 100644 icons-topo/forest-64.svg create mode 100644 icons-topo/forest-laub-64.png create mode 100644 icons-topo/forest-nadel-64.png create mode 100644 icons-topo/forest.svg create mode 100644 icons-topo/funkturm-64.png create mode 100644 icons-topo/funkturm.svg create mode 100644 icons-topo/gaststaette-64.png create mode 100644 icons-topo/gaststaette.svg create mode 100644 icons-topo/gipfel-20.png create mode 100644 icons-topo/gipfel.svg create mode 100644 icons-topo/grass-64.png create mode 100644 icons-topo/grass.svg create mode 100644 icons-topo/haltestelle-64.png create mode 100644 icons-topo/haltestelle.svg create mode 100644 icons-topo/heath-64.png create mode 100644 icons-topo/heath.svg create mode 100644 icons-topo/hgg-40.png create mode 100644 icons-topo/hgg.svg create mode 100644 icons-topo/hoehle-64.png create mode 100644 icons-topo/hoehle.svg create mode 100644 icons-topo/hotel-68.png create mode 100644 icons-topo/hotel.svg create mode 100644 icons-topo/imbiss-64.png create mode 100644 icons-topo/imbiss.svg create mode 100644 icons-topo/information-48.png create mode 100644 icons-topo/information.svg create mode 100644 icons-topo/jugendherberge-64.png create mode 100644 icons-topo/jugendherberge.svg create mode 100644 icons-topo/kirche-64.png create mode 100644 icons-topo/kirche.svg create mode 100644 icons-topo/kreuz-40.png create mode 100644 icons-topo/kreuz.svg create mode 100644 icons-topo/mast-40.png create mode 100644 icons-topo/mast.svg create mode 100644 icons-topo/military-64.png create mode 100644 icons-topo/orchard-64.png create mode 100644 icons-topo/orchard.svg create mode 100644 icons-topo/ort-0-32.png create mode 100644 icons-topo/ort-1-32.png create mode 100644 icons-topo/ort-2-64.png create mode 100644 icons-topo/ort-3-64.png create mode 100644 icons-topo/ort-4-64.png create mode 100644 icons-topo/ort.svg create mode 100644 icons-topo/parkplatz-40.png create mode 100644 icons-topo/parkplatz.svg create mode 100644 icons-topo/quarry-64.png create mode 100644 icons-topo/quelle-64.png create mode 100644 icons-topo/quelle.svg create mode 100644 icons-topo/reserve-64.png create mode 100644 icons-topo/restaurant-64.png create mode 100644 icons-topo/restaurant.svg create mode 100644 icons-topo/rock-64.png create mode 100644 icons-topo/rock.svg create mode 100644 icons-topo/sand.png create mode 100644 icons-topo/schraffur.svg create mode 100644 icons-topo/schrein-40.png create mode 100644 icons-topo/schrein.svg create mode 100644 icons-topo/scree-64.png create mode 100644 icons-topo/scree.svg create mode 100644 icons-topo/scrub-64.png create mode 100644 icons-topo/scrub.svg create mode 100644 icons-topo/shelter-64.png create mode 100644 icons-topo/shelter-fire-64.png create mode 100644 icons-topo/shelter.svg create mode 100644 icons-topo/swamp.png create mode 100644 icons-topo/tankstelle-64.png create mode 100644 icons-topo/tankstelle.svg create mode 100644 icons-topo/telefon-64.png create mode 100644 icons-topo/telefon.svg create mode 100644 icons-topo/topo_shelter.svg create mode 100644 icons-topo/topo_shelter_fire.svg create mode 100644 icons-topo/trinkwasser-56.png create mode 100644 icons-topo/trinkwasser.svg create mode 100644 icons-topo/turm-64.png create mode 100644 icons-topo/turm.svg create mode 100644 icons-topo/vineyard-64.png create mode 100644 icons-topo/vineyard.svg create mode 100755 icons/alpineHut.svg create mode 100755 icons/areaBeach.svg create mode 100755 icons/areaCemetery.svg create mode 100755 icons/areaFieldHospital.svg create mode 100755 icons/areaGolf.svg create mode 100755 icons/areaHeath.svg create mode 100755 icons/areaHospital.svg create mode 100755 icons/areaMarsh.svg create mode 100755 icons/areaMilitary.png create mode 100755 icons/areaParking.svg create mode 100755 icons/areaPlayground.svg create mode 100755 icons/areaRefugees.svg create mode 100755 icons/areaScree.svg create mode 100755 icons/areaSport.png create mode 100755 icons/atm.svg create mode 100644 icons/bank.png create mode 100755 icons/bench.svg create mode 100755 icons/binoculars.svg create mode 100755 icons/blitzer.svg create mode 100755 icons/buildingCollapsed.svg create mode 100755 icons/buoy.svg create mode 100755 icons/bus.svg create mode 100755 icons/busstation.svg create mode 100755 icons/cafe.svg create mode 100755 icons/church.svg create mode 100755 icons/citylimit.svg create mode 100755 icons/cliff.svg create mode 100755 icons/fastfood.svg create mode 100755 icons/fieldHospital.svg create mode 100755 icons/firestation.svg create mode 100755 icons/fountain.svg create mode 100644 icons/fuel.png create mode 100755 icons/fuel.svg create mode 100755 icons/gate.svg create mode 100755 icons/glasses.svg create mode 100755 icons/guidepost.svg create mode 100755 icons/hospital.svg create mode 100644 icons/hospital2.png create mode 100755 icons/hotel.svg create mode 100755 icons/info.svg create mode 100644 icons/kindergarten.png create mode 100755 icons/kindergarten.svg create mode 100755 icons/landslide.svg create mode 100755 icons/lighthouse.svg create mode 100755 icons/mountainPass.svg create mode 100644 icons/parking.png create mode 100644 icons/peak.png create mode 100644 icons/peak.svg create mode 100755 icons/pharmacy.svg create mode 100755 icons/picnic.svg create mode 100755 icons/pier.svg create mode 100644 icons/playground.png create mode 100644 icons/police.png create mode 100755 icons/postbox.svg create mode 100755 icons/powerPole.svg create mode 100755 icons/powerSubStation.svg create mode 100755 icons/powerTower.svg create mode 100755 icons/powerWind.svg create mode 100755 icons/pub.svg create mode 100755 icons/radar.svg create mode 100755 icons/railwaystation.svg create mode 100644 icons/recycling.png create mode 100755 icons/reservoir.svg create mode 100755 icons/restaurant.svg create mode 100755 icons/restaurant1.svg create mode 100755 icons/restaurant2.svg create mode 100755 icons/school.svg create mode 100755 icons/shelter.svg create mode 100755 icons/shield_ger_bab.def create mode 100755 icons/shield_ger_bs.def create mode 100755 icons/shield_ger_ls.def create mode 100755 icons/soccer.svg create mode 100644 icons/speed_camera.png create mode 100755 icons/spring.svg create mode 100755 icons/supermarket.svg create mode 100755 icons/swimming.svg create mode 100755 icons/telephone.svg create mode 100755 icons/toilets.svg create mode 100755 icons/tower.svg create mode 100755 icons/towerRadio.svg create mode 100755 icons/viewpoint.svg create mode 100755 icons/wall.svg create mode 100755 icons/waterpark.svg create mode 100644 mw.pl create mode 100644 mwCoastLines.pm create mode 100644 mwConfig.pm create mode 100755 mwDir.pl create mode 100644 mwFile.pm create mode 100755 mwGPX.pm create mode 100644 mwInteractive.pl create mode 100644 mwLabel.pm create mode 100644 mwMap.pm create mode 100644 mwMisc.pm create mode 100644 mwMulti.pm create mode 100644 mwNodes.pm create mode 100644 mwOccupy.pm create mode 100644 mwRelations.pm create mode 100644 mwRules.pm create mode 100644 mwStandardRules.txt create mode 100644 mwTopoRules.txt create mode 100644 mwWayLabel.pm create mode 100644 mwWays.pm diff --git a/OSM/QuadTree.pm b/OSM/QuadTree.pm new file mode 100644 index 0000000..256ff5a --- /dev/null +++ b/OSM/QuadTree.pm @@ -0,0 +1,365 @@ +package OSM::QuadTree; + +use strict; +use Carp; + +our $VERSION = 0.1; + +1; + +############################### +# +# sub new() - constructor +# +# Arguments are a hash: +# +# -xmin => minimum x value +# -xmax => maximum x value +# -ymin => minimum y value +# -ymax => maximum y value +# -depth => depth of tree +# +# Creating a new QuadTree objects automatically +# segments the given area into quadtrees of the +# specified depth. +# +############################### + +sub new { + my $self = shift; + my $class = ref($self) || $self; + + my $obj = bless {} => $class; + + $obj->{BACKREF} = {}; + $obj->{OBJECTS} = []; + $obj->{ORIGIN} = [0, 0]; + $obj->{SCALE} = 1; + + my %args = @_; + + for my $arg (qw/xmin ymin xmax ymax depth/) { + unless (exists $args{"-$arg"}) { + carp "- must specify $arg"; + return undef; + } + + $obj->{uc $arg} = $args{"-$arg"}; + } + + $obj->_segment; + + return $obj; +} + +############################### +# +# sub _segment() - private method +# +# This method does the actual segmentation +# and stores everything internally. +# +############################### + +sub _segment { + my $obj = shift; + + $obj->_addLevel( + $obj->{XMIN}, + $obj->{YMIN}, + $obj->{XMAX}, + $obj->{YMAX}, + 1, # current depth + 0, # current index + undef, # parent index + ); + +} + +############################### +# +# sub _addLevel() - private method +# +# This method segments a given area +# and adds a level to the tree. +# +############################### + +sub _addLevel { + my ($obj, + $xmin, + $ymin, + $xmax, + $ymax, + $curDepth, + $index, + $parent, + ) = @_; + + $obj->{AREA} [$index] = [$xmin, $ymin, $xmax, $ymax]; + $obj->{PARENT} [$index] = $parent; + $obj->{CHILDREN}[$index] = []; + $obj->{OBJECTS} [$index] = []; + + if (defined $parent) { + push @{$obj->{CHILDREN}[$parent]} => $index; + } + + return if $curDepth == $obj->{DEPTH}; + + my $xmid = $xmin + ($xmax - $xmin) / 2; + my $ymid = $ymin + ($ymax - $ymin) / 2; + + # now segment in the following order (doesn't matter): + # top left, top right, bottom left, bottom right + $obj->_addLevel($xmin, $ymid, $xmid, $ymax, # tl + $curDepth + 1, 4 * $index + 1, $index); + $obj->_addLevel($xmid, $ymid, $xmax, $ymax, # tr + $curDepth + 1, 4 * $index + 2, $index); + $obj->_addLevel($xmin, $ymin, $xmid, $ymid, # bl + $curDepth + 1, 4 * $index + 3, $index); + $obj->_addLevel($xmid, $ymin, $xmax, $ymid, # br + $curDepth + 1, 4 * $index + 4, $index); +} + +############################### +# +# sub add() - public method +# +# This method adds an object to the tree. +# The arguments are a unique tag to identify +# the object, and the bounding box of the object. +# It automatically assigns the proper quadtree +# sections to each object. +# +############################### + +sub add { + my ($self, + $objRef, + @coords, + ) = @_; + + # assume that $objRef is unique. + # assume coords are (xmin, ymix, xmax, ymax). + + # modify coords according to window. + @coords = $self->_adjustCoords(@coords); + + ($coords[0], $coords[2]) = ($coords[2], $coords[0]) if + $coords[2] < $coords[0]; + ($coords[1], $coords[3]) = ($coords[3], $coords[1]) if + $coords[3] < $coords[1]; + + $self->_addObjToChild( + 0, # current index + $objRef, + @coords, + ); +} + +############################### +# +# sub _addObjToChild() - private method +# +# This method is used internally. Given +# a tree segment, an object and its area, +# it checks to see whether the object is to +# be included in the segment or not. +# The object is not included if it does not +# overlap the segment. +# +############################### + +sub _addObjToChild { + my ($self, + $index, + $objRef, + @coords, + ) = @_; + + # first check if obj overlaps current segment. + # if not, return. + my ($cxmin, $cymin, $cxmax, $cymax) = @{$self->{AREA}[$index]}; + + return if + $coords[0] > $cxmax || + $coords[2] < $cxmin || + $coords[1] > $cymax || + $coords[3] < $cymin; + + # Only add the object to the segment if we are at the last + # level of the tree. + # Else, keep traversing down. + + unless (@{$self->{CHILDREN}[$index]}) { + push @{$self->{OBJECTS}[$index]} => $objRef; # points from leaf to object + push @{$self->{BACKREF}{$objRef}} => $index; # points from object to leaf + + } else { + # Now, traverse down the hierarchy. + for my $child (@{$self->{CHILDREN}[$index]}) { + $self->_addObjToChild( + $child, + $objRef, + @coords, + ); + } + } +} + +############################### +# +# sub delete() - public method +# +# This method deletes an object from the tree. +# +############################### + +sub delete { + my ($self, + $objRef, + ) = @_; + + return unless exists $self->{BACKREF}{$objRef}; + + for my $i (@{$self->{BACKREF}{$objRef}}) { + $self->{OBJECTS}[$i] = grep {$_ ne $objRef} @{$self->{OBJECTS}[$i]}; + } + + delete $self->{BACKREF}{$objRef}; +} + +############################### +# +# sub getEnclosedObjects() - public method +# +# This method takes an area, and returns all objects +# enclosed in that area. +# +############################### + +sub getEnclosedObjects { + my ($self, + @coords) = @_; + + $self->{TEMP} = []; + + @coords = $self->_adjustCoords(@coords); + + $self->_checkOverlap( + 0, # current index + @coords, + ); + + # uniquify {TEMP}. + my %temp; + @temp{@{$self->{TEMP}}} = undef; + + # PS. I don't check explicitly if those objects + # are enclosed in the given area. They are just + # part of the segments that are enclosed in the + # given area. TBD. + + return [keys %temp]; +} + +############################### +# +# sub _adjustCoords() - private method +# +# This method adjusts the given coordinates +# according to the stored window. This is used +# when we 'zoom in' to avoid searching in areas +# that are not visible in the canvas. +# +############################### + +sub _adjustCoords { + my ($self, @coords) = @_; + + # modify coords according to window. + $_ = $self->{ORIGIN}[0] + $_ / $self->{SCALE} + for $coords[0], $coords[2]; + $_ = $self->{ORIGIN}[1] + $_ / $self->{SCALE} + for $coords[1], $coords[3]; + + return @coords; +} + +############################### +# +# sub _checkOverlap() - private method +# +# This method checks if the given coordinates overlap +# the specified tree segment. If not, nothing happens. +# If it does overlap, then it is called recuresively +# on all the segment's children. If the segment is a +# leaf, then its associated objects are pushed onto +# a temporary array for later access. +# +############################### + +sub _checkOverlap { + my ($self, + $index, + @coords, + ) = @_; + + # first check if obj overlaps current segment. + # if not, return. + my ($cxmin, $cymin, $cxmax, $cymax) = @{$self->{AREA}[$index]}; + + return if + $coords[0] >= $cxmax || + $coords[2] <= $cxmin || + $coords[1] >= $cymax || + $coords[3] <= $cymin; + + unless (@{$self->{CHILDREN}[$index]}) { + push @{$self->{TEMP}} => @{$self->{OBJECTS}[$index]}; + } else { + # Now, traverse down the hierarchy. + for my $child (@{$self->{CHILDREN}[$index]}) { + $self->_checkOverlap( + $child, + @coords, + ); + } + } +} + +############################### +# +# sub setWindow() - public method +# +# This method takes an area as input, and +# sets it as the active window. All new +# calls to any method will refer to that area. +# +############################### + +sub setWindow { + my ($self, $sx, $sy, $s) = @_; + + $self->{ORIGIN}[0] += $sx / $self->{SCALE}; + $self->{ORIGIN}[1] += $sy / $self->{SCALE}; + $self->{SCALE} *= $s; +} + +############################### +# +# sub setWindow() - public method +# This resets the window. +# +############################### + +sub resetWindow { + my $self = shift; + + $self->{ORIGIN}[$_] = 0 for 0 .. 1; + $self->{SCALE} = 1; +} + + + diff --git a/OSM/gpx.pm b/OSM/gpx.pm new file mode 100755 index 0000000..fe14a34 --- /dev/null +++ b/OSM/gpx.pm @@ -0,0 +1,199 @@ +# +# 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 OSM::gpx ; + +use strict ; +use warnings ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( readGPXFile + ) ; + +my $file ; +my $line ; + +my $wptNr = 0 ; +my $trkNr = 0 ; +my $rteNr = 0 ; +my %wpt = () ; +my %trk = () ; +my %rte = () ; + + + + +sub readGPXFile { + my $name = shift ; + + my $res = open ($file, "<", $name) ; + + if ($res) { + + $line = getLine() ; + while (defined $line) { + + if ( grep / ; + if (defined $line) { + $line =~ s/\r//g ; # remove dos/win char at line end + } + + if (defined $line) { + $line =~ s/^\s// ; + $line =~ s/\s$// ; + } + + while ( (defined $line) and (length $line == 0) ) { + $line = <$file> ; + } + return $line ; +} + + +sub readWpt { + $wptNr++ ; + # print "read wpt $wptNr\n" ; + my ($lon) = ( $line =~ /lon=\"(.+?)\"/ ) ; + my ($lat) = ( $line =~ /lat=\"(.+?)\"/ ) ; + + $wpt{$wptNr}{"lon"} = $lon ; + $wpt{$wptNr}{"lat"} = $lat ; + + while ( ! grep /<\/wpt>/i, $line) { + my ($ele) = ( $line =~ /(.+?)<\/ele>/ ) ; + my ($name) = ( $line =~ /(.+?)<\/name>/ ) ; + if (defined $name) { $wpt{$wptNr}{"name"} = cleanName ($name) ; } + if (defined $ele) { $wpt{$wptNr}{"ele"} = $ele ; } + $line = getLine() ; + } +} + + +sub readRte { + $rteNr++ ; + # print "read route $rteNr\n" ; + my $rteWptNr = 0 ; + + $line = getLine() ; + while ( ! grep /<\/rte>/i, $line) { + + if ( grep //i, $line) { + $line = getLine() ; + } + } + + my ($name) = ( $line =~ /(.+?)<\/name>/ ) ; + # if (defined $name) { $rte{$rteNr}{"name"} = cleanName ($name) ; } + + $line = getLine() ; + } +} + + + +sub readTrk { + $trkNr++ ; + my $trkSegNr = 0 ; + # print "read track $trkNr\n" ; + + $line = getLine() ; + while ( ! grep /<\/trk>/i, $line) { + + if ( grep //i, $line) { + + if ( grep //i, $line) { + $line = getLine() ; + } + } + + $line = getLine() ; + + } + + # print " track segment finished\n" ; + } + + my ($name) = ( $line =~ /(.+?)<\/name>/ ) ; + # if (defined $name) { $trk{$trkNr}{"name"} = cleanName ($name) ; } + + $line = getLine() ; + # print " track finished\n" ; + } + # print "readTrK finished\n" ; +} + + + +sub cleanName { + my $name = shift ; + $name =~ s/\//i ; + return $name ; +} + + +1 ; + + diff --git a/OSM/mapgen.pm b/OSM/mapgen.pm new file mode 100755 index 0000000..ac82144 --- /dev/null +++ b/OSM/mapgen.pm @@ -0,0 +1,2291 @@ +# +# PERL mapgen module by gary68 +# +# This module contains a lot of useful graphic functions for working with osm files and data. This enables you (in conjunction with osm.pm) +# to easily draw custom maps. +# Have a look at the last (commented) function below. It is useful for your main program! +# +# +# +# +# Copyright (C) 2010, 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 + +# +# INFO +# +# graph top left coordinates: (0,0) +# size for lines = pixel width / thickness +# +# 1.051 l0 calculation adapted + + +package OSM::mapgen ; # + +use strict ; +use warnings ; + +use Math::Trig; +use File::stat; +use Time::localtime; +use List::Util qw[min max] ; +use Encode ; +use OSM::osm ; +use OSM::QuadTree ; +use GD ; +use Geo::Proj4 ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +$VERSION = '1.19' ; + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( addAreaIcon + addOnewayArrows + center + convert + createLabel + createWayLabels + declutterStat + drawArea + drawAreaMP + drawAreaOcean + drawAreaPix + drawCircle + drawCircleRadius + drawCircleRadiusText + drawCoords + drawHead + drawFoot + drawGrid + drawLegend + drawNodeDot + drawNodeDotRouteStops + drawNodeDotPix + drawNodeCircle + drawNodeCirclePix + drawPageNumber + drawPageNumberTop + drawPageNumberBottom + drawPageNumberLeft + drawPageNumberRight + drawRuler + drawTextPix + drawTextPix2 + drawTextPixGrid + drawWay + drawWayBridge + drawWayPix + drawWayRoute + fitsPaper + getDimensions + getScale + getValue + gridSquare + initGraph + initOneways + labelWay + placeLabelAndIcon + printScale + scalePoints + scaleBase + setdpi + setBaseDpi + simplifiedPercent + sizePNG + sizeSVG + writeSVG ) ; + +# +# constants +# + +my %dashStyle = () ; +my %dashDefinition = () ; # for 300 dpi +@{$dashDefinition{1}} = (60,20,"round") ; #grid +@{$dashDefinition{11}} = (16,16,"butt") ; # tunnel + +my $wayIndexLabelColor = 9 ; +my $wayIndexLabelSize = 10 ; +my $wayIndexLabelFont = 11 ; +my $wayIndexLabelOffset = 12 ; +my $wayIndexLegendLabel = 14 ; + +my $lineCap = "round" ; +my $lineJoin = "round" ; + +my @occupiedAreas = () ; +my $labelPathId = 0 ; + +my $qtWayLabels ; +my $qtPoiLabels ; + +# +# variables +# +my $proj ; +my $projSizeX ; +my $projSizeY ; +my ($projLeft, $projRight, $projBottom, $projTop) ; + + +my ($top, $bottom, $left, $right) ; # min and max real world coordinates +my ($sizeX, $sizeY) ; # pic size in pixels + +my %svgOutputWays ; +my %svgOutputNodes ; +my @svgOutputAreas = () ; +my @svgOutputText = () ; +my @svgOutputPixel = () ; +my @svgOutputPixelGrid = () ; +my @svgOutputDef = () ; +my @svgOutputPathText = () ; +my @svgOutputIcons = () ; +my @svgOutputRouteStops = () ; +my $pathNumber = 0 ; +my $svgBaseFontSize = 10 ; +my @svgOutputRoutes = () ; + +my %areaDef = () ; +my $areaNum = 1 ; + +my $numIcons = 0 ; +my $numIconsMoved = 0 ; +my $numIconsOmitted = 0 ; +my $numLabels = 0 ; +my $numLabelsMoved = 0 ; +my $numLabelsOmitted = 0 ; +my $numWayLabelsOmitted = 0 ; + +my $dpi = 0 ; +my $baseDpi ; + +# clutter information +my %clutter = () ; +my %clutterIcon = () ; +my @lines ; + +my $simplified = 0 ; +my $simplifyTotal = 0 ; + +my $shieldPathId = 0 ; +my %createdShields = () ; # key = name; value = id of path +my %shieldXSize = () ; +my %shieldYSize = () ; + + +sub setdpi { + $dpi = shift ; +} + +sub setBaseDpi { + $baseDpi = shift ; +} + + +sub initGraph { +# +# function initializes the picture, the colors and the background (white) +# + my ($x, $l, $b, $r, $t, $color, $projection, $ellipsoid) = @_ ; + + # my $l0 = int($l) - 1 ; + my $l0 = int(($r+$l) / 2 ) ; + + $proj = Geo::Proj4->new( + proj => $projection, + ellps => $ellipsoid, + lon_0 => $l0 + ) or die "parameter error: ".Geo::Proj4->error. "\n"; + + + ($projLeft, $projBottom) = $proj->forward($b, $l) ; # lat/lon!!! + ($projRight, $projTop) = $proj->forward($t, $r) ; # lat/lon!!! + + # print "PROJ: bounds: $projLeft $projRight $projBottom $projTop\n" ; + + $projSizeX = $projRight - $projLeft ; + $projSizeY = $projTop - $projBottom ; + + my $factor = $projSizeY / $projSizeX ; + + # print "PROJ: $projSizeX x $projSizeY units, factor = $factor\n" ; + + $sizeX = int ($x) ; + $sizeY = int ($x * $factor) ; + + # print "PROJ: $sizeX x $sizeY pixels\n" ; + # print "PROJ: t b l r $t $b $l $r\n" ; + # print "PROJ: pt pb pl pr $projTop $projBottom $projLeft $projRight\n" ; + # print "PROJ: factor $factor\n" ; + # print "PROJ: l0 $l0\n" ; + + $top = $t ; + $left = $l ; + $right = $r ; + $bottom = $b ; + + drawArea ($color, "", $l, $t, $r, $t, $r, $b, $l, $b, $l, $t) ; + + $qtWayLabels = OSM::QuadTree->new( -xmin => 0, + -xmax => $sizeX+100, + -ymin => 0, + -ymax => $sizeY+40, + -depth => 5); + $qtPoiLabels = OSM::QuadTree->new( -xmin => 0, + -xmax => $sizeX+100, + -ymin => 0, + -ymax => $sizeY+40, + -depth => 5); + initDashes() ; +} + +sub initDashes { +# +# sub creates internal dash styles according to base definition +# + foreach my $style (keys %dashDefinition) { + my @array = @{$dashDefinition{$style}} ; + my $lc = pop @array ; + my $dashString = "" ; + foreach my $entry (@array) { + my $entryScaled = scalePoints ( scaleBase ($entry) ) ; + $dashString .= "$entryScaled," ; + } + $dashString .= $lc ; + $dashStyle{$style} = $dashString ; + } +} + + + +sub convert { +# +# converts real world coordinates to system graph pixel coordinates +# + my ($x, $y) = @_ ; + + my ($x1, $y1) = $proj->forward($y, $x) ; # lat/lon!!! + + my $x2 = int ( ($x1 - $projLeft) / ($projRight - $projLeft) * $sizeX ) ; + my $y2 = $sizeY - int ( ($y1 - $projBottom) / ($projTop - $projBottom) * $sizeY ) ; + + return ($x2, $y2) ; +} + +sub gridSquare { +# +# returns grid square of given coordinates for directories +# + my ($lon, $lat, $parts) = @_ ; + my ($x, $y) = convert ($lon, $lat) ; + # my $partsY = $sizeY / ($sizeX / $parts) ; + my $xi = int ($x / ($sizeX / $parts)) + 1 ; + my $yi = int ($y / ($sizeX / $parts)) + 1 ; + if ( ($x >= 0) and ($x <= $sizeX) and ($y >= 0) and ($y <= $sizeY) ) { + return (chr($xi+64) . $yi) ; + } + else { + return undef ; + } +} + + + +sub occupyArea { +# +# occupy area and make entry in quad tree for later use +# + my ($x1, $x2, $y1, $y2) = @_ ; + # left, right, bottom, top (bottom > top!) + push @occupiedAreas, [$x1, $x2, $y1, $y2] ; + $qtPoiLabels->add ($#occupiedAreas, $x1, $y1, $x2, $y2) ; +} + +sub areaOccupied { +# +# look up possible interfering objects in quad tree and check for collision +# + my ($x1, $x2, $y1, $y2) = @_ ; + # left, right, bottom, top (bottom > top!) + my $occupied = 0 ; + + my $ref2 = $qtPoiLabels->getEnclosedObjects ($x1, $y2, $x2, $y1) ; + my @index = @$ref2 ; + my @occupiedAreasTemp = () ; + foreach my $nr (@index) { + push @occupiedAreasTemp, $occupiedAreas[$nr] ; + } + + LAB1: foreach my $area (@occupiedAreasTemp) { + my $intersection = 1 ; + if ($x1 > $area->[1]) { $intersection = 0 ; } ; + if ($x2 < $area->[0]) { $intersection = 0 ; } ; + if ($y1 < $area->[3]) { $intersection = 0 ; } ; + if ($y2 > $area->[2]) { $intersection = 0 ; } ; + if ($intersection == 1) { + $occupied = 1 ; + last LAB1 ; + } + } + return ($occupied) ; +} + +sub splitLabel { +# +# split label text at space locations and then merge new parts if new part will be smaller than 21 chars +# + my $text = shift ; + my @lines = split / /, $text ; + my $merged = 1 ; + while ($merged) { + $merged = 0 ; + LAB2: for (my $i=0; $i<$#lines; $i++) { + if (length ($lines[$i] . " " . $lines[$i+1]) <= 20) { + $lines[$i] = $lines[$i] . " " . $lines[$i+1] ; + splice (@lines, $i+1, 1) ; + $merged = 1 ; + last LAB2 ; + } + } + } + return (\@lines) ; +} + + +sub svgElementIcon { +# +# create SVG text for icons +# + my ($x, $y, $icon, $sizeX, $sizeY) = @_ ; + my ($out) = " 0) { $out .= " width=\"" . $sizeX . "\"" ; } + if ($sizeY > 0) { $out .= " height=\"" . $sizeY . "\"" ; } + $out .= " xlink:href=\"" . $icon . "\" />" ; + + return ($out) ; +} + +sub drawHead { +# +# draws text on top left corner of the picture +# + my ($text, $col, $size, $font) = @_ ; + push @svgOutputText, svgElementText (20, 20, $text, $size, $font, $col) ; +} + +sub drawFoot { +# +# draws text on bottom left corner of the picture +# + my ($text, $col, $size, $font) = @_ ; + my $posX = 80 ; + my $posY = 40 ; + push @svgOutputText, svgElementText ( + scalePoints ( scaleBase ($posX) ), + $sizeY - ( scalePoints ( scaleBase ($posY) ) ), + $text, + scalePoints ( scaleBase ($size) ) , + $font, + $col + ) ; +} + + + +sub drawTextPix { +# +# draws text at pixel position +# with small offset direction bottom +# + my ($x1, $y1, $text, $col, $size, $font) = @_ ; + + push @svgOutputPixel, svgElementText ($x1, $y1, $text, $size, $font, $col) ; +} + +sub drawTextPixGrid { +# +# draws text at pixel position. code goes to grid +# + my ($x1, $y1, $text, $col, $size) = @_ ; + + push @svgOutputPixelGrid, svgElementText ($x1, $y1+9, $text, $size, "sans-serif", $col) ; +} + +sub drawNodeDot { +# +# draws node as a dot at given real world coordinates +# + my ($lon, $lat, $col, $size) = @_ ; + my ($x1, $y1) = convert ($lon, $lat) ; + push @{$svgOutputNodes{0}}, svgElementCircleFilled ($x1, $y1, $size, $col) ; +} + +sub drawNodeDotRouteStops { +# +# draws node as a dot at given real world coordinates +# + my ($lon, $lat, $col, $size) = @_ ; + my ($x1, $y1) = convert ($lon, $lat) ; + push @svgOutputRouteStops, svgElementCircleFilled ($x1, $y1, $size, $col) ; +} + +sub drawNodeDotPix { +# +# draws node as a dot at given pixels +# + my ($x1, $y1, $col, $size) = @_ ; + push @svgOutputPixel, svgElementCircleFilled ($x1, $y1, $size, $col) ; +} + + +sub drawCircle { + my ($lon, $lat, $radius, $color, $thickness) = @_ ; + # radius in meters + + my ($x, $y) = convert ($lon, $lat) ; + my $thickness2 = scalePoints ($thickness) ; + + my $radiusPixel = $radius / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ; + push @svgOutputPixelGrid, svgElementCircle ($x, $y, $radiusPixel, $thickness2, $color) ; +} + +sub drawWay { +# +# draws way as a line at given real world coordinates. nodes have to be passed as array ($lon, $lat, $lon, $lat...) +# $size = thickness +# + my ($layer, $col, $size, $dash, @nodes) = @_ ; + my $i ; + my @points = () ; + + for ($i=0; $i<$#nodes; $i+=2) { + my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ; + push @points, $x ; push @points, $y ; + } + push @{$svgOutputWays{$layer+$size/100}}, svgElementPolyline ($col, $size, $dash, @points) ; +} + +sub drawWayBridge { +# +# draws way as a line at given real world coordinates. nodes have to be passed as array ($lon, $lat, $lon, $lat...) +# $size = thickness +# + my ($layer, $col, $size, $dash, @nodes) = @_ ; + my $i ; + my @points = () ; + + if ($dash eq "11") { $dash = $dashStyle{11} ; } + + for ($i=0; $i<$#nodes; $i+=2) { + my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ; + push @points, $x ; push @points, $y ; + } + push @{$svgOutputWays{$layer+$size/100}}, svgElementPolylineBridge ($col, $size, $dash, @points) ; +} + +sub drawWayPix { +# +# draws way as a line at given pixels. nodes have to be passed as array ($x, $y, $x, $y...) +# $size = thickness +# + my ($col, $size, $dash, @nodes) = @_ ; + my $i ; + my @points = () ; + + for ($i=0; $i<$#nodes; $i+=2) { + my ($x, $y) = ($nodes[$i], $nodes[$i+1]) ; + push @points, $x ; push @points, $y ; + } + push @svgOutputPixel, svgElementPolyline ($col, $size, $dash, @points) ; +} + +sub drawWayPixGrid { +# +# draws way as a line at given pixels. nodes have to be passed as array ($x, $y, $x, $y...) +# $size = thickness +# + my ($col, $size, $dash, @nodes) = @_ ; + my $i ; + my @points = () ; + + for ($i=0; $i<$#nodes; $i+=2) { + my ($x, $y) = ($nodes[$i], $nodes[$i+1]) ; + push @points, $x ; push @points, $y ; + } + push @svgOutputPixelGrid, svgElementPolyline ($col, $size, $dash, @points) ; +} + + +sub labelWay { +# +# labels a way +# + my ($col, $size, $font, $text, $tSpan, @nodes) = @_ ; + my $i ; + my @points = () ; + + for ($i=0; $i<$#nodes; $i+=2) { + my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ; + push @points, $x ; push @points, $y ; + } + my $pathName = "Path" . $pathNumber ; $pathNumber++ ; + push @svgOutputDef, svgElementPath ($pathName, @points) ; + push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $text, $pathName, $tSpan, "middle", 50, 0) ; +} + + +sub createWayLabels { +# +# finally take all way label candidates and try to label them +# + my ($ref, $ruleRef, $declutter, $halo, $svgName) = @_ ; + my @labelCandidates = @$ref ; + my @wayRules = @$ruleRef ; + my %notDrawnLabels = () ; + my %drawnLabels = () ; + + # calc ratio to label ways first where label just fits + # these will be drawn first + foreach my $candidate (@labelCandidates) { + my $wLen = $candidate->[2] ; + my $lLen = $candidate->[3] ; + if ($wLen == 0) { $wLen = 1 ; } + if ($lLen == 0) { $lLen = 1 ; } + $candidate->[5] = $lLen / $wLen ; + } + @labelCandidates = sort { $b->[5] <=> $a->[5] } @labelCandidates ; + + foreach my $candidate (@labelCandidates) { + my $rule = $candidate->[0] ; # integer + my @ruleData = @{$wayRules[$rule]} ; + my $name = $candidate->[1] ; + my $wLen = $candidate->[2] ; + my $lLen = $candidate->[3] ; + my @points = @{$candidate->[4]} ; + + my $toLabel = 1 ; + if ( ($declutter eq "1") and ($points[0] > $points[-2]) and ( ($ruleData[1] eq "motorway") or ($ruleData[1] eq "trunk") ) ) { + $toLabel = 0 ; + } + + if ($lLen > $wLen*0.95) { + $notDrawnLabels { $name } = 1 ; + } + + if ( ($lLen > $wLen*0.95) or ($toLabel == 0) ) { + # label too long + $numWayLabelsOmitted++ ; + } + else { + + if (grep /shield/i, $name) { + # create shield if necessary + if ( ! defined $createdShields{ $name }) { + createShield ($name, $ruleData[$wayIndexLabelSize]) ; + } + + # @points = (x1, y1, x2, y2 ... ) + # $wLen in pixels + # $lLen in pixels + # + + my $shieldMaxSize = $shieldXSize{ $name } ; + if ($shieldYSize{ $name } > $shieldMaxSize) { $shieldMaxSize = $shieldYSize{ $name } ; } + + my $numShields = int ($wLen / ($shieldMaxSize * 12) ) ; + # if ($numShields > 4) { $numShields = 4 ; } + + if ($numShields > 0) { + my $step = $wLen / ($numShields + 1) ; + my $position = $step ; + while ($position < $wLen) { + my ($x, $y) = getPointOfWay (\@points, $position) ; + # print "XY: $x, $y\n" ; + + # place shield if not occupied + + my $x2 = int ($x - $shieldXSize{ $name } / 2) ; + my $y2 = int ($y - $shieldYSize{ $name } / 2) ; + + # print "AREA: $x2, $y2, $x2+$lLen, $y2+$lLen\n" ; + + if ( ! areaOccupied ($x2, $x2+$shieldXSize{ $name }, $y2+$shieldYSize{ $name }, $y2) ) { + + my $id = $createdShields{$name}; + push @svgOutputIcons, "" ; + + occupyArea ($x2, $x2+$shieldXSize{ $name }, $y2+$shieldYSize{ $name }, $y2) ; + } + + $position += $step ; + } + } + + } + + else { + + # print "$wLen - $name - $lLen\n" ; + my $numLabels = int ($wLen / (4 * $lLen)) ; + if ($numLabels < 1) { $numLabels = 1 ; } + if ($numLabels > 4) { $numLabels = 4 ; } + + if ($numLabels == 1) { + my $spare = 0.95 * $wLen - $lLen ; + my $sparePercentHalf = $spare / ($wLen*0.95) *100 / 2 ; + my $startOffset = 50 - $sparePercentHalf ; + my $endOffset = 50 + $sparePercentHalf ; + # five possible positions per way + my $step = ($endOffset - $startOffset) / 5 ; + my @positions = () ; + my $actual = $startOffset ; + while ($actual <= $endOffset) { + my ($ref, $angle) = subWay (\@points, $lLen, "middle", $actual) ; + my @way = @$ref ; + my ($col) = lineCrossings (\@way) ; + # calc quality of position. distance from middle and bend angles + my $quality = $angle + abs (50 - $actual) ; + if ($col == 0) { push @positions, ["middle", $actual, $quality] ; } + $actual += $step ; + } + if (scalar @positions > 0) { + $drawnLabels { $name } = 1 ; + # sort by quality and take best one + @positions = sort {$a->[2] <=> $b->[2]} @positions ; + my ($pos) = shift @positions ; + my ($ref, $angle) = subWay (\@points, $lLen, $pos->[0], $pos->[1]) ; + my @finalWay = @$ref ; + my $pathName = "Path" . $pathNumber ; $pathNumber++ ; + push @svgOutputDef, svgElementPath ($pathName, @points) ; + push @svgOutputPathText, svgElementPathTextAdvanced ($ruleData[$wayIndexLabelColor], $ruleData[$wayIndexLabelSize], + $ruleData[$wayIndexLabelFont], $name, $pathName, $ruleData[$wayIndexLabelOffset], $pos->[0], $pos->[1], $halo) ; + occupyLines (\@finalWay) ; + } + else { + $numWayLabelsOmitted++ ; + } + } + else { # more than one label + my $labelDrawn = 0 ; + my $interval = int (100 / ($numLabels + 1)) ; + my @positions = () ; + for (my $i=1; $i<=$numLabels; $i++) { + push @positions, $i * $interval ; + } + + foreach my $position (@positions) { + my ($refFinal, $angle) = subWay (\@points, $lLen, "middle", $position) ; + my (@finalWay) = @$refFinal ; + my ($collision) = lineCrossings (\@finalWay) ; + if ($collision == 0) { + $labelDrawn = 1 ; + $drawnLabels { $name } = 1 ; + my $pathName = "Path" . $pathNumber ; $pathNumber++ ; + push @svgOutputDef, svgElementPath ($pathName, @finalWay) ; + push @svgOutputPathText, svgElementPathTextAdvanced ($ruleData[$wayIndexLabelColor], $ruleData[$wayIndexLabelSize], + $ruleData[$wayIndexLabelFont], $name, $pathName, $ruleData[$wayIndexLabelOffset], "middle", 50, $halo) ; + occupyLines (\@finalWay) ; + } + else { + # print "INFO: $name labeled less often than desired.\n" ; + } + } + if ($labelDrawn == 0) { + $notDrawnLabels { $name } = 1 ; + } + } + } + } + } + my $labelFileName = $svgName ; + $labelFileName =~ s/\.svg/_NotDrawnLabels.txt/ ; + my $labelFile ; + open ($labelFile, ">", $labelFileName) or die ("couldn't open label file $labelFileName") ; + print $labelFile "Not drawn labels\n\n" ; + foreach my $labelName (sort keys %notDrawnLabels) { + if (!defined $drawnLabels { $labelName } ) { + print $labelFile "$labelName\n" ; + } + } + close ($labelFile) ; + +} + + +sub occupyLines { +# +# store drawn lines and make quad tree entries +# accepts multiple coordinates that form a way +# + my ($ref) = shift ; + my @coordinates = @$ref ; + + for (my $i=0; $i<$#coordinates-2; $i+=2) { + push @lines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ; + # print "PUSHED $coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]\n" ; + # drawWayPix ("black", 1, 0, @coordinates) + + $qtWayLabels->add ($#lines, $coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]) ; + + } +} + + +sub lineCrossings { +# +# checks for line collisions +# accepts multiple lines in form of multiple coordinates +# + my ($ref) = shift ; + my @coordinates = @$ref ; + my @testLines = () ; + + for (my $i=0; $i<$#coordinates-2; $i+=2) { + push @testLines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ; + } + + # find area of way + my ($found) = 0 ; + my $xMin = 999999 ; my $xMax = 0 ; + my $yMin = 999999 ; my $yMax = 0 ; + foreach my $l1 (@testLines) { + if ($l1->[0] > $xMax) { $xMax = $l1->[0] ; } + if ($l1->[0] < $xMin) { $xMin = $l1->[0] ; } + if ($l1->[1] > $yMax) { $yMax = $l1->[1] ; } + if ($l1->[1] < $yMin) { $yMin = $l1->[1] ; } + } + + # get indexes from quad tree + my $ref2 = $qtWayLabels->getEnclosedObjects ($xMin, $yMin, $xMax, $yMax) ; + # create array linesInArea + my @linesInAreaIndex = @$ref2 ; + my @linesInArea = () ; + foreach my $lineNr (@linesInAreaIndex) { + push @linesInArea, $lines[$lineNr] ; + } + + LABCR: foreach my $l1 (@testLines) { + foreach my $l2 (@linesInArea) { + my ($x, $y) = intersection (@$l1, @$l2) ; + if (($x !=0) and ($y != 0)) { + $found = 1 ; + last LABCR ; + } + } + } + if ($found == 0) { + return 0 ; + } + else { + return 1 ; + } +} + +sub triangleNode { +# +# get segment of segment as coordinates +# from start or from end of segment +# + # 0 = start + # 1 = end + my ($x1, $y1, $x2, $y2, $len, $startEnd) = @_ ; + my ($c) = sqrt ( ($x2-$x1)**2 + ($y2-$y1)**2) ; + my $percent = $len / $c ; + + my ($x, $y) ; + if ($startEnd == 0 ) { + $x = $x1 + ($x2-$x1)*$percent ; + $y = $y1 + ($y2-$y1)*$percent ; + } + else { + $x = $x2 - ($x2-$x1)*$percent ; + $y = $y2 - ($y2-$y1)*$percent ; + } + return ($x, $y) ; +} + + +sub subWay { +# +# takes coordinates and label information and creates new way/path +# also calculates total angles / bends +# + my ($ref, $labLen, $alignment, $position) = @_ ; + my @coordinates = @$ref ; + my @points ; + my @dists ; + my @angles = () ; + + for (my $i=0; $i < $#coordinates; $i+=2) { + push @points, [$coordinates[$i],$coordinates[$i+1]] ; + } + + $dists[0] = 0 ; + my $dist = 0 ; + if (scalar @points > 1) { + for (my $i=1;$i<=$#points; $i++) { + $dist = $dist + sqrt ( ($points[$i-1]->[0]-$points[$i]->[0])**2 + ($points[$i-1]->[1]-$points[$i]->[1])**2 ) ; + $dists[$i] = $dist ; + } + } + + # calc angles at nodes + if (scalar @points > 2) { + for (my $i=1;$i<$#points; $i++) { + $angles[$i] = angleMapgen ($points[$i-1]->[0], $points[$i-1]->[1], $points[$i]->[0], $points[$i]->[1], $points[$i]->[0], $points[$i]->[1], $points[$i+1]->[0], $points[$i+1]->[1]) ; + } + } + + my $wayLength = $dist ; + my $refPoint = $wayLength / 100 * $position ; + my $labelStart ; my $labelEnd ; + if ($alignment eq "start") { # left + $labelStart = $refPoint ; + $labelEnd = $labelStart + $labLen ; + } + if ($alignment eq "end") { # right + $labelEnd = $refPoint ; + $labelStart = $labelEnd - $labLen ; + } + if ($alignment eq "middle") { # center + $labelEnd = $refPoint + $labLen / 2 ; + $labelStart = $refPoint - $labLen / 2 ; + } + + # find start and end segments + my $startSeg ; my $endSeg ; + for (my $i=0; $i<$#points; $i++) { + if ( ($dists[$i]<=$labelStart) and ($dists[$i+1]>=$labelStart) ) { $startSeg = $i ; } + if ( ($dists[$i]<=$labelEnd) and ($dists[$i+1]>=$labelEnd) ) { $endSeg = $i ; } + } + + my @finalWay = () ; + my $finalAngle = 0 ; + my ($sx, $sy) = triangleNode ($coordinates[$startSeg*2], $coordinates[$startSeg*2+1], $coordinates[$startSeg*2+2], $coordinates[$startSeg*2+3], $labelStart-$dists[$startSeg], 0) ; + push @finalWay, $sx, $sy ; + + if ($startSeg != $endSeg) { + for (my $i=$startSeg+1; $i<=$endSeg; $i++) { + push @finalWay, $coordinates[$i*2], $coordinates[$i*2+1] ; + $finalAngle += abs ($angles[$i]) ; + } + } + + my ($ex, $ey) = triangleNode ($coordinates[$endSeg*2], $coordinates[$endSeg*2+1], $coordinates[$endSeg*2+2], $coordinates[$endSeg*2+3], $labelEnd-$dists[$endSeg], 0) ; + push @finalWay, $ex, $ey ; + + return (\@finalWay, $finalAngle) ; +} + +sub intersection { +# +# returns intersection point of two lines, else (0,0) +# + my ($g1x1) = shift ; + my ($g1y1) = shift ; + my ($g1x2) = shift ; + my ($g1y2) = shift ; + + my ($g2x1) = shift ; + my ($g2y1) = shift ; + my ($g2x2) = shift ; + my ($g2y2) = shift ; + + if (($g1x1 == $g2x1) and ($g1y1 == $g2y1)) { # p1 = p1 ? + return ($g1x1, $g1y1) ; + } + if (($g1x1 == $g2x2) and ($g1y1 == $g2y2)) { # p1 = p2 ? + return ($g1x1, $g1y1) ; + } + if (($g1x2 == $g2x1) and ($g1y2 == $g2y1)) { # p2 = p1 ? + return ($g1x2, $g1y2) ; + } + + if (($g1x2 == $g2x2) and ($g1y2 == $g2y2)) { # p2 = p1 ? + return ($g1x2, $g1y2) ; + } + + my $g1m ; + if ( ($g1x2-$g1x1) != 0 ) { + $g1m = ($g1y2-$g1y1)/($g1x2-$g1x1) ; # steigungen + } + else { + $g1m = 999999 ; + } + + my $g2m ; + if ( ($g2x2-$g2x1) != 0 ) { + $g2m = ($g2y2-$g2y1)/($g2x2-$g2x1) ; + } + else { + $g2m = 999999 ; + } + + if ($g1m == $g2m) { # parallel + return (0, 0) ; + } + + my ($g1b) = $g1y1 - $g1m * $g1x1 ; # abschnitte + my ($g2b) = $g2y1 - $g2m * $g2x1 ; + + my ($sx) = ($g2b-$g1b) / ($g1m-$g2m) ; # schnittpunkt + my ($sy) = ($g1m*$g2b - $g2m*$g1b) / ($g1m-$g2m); + + my ($g1xmax) = max ($g1x1, $g1x2) ; + my ($g1xmin) = min ($g1x1, $g1x2) ; + my ($g1ymax) = max ($g1y1, $g1y2) ; + my ($g1ymin) = min ($g1y1, $g1y2) ; + + my ($g2xmax) = max ($g2x1, $g2x2) ; + my ($g2xmin) = min ($g2x1, $g2x2) ; + my ($g2ymax) = max ($g2y1, $g2y2) ; + my ($g2ymin) = min ($g2y1, $g2y2) ; + + if (($sx >= $g1xmin) and + ($sx >= $g2xmin) and + ($sx <= $g1xmax) and + ($sx <= $g2xmax) and + ($sy >= $g1ymin) and + ($sy >= $g2ymin) and + ($sy <= $g1ymax) and + ($sy <= $g2ymax)) { + return ($sx, $sy) ; + } + else { + return (0, 0) ; + } +} + +sub angleMapgen { +# +# angle between lines/segments +# + my ($g1x1) = shift ; + my ($g1y1) = shift ; + my ($g1x2) = shift ; + my ($g1y2) = shift ; + my ($g2x1) = shift ; + my ($g2y1) = shift ; + my ($g2x2) = shift ; + my ($g2y2) = shift ; + + my $g1m ; + if ( ($g1x2-$g1x1) != 0 ) { + $g1m = ($g1y2-$g1y1)/($g1x2-$g1x1) ; # steigungen + } + else { + $g1m = 999999999 ; + } + + my $g2m ; + if ( ($g2x2-$g2x1) != 0 ) { + $g2m = ($g2y2-$g2y1)/($g2x2-$g2x1) ; + } + else { + $g2m = 999999999 ; + } + + if ($g1m == $g2m) { # parallel + return (0) ; + } + else { + my $t1 = $g1m -$g2m ; + my $t2 = 1 + $g1m * $g2m ; + if ($t2 == 0) { + return 90 ; + } + else { + my $a = atan (abs ($t1/$t2)) / 3.141592654 * 180 ; + return $a ; + } + } +} + + +#------------------------------------------------------------------------------------------------------------ + + +sub drawArea { +# +# draws an area like waterway=riverbank or landuse=forest. +# pass color as string and nodes as list (x1, y1, x2, y2...) - real world coordinates +# + my ($col, $icon, @nodes) = @_ ; + my $i ; + my @points = () ; + + for ($i=0; $i<$#nodes; $i+=2) { + my ($x1, $y1) = convert ($nodes[$i], $nodes[$i+1]) ; + push @points, $x1 ; push @points, $y1 ; + } + push @svgOutputAreas, svgElementPolygonFilled ($col, $icon, @points) ; +} + +sub drawAreaPix { +# +# draws an area like waterway=riverbank or landuse=forest. +# pass color as string and nodes as list (x1, y1, x2, y2...) - pixels +# used for legend +# + my ($col, $icon, @nodes) = @_ ; + my $i ; + my @points = () ; + for ($i=0; $i<$#nodes; $i+=2) { + my ($x1, $y1) = ($nodes[$i], $nodes[$i+1]) ; + push @points, $x1 ; push @points, $y1 ; + } + push @svgOutputPixel, svgElementPolygonFilled ($col, $icon, @points) ; +} + +sub drawAreaMP { +# +# draws an area like waterway=riverbank or landuse=forest. +# pass color as string and nodes as list (x1, y1, x2, y2...) - real world coordinates +# +# receives ARRAY of ARRAY of NODES LIST! NOT coordinates list like other functions +# + my ($col, $icon, $ref, $refLon, $refLat) = @_ ; + # my %lon = %$refLon ; + # my %lat = %$refLat ; + my @ways = @$ref ; + my $i ; + my @array = () ; + + foreach my $way (@ways) { + my @actual = @$way ; + # print "drawAreaMP - actual ring/way: @actual\n" ; + my @points = () ; + for ($i=0; $i<$#actual; $i++) { # without last node! SVG command 'z'! + my ($x1, $y1) = convert ( $$refLon{$actual[$i]}, $$refLat{$actual[$i]} ) ; + push @points, $x1 ; push @points, $y1 ; + } + push @array, [@points] ; + # print "drawAreaMP - array pushed: @points\n" ; + } + + push @svgOutputAreas, svgElementMultiPolygonFilled ($col, $icon, \@array) ; +} + + + +sub drawRuler { +# +# draws ruler in top right corner, size is automatic +# + my $col = shift ; + + my $B ; my $B2 ; + my $L ; my $Lpix ; + my $x ; + my $text ; + my $rx = $sizeX - scalePoints (scaleBase (80)) ; + my $ry = scalePoints (scaleBase (60)) ; #v1.17 + # my $ry = scalePoints (scaleBase (80)) ; + my $lineThickness = 8 ; # at 300dpi + my $textSize = 40 ; # at 300 dpi + my $textDist = 60 ; # at 300 dpi + my $lineLen = 40 ; # at 300 dpi + + $B = $right - $left ; # in degrees + $B2 = $B * cos ($top/360*3.14*2) * 111.1 ; # in km + $text = "50m" ; $x = 0.05 ; # default length ruler + + if ($B2 > 0.5) {$text = "100m" ; $x = 0.1 ; } # enlarge ruler + if ($B2 > 1) {$text = "500m" ; $x = 0.5 ; } # enlarge ruler + if ($B2 > 5) {$text = "1km" ; $x = 1 ; } + if ($B2 > 10) {$text = "5km" ; $x = 5 ; } + if ($B2 > 50) {$text = "10km" ; $x = 10 ; } + $L = $x / (cos ($top/360*3.14*2) * 111.1 ) ; # length ruler in km + $Lpix = $L / $B * $sizeX ; # length ruler in pixels + + push @svgOutputText, svgElementLine ($rx-$Lpix,$ry,$rx,$ry, $col, scalePoints( scaleBase ($lineThickness) ) ) ; + push @svgOutputText, svgElementLine ($rx-$Lpix,$ry,$rx-$Lpix,$ry+scalePoints(scaleBase($lineLen)), $col, scalePoints( scaleBase ($lineThickness) ) ) ; + push @svgOutputText, svgElementLine ($rx,$ry,$rx,$ry+scalePoints(scaleBase($lineLen)), $col, scalePoints( scaleBase ($lineThickness) )) ; + push @svgOutputText, svgElementLine ($rx-$Lpix/2,$ry,$rx-$Lpix/2,$ry+scalePoints(scaleBase($lineLen/2)), $col, scalePoints( scaleBase ($lineThickness) ) ) ; + push @svgOutputText, svgElementText ($rx-$Lpix, $ry+scalePoints(scaleBase($textDist)), $text, scalePoints(scaleBase($textSize)), "sans-serif", $col) ; +} + +sub drawGrid { +# +# draw grid on top of map. receives number of parts in x/lon direction +# + my ($number, $color) = @_ ; + my $part = $sizeX / $number ; + my $numY = $sizeY / $part ; + # vertical lines + for (my $i = 1; $i <= $number; $i++) { + drawWayPixGrid ($color, 1, $dashStyle{1}, $i*$part, 0, $i*$part, $sizeY) ; + drawTextPixGrid (($i-1)*$part+$part/2, scalePoints(scaleBase(160)), chr($i+64), $color, scalePoints(scaleBase(60))) ; + } + # hor. lines + for (my $i = 1; $i <= $numY; $i++) { + drawWayPixGrid ($color, 1, $dashStyle{1}, 0, $i*$part, $sizeX, $i*$part) ; + drawTextPixGrid (scalePoints(scaleBase(20)), ($i-1)*$part+$part/2, $i, $color, scalePoints(scaleBase(60))) ; + } +} + + + +##### +# SVG +##### + + +sub writeSVG { +# +# writes svg elemets collected so far to file +# + my ($fileName) = shift ; + my $file ; + my ($paper, $w, $h) = fitsPaper ($dpi) ; + + open ($file, ">", $fileName) || die "can't open svg output file"; + print $file "\n" ; + print $file "\n" ; + + my ($svg) = "\n" ; + print $file $svg ; + + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputDef) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputAreas) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach my $layer (sort {$a <=> $b} (keys %svgOutputWays)) { + foreach (@{$svgOutputWays{$layer}}) { print $file $_, "\n" ; } + } + print $file "\n" ; + + print $file "\n" ; + foreach my $layer (sort {$a <=> $b} (keys %svgOutputNodes)) { + foreach (@{$svgOutputNodes{$layer}}) { print $file $_, "\n" ; } + } + print $file "\n" ; + + + print $file "\n" ; + foreach (@svgOutputRoutes) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputRouteStops) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputText) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputIcons) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputPathText) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputPixelGrid) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + foreach (@svgOutputPixel) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + close ($file) ; +} + +sub svgElementText { +# +# creates string with svg element incl utf-8 encoding +# + my ($x, $y, $text, $size, $font, $col) = @_ ; + my $svg = "" . $text . "" ; + return $svg ; +} + +sub svgElementCircleFilled { +# +# draws circle filled +# + my ($x, $y, $size, $col) = @_ ; + my $svg = "" ; + return $svg ; +} + +sub svgElementCircle { +# +# draws not filled circle / dot +# + my ($x, $y, $radius, $size, $col) = @_ ; + my $svg = "" ; + return $svg ; +} + +sub svgElementLine { +# +# draws line between two points +# + my ($x1, $y1, $x2, $y2, $col, $size) = @_ ; + my $svg = "" ; + return $svg ; +} + + + + +sub svgElementPolyline { +# +# draws way to svg +# + my ($col, $size, $dash, @points) = @_ ; + + my $refp = simplifyPoints (\@points) ; + @points = @$refp ; + + + my $svg = "" ; + } + else { + my $lc = "" ; my $ds = "" ; + ($lc, $ds) = getDashElements ($dash) ; + $svg = $svg . "\" stroke=\"" . $col . "\" stroke-width=\"" . $size . "\" stroke-linecap=\"" . $lc . "\" stroke-linejoin=\"" . $lineJoin . "\" stroke-dasharray=\"" . $ds . "\" fill=\"none\" />" ; + } + return $svg ; +} + + +sub svgElementPolylineBridge { +# +# draws way to svg +# + my ($col, $size, $dash, @points) = @_ ; + + my $refp = simplifyPoints (\@points) ; + @points = @$refp ; + + my $svg = "" ; + } + else { + my $lc = "" ; my $ds ; + ($lc, $ds) = getDashElements ($dash) ; + $svg = $svg . "\" stroke=\"" . $col . "\" stroke-width=\"" . $size . "\" stroke-linecap=\"" . $lc . "\" stroke-dasharray=\"" . $ds . "\" fill=\"none\" />" ; + } + return $svg ; +} + + + +sub getDashElements { + my $string = shift ; + my @a = split /,/, $string ; + my $cap = pop @a ; + my $ds = "" ; my $first = 1 ; + foreach my $v (@a) { + if ($first) { + $first = 0 ; + } + else { + $ds .= "," ; + } + $ds .= $v ; + } + # print "GETDE $cap, $ds\n" ; + return ($cap, $ds) ; +} + + + +sub svgElementPath { +# +# creates path element for later use with textPath +# + my ($pathName, @points) = @_ ; + + my $refp = simplifyPoints (\@points) ; + @points = @$refp ; + + my $svg = "\n" ; +} + + +sub svgElementPathTextAdvanced { +# +# draws text to path element; anchors: start, middle, end +# + my ($col, $size, $font, $text, $pathName, $tSpan, $alignment, $offset, $halo) = @_ ; + + my $svg = " 0) { + $svg = $svg . "font-weight=\"bold\" " ; + $svg = $svg . "stroke=\"white\" " ; + $svg = $svg . "stroke-width=\"" . $halo . "\" " ; + $svg = $svg . "opacity=\"90\%\" " ; + } + + $svg = $svg . "fill=\"" . $col . "\" >\n" ; + $svg = $svg . "\n" ; + $svg = $svg . "" . $text . " \n" ; + $svg = $svg . "\n\n" ; + return $svg ; +} + + +sub svgElementPolygonFilled { +# +# draws areas in svg, filled with color +# + my ($col, $icon, @points) = @_ ; + + my $refp = simplifyPoints (\@points) ; + @points = @$refp ; + + my $i ; + my $svg ; + if (defined $areaDef{$icon}) { + $svg = "" ; + return $svg ; +} + +sub svgElementMultiPolygonFilled { +# +# draws mp in svg, filled with color. accepts holes. receives ARRAY of ARRAY of coordinates +# + my ($col, $icon, $ref) = @_ ; + + my @ways = @$ref ; + my $i ; + my $svg ; + if (defined $areaDef{$icon}) { + $svg = "" ; + # print "svg - text = $svg\n" ; + return $svg ; +} + +sub createLabel { +# +# takes @tags and labelKey(s) from style file and creates labelTextTotal and array of labels for directory +# takes more keys in one string - using a separator. +# +# § all listed keys will be searched for and values be concatenated +# # first of found keys will be used to select value +# "name§ref" will return all values if given +# "name#ref" will return name, if given. if no name is given, ref will be used. none given, no text +# + my ($ref1, $styleLabelText, $lon, $lat) = @_ ; + my @tags = @$ref1 ; + my @keys ; + my @labels = () ; + my $labelTextTotal = "" ; + + if (grep /!/, $styleLabelText) { # AND + @keys = split ( /!/, $styleLabelText) ; + # print "par found: $styleLabelText; @keys\n" ; + for (my $i=0; $i<=$#keys; $i++) { + if ($keys[$i] eq "_lat") { push @labels, $lat ; } + if ($keys[$i] eq "_lon") { push @labels, $lon ; } + foreach my $tag (@tags) { + if ($tag->[0] eq $keys[$i]) { + push @labels, $tag->[1] ; + } + } + } + $labelTextTotal = "" ; + foreach my $label (@labels) { $labelTextTotal .= $label . " " ; } + } + else { # PRIO + @keys = split ( /#/, $styleLabelText) ; + my $i = 0 ; my $found = 0 ; + while ( ($i<=$#keys) and ($found == 0) ) { + if ($keys[$i] eq "_lat") { push @labels, $lat ; $found = 1 ; $labelTextTotal = $lat ; } + if ($keys[$i] eq "_lon") { push @labels, $lon ; $found = 1 ; $labelTextTotal = $lon ; } + foreach my $tag (@tags) { + if ($tag->[0] eq $keys[$i]) { + push @labels, $tag->[1] ; + $labelTextTotal = $tag->[1] ; + $found = 1 ; + } + } + $i++ ; + } + } + return ( $labelTextTotal, \@labels) ; +} + +sub center { +# +# calculate center of area by averageing lons/lats. could be smarter because result could be outside of area! TODO +# + my @nodes = @_ ; + my $x = 0 ; + my $y = 0 ; + my $num = 0 ; + + while (scalar @nodes > 0) { + my $y1 = pop @nodes ; + my $x1 = pop @nodes ; + $x += $x1 ; + $y += $y1 ; + $num++ ; + } + $x = $x / $num ; + $y = $y / $num ; + return ($x, $y) ; +} + +sub printScale { +# +# print scale based on dpi and global variables left, right etc. +# + my ($dpi, $color) = @_ ; + + my $dist = distance ($left, $bottom, $right, $bottom) ; + my $inches = $sizeX / $dpi ; + my $cm = $inches * 2.54 ; + my $scale = int ( $dist / ($cm/100/1000) ) ; + $scale = int ($scale / 100) * 100 ; + my $text = "1 : $scale" ; + # sizes for 300 dpi + my $posX = 350 ; + my $posY = 50 ; + my $size = 56 ; + drawTextPix ( + $sizeX-scalePoints( scaleBase($posX) ), + scalePoints( scaleBase($posY) ), + $text, $color, + scalePoints( scaleBase ($size) ), "sans-serif" + ) ; +} + + +sub getScale { +# +# calcs scale of map +# + my ($dpi) = shift ; + + my $dist = distance ($left, $bottom, $right, $bottom) ; + my $inches = $sizeX / $dpi ; + my $cm = $inches * 2.54 ; + my $scale = int ( $dist / ($cm/100/1000) ) ; + $scale = int ($scale / 100) * 100 ; + + return ($scale) ; +} + +sub fitsPaper { +# +# takes dpi and calculates on what paper size the map will fit. sizes are taken from global variables +# + my ($dpi) = shift ; + + + + my @sizes = () ; + my $width = $sizeX / $dpi * 2.54 ; + my $height = $sizeY / $dpi * 2.54 ; + my $paper = "" ; + push @sizes, ["4A0", 168.2, 237.8] ; + push @sizes, ["2A0", 118.9, 168.2] ; + push @sizes, ["A0", 84.1, 118.9] ; + push @sizes, ["A1", 59.4, 84.1] ; + push @sizes, ["A2", 42, 59.4] ; + push @sizes, ["A3", 29.7, 42] ; + push @sizes, ["A4", 21, 29.7] ; + push @sizes, ["A5", 14.8, 21] ; + push @sizes, ["A6", 10.5, 14.8] ; + push @sizes, ["A7", 7.4, 10.5] ; + push @sizes, ["none", 0, 0] ; + + foreach my $size (@sizes) { + if ( ( ($width<=$size->[1]) and ($height<=$size->[2]) ) or ( ($width<=$size->[2]) and ($height<=$size->[1]) ) ) { + $paper = $size->[0] ; + } + } + + return ($paper, $width, $height) ; +} + + + + +sub drawCoords { +# +# draws coordinates grid on map +# + my ($exp, $color) = @_ ; + my $step = 10 ** $exp ; + + # vert. lines + my $start = int ($left / $step) + 1 ; + my $actual = $start * $step ; + while ($actual < $right) { + # print "actualX: $actual\n" ; + my ($x1, $y1) = convert ($actual, 0) ; + drawTextPixGrid ($x1+scalePoints(scaleBase(10)), $sizeY-scalePoints(scaleBase(50)), $actual, $color, scalePoints(scaleBase(40))) ; + drawWayPixGrid ($color, 1, "none", ($x1, 0, $x1, $sizeY) ) ; + $actual += $step ; + } + + # hor lines + $start = int ($bottom / $step) + 1 ; + $actual = $start * $step ; + while ($actual < $top) { + # print "actualY: $actual\n" ; + my ($x1, $y1) = convert (0, $actual) ; + drawTextPixGrid ($sizeX-scalePoints(scaleBase(180)), $y1+scalePoints(scaleBase(30)), $actual, $color, scalePoints(scaleBase(40))) ; + drawWayPixGrid ($color, 1, "none", (0, $y1, $sizeX, $y1) ) ; + $actual += $step ; + } +} + + +sub getValue { +# +# gets value of a certain tag +# + my ($key, $ref) = @_ ; + my @relationTags = @$ref ; + + my $value = "" ; + foreach my $tag (@relationTags) { + if ($tag->[0] eq $key) { $value = $tag->[1] ; } + } + return ($value) ; +} + + +sub drawWayRoute { +# +# draws way as a line at given real world coordinates. nodes have to be passed as array ($lon, $lat, $lon, $lat...) +# $size = thickness +# + my ($col, $size, $dash, $opacity, @nodes) = @_ ; + my $i ; + my @points = () ; + + for ($i=0; $i<$#nodes; $i+=2) { + my ($x, $y) = convert ($nodes[$i], $nodes[$i+1]) ; + push @points, $x ; push @points, $y ; + } + push @svgOutputRoutes, svgElementPolylineOpacity ($col, $size, $dash, $opacity, @points) ; +} + + +sub svgElementPolylineOpacity { +# +# draws way to svg with opacity; for routes +# + my ($col, $size, $dash, $opacity, @points) = @_ ; + + my $refp = simplifyPoints (\@points) ; + @points = @$refp ; + + + my $svg = "" ; + } + else { + my $lc = "" ; my $ds = "" ; + ($lc, $ds) = getDashElements ($dash) ; + $svg = $svg . "\" stroke=\"" . $col . + "\" stroke-width=\"" . $size . + "\" stroke-opacity=\"" . $opacity . + "\" stroke-linecap=\"" . $lc . + "\" stroke-linejoin=\"" . $lineJoin . + "\" stroke-dasharray=\"" . $ds . + "\" fill=\"none\" />" ; + } + return $svg ; +} + + +sub addAreaIcon { +# +# initial collection of area icons +# + my $fileNameOriginal = shift ; + # print "AREA: $fileNameOriginal\n" ; + my $result = open (my $file, "<", $fileNameOriginal) ; + close ($file) ; + if ($result) { + my ($x, $y) ; + if (grep /.svg/, $fileNameOriginal) { + ($x, $y) = sizeSVG ($fileNameOriginal) ; + if ( ($x == 0) or ($y == 0) ) { + $x = 32 ; $y = 32 ; + print "WARNING: size of file $fileNameOriginal could not be determined. Set to 32px x 32px\n" ; + } + } + + if (grep /.png/, $fileNameOriginal) { + ($x, $y) = sizePNG ($fileNameOriginal) ; + } + + if (!defined $areaDef{$fileNameOriginal}) { + + my $x1 = scalePoints( $x ) ; # scale area icons + my $y1 = scalePoints( $y ) ; + my $fx = $x1 / $x ; + my $fy = $y1 / $y ; + + # add defs to svg output + my $defName = "A" . $areaNum ; + # print "INFO area icon $fileNameOriginal, $defName, $x, $y --- $x1, $y1 --- $fx, $fy --- processed.\n" ; + $areaNum++ ; + + my $svgElement = "\n" ; + $svgElement .= " \n" ; + $svgElement .= "\n" ; + push @svgOutputDef, $svgElement ; + $defName = "#" . $defName ; + $areaDef{$fileNameOriginal} = $defName ; + } + } + else { + print "WARNING: area icon $fileNameOriginal not found!\n" ; + } +} + + + + +sub svgEle { +# +# creates svg element string +# + my ($a, $b) = @_ ; + my $out = $a . "=\"" . $b . "\" " ; + return ($out) +} + + + +sub initOneways { +# +# write marker defs to svg +# + my $color = shift ; + my $markerSize = scalePoints (scaleBase (20)) ; + + push @svgOutputDef, "" ; + push @svgOutputDef, "" ; + push @svgOutputDef, "" ; +} + + +sub addOnewayArrows { +# +# adds oneway arrows to new pathes +# + my ($wayNodesRef, $lonRef, $latRef, $direction, $thickness, $color, $layer) = @_ ; + my @wayNodes = @$wayNodesRef ; + my $minDist = scalePoints(scaleBase(25)) ; + # print "OW: mindist = $minDist\n" ; + + if ($direction == -1) { @wayNodes = reverse @wayNodes ; } + + # create new pathes with new nodes + for (my $i=0; $i $minDist) { + # create path + # use path + my $svg = "" ; + + push @{$svgOutputWays{$layer+$thickness/100}}, $svg ; + } + } +} + +sub declutterStat { +# +# creates print string with clutter/declutter information +# + my $perc1 ; + my $perc2 ; + my $perc3 ; + my $perc4 ; + if ($numIcons != 0) { + $perc1 = int ($numIconsMoved / $numIcons * 100) ; + $perc2 = int ($numIconsOmitted / $numIcons * 100) ; + } + else { + $perc1 = 0 ; + $perc2 = 0 ; + } + if ($numLabels != 0) { + $perc3 = int ($numLabelsMoved / $numLabels * 100) ; + $perc4 = int ($numLabelsOmitted / $numLabels * 100) ; + } + else { + $perc3 = 0 ; + $perc4 = 0 ; + } + + my $out = "$numIcons icons drawn.\n" ; + $out .= " $numIconsMoved moved. ($perc1 %)\n" ; + $out .= " $numIconsOmitted omitted (possibly with label!). ($perc2 %)\n" ; + + $out .= "$numLabels labels drawn.\n" ; + $out .= " $numLabelsMoved moved. ($perc3 %)\n" ; + $out .= " $numLabelsOmitted omitted. ($perc4 %)\n\n" ; + $out .= "$numWayLabelsOmitted way labels omitted because way was too short, collision or declutter.\n" ; + + +} + +sub placeLabelAndIcon { +# +# intelligent icon and label placement alg. +# + my ($lon, $lat, $offset, $thickness, $text, $color, $textSize, $font, $ppc, $icon, $iconSizeX, $iconSizeY, $allowIconMove, $halo) = @_ ; + + my ($x, $y) = convert ($lon, $lat) ; # center ! + $y = $y + $offset ; + + my ($ref) = splitLabel ($text) ; + my (@lines) = @$ref ; + my $numLines = scalar @lines ; + my $maxTextLenPix = 0 ; + my $orientation = "" ; + my $lineDist = 2 ; + my $tries = 0 ; + + foreach my $line (@lines) { + my $len = length ($line) * $ppc / 10 * $textSize ; # in pixels + if ($len > $maxTextLenPix) { $maxTextLenPix = $len ; } + } + my $spaceTextX = $maxTextLenPix ; + my $spaceTextY = $numLines * ($lineDist+$textSize) ; + + + if ($icon ne "none") { + $numIcons++ ; + # space for icon? + my $sizeX1 = $iconSizeX ; if ($sizeX1 == 0) { $sizeX1 = 20 ; } + my $sizeY1 = $iconSizeY ; if ($sizeY1 == 0) { $sizeY1 = 20 ; } + my $iconX = $x - $sizeX1/2 ; # top left corner + my $iconY = $y - $sizeY1/2 ; + + my @shifts = (0) ; + if ($allowIconMove eq "1") { + @shifts = ( 0, scalePoints(scaleBase(-15)), scalePoints(scaleBase(15)) ) ; + } + my $posFound = 0 ; my $posCount = 0 ; + LABAB: foreach my $xShift (@shifts) { + foreach my $yShift (@shifts) { + $posCount++ ; + if ( ! areaOccupied ($iconX+$xShift, $iconX+$sizeX1+$xShift, $iconY+$sizeY1+$yShift, $iconY+$yShift) ) { + push @svgOutputIcons, svgElementIcon ($iconX+$xShift, $iconY+$yShift, $icon, $sizeX1, $sizeY1) ; + occupyArea ($iconX+$xShift, $iconX+$sizeX1+$xShift, $iconY+$sizeY1+$yShift, $iconY+$yShift) ; + $posFound = 1 ; + if ($posCount > 1) { $numIconsMoved++ ; } + $iconX = $iconX + $xShift ; # for later use with label + $iconY = $iconY + $yShift ; + last LABAB ; + } + } + } + if ($posFound == 1) { + + # label text? + if ($text ne "") { + $numLabels++ ; + + + $sizeX1 += 1 ; $sizeY1 += 1 ; + + my ($x1, $x2, $y1, $y2) ; + # $x, $y centered + # yes, check if space for label, choose position, draw + # no, count omitted text + + my @positions = () ; my $positionFound = 0 ; + # pos 1 centered below + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $sizeY1/2 + $spaceTextY ; $y2 = $y + $sizeY1/2 ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # pos 2/3 to the right, bottom, top + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # pos 4 centered upon + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y - $sizeY1/2 - $spaceTextY ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # pos 5/6 to the right, below and upon + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # left normal, bottom, top + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # left corners, bottom, top + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + + $tries = 0 ; + LABB: foreach my $pos (@positions) { + $tries++ ; + $positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines, \@lines, $color, $textSize, $font, $lineDist, $halo) ; + if ($positionFound == 1) { + last LABB ; + } + } + if ($positionFound == 0) { $numLabelsOmitted++ ; } + if ($tries > 1) { $numLabelsMoved++ ; } + } + } + else { + # no, count omitted + $numIconsOmitted++ ; + } + } + else { # only text + my ($x1, $x2, $y1, $y2) ; + # x1, x2, y1, y2 + # left, right, bottom, top + # choose space for text, draw + # count omitted + + $numLabels++ ; + my @positions = () ; + $x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y + $spaceTextY ; $y2 = $y ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + $x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $thickness ; $y2 = $y - ($thickness + $spaceTextY) ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $thickness + $spaceTextY ; $y2 = $y + $thickness ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + my $positionFound = 0 ; + $tries = 0 ; + LABA: foreach my $pos (@positions) { + $tries++ ; + # print "$lines[0] $pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines\n" ; + $positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines, \@lines, $color, $textSize, $font, $lineDist, $halo) ; + if ($positionFound == 1) { + last LABA ; + } + } + if ($positionFound == 0) { $numLabelsOmitted++ ; } + if ($tries > 1) { $numLabelsMoved++ ; } + } +} + + +sub checkAndDrawText { +# +# checks if area available and if so draws text +# + my ($x1, $x2, $y1, $y2, $orientation, $numLines, $ref, $col, $size, $font, $lineDist, $halo) = @_ ; + my @lines = @$ref ; + + if (!areaOccupied ($x1, $x2, $y1, $y2)) { + + for (my $i=0; $i<=$#lines; $i++) { + my @points = ($x1, $y2+($i+1)*($size+$lineDist), $x2, $y2+($i+1)*($size+$lineDist)) ; + my $pathName = "LabelPath" . $labelPathId ; + $labelPathId++ ; + push @svgOutputDef, svgElementPath ($pathName, @points) ; + if ($orientation eq "centered") { + push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $lines[$i], $pathName, 0, "middle", 50, $halo) ; + } + if ($orientation eq "left") { + push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $lines[$i], $pathName, 0, "start", 0, $halo) ; + } + if ($orientation eq "right") { + push @svgOutputPathText, svgElementPathTextAdvanced ($col, $size, $font, $lines[$i], $pathName, 0, "end", 100, $halo) ; + } + } + + occupyArea ($x1, $x2, $y1, $y2) ; + + return (1) ; + } + else { + return 0 ; + } +} + +sub getDimensions { +# +# returns dimensions of map +# + return ($sizeX, $sizeY) ; +} + + + +sub drawAreaOcean { + my ($col, $ref) = @_ ; + push @svgOutputAreas, svgElementMultiPolygonFilled ($col, "none", $ref) ; +} + +sub sizePNG { +# +# evaluates size of png graphics +# + my $fileName = shift ; + + my ($x, $y) ; + my $file ; + my $result = open ($file, "<", $fileName) ; + if ($result) { + my $pic = newFromPng GD::Image($file) ; + ($x, $y) = $pic->getBounds ; + close ($file) ; + } + else { + ($x, $y) = (0, 0) ; + } + return ($x, $y) ; +} + +sub sizeSVG { +# +# evaluates size of svg graphics +# + my $fileName = shift ; + my $file ; + my ($x, $y) ; undef $x ; undef $y ; + + my $result = open ($file, "<", $fileName) ; + if ($result) { + my $line ; + while ($line = <$file>) { + my ($x1) = ( $line =~ /^.*width=\"([\d]+)px\"/ ) ; + my ($y1) = ( $line =~ /^.*height=\"([\d]+)px\"/ ) ; + if (!defined $x1) { + ($x1) = ( $line =~ /^\s*width=\"([\d]+)\"/ ) ; + + } + if (!defined $y1) { + ($y1) = ( $line =~ /^\s*height=\"([\d]+)\"/ ) ; + } + if (defined $x1) { $x = $x1 ; } + if (defined $y1) { $y = $y1 ; } + } + close ($file) ; + } + + if ( (!defined $x) or (!defined $y) ) { + $x = 0 ; $y = 0 ; + print "WARNING: size of file $fileName could not be determined.\n" ; + } + return ($x, $y) ; +} + +sub scalePoints { + my $a = shift ; + # my $b = $a ; + my $b = $a / $baseDpi * $dpi ; + + return (int ($b*10)) / 10 ; +} + + +sub scaleBase { +# +# function scales sizes given in 300dpi to base dpi given in rules so texts in legend, ruler etc. will appear in same size +# + my $a = shift ; + my $b = $a / 300 * $baseDpi ; + return $b ; +} + +#----------------------------------------------------------------------------- + +sub simplifyPoints { + my $ref = shift ; + my @points = @$ref ; + my @newPoints ; + my $maxIndex = $#points ; + + if (scalar @points > 4) { + # push first + push @newPoints, $points[0], $points[1] ; + + # push other + for (my $i=2; $i <= $maxIndex; $i+=2) { + $simplifyTotal++ ; + if ( ($points[$i]==$points[$i-2]) and ($points[$i+1]==$points[$i-1]) ) { + # same + $simplified++ ; + } + else { + push @newPoints, $points[$i], $points[$i+1] ; + } + } + return (\@newPoints) ; + } + else { + return ($ref) ; + } + +} + +sub simplifiedPercent { + return ( int ($simplified / $simplifyTotal * 100) ) ; +} + +sub drawPageNumber { + my ($size, $col, $num) = @_ ; + my $x = $sizeX - scalePoints (scaleBase (80)) ; + my $y = $sizeY - scalePoints (scaleBase (80)) ; + drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ; +} + +sub drawPageNumberLeft { + my ($size, $col, $num) = @_ ; + my $x = scalePoints (scaleBase (80)) ; + my $y = $sizeY / 2 ; + drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ; + +} + +sub drawPageNumberBottom { + my ($size, $col, $num) = @_ ; + my $x = $sizeX / 2 ; + my $y = $sizeY - scalePoints (scaleBase (80)) ; + drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ; + +} + +sub drawPageNumberRight { + my ($size, $col, $num) = @_ ; + my $x = $sizeX - scalePoints (scaleBase (80)) ; + my $y = $sizeY / 2 ; + drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ; + +} + +sub drawPageNumberTop { + my ($size, $col, $num) = @_ ; + my $x = $sizeX / 2 ; + my $y = scalePoints (scaleBase (80)) ; + drawTextPixGrid ($x, $y, $num, $col, scalePoints ( scaleBase ($size) ) ) ; + +} + + +sub createShield { + my ($name, $targetSize) = @_ ; + my @a = split /:/, $name ; + my $shieldFileName = $a[1] ; + my $shieldText = $a[2] ; + + if (! defined $createdShields{$name}) { + open (my $file, "<", $shieldFileName) or die ("ERROR: shield definition $shieldFileName not found.\n") ; + my @defText = <$file> ; + close ($file) ; + + # get size + # calc scaling + my $sizeX = 0 ; + my $sizeY = 0 ; + foreach my $line (@defText) { + if (grep / 0) { + my $x = shift @points ; + my $y = shift @points ; + push @double, [$x, $y] ; + } + + my $i = 0 ; my $actLen = 0 ; + while ($actLen < $position) { + $actLen += sqrt ( ($double[$i]->[0]-$double[$i+1]->[0])**2 + ($double[$i]->[1]-$double[$i+1]->[1])**2 ) ; + $i++ ; + } + + my $x = int (($double[$i]->[0] + $double[$i-1]->[0]) / 2) ; + my $y = int (($double[$i]->[1] + $double[$i-1]->[1]) / 2) ; + + # print "POW: $x, $y\n" ; + + return ($x, $y) ; +} + + + + + + +1 ; + + diff --git a/OSM/mapgenRules.pm b/OSM/mapgenRules.pm new file mode 100755 index 0000000..4eed571 --- /dev/null +++ b/OSM/mapgenRules.pm @@ -0,0 +1,180 @@ +# +# PERL mapgenRules module by gary68 +# +# +# Copyright (C) 2010, 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 OSM::mapgenRules ; # + +use strict ; +use warnings ; + +use List::Util qw[min max] ; +use OSM::osm ; +use OSM::mapgen 1.19 ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +$VERSION = '1.19' ; + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( readRules printRules ) ; + +# +# constants +# + +# +# variables +# +my @nodes = () ; +my @ways = () ; +my @routes = () ; + +sub readRules { + my $csvName = shift ; + # READ STYLE File + print "read style file and preprocess tile icons for areas...\n" ; + open (my $csvFile, "<", $csvName) or die ("ERROR: style file not found.") ; + my $line = <$csvFile> ; # omit SECTION + + # READ NODE RULES + $line = <$csvFile> ; + while (! grep /^\"SECTION/, $line) { + if (! grep /^\"COMMENT/i, $line) { + my ($key, $value, $color, $thickness, $label, $labelColor, $labelSize, $labelFont, $labelOffset, $legend, $legendLabel, $icon, $iconSize, $fromScale, $toScale) = ($line =~ /\"(.+)\" \"(.+)\" \"(.+)\" (\d+) \"(.+)\" \"(.+)\" (\d+) \"(.+)\" (\d+) (\d) \"(.+)\" \"(.+)\" (\d+) (\d+) (\d+)/ ) ; + # print "N $key, $value, $color, $thickness, $label, $labelColor, $labelSize, $labelFont, $labelOffset, $legend, $legendLabel, $icon, $iconSize, $fromScale, $toScale\n" ; + push @nodes, [$key, $value, $color, $thickness, $label, $labelColor, $labelSize, $labelFont, $labelOffset, $legend, $legendLabel, $icon, $iconSize, $fromScale, $toScale] ; + } + $line = <$csvFile> ; + } + + # READ WAY RULES + $line = <$csvFile> ; # omit SECTION + while ( (! grep /^\"SECTION/, $line) and (defined $line) ) { + if (! grep /^\"COMMENT/i, $line) { + # print "way line: $line\n" ; + my ($key, $value, $color, $thickness, $dash, $borderColor, $borderSize, $fill, $label, $labelColor, $labelSize, $labelFont, $labelOffset, $legend, $legendLabel, $baseLayer, $areaIcon, $fromScale, $toScale) = + ($line =~ /\"(.+)\" \"(.+)\" \"(.+)\" (\d+) \"(.+)\" \"(.+)\" (\d+) (\d+) \"(.+)\" \"(.+)\" (\d+) \"(.+)\" ([\d\-]+) (\d) \"(.+)\" (\d) \"(.+)\" (\d+) (\d+)/ ) ; + # print "W $key, $value, $color, $thickness, $dash, $borderColor, $borderSize, $fill, $label, $labelColor, $labelSize, $labelFont, $labelOffset, $legend, $legendLabel, $baseLayer, $areaIcon, $fromScale, $toScale\n" ; + push @ways, [$key, $value, $color, $thickness, $dash, $borderColor, $borderSize, $fill, $label, $labelColor, $labelSize, $labelFont, $labelOffset, $legend, $legendLabel, $baseLayer, $areaIcon, $fromScale, $toScale] ; + if (($areaIcon ne "") and ($areaIcon ne "none")) { addAreaIcon ($areaIcon) ; } + } + $line = <$csvFile> ; + } + + # READ ROUTE RULES + #print "ROUTE LINE: $line\n" ; + $line = <$csvFile> ; # omit SECTION + #print "ROUTE LINE: $line\n" ; + while ( (! grep /^\"SECTION/, $line) and (defined $line) ) { + if (! grep /^\"COMMENT/i, $line) { + #print "ROUTE LINE: $line\n" ; + my ($route, $color, $thickness, $dash, $opacity, $label, $nodeThickness, $fromScale, $toScale) = ($line =~ /\"(.+)\" \"(.+)\" (\d+) \"(.+)\" (\d+) \"(.+)\" (\d+) (\d+) (\d+)/ ) ; + $opacity = $opacity / 100 ; + push @routes, [$route, $color, $thickness, $dash, $opacity, $label, $nodeThickness, $fromScale, $toScale] ; + } + $line = <$csvFile> ; + } + close ($csvFile) ; + + foreach my $node (@nodes) { + $node->[3] = scalePoints ($node->[3]) ; + $node->[6] = scalePoints ($node->[6]) ; + $node->[8] = scalePoints ($node->[8]) ; + $node->[12] = scalePoints ($node->[12]) ; + } + + foreach my $way (@ways) { + $way->[3] = scalePoints ($way->[3]) ; + $way->[6] = scalePoints ($way->[6]) ; + $way->[10] = scalePoints ($way->[10]) ; + $way->[12] = scalePoints ($way->[12]) ; + } + + foreach my $route (@routes) { + $route->[2] = scalePoints ($route->[2]) ; + $route->[6] = scalePoints ($route->[6]) ; + } + + foreach my $way (@ways) { + if ($way->[4] ne "none") { + # print "DASH BEFORE $way->[4]\n" ; + my @dash = split /,/, $way->[4] ; + my $dashNew = "" ; + my $cap = pop @dash ; + my $validCap = 0 ; + foreach my $c ("butt", "round", "square") { + if ($cap eq $c) { $validCap = 1 ; } + } + if ($validCap == 0) { $cap = "round" ; } + if (scalar @dash % 2 != 0) { die "ERROR: odd number in dash definition $way->[4]\n" ; } + foreach my $v (@dash) { + $v = scalePoints ($v) ; + $dashNew .= $v . "," ; + } + $dashNew .= $cap ; + $way->[4] = $dashNew ; + # print "DASH AFTER $way->[4]\n" ; + } + } + + foreach my $route (@routes) { + if ($route->[3] ne "none") { + my @dash = split /,/, $route->[3] ; + my $dashNew = "" ; + my $cap = pop @dash ; + my $validCap = 0 ; + foreach my $c ("butt", "round", "square") { + if ($cap eq $c) { $validCap = 1 ; } + } + if ($validCap == 0) { $cap = "round" ; } + if (scalar @dash % 2 != 0) { die "ERROR: odd number in dash definition $route->[3]\n" ; } + foreach my $v (@dash) { + $v = scalePoints ($v) ; + $dashNew .= $v . "," ; + } + $dashNew .= $cap ; + $route->[3] = $dashNew ; + } + } + + return (\@nodes, \@ways, \@routes) ; +} + + +sub printRules { + print "WAYS/AREAS\n" ; + foreach my $way (@ways) { + printf "%-20s %-20s %-10s %-6s %-6s %-10s %-6s %-6s %-10s %-10s %-10s %-10s %-6s %-6s %-15s %-6s %-20s %-10s %-10s\n", $way->[0], $way->[1], $way->[2], $way->[3], $way->[4], $way->[5], $way->[6], $way->[7], $way->[8], $way->[9], $way->[10], $way->[11], $way->[12], $way->[13], $way->[14], $way->[15], $way->[16], $way->[17], $way->[18] ; + } + print "\n" ; + print "NODES\n" ; + foreach my $node (@nodes) { + printf "%-20s %-20s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-15s %-20s %6s %-10s %-10s\n", $node->[0], $node->[1], $node->[2], $node->[3], $node->[4], $node->[5], $node->[6], $node->[7], $node->[8], $node->[9], $node->[10], $node->[11], $node->[12], $node->[13], $node->[14] ; + } + print "\n" ; + + print "ROUTES\n" ; + foreach my $route (@routes) { + printf "%-20s %-20s %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n", $route->[0], $route->[1], $route->[2], $route->[3], $route->[4], $route->[5], $route->[6], $route->[7], $route->[8] ; + } + print "\n" ; +} + + +1 ; + + diff --git a/OSM/osm.pm b/OSM/osm.pm new file mode 100755 index 0000000..2623cb3 --- /dev/null +++ b/OSM/osm.pm @@ -0,0 +1,1760 @@ +# +# +# PERL osm module by gary68 +# +# !!! store as osm.pm in folder OSM in lib directory !!! +# +# This module contains a lot of useful functions for working with osm files and data. it also +# includes functions for calculation and output. +# +# +# Copyright (C) 2008, 2009, 2010 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 +# +# +# version 2 +# - added html table functions +# +# Version 3 +# - added project and angle +# - added support for bz2 files +# +# Version 4 +# - add support for relations +# - select multiple ways in JOSM link +# - getNode2, getWay2: return tags as arrays +# +# Version 4.1 +# - getBugs added +# +# Version 4.2 +# - map compare link added +# +# Version 4.3 +# - regex for k/v changed +# +# Version 4.4 +# -added relation analyzer link +# +# Version 4.41 (gary68) +# - changed regex for openosmfile from /node/ to /JbnZ?ASm}QnS%YsNO3nF2A9kSSWyScJ_he+fsNbLOi)aUpm*Z)?V z5xeo_EXc&6LqyM + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/imbiss-64.png b/icons-topo/imbiss-64.png new file mode 100644 index 0000000000000000000000000000000000000000..58b730659d7c8b303a12a448fc87c9d3798122ee GIT binary patch literal 2060 zcmV+n2=n)eP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12Z>2UK~#90?VE2*Tjd(Se=Th(TxfxN3j@(8#?8&RIm-fThzX0! z%z{So&;77u?$h=~b8}-BB`z}Y&s>ZnHyH`xlf!7Tm~3V$nTgTFkp=>F8}3dw6PCCM zD=-GMgL2Mk&;4*{t!HgHr#+_(?|#Wi;eDPy=Xsvh>HAt_Sw)FHI_iPP zfnu!&Oky7R5eNXoOP`m3n3gYFG$fz`=mc7UlC0g#FJa&W@NZx`lj3p-cpB&d))?km zez^|p1-?wDU`^9r1{?!Uv3i3C#rIPvv6Q7!NL2w%z;U2PEr5tU5&%*#qRausISMdWeWvWRjE|RfPVu0$&Km+lmY)m6}&76%yo1yclaN$cRZEn}dDhMk+r3 z7{}(#YEduna{QmSG!X9UQj2z@#0vaT0Tn2vpBV6j zvuBxTXh0UlJh*Ef7Q3Cw!9jH;Tu12&)A0)EBq?}V5SZSxXVnJ}Ad8|Biy$O7uR)0! z>(bGhES>A%)|xund%pe}ITB&&`R7fM zr^IHX=Gry&Epmem<+(nAFf?Rp@Rrh2oI7_S&(E78Pm3fW3=L7#(vsZjwW3UF5{Q9- zKEpbRUx8>aXo@@&!tk)VsosjZ0&Z6_6x#z201zD;Gew?>6c^WPMVZnj5KOLsqGz4~ zVDbC!O_66JMQo2-WYweKRf|QpVXau*ZmNI%6|2X?^~WAVJb&I$xfbqK&3)3%22H_z z_%K$Fhs85zQaYcjJR9;>0fkLXRQ~V-vM3VVv<>Ro)%o*S>~YMvF6-4Vw01MKlBi>*;zy7*^t)?D1PAu0OpS$Pw9NF zaw9M`0fo)Y04$t3Ws2MgvPyva;6Y7#1vSdMRq4tVQ{>tBq(*lF+y@WhYH!!1o6nh_ ztGzv|B-r#QCkJ1;e?QJ`+pw)&OX$cEZuRus_6jX>`ZN}&6FC%0X)jxO!F~5p{L)Jl zwX{%h|NVqM`UoK5+pLa6Xub<9cjcukSD0#PLA-b|t&S1cH*BEt%o(f|6=`+m-hMl6 z64ZAA-M2y4fdjbTc_*bTdhHsMJ9i@f^ixI~*yRl-M)X0(y> zZLoOPuC%g(+FC06`*CjHuGj3cIJa-7vcErV@Bk%y_v*H*I|0(=%b5+jTzLBWDA~79 zw=?M|*|!f*UmsSNE2EwqiRiYiKLM96Ew5y=QQqB6mEVtJ>sH-P#c^!iN|oPFd3QJ1 zmLIQg9XqD`9JiVu6 z3K9ACTWc1m7(W!X7KbZm_I&pzYUC!gqE%%$!EHk9@Di8cQP)z!Mz|K1yoB1;m8 zqVg$8%HWmhGg*J>v#_xdtH<*v+p(KB5zn1Nyl?^W!Ud$?ej_$BgFG`sZ0XQLe9%o| z-iET-pef$s@OoMJ>MQM5tyX1CuET+mwKMbDYlM#;Rj=>M35VCKZu-%RvcuvJrTTic z(uyEQB1(lAi2e2i1Z*hVOfo5DCKfZL3zTq{?<_; zDK75tiY$~&h3SJ}nuU@US(dH9EJ^!;W)B=-?!EVP2Vn$e-3DE6zKQ$YcaxjKfIGfg z{7?G?WI-UfWy@b?y|8`bMyiH})Eg*zftT+tmtSg_M%i7Jz_EEVWu2Y+UVPqRSnPJn zIy+PC8rjRzcDn!jEi-pFP2Jr#m-=>62K)zAcUZ;r0{=SRp8JZRfJ9_KH)eg1!nPLZ|bo_4PQ0000 + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/information-48.png b/icons-topo/information-48.png new file mode 100644 index 0000000000000000000000000000000000000000..7c369b970baff65548855fe6056b7cf82c09b825 GIT binary patch literal 946 zcmV;j15NyiP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H1120KLK~!jg?U~O@ltCEBKhO5o3WXajB$T$IEp$rF4$&nqox2M` z2$4Y^CGlci!st*C)FJ3nq<@45gJ>_7(W!$$y1I)*<{C&_yFML!v)$L#=AC(A3-=4} zVP>9R-*@JHo_S|PRf!S{BE2!Vtk4Ve_}#O>jKcF1s&At7Cs7fY6N$%R3>XHy9mGn& zy&~K`salM}ZzKf#;9mo03{~D1;d~S%Vy*@N{5NkJJir-W#~IE97DRg4TsB+qQYn%X z6CBIsIF`$ioS48%rGl!%zOEZo)#=H?g9XsvPcD_x^6IJvQC?luTq>oNvI}tE)xYx$ z_}OL)iScn_iNrR!SRz4Ud_35F*44io0TGWssS<$rg$p~1*BI;ZZCE3ckGP(2BcNJh zvq){B`10kB;x)!*eH+$ro&kZX%32s1VWUvkCbv;2P#75rcF(x_cP6j_&)BT60F>tE zS?=m$eR7g=K2JHHXMJ*#<*qJD^YfLeC57j%{;ewUqR0`T9~jWUM{Ej(Z!sAC4o_Sv za%cknw&5di+}3FTO9H1`Rab13LocBwxJq=vWMuR}T(I8N7Gzioidz-+VNOM$y*xIrqAhK^! zWGo)|GxIt+j7@m$?N#kKe9nqI`Xq8VOemoVEQz#c;g!NIps8}K>EcCWJMq50VAlcQ z`abyJA6*(DG=VY<0q3?EN+vlpIB0CAd32OmGFfY=g%BroHcRMIWy5Q4=irqqv}7{u z@9s7>;-%9Zotom%wQIGOp7BXt-A?9e*sW@t{v-eQX*Bc&o4{=4oj~x-MTA9DL7N$P zYM~Pf0(XsNEshqCD7+`yJAz6q6`|Ag*2d~&t_Yp7nNE$T`fm`J6&MmY9p=jE7p5Y4 Uf5So_vj6}907*qoM6N<$g2b7qO#lD@ literal 0 HcmV?d00001 diff --git a/icons-topo/information.svg b/icons-topo/information.svg new file mode 100644 index 0000000..5b715fb --- /dev/null +++ b/icons-topo/information.svg @@ -0,0 +1,123 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/jugendherberge-64.png b/icons-topo/jugendherberge-64.png new file mode 100644 index 0000000000000000000000000000000000000000..5f68d7cde038b0680cbe2df082255026d088260c GIT binary patch literal 2192 zcmV;B2ygd^P)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12n|U@K~#90?VD?C6xS8Uf4l1ket;c6a8e2_kkY79Yxe=jwlI;3 z`$$}Y;wF(*ARj4))dW@l%2W?y!krIAF9TZuSF#)>6Un>YdJ=_5v zh=UhIB+;WqRG7YcRqP;kkm;*eMT3?&ef&U8;=OZ6d}Cu;?!tJu6TJo20gtE!clYhn zJ)gxRvo^8fr|N>Is|%mquC22?4&H~a6#=|eeY`z zow49T41UVghQ85JvD<9YzhZ@m->>EU&0Gawz1Fkd)i=n3^(KA&Rc$nJ=FAs?y>dm% z*&`x3a}j`xYOm3*ev~Y@Xu{7D5gAb%jqKPF0PH#1j1N*35>IrV(qCn6z}{n(y8VVu1= zIpVe3wY&}yS(EGlWQ)j+Fi$3~zCjk;nB`XcS}Y=`)RlZgL(-d@tIIpId9xmGkBBT! zRsgIba!%`Eys^=A=L5%hW20VU&&~Qs6OqFr;?`CeZD@$fd-u8L^muzknMD!~z+o-( zY~s0dd8*$6?9MrKF>S^s+L;y1w3C2CJ*_e)_2~vDHf9>C3@CpI z91Cese?LPtHF0+yKwK_{YHINH_eZSb?e1p0u@OgQrMb=n`2CD*+lK4dF|DRLl1a1- z_#yCbV2Qfnjtv{|bak0)kO9)m$|&jRz>*tjl^Y80XP+_f;DaHuWy8Z*3k!`*yMgcN zDI#US{XjFS&g1hjQdgI>&VzC*jnvhF&u6Os^u>$e9G`q*#`O6}3cUbi15LmRwQ#h) zo~h<$GtOedRC6<<_4TIex?X%SOy<3H%gC%BI2lQ)4S*G819NSNmOS9o?%0ubWCsKEiuNdOXnwV;6f zBS*;DwoOkRTpN64zYp*`ojA8|XK2$V+#MZSb#QGE_xAyju9Vr#Fbg%GxLoutUL2zT zmcxN<>C#wfR|QTMR0jA42Et^Xk3Wuw>PX;ZK?T6w*%>B_BLMxtKLUjX6@clcrZCy$ zE3cTHDRkEcanAygkmwko^&jAqnHl0Mbvq2-D3E$$B>yBwKyH+5x+7Tujv3-GBU6h zB=kK}+;DevF!}oHQR_^%w&L8n75sj+I2&jMc0{r>d=6MPIEW=bA4_^V0DZZ+$i#%1 zb_20L32Gs3H>10DnesJx<_wnndN114h+K!wit=>0svpGRw(NI;%Kou2BYHzj zz4cbiHsc2mGO~3m{(JYds&4}=z@^!969HJl`oms8Z#L}pbLwvrNTD^`$IQ$yZ? z0~EgTMo3-vwWymhDaGV#ui+>yjnsx}pcD9x;d6i`J)NSK78X@Ue76u0gH=^}3y~0{ zYBd;y&Bl^Fd&t|n7t+Fpyl0Ka0FQ@}#~;UIy!{-4@xzA=J3lAz`M9@#KX*z?nSSRT zt+v|qlVIAaRTN*kMB3`rQT>U_g>&al!)Yr3qYQ{uA0xU=FNeLKf6l;~HDuM)kn_}2 zq&@TyX)9MEGc$N@-V7eo@4k!o_U%L(HJ5lk@wGsl+g4_g!VKsKRww=_oWjnh2qCM& zWdAYpWk5_RykHhXse(mB{)@72{#8I9F@7{{1^$_uw#*Cu7OR0pL<&$A3Coh-o;gK7 zu$mbEP_qKgS;Ji@DfDH-2PnVZ?MA?C;8 Sx*1OZ0000 + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/kirche-64.png b/icons-topo/kirche-64.png new file mode 100644 index 0000000000000000000000000000000000000000..db9a0e6815ce5bbd4a361bb64cf7cb38e8b54e77 GIT binary patch literal 1147 zcmV->1cdvEP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11NccqK~#90?V7z$<473BpX?dQ{Y41e@F7GLq}_rbcZp1cQh~0a zJ?ZXr+ByYgRyu7>vBe3YyOZu8@F`c)1jV8O4ALUD1YZ-b2BZ`u`)m=1wP9^OX6zw~ zf6_?NOlID99?#hGzIKizQf3;!FaGa80C;IT5Q1cyWt(J=U+MXEY5Q?o(lj@pxOb|WCEymKs7m1I2M=&@FO06mX`r6=tg3tS5j0IjM*%lBR z;CF@-91n7LVo#*!+mteSlG$TWnv#zwTQMtOD@8xDn_!Sk0M!ntCKEul1FFdcQ0;(f zG67UOpqfko)efj8O#!}tW?Q1jhz8?W0Pq&TDu5{kdx5`(Uiyd+86wpl0?9-Z|m z6biI$dmzN6Kh{tQutBotc^=E<^0U)l<#L&x=aKyk(8jO`utAc|W|R4RUN{Gs&*#}} zHV3OAF%e|l1Xlt4v%9_yhi=H_PXBhu%?OA^3yT{q;YrfIQzK8{YO zL&Gp4Meq+xo=gEmVu3dRrW%b#sIpqEVrgmVSp(|2j^*X$$ade>ly6%KA_BYyfL5y& zs(kL`2h7aOM79NNy%sVOKtuoo0NU+#sIpWliTH!r+1bdpfGtDFNB|K5CIR^Z#I1mU zEoq=9hle8m#IAsVtrH<5K}P)`lpTcy2N?Na2psWn7##A1NR|?lsOC^6MP&~- z(rNKQ8LAf}Nxd7K0Pq`tp8$M!baaG9qk&edg?77r8`T+x0mCp*sZ_AGwuZ&UMF8jl z_zOTC!2d}&7bNwUqcJFXIhwQpLEfOB$uaOXp1(jpusH$nF)c@ed;t!8>VcRYjJ5y( N002ovPDHLkV1l-9{-OW? literal 0 HcmV?d00001 diff --git a/icons-topo/kirche.svg b/icons-topo/kirche.svg new file mode 100644 index 0000000..f4edb5a --- /dev/null +++ b/icons-topo/kirche.svg @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/kreuz-40.png b/icons-topo/kreuz-40.png new file mode 100644 index 0000000000000000000000000000000000000000..466055aa5e1ef09004d65b3e957b3965d881490a GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEEX7WqAsj$Z!;#Vf4nJ zn8$)Jqhx~{kij5X;u=vBoS#-wo>-L1P+nfHmzkGcoSayYs+V7sKKq@G6axd}N>3NZ zkcif|GxmEQb`WXU-|^te($ys^Py3b`%I5hLmWbWCtEI>K*Z2?fANDzOk66yVG;LRx z-qLJYE6zj39ziCZf4{DKP#|x^p;`HJQue!hyB%B#9GETG{TkH@*zYjUzjdUM`CzRA zV^r(s;0F>jGAF&y)I0ZraqYnbhuwFVU4D7t{dY#bM()xw!HdUyT#iU6H!vivo!Ya| zY=5_+Sc3wG7ymrL1qY4_ycf}5ag;mjV^h>Wp*7602_9xY^*Te>?5bnCGf8iz>MFHY z%ny1?Rz}TM75=)br_nee$Sb5{U5AIv@uan}U%$LEIOZ`)<>#dv+P^|L_jPr+1a2wb zX>-Wn4D0+ZyU_m!%Nu^y?0YGY>$^7WH2>_S^4j+j{9RN$C&i1NT6ZW)yHMtN{)XLt zrk5GyG{qNq7k-%N)2Pf9T5(mtzd`tt|NrF6yK@e5>`+>}&s(=?J^R)QN8Px9Zt*LR zSP@L80+xiev2zTLJ!${dbp0UT>qWl3i#!e){JH6N?@znzHLDL!J1@#l-o_U{>xmwV r;@#dIEbBaPUrT?`_(<=dSPgU5AH}W$l?$hUG0WiT>gTe~DWM4f?Vaok literal 0 HcmV?d00001 diff --git a/icons-topo/kreuz.svg b/icons-topo/kreuz.svg new file mode 100644 index 0000000..637d1ec --- /dev/null +++ b/icons-topo/kreuz.svg @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons-topo/mast-40.png b/icons-topo/mast-40.png new file mode 100644 index 0000000000000000000000000000000000000000..043538576919aef79dfad12886936dcc36e3c6fc GIT binary patch literal 1086 zcmV-E1i|}>P)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11G`B?K~z|U?U+w!B4-%JAG1+ukSOdW#8NsSUJTP9guUp7oR$b8 z3qokhWXO4;1?fVeH!1YAx3IK=P*4z8C`d{!TTN&#(>(+cq#$GtyUlK(rBk7+s2!dj zqLRtjjXLfouzBId`QG{d{J!^_?|ncUNnB@}xoUvR>;}-P-2hs(8$hde18CLWE5L04 z&j9QLcrdkDNlwE*R}_W8V349HvR12+$?8>Il224srOjp=0NHFdQ(IM4 zl3c!uBi_npvuC^PStt}jC=@cBPBxnbtX!4}cXo^9OTXW5IMP%q#Z)R~i240~l6=`4 z^UjX=o}ws2Z|$&Kttbjf-g}ovY^r_HXcQigr{wW?O3`T4@Xkt2oCMI-?RM|y^Laxs z7z_fyZ2-4>&jZWn^XPWF_W`)ZUTuQ350y5D!@+bq&2&0Vhr=;7>6--bX*?b`EWW(F zYy!F%i^t<6`SfiA4wbf2sYFo}2W?RlSt^yLB7NfmKD<~)1VLaR5Fh}$-EO+wZUSH+ z5TGCkrZPnG;oAf>N!#4q+yF>bRjR5w0Per*`y|pg4xCBa`W}fy*l09bBsWQJHX4mq zBoZ<8yC{7C>;OIna2LR50KNk77(fxg4*>k*06t14lcq%zi3D|Be?{`nxxROFU4NBG zBuxF2$s~7ocYg(t1@JR~@*Di|sBpJP{^|4i42#LKOp?Eo{P1EnR#sNN)imuNS(XiB zd_Er=jYem7c6R>~xH5q6M+G!Vo84}wrfCd^!{7a1HbhZWG)<%3ZZ}Ea#>PhF61W0@ zk49AzleEQRG1TjI?eOr>4`2~PRH<66HtY4e7K_CU@k}NINs<eSQ5muh%y zU;02M8}S?b;^^pzOG`^!U0vnD!2x%6c7DWJX}AkO0APQ^`0ZM)Mo| + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/icons-topo/military-64.png b/icons-topo/military-64.png new file mode 100644 index 0000000000000000000000000000000000000000..85922a7b212b3be26884256f88c670fef41e0e18 GIT binary patch literal 456 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zn8$)Jqhx~{kij5X;u=vBoS#-wo>-L1P+nfHmzkGcoSayYs+V7sKKq@G6axdJtEY=& zNX4ADvrh9KG7w-r?wR?ri|Iud(}WDoC_VvAr$=j_dYb-Jw>#jprT?A%iUw$ z_YVCvV$y1C7h=+CYQMvz)!aUZNvowjhDocnU4}`kt$i<(R(rcGlT<_dS|+K+c3mc^ zruMB&QqAqAOj0fFOPQov+f|vQ+S=opq}to_nV1e7R%dj4@NjN}!-o%A3KBMFSXo-y zJskonX8e=n;^OCL3}0WEUlAl_b-s}A!;`c7nI1l9tP>ItlDopbfz$j z8DBA9VXx>*w}-IS+zkgx?RyPTG$A>@w&D4YN5S$7>>O$xR@Q!HocB22zqa8QP~-nQ i;Xq*p1DjXOFW$>CcYF@8cvS%mbp}sYKbLh*2~7Z3^{aRQ literal 0 HcmV?d00001 diff --git a/icons-topo/orchard-64.png b/icons-topo/orchard-64.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef70ac83e5603ae5a6168f71e8181bfea736b6b GIT binary patch literal 1037 zcmV+o1oHcdP){xzf+RVo0+`K zdo!K+57n>h*Suns?#05dg?m5r-up4A_KJUzm(8npz)+k60wPnjulBL3Ew&f)&W(kigu)7w#|Fb^_Gs_(%EV3u=IY3yqbTo zo`K8(RjbO1v4Vp`M$IrcKF4%&nwnK(+uY{j>H+`-h1JaJgH9MY2iT6ya%~y2ZBm#h zFh4oZ5JR`WH#<$%4%V>#S^%U+(yV`4_Y8+eg@A~d3rqRDjM+BHsF}W(2S^R4C`=UC zHn&kwFxw{O^D=MiIt}!e&ZfD^w}Wp~t!kg0 z=-Fb=+5=#2e6H{1yL?D|0KmbyXX8eo5fCKiPOeT^F;;wIE-c-VH=UgJEc5Q|J69Vw zeAfZCW1}5uI5-q03M^(8nHilSHJoBLHB0GZ3H6N%+<06&(y$#n(96g`p98i&Z{52O z8gn<5{f{}X`uylE-+P};W&x;Q);;Su{&VbV&9jOtLiPzZCX$Ku*n}w{$9J6~2sqam@pFW6r^OFy=&& zSM#iYS`WQ2^IZyyUV6%gZxR$0bF%FxJZe8cWX#D%;6Fp6Voo*&?(eW@Uj43c?10_} zvS#b2vX4hV)E$VLcovAd15pz@pnEZB*;|x-JOZNbK-9#uK+u9w$9Pxj1ovRv#c*2jGS0e!z3n+kV(H$)N8ewLaiE&LpMR2R!weWULSHD-i1g zi1h(LtPgmxy!u_?*a5u{WX;x3Wgm}#s5=lf@hlK^2cqU5vY=9z(|2@~00000NkvXX Hu0mjfg)ZTy literal 0 HcmV?d00001 diff --git a/icons-topo/orchard.svg b/icons-topo/orchard.svg new file mode 100644 index 0000000..b7c34dc --- /dev/null +++ b/icons-topo/orchard.svg @@ -0,0 +1,1253 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/ort-0-32.png b/icons-topo/ort-0-32.png new file mode 100644 index 0000000000000000000000000000000000000000..4869da5468890e1edd0703a3d40d8003aea8cc9a GIT binary patch literal 627 zcmV-(0*w8MP)|%)&C3l0|H(cdr$!ZPz5@`XJZ}X3#Mk1l-G)q-bp%>8)s%sF)wePMHNYV zk}fj}r*Ua=Dn9-N&pS{7K1{u>zvC6qbp?Gr@OH-HUXH9CC<0%Br?%kr_?m<60RWqo zO%{Vi7ta;&9QbaHW}HvMcDVhEzT(4J(RL)Z5L+nPjuaop>RLAOP)>(F0WWPit{!-uE)LKno-!**iiRVEkhfhG4oBw>Do2|@?E0dEs z3jlw|NS3xhQdLq%(hp-D;|r$c^NnOaOh)oA_a&HBz|44*pV7Z1;3p6K_!TCpLS_H} N002ovPDHLkV1iAP4FLcE literal 0 HcmV?d00001 diff --git a/icons-topo/ort-1-32.png b/icons-topo/ort-1-32.png new file mode 100644 index 0000000000000000000000000000000000000000..dfce87f80d9157d8db5123a8ea8c6a3fe659301b GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ zsJsMW#sHP)KY)UgC9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NC6cs^K@|x zvFKeJY{+-mfMZXH?55w^l>yiEK1k>t?$Y`8zrNa5(U=QKhQj z^(nRj`wDJ`XJ_k~Doi6)vS)kfsf5>U$lJK`+4f22H)sdFe=u?G6rm8&Rb5kpCI|Nm z6y1L3$6#Ijy=cybR|#weOb$ICv%_6;bf(?bPyWuamCHMArlf0&l9RD^Xv9CQ4nJ zcwPcw#uctzKY)UgC9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NHH)lJ9)Y| zhE&XXJ8N(LtpJgu{xK7ldPJUDezGm3G_(E88;_|{os6u8U;AGOm3B4sP84;0)pF#{ z3Q4U^X|FG=d?j<7ZHi%`$c2|l?tR%jylvh`tS^bO?JvK-)83xB?i-(H&SsNL=1V~g zOL#rJ87G)2=(0N8YS<#efGU{DV4c^Yx$)S>IOZQ5511Rw89p%-y|a^UV12;f@Sb5G zq8Dd(1z$9w_8I zfBRN<-TL*3kzx!A^UohP;Cb`z-MLfWg%7AR>`y9}G7jv0B0lN+LFdfdX1Cv(c`p?* z^;K?AUzjNcWC8_pZ}ZLO{hpVn>vz78`G?H2J=b3wPWAd5{AG>D(x5kAs}fhf=-W7v z;ohV@pRHy2+Si1w<`$Rh$=Z7AVSxlo!SlYYfedjI_k7tc!^3taZS#4(d)+x^rw=9w zv=qGT+iA$q_xIhg$A-l_?_4@sJ@NEY3%UMgfj)EA1J;xJ?nkZ->t0)-pS3kgpy6?! zF5iPQ?^oS_e>`rF_}@Bvj)y){2j=~M(eI|b?DEUjE>E?|Yp%ZrA|JKM++RJHUzX{0 zI~ch^o%6wo_do?_bQc9^Y`B?oY15kHb<i-jmP6om!@Tz=U5YFsR?(wr0D&B`M9d0jQ6x&$D3;D@bK`7yxLQ_cn;5) zeZhbyL~H5^v&N0z(ziytT+(*>6tw(6kA2sIK>*;25eAHf)?JgEB+Z_w^%FN%T zvF>VHst>PSbTfi#jLFXI%0{l zF0z}X^nBh>#+`jqa>Lj9)`PnHC77;0H8`jnFTi+pk_3B06zfu4#3@H zlQJdwT_6L%HURGcSOp+02H!A%p8%W%a0I}p?(6`7WHOqGWF5(Ws1JrnZYPjY^2L^Bxz!pSihAz-sfXx7oBeJd)xPSjX+-^6nT)6^|#{-YYgW=&}6ciMo zxVRX_#l={?dNnF4Dv+C-s}>`G$g(=63u7Ip{KsnZx4pfc<>lqHSS$qa%x1M(xn|88 zoV z7-V^Qxu#@nHXH4BJ6En;NxR)no6V-lZ|&N(4242+5pI#pOtk#wJR$5{QP|G z*|Ueey}b;F!;^eNp%A;fyXkN^n46m$bsVSDDGyDOjj0sC#>m6T1WukjsY=*vHq+^J za%5ygL|lR4;bHFHy_+VJNp;M&wl=xjeUka95};8U+2`}Iu&_`_G&?(+E|*JF;&Plh zb4GP(%$YMsv>$w$Dgh>~{r&s*3rSim7It-Y#g+UMxZQ51rKJhSKX~w<-2AUp2(VZh z(dY9qJ3CuQu(`Q8q2!<7(4j-Bd&=0@nB06>vIW>Hjdu3zSs}TlOP7jv>j^w~@PJ2- z9O1fk>)6!P#9Oy+soI7@Aud_6L>R~IcFWB_O11z$N~6`+*J~w4lI-j2V|{%+r%#_g z$v!PDjTIFYJa_J#(67C{T^MJ_jvaE-Uy?1rpVDZe&2Pqx8GQ8Uks^ul@o|=vltjJ7 znaySn3=AlI1Azduva%HRrKP2E(|gGlU}Ed_`~AWss;jFN36SLJ)2B5(?CjjRQ|P~O z<3^!i0)@fBt-7e{*xQrXa_UAIHeZh^BGF;V|yrRaDN3 zGzCKJMbTN(A0CfKXjE8OsOanI>A{T~H#7zD`FuEh_^_sNRd~!2*@+OV0F&x+X0zCx zq@ph)BST=Rj?7GDO+_@W3J=Zo!&n8lgUH$g02CF8XWE@RcNBfgmoGMU6Px>8%sX%2#`rh8EkB9j0FV+LUK(_O{z)70;kg{9KWcjNG@`K zWLB~T0LgEqQCnJCR3B@fQQCecPMtcXx?i-mw#v=Fk2RNg1z1jUqNWuH1Xx;HDkPbn zp3V~|PQ;abYiq0Ow*LMgb#-*PTzvTO;UxB>qoeHX>|}LyHLX@_ z)Nusg2*Ql95Tb_wAoDYmo7ms4(3d3tCHYFC-1QOwBxjNA6*2AU>0wn>mFBgc zmzT%d+FD+}eqA&iN%oN})`Pd+2PD!FInPrs`ZE{|qN}UxnFl1w%F1A|sGi^Y2Eh9O zlrIMHK*t#%ZEH#Xrgr(_#2+Lp_2!x^0l>%|qJfbww~1hg z0HX;QjVS?)CSWwC1TdO_(U=mzXaYv#g%{v|wA<_&RiGN<00000NkvXXu0mjf_TrO@ literal 0 HcmV?d00001 diff --git a/icons-topo/ort-4-64.png b/icons-topo/ort-4-64.png new file mode 100644 index 0000000000000000000000000000000000000000..992bec2088e0f9d069946677a6ee1504bf5bcdfc GIT binary patch literal 2679 zcmV--3W)WIP)9J0;vjZgIq3LaSPG-hathlErR5t zMk_A4FNkq{xLo5O23(?vJ~fdL)aMo;8rOgtHv}OfCT>v-af=#3B2g%Hfda1-ZTowF z*qUMHn|5eR@}7EKKXWy^~v< zzfDARgNQy2WN@+pD!}hVG|R~;M5OQ%$4*4IiRhA(-}ogu4*9bHp+uBJME@Y7G5!MO z(K8}CK}7#1qW|~>5rD580ALk>R(L@@Jw4u-wK{zU_!_^jExw#JYm8FN=r*4wOZ{C`ZW>d5z#rXGw`x+?_9h|eiDEWtSB4~2Tq?p zjrjO@uag#InM{Uh)25-Wu1>57ADpobXaW8S;48~}Wo4yLce_7@Sg>FL8X6i{VSIJQ z==TBy0yx6*_~gkGOqnuez!T?lU|=9tuU?Igjt*8>N1QSH2UHFw=+1%F1Z* z;>BdKSUh2TC#6zJTCJ9%qoYZy)skARCX>lTMx&98MkBSgwUNzclZ-cN)F`@i=@KO* zB=9fYAfg#W^wlkZgeo27m)GHNV9S;*kjZ2o@046F$Ha*fQBY8Trluy=-4bBe)zu+4 zHy6W)50}&$A|oSFRaM1;JL)MvPsy+0BX)IlVb-i!9)pUGj>f)y`_R_b<}qn^^z`Xd zi`3iOi-{8_x(!6FR%6GG9q8`v_9<~bH8wV4 z;lhO;TiuQwJNTwHXDo1tW@aWnd|>vFextg&I=7F8OeVvH3m5oi zpPe~LE`VT=Q&3Rg*6H@`+XpUd9<{f(WBT;zqVa@=hN8Z{o^N&^5(_YoZ+ZRtbtn`H zrn8Wc5R{dbNebL#GNGiT1WT7LMS6NV#*Q6}*w|Q%A3q)$85vl)awX25Jqxqh>=Dam zvtix3b)rRxiHU*HXylvC^HhL90I#_gMxzlC5fMx$K|w*db?cVfKwYE5l#~?6<#I`H zb=MHMqN2iMA+~PaDvC38>Quh^Yw*Sg`~u|gEmo~sCF-EKxY%uQ)z#HLj}9kKo{WbN zAG$5@oH=tu?XO(9!Z*ngD}V|>pFZo=t5*mJ2w(zl*swt~D4WfO_3PLBlsLbO+q-wK zsK4&+ZX_ioF>S|;83ViB&Q))7<|-(_CO)uOWhYOb%m@8WX0sVHX3X$g@?6f&&PH!< zFVk;RQxnu`HPi0cv15GoO{@Yu=4#x#caI4sm&@_^@nb$9fN$f3}mC7xSSeI+DSfJDCm^LR*p5&|l#x20# z+$&bA6=7jv0+8U~U^F*3Go2qgbja;9ylK-Wbai!!7LQ9NlL^b0FBip=$z-^8?Hbdz zsHli(V=x%_s(*6}aFctbyu6$VvUKTErc<-o42?#^v{9?oqQQEil9Cbx1qCs2M~oN& zo6RP$vDs{hj*ezNa=gdjrmFxN@W%XN)fY=7FE5X&9~2aX+qZ9fmAs2eOG`z^|3{As zCaM5{Wy_XHiQfi5LjeBbN*xXdvw|v>ieEthz~|4OMPFb=MMXX(@1o6{H#70|dOfo@ zx_b30Q-8&Z6@1md2*6gZO)J+_`f{px@KegV4}Wfo^PUEMIji0XW2!R905@>u%1UKcA_8&QCX)%|MMXu_)z!tscTrDI4;?vjL?90j52uur6oI^< zp@E5`)oPjY*49?;U!9zY`e?p?|DI{6)9C~<9@uvpIdY^xX1CjE-@biJeLfvHaDcvi z`63Cox3`zt+uH?lR=h-ln6+{uisDLG1qcZVq44lFg_Tb>)eqk$>N=dlx;*dgeB5LCbSkJn}Vj;8HERgr- zj-;ojQ(|JGq@d#B;>chyNWvA1L+tV7>9)y+OY&zx^~vmoM^B{8vSv5T{_vpt>j z`A#oizQoL#Gexl!3Wex~jJHc-epwXtv=^XMDzRkA5>!-F2#&{fc6Q?Y`SZxk%oN>= zaB*Q_A=AGvi=uv67Pr}K{n`_Vh=@R3T%5-#GMASxUoLtBd|4Lv+s261YDHF7mgI!` z!@0S+qI&}V*ckEK)))Zn*|SITW~e9j^dj}w)|lTnN9*hBk(!$7b>glgagB|QqCWh& zIXWQQ;{Z@oQv-v+!0cIAtkGzYm6e4@j~;o%_H}!lbxdv^5tVWURaI4#o}Ny2yPdB~ zAt51j{P=O^^p#J&y}eXZQ$u&}-ldk77HVl}q4xH68a#L~>2x~M>-98g(j-brNny?g zxn#H7X~TvMR9INZydD!1Lk}N5q^PJq>phu7RMtn4*8+YWk-@J+QooMLarvh{DCy!K z`l#%$!_pF3fNOm4LmroxS^yXSv>!}xc@lo&Pi9>F!+bRL>$5p8TL71T>JKNm^ndy3 l?5~ff2dDsC`k(%M{=WzY(mOmC#j5}S002ovPDHLkV1iZ`8xQ~h literal 0 HcmV?d00001 diff --git a/icons-topo/ort.svg b/icons-topo/ort.svg new file mode 100644 index 0000000..8d4e763 --- /dev/null +++ b/icons-topo/ort.svg @@ -0,0 +1,375 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/parkplatz-40.png b/icons-topo/parkplatz-40.png new file mode 100644 index 0000000000000000000000000000000000000000..5c0c18c403d19c711ce02999df3e05508dbf1a24 GIT binary patch literal 920 zcmV;J184k+P)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10~JX`K~z|U?U>C=6k#04Ka&}jIW3~LS{o>qc@bB`gNqVjDxR#p zD1v{1=*6=R{sW42$cqSeQpzCIQcN)xq%`RuhJr8a3nH4k+lzuP(?Mt2c4s!n*;NmI zVd42b&+p~4&olFbc?4OOInTO$3?Bh_0u%yv;D*VXUSJX!1$uzcJ0#0;s9%~a%foWy zG<;M;mq7?|#A!$WR5ZC`|C5iylbO?6)B_ZN55Pl!r6muNG=R%B&)V7#Q#4j<91ce+ zl2nY%W>Ka`fJbN?w;teA;FSXBa6HH7+c59nE0p=(Jmuo(ZSpO;@w_VFeug$2fMC+OB>Z&F?J4G{afB%5hRUdBm z7H+qP{{C-_jV)^W2ISUkU&rZ#L5h#RMpDvs-Sy;Tk=)$d0MykL(APIjW8-^fXEzMj zIuW4178kczUiPSVRMaJGHVY{!B7z_QP*#>nUfvz@^PjQ4{_}JJJ32no+&mgIpO6qs zL&IaLs~@O#Yil_b6)z3t*Lz7!Yh&XVwY6{P?3_^NN=xr!x8E|9Ct`q#8XDf=-)l27 zZyJh^IN;XSHg5OdXEY-t(NKKE0RcRoZFOw1Ts0IMaX>*3NKa2tZKJIw;(#S3_lS+X zqS`Yv>xSYZ3Yd|RNJ~q(YJ0sqOiU~r%3-<>rl%)RRCHIhl}4SFl}u%&o#^N&b*;Ys zEt{LaPZzMNDu=47oRB`UEYsFDMtApD!}(1EhSI@-%;4Y*O-+MLO|6|MhiSmx-f4!0 zW>tG{?|_vRFAEFbal1Y2?(Un)ZyIoXe2MnQoxF8C?LH)diqgT>zTZxd+s1 z)Lvy^wZ;W~8y6M52upFRHBPgpzw~zSi!yLHQo{nHrGVU%T0VM!AC-Y56(fo&Eb_HcM5hRtTd<*LEy%nCe`6Ul0g!|BWlyxAH#su`qne}Nh5EuK3H u90@;@su4oKLFgS+eK^BQ^y2`Ysr~>$wt(Mjj7by#0000 + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + P + + diff --git a/icons-topo/quarry-64.png b/icons-topo/quarry-64.png new file mode 100644 index 0000000000000000000000000000000000000000..db60319022f862a65f114e148d78eebed8e7f766 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85pG3gD_*9)I=GeAbW|YuPgg4Ru)lN>zvBWT%gcWPZ!6Kid%1Q z?BqS5AmDQG;Mz+Zy#cbVYn0WeX@9W0q$+h*@$bfKt4!*`XYDUMw^HS1wYulCIrHWJ zS3PHtUBK*Ddg{;bd-oVwHZX-9;0ihZt^S?Y1O^~8g3-o-{RM+;1A7Mp;~EBb5TAu3 zfz^V6{{VAI1K$DW4-A3|4WbViY#8_j7?~m%!9qYIKQJJvbZKC)uQq3QNL}GjpfMBZ OU*JTvPKbs@nlpGprZBwIV)ZEB&&e95(^n%Gy z(+l75qC`wmyin^RiwwI`45A_^Q}ZHOi3_rFPBclKt0smuXX>0TTg>;Dob%Jg^MBj^ zZlN35MF$Q%&pGEg=leVVbI$YpQ&esxrQXu${{#3h07$!zEC-kj3k!ObipiO<)y#K-1jR~?hnGkp-^xiq<7~uumJd^%MhqhUaz`llV*-o@>+A|$e@eR z`h;_x;^<h#6Qf!p%*#W4`Gx&lCongi@xTSn&0SK^t;?hj)|gmuLps6QFktyvB; z#&a4dptCihF1&cTb=oH_(DN5#U+VdbL6-P)T)eF6!mjuU*6*s0JI^j|9Xt_4c5&Qp zS9MY?$6;$kj3zw3!aN$zWMX7Ko0DZr&CcR`HG@gR2elX=3 z$iEkQX25|%@X;4gwheYv!G;y|8T$yOz)+wb=v)ytozc6-1lg2@VXt0LE7dLPsphLX z`}O#_`je`Dl~{y1>ZvuGTK9W)vCdc$II;N1iw~rimWBO;leDEp)lxMxp~!lv@2fub zVjO6M?vDbvsX^3yRr`(#vh90S{Y6b{r`pSZJP>TV57X1SXLy6t;|{Pv=l zY<7FCrk1GYJ{!(_wprDRXd6apD)7yW@vMCQseinT=KNMq4!}J*vGWPdoJ2bT@^YcJ z4(b|!aO)LVH0S)cg4+1zIxlej!0B{~|{zcRf{qownm@GFz{}}KB&?~YZ0#Fl~ zfswNTP6vh45idJ)PJ28QimK*_s^2E)KaGwM^<(wx#wJb85B7(Ssro_NF1QhXKy~8$ z;Bdmru})Z`rmJUHt<$CN>hUmmoVqC3j+`2^E$IvZ)EzZPG~?mmc-6X?;~LfH=EmD} z?wetqny*`Tt6Ht5bQZu6^}@O`O)Cfio}R6rZ_&C2ZD`baZ^hG2UdTvbJ20}2$}`kO>WK)r`D)T1lYfJ5BRtJYfwO-DMYd6dLW;)f00000NkvXXu0mjf DUKBnV literal 0 HcmV?d00001 diff --git a/icons-topo/quelle.svg b/icons-topo/quelle.svg new file mode 100644 index 0000000..5aee8ba --- /dev/null +++ b/icons-topo/quelle.svg @@ -0,0 +1,118 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + Q + + Q + diff --git a/icons-topo/reserve-64.png b/icons-topo/reserve-64.png new file mode 100644 index 0000000000000000000000000000000000000000..fc594e6ea9f794006b15688867e709a6cded2d50 GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zn8$)Jqhx~{kij5X;u=vBoS#-wo>-L1P+nfHmzkGcoSayYs+V7sKKq@G6axdJx2KC^ zNX4ADv%I?w8wjv^pS)PJT1H{}DaL033)3I6_{``}*`DG3x9X**y4w$i#lbVv;5!lHd;Z}rl6MF zgOmStw1V!RUuPmV9b!Iz>6ho3;yJ>9KF_l2tn7C`EdA|;)`K@6F8m0*QoZN-)pzBB z23$5J0tP}h7TglNax$zv9Q=MvZmsO>jLL@(J2Eale3+5Z_`qR5#?1!~^D!D9Z2raY zmGKp`((!D++6E9i_WM2p84n%@B;F!Axd<#r%; m!?W72j1wQb1I@Vn&s9pl literal 0 HcmV?d00001 diff --git a/icons-topo/restaurant-64.png b/icons-topo/restaurant-64.png new file mode 100644 index 0000000000000000000000000000000000000000..6609612fe9d5f96652b1dc651c09214bd24c2f1c GIT binary patch literal 1845 zcmV-52g>+~P)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12C_*+K~#90?VCMp6G;@ue>=PD_(Co=PDtQR1oI&Q3OIm+0^&;% zH77)K5=dM`aT209g(eM35ooB;6_J331kq5C0vaS}KqmwuCJ=E24FVkj5{W28299+S zU$L>-9dGPi?>fQeD*V9rfr#f1Z6z&YR$P*-+07Z+(@2$py3H;}{Pgl1Zg7hKDgyDQ4fi3AoSRpsrX! zD49f0CJD8-6WYEVwYk};>TBRv;4iCCc0d&P8_D>rVKCj-$Mo5=Ff$XZ1wlj8sPE~a zzPA@86p~9{1FgVRP9QsA5IAZT7$YN0c6E^%9j#E0D+4`|AT}^SXzNzF=rZtI?u#`B z96&NY-QUkdM@Q8e50DuhWul{lbbr5GbQIH8;EMxdn9)8DV`PM>?ryS~Owk85T)Bd% zulL_$3$%FL_FY7yh^Re=X{%P1)RWC*nCkAv7#Wd^E@Il8cfdI;(LQS!Om=mZG#;S# z$Pt>KJfUXSu99_KK~)Lu-fb7f+?i= zEgomt?c0QsN!M)_f`~>5ZP`+wUuH#U=T4^}=ZMVcDARp?viJ}na?9KN3U79N+>G`f zp@hRU4hfw`{F!+7|x?4LTd zrG>_;S3yzAY7ZcmE=6x^v+I?ofTEyp+JtCmaIbh~#%E6Ap|-RT-oM{-JDCC2k=DAId0oulmD7tPBe(^X;IE=P#9RR9F1~@*?w}vA4;ow1(B}+WD$&IR_>u4J{ z7C1m}Z3R`e)%AmlIzS1BsoAr~Q=1$Ez>EX(6v)kg@E!vkpXXac33!bI)=||Hi9!Zg zejS2ffaY663DkIXfQ12iTN`RqQ~tXakK3kH5Dcie143hR*zN#+NEJ3w2%J~&+t#=3bM>;=IyAV3FHCj$bU0@})z zu4I1jRWm>|HoB7edPbny0B!AB(1KZEJFWGDSpVDf1*#1|(@ZA>fa-Jr!A*mzbwE&o zuxcF;2m=%a)nU7<#$vuso@7*p0RcEbL?X_?RMTu*WZ%yTOp^scEQgCxJ;-TL5eJBd z2H%~mY6pOS7AUm~UqCHZ<`orlfZsTv6ggHaR^}CzV}Rd4SS-?i?Y{yLz*?Z#D1Q?S zsCEbV-4ta%eJZQWD+K0!`#e<3!GkaO+!PhNr%>)41_#?d3e04KJc79DmV=*ZPzeV( z*!D|cw)ZQ*vDjR+RVK$v6n@+oyyMbQlfRw;YGbvW7Z1@d?Gp}B`lDUP$;0_lu zVEgvK-st#U3^^Trm3@+_g5Txvr)$?-$=sm6r$Q9}bX766KHERHvdH$TwckeQN=8Wrm0Sh6tV+Rcb z0|311>cY5x-&1=<*J*n2Ku(-^iee)?Z-;<`kr_DKMjz)n$k)}fx<1+9EmXXild+KZn1vfC~%w;T#r7A!gv(K#* jF&^AykLU2eOrQS`CZhf&mTfu{00000NkvXXu0mjfc3@27 literal 0 HcmV?d00001 diff --git a/icons-topo/restaurant.svg b/icons-topo/restaurant.svg new file mode 100644 index 0000000..296c838 --- /dev/null +++ b/icons-topo/restaurant.svg @@ -0,0 +1,167 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/rock-64.png b/icons-topo/rock-64.png new file mode 100644 index 0000000000000000000000000000000000000000..e70432613fca6bba3c00401498ae081c0961d680 GIT binary patch literal 7134 zcmWkz2RK!I97dAJ$WB(s3JD>bY(hv@$gX51L@1IZBneT%C&>ySWn{%Q5)!hKmF$&S zzE{tK+~+#~bAIDpH^NX~lZt|sf{2KSN?S|K2tSo~zsSk({q-f!A^ae{rmAg1j(<1F z9q!@x{T^E8*YN(H-7jKQfn-0tc*tAb%-h)A$=mm$mm`s{udnDOw=37|FM2qNx_db% z&&jhA5goSFR#P$YOKC`+GB!5fY*dmC=q)4Vr6t!jG(DK$wkGiIMQPsPN#m1$ZM?fH z*5(sm-;nO=?&`jfXD?T(n{ls*z{l0ZM6XKjK&(nv#9z%!?fc*s)u6=cR#lqJ+StId z{A9}H&D3!UsbmpC@czom%KA~y2O2yZ8yi}@bpIA(f*+=)8eF-O(|eqOBF#kTAn9!n znb!4n8A?h@HSU8|>pv-Z=?M8lYlFVSo+Gt;y}Z1L%r0EW%*Y76f1l!Ma>F_l|=K3=`8>g2@dR+bze&*|&dz&w|72-w|7~TWI70pR@2ox_o_WYTXrJWa=MRsHq9VCXbolV$S0+M|tW@FD z?6r16!ZV>&2^A_v{;^ zkC4~qGjjdioGx(J&NRZUpUpP$_VM|->fgXf?1#JEty4)!d`HJwK7M{dQPGLj>7G~J zm(lKDGc)yWWf|RWQlSnxD_g$%$NDy^g{WK7W2$P(Tx1pAYYu9-r+m8=II&@0v+~T{#f34 z(?9z&U8lgR+^fZ{Y}8v8^M*qhDE4C3%KFb>Y-6?Fl$PE#%2vzjI+Dy!D;i(%4{Ux-9&)SuawfV`Ri)V6+{|+5P+CPr z3O3WaIw-1Z>c7%bCKXyUD=XeuEmX@xhRrO`6hjml8F_$)#@NK9zAA95sku2lE$w!0 zuJFs3FMrL=IXO9zRS&mk8)vz=y597qzwYbHe_eKvY4l>D*{|to9yz(gSSRr$OofDk zLR8E6&yTnFU?EspSrsqm`G#O@Ibz~sV^zeH)I<{`PMlEj^gMxWh=|zlnbp?XinZ6` zqE*f_rH@e4($yUustmyV;^{wO=7@0vWIty5iUk-bG&D6E1}~QN69|Nw(M8$=WP*Z% zD&L2Of^;)FTnqn6C7-d%uBlP^Jw1(q(K9f(w)j)Mxuqqd!Oq*)*VxeTR^#Zws~;O_ zV;W<%c+rrBl_}%&j#n=3sK9OITMZ5BrsvNmrKC7`dq2j2nwpxDhpFnFIfEndDzAu0 zl&zQ+ckIXMRl3NASFc~!jxK)b?{E3~m7&AsadWdyiavi%PL6|C|J)laV)SzCXO<F@vE zrJFv2)Zn>1N@FXYWGKLZMX)ci@GQ4lzo}?f$YPZJ*g;(IpNO#Vf0z&K+2b&3QWEmw z;$p&l)mHRjX}5>5v4xJE95Lbi2G1XA@w!~OV(;pDprEG(SVd@U)ti!&m)8UI#u+H~ zdG1SWbGm!??mN#>GCcicd+Ml%jDVnE+RK-B2L`OHt8Qw`b2K`LUzPi`y)EDVTw(Ng zcTW24y)2xZjV_XO6k)Wa#DHa^?6L8d1Pt<*p`j{A;VB^Uq$P?-8d~!>K2jrNWo1PS zhXw{JTrPPUxb^4kxpV3Q3_mxPCx2eP$_f&x{qlu&Yi)iw)_h`O!pP9@V{WnQcbb^DGrlzLSV*@$+`uae27$?Ew$C-yqZcv$?I|r_dZ5hYQ z*xAIVPgQB}h_GoEoyykY<(Bf7=WrXYiZT%zZ#iCFkK3mXFAv<3QczHkl#=q=SaPf# z5d+ZvoTs3q)HX0^ob_=(Ca=s(chSr1(Z7E|pHr1$x~3d%-b_*BUQE^2=Cyk79WgiO zR$)C~I{Nk7w?}btaZ44MnR|=-^n{uEJ9nC5c~<`XaXV!2rq7k&Ag+xo`TIZ3%se{a zCM_Z&qNlI#wYhT1lM%j zIFOPMKbOC;%W;4a?I|UB`3G3k+AvBvp_S9yD_sV7l9#mQYmLSIvsM*;2g|Lfw*&T( zhkP6xQcuK9(al9ac}L%E*4M9JC#Iwf{4vM2c$KF- zf6h^OW^=X2W|)GML_TOcF_QVXIC$*pRS}_hor-|Q#>VXYd?t}!FEfhiLu$BUI26($ zD2n?YmtS46-O7q2<|w$Qbc*`8sHl;JMKg`kwz8w+(bK0-kByHnZcL`mZ;o?v zGes7bmhyS6XK2RUZ-0`IFf}*#5}b-(=LqaOCxNG=D!qSLZj$e>=<)^yE=0cqntA;G zDhM^P{(+A>jzCL#+g2=*TfRafadvk0ot3DptZYzEfTEhYd0+mW@Ze)V0@X6ke*5H?sHxQUvdlm`JCaIk14-7wCB1ADui5V>A#|#y*S5=!^!4^?UAm`{@!-J& zTS?aJ+}wdbnvf5X4Uak}yk*OScY<^*dFg5{mpszqUEdns(LaBlIn7WYGlq-&zpSjC zAnhO?UZtIF39ORy#f!v#zHEwrSBz8iohhXKIHCP4g{9u8KWytA=1;v@d)_86%pz~% z&uqWoz{t+wqUoN3$mheoy)5J7UbT75EwK-nB8inbNJ&P`? zrqRY~k%p<(U@oDThU01|!|UX4uG7X5-in;tseA~5g(jBYSmyJud|qCDiiwHIZ+Rk$ zWnV8fre-`CeZ>G}WMtSS8)g5OoBJR?U$n5g#`eXF7q&h0H#b)VE%Ky}-dz~Jvk+u9 zFu%4u8l%LW(6T?gE=3{W2}TSWHu7=6@Z@sD^Ya1>+^kV_Q5xU~Ghrs^ATR@nm@a}8 zl1rIuxBr^aY%19%bC{%2SIiO}csQTBF{QUf%Ugck= zS8r}kWmW6E4BB4Q{Qmv>7Z=GL`jL_0i;HIfkA~*v+SsB)hYnQ)Zaq=j9--OQ%zb_4 z=^X+n#n-Q27cih%DFij{n%Y{D)wVdA|js@H*m;ILLTL)6GpZoQ88K zPOQe3wyZyDcz$@*-KRC=aeO=k1X@c)9e$cu1jj+FJ(Kg53I4auR8@IsmWnLY&hqTp zSQhi48OY5J7fEk#Zx475D91w_97Ay*7u>QAP*dN}E&(|mAlq~1%o)o~x4R1_Lh(&{ zWdc7SA09+S6&4n%06;*05FGzjvXmlvdKO0;r>3XvaXsCye5_GFmp6LL+*5l&0}X>8 zd@s4VF@^F&!GZfdvA7%x{%V&lO+6uBa{JQVt!`n#nwOUcOql&8b2LudKOkUn>#yK7 z-LuMI7<+qr1|}x2xq(v-;)k$IlRs)g1ud;+I&%mNDI{4-JL(jrHDxJ`fvN}+uI5qv*Z5r2G{j)DI z_c`Ya(z`G3;!k#V#MIO!4Gj&W?EttgkrM!X;JMo+X_f-($^b`;aRO9RjvXo=C|H_TtVyRYs~ zI(&ev<1X7NFVgDa<75)(S@rf3rGD13bfr6eSXkKGl9EiNv7IwC_ zx1Kyu?E5cm$=&!p=Tk z+^)#OMm-;~(`a{siJ7^u&lOMEp>a}LIt*wjS`W4C>$7v8NSg((Rn}>FVj>f1eMpm?GyZ znmIZ%BV1QkXZ68{3UG+6E-t1|(knfhQpwyq>jNcJujj3;y&q>ltSt2 zy0v&!uUwff-OEMx zudk|jl3w9K7-Td)CceC>Iqk#v*j`krnFA~S$08}@7 zs{gH;Ubw*SRD8&c%XeHX{DmBld7{HlkcyAXv zjEuN9*UHPwcOetf^yuje$PmzX35mGZuZ0*W8lK-7UQrfdqJ%)pzqOhpAIC%9} z)3_J@bK~FkDUFO1VXIeNUEjQU^AU~$-T}}CKMt2&XDj9bXh4sk%|Ctm#9(6|82AiK zb|+wM?x^&oSuijBw;&_uhzCnfcQfc1eiF3a)!QpTmuM#Z35M0&#sgwZ&cY|)&3>lx9-u(DQMHqG<4o*Ii1rsfZ4Tg$y5GO#imbSV%Io(Cr z;5P6s*3Jg+luC{Q^^0`4cFf4KQKMo?e^=-gQ)9xy_Tm>X1<-L3u@5zy%q`Wlk8Wj1 zAPjEYI7LN8_0x8j5dBx~?b$~?W4?f9pu3}^qX;x$(02##2}y(+_w6>P(`kl=Ha4kC zx$oaU1+xn?QIdtKxbB!eH(dYI?;b}MdK)R|N*x*<+zVetdjj}W|H-4skgf0Ezkk>=&L-njacp1ZR_i@*%l+`7U~1ychOLfbIO zyTZ;)uypE+t7$^rhiiF8*}!kcs6{@UhwS3w&cDBQ0)cLB(V8(FkllB!M42OBqA;~E z29F>QwRme$O)x1)0sDz5Xq@uy?CT?N?yNUHX8Xu@%B z$w7u~H+V{J(>Qa6s-&dE(#pDeIHqy*B7`cG3}OIa!HdGev+`cZDc!3!?!q3=Bw9XD zq48dCvZuo8exvEnFzem#<$8Rw7IyYBMC>}AV7Su*kQW6#cOY(7GfDPuD|JX6g z$c9?Hj|e9DA~b-vDbk+q?ruBs=5eoxW-l4GJE})+DssZSs%3P185q!m8z}Wu1_A2p zQzKh?oRDzs$4BB7e}&M{Pz0n1CPTQ8l4yvr4E$*l77G`?iUOfn@Sn7yD__WFDiOpLt7{w z_A92%sm?+Ctg&(3$B#tD#>OA=iI5gDFfuNV5$I8e`uhcK8PW<0B3`%3+=n~6yBnsu zva<8?80jUc!+F$+iHzb1-qV4)qKXf7Kq}k^{ev^F%d*35@v}yWpEyy2^K~+fa{m(A z+SGBPghU;b+lk~hJCQGZ=Rv|q8mvTnn3js@zg|9xRqX6E8uKcDXPj%A^Q+WAPfrX2 zB2zcPD1UL50q*x3Nd!&DmoEsy&Kwb1_$=`Na%PCO9BQy;s)i=CS&_n<4ou3?Rz(DksO@!{Z^AC*QjA{nfw`pNF#yLf`MG zroDLgE)j}{T|qHQqj>!L?Hyiih-7msD~1eh)+n+ljZmG;pS^|lP#RYQjAxh#4v#Pg z#YIK6NK?w9ND1R!7|F8#<{gtwx9y>8u^W^r)y2~X+@7~?38~L%mIBH^Q>ZW~_pKgDvlSK0PDaBM8 zND1dnwwOlPMnzbT6vaT+2sH?RdRM8)Q)Hnv$`GS~Bb+P7qStrigV9Ip#e(3!l$l0? zK{N<7{&f6HRt$#BD0tFe9Rpqgc_4P^E0ygY@wlg`eD&&VseKcW6X6qV5QZ|y_ee

$_8$FH#hDLr~!E5PY{?R z>1ziT$z1>a_}x!&`d@EfB}V13|8u8~Bl+x@@|Jn_*_SwW z_}ZMb(yixDpVm&VBC!fZz+P6yoYB#=8!&0{%2}s@0OrXF90z{z0on@99zbCTFnqtR zH!JmNO5Z#Sv+%>#OCf+J^iO=VsUX literal 0 HcmV?d00001 diff --git a/icons-topo/rock.svg b/icons-topo/rock.svg new file mode 100644 index 0000000..63948a3 --- /dev/null +++ b/icons-topo/rock.svg @@ -0,0 +1,1026 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/sand.png b/icons-topo/sand.png new file mode 100644 index 0000000000000000000000000000000000000000..ce3fd4fadee65253ab17cf08f548e34a9a165c5f GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJNKY5Xkcif|mkhZW6nIz;R#(-@ zWwUxNej^$l;nDHH=v7$$^1k2Ccdqj`wfU8y_vw2Zs{wODtAv4JLa+iG9%jVjps8u> Ti$7ZeEn)C<^>bP0l+XkKxJ4`U literal 0 HcmV?d00001 diff --git a/icons-topo/schraffur.svg b/icons-topo/schraffur.svg new file mode 100644 index 0000000..caabded --- /dev/null +++ b/icons-topo/schraffur.svg @@ -0,0 +1,130 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/icons-topo/schrein-40.png b/icons-topo/schrein-40.png new file mode 100644 index 0000000000000000000000000000000000000000..be4a4c813ff86107cc2d787dfaec5e5e6073f974 GIT binary patch literal 876 zcmV-y1C#uTP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10_jOaK~z|U?Up@n8bJ_-=c1F2>Y_+|R4G!R2)amaQbZ?2lahvn zntwo(8u$lDNNy~I5G`&%E)5h>Itq6ORBmipP`JS8D0ouj_Jn=@!gv0P{7NIFo!yz| zW_Om=00~iF-QPc91$-A$ocAKKDI@w&WTudCrxH`msHdn&OCTC3H3NvG2(mSuSjq0F)@rqii!eyvvX zK6c|n_O=Dw0az}VB$G)*gle_wOEwyfNF)+IIAw`Mg3)N?TUV`C5fPHfB+KPe4ex9N zSXN&vBBax4R;$&eB(`mP-K?zF>(S|SE{&O{dFcuQSgls1(`iIxb63jS03HJ5aydkV zMx)`YLO!4ORZQvXcs`%^t#33MhzPk{P7NJy1GoUZ?VC&{z69}j{N8}^c-*&sGMRYC zz}4b97VTd=|0734MAJ0y#dB54clBj-m+!h58$RE{K<~j$r*nBbHei(Xdc7bif&m(a zapxSzae}1y&$sv~^adDIFCI`2GuZzA{=GVT8-?1%x9P2mpGyxugEt(F(DmXg_y!nM zZ-7Dd;sNh<&20Ox5vE>u1)u6NzD=*Tg3ov8uIMk6*R=C=#ZnId0000 + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/icons-topo/scree-64.png b/icons-topo/scree-64.png new file mode 100644 index 0000000000000000000000000000000000000000..ee687d3bbfc858cfa2eadbb1dd396696952772f2 GIT binary patch literal 4429 zcmW+)2RzjMA3rnBI^)sV>q6$)vT|0=UP+R@$xc)@Nr;qmkL;}M6$%+ySy_*eBFWy5 z^%(s>{=e7ncfY&e-FJOH@6Y@F{$8As!8IBrD-wY~(BQQ-OyI71eo>Ia>svS9?{Fja zSHqi8!10j6@iBa+^wGZQ55J!{zlhWX(}Un4OMs?DfT_20z&$%ZC&ayb_r%=1Jp3K( ze4NC*{an&Flvoi6)MvZ~&MfHlz%931rW4GejKn;ceknIWbwnbHoMiYdHPVvGe*8jK zA=i}nDc`|`f4Tl$rYpMs*3g%fv?^Qzh0&wrCMTw3p>X7$@4a$Bl4fdeptbk?IJ8gY z!Fuz=lt)F~zNMl_v$f&eA8PU_qWSrGS8wm`8#W<(nv3>XCv)3Dv%5zvOVQierKQv} zGc!SZD}U|p@3$;jv8VNs#dk)|%-k+5E6b^^Wl~mFwh0&snmssfbFQ(&qLZ_-vargU z#`5y=s$O1RW1*LMpDo$6*`g*VCmVd`h-)oHEku}+NMyUhb4*-*{-wjy(5)b)H*elx znks`T>+56F#BFk$n>l1;Wbla+%E~mUIx+9xzkj^?ZDxi{w{X;0KyhtzbAEZWu)%+k zw!unl#vldL)!hw~R!zacER)mHVn;?yUc7vn+t7e6E-uE4?mH-jow1LPkE7x{@x$Vx zqPUXr-0WwjLf-rlzJ#tE(hweVwzj++tP@D0@B1$kx1TN!+PA z1Tu)DZT+;dnc1hF6MQm;vd%K`zyI#P8gNEaldo-Tv_s5nZ4pYG;-1frkB`4jPDXUv zN6dJ|POQcZEp`}QJtbw1Pi30#wy?6&aC5tSM~<4DgrA?^XK|3@zPgpzMe`dshDS!) zMZ%x=YdUZu)&do+n}Qr1dS!ruD@vFS|k5U!=2 z;3>J@^)PKCM2TAWiUD15r!TWX4! zKS+tQw7gs`ZW(TdzFaxEc6grUt)HD#u#~eQ!XLAy&{NhzgN$~-=sY6=pMvJ(d}JYFprCXb7NM0F zF|n#?YRU-d_}XTR)(Xp}UrY5DPplxEm zMkV`22Od{xRtS$IYr8F$BC0L?-sbnV`R!~#+(h|pDtZlQZenWsj6jHkj-%11XecKm zq0~wX5xZ_id4>0Tdwa&+vk2AT;43Z18;S`D39ZgGIR57n8=UC_k2HAWjje9|AqMy|GBOGZ3#*5OC?lz;97c-Fgb>%#8>*`#`}_5nn3!6Z1KL;j zMl5@y8%TJPx*4SqJO7U+eMM$fws}caWo5X>M0qE548fHOi3Ea}1A3^ku&uMC?;Gg8 zgs7N^zxwBdaiYQ|>2Ig&#b)rG@g_rewxE#S-8-^uD=RAu&ME=|7jBeWMQCGkr=L~~ zV(?n|iL39^pb8qTo<4g<4}99C0+?lEW3zKv!$29lOiPoHme#OoG*sh%4$Noo;v$gg zG+gxdEz<4`V?X1{SWPrKI_lGCm2fv|ZqAPB;zf0#)JL@+X1taH6g)jWTfOS@nwr>C zQ&UmMn1n8mJ9lWjf3#@cxg*NT$_n+zxw*sJzTh{!%f-o=`{qq_U^*pbc&?yQ=m}aa z2kTgvsKp1Ig~ei3b*(BU9701?#0suH>Yk6VEbBTx*yLf3=SkIh&hV+Njfe?J7M{oV zEGvsnB`XPt=I2PhEslFx@3=zLfv zoSULcuMC*ix}q@5U;Z_A{KC_~o$dMVWNnNwNs$Ez)YkE7LhlWPg^f+DT23oL%Efh@ zlZB6h+9TgQ7scLF=y6A=}veW7T->mNa{Mi}LBM}8%b zO>*k%FM7%za}gDdyMun1UB8YKWQy!th|Cp)ER~j)W)~I;EbAC@qgsFe=9gxr@$>ih zookPICH>&RgD@5URqXGa_Z&jX~^38dYe~$2b}AHe8R$1evPlo zl}>N);!-d<E||G%G7{*3h+Ex7c=ec6j&eEX&Rr&`hWk2vPmF1VInY!h-Ym_HHLj zaeb_d(ACv_m6a8%%B>1>hk!CnQPxZ*ySNY`T!u0eA8c4W zc25N914yYFGyQjb%!8uH9$m&>xe~Xw=Gn8b(-6qVyu111V|Tio_m|O8#yW3JO|q`8 zE>|L$xoe#zIG4HK1L-`nPcDx+t7*`Daj$!okr87iM58MNn$+2;2_ZJSaU&1h$+F)& z^O8;=hnCa*>4xaw&F{+6a&jH6()J+zAV>fZyIQaSSk}a%l9DJ3k*8?w7|Pc5zJTBKZC&DJsSf4CqVC$l!RNVa&3j0h*1iBauxm8F>3*qDk>5fHwCt?WMJm z?#fBep#3!#!0EYuWf)ulDxaE~s?1~K1wcJ2G|b9vX*pZkR(;+7)l*iG3CRwH2l*-Z zOmeM!yDjR%(#ndxNU2s{{KC`NM~{HF6GsaTN2OO+R{;qC<)Vs;Zon|&AqJ6xnF~FGYtS z5C{Nw--D!2O`Y~_Fnxy5>T4*{?=2zmyNg5D#;k#nP23C`PD;#6B(7eidxEUbaC38$ zAX7zYjH%Sr)L2+pg!e79Z*5_vS@pkpJUH0k07lQ{EVjiSx)B3GMfN(jgZa?oH|>$5 zjb%)2tgDmx_A!iWdU`tH$B*U*7oOz$9PKzsOG{@%c|gvSZZ;4-C6-CliUP?hI;Dtw zEEaxxdRjyR6SZo-{}&*r^XpgixY{nki`1?y~X=$mVNN3Egtf*5k zcCzVwBg=dDzpn4U(nN8eJQ*T-I<~mbmj}+`X?sYRWjb_Bc;^I6wz;mh?mPAu0_bmWRJ?=fkzer8x z0nw3?k}@h8zlNt`7IxR$*_*I&aCb+Fr3pLhsECV;J4Ugn&&~H4f+&Ei2BSFm`U<1O zjsoS!AAfpfK01KquqZPJaeCbOvnU-aBSR*ZmcFHVSpxyCtHQcj4U5(QT@uwV8GjNJ zL+0u2o%imYY_W?s2H(}B1uW_4!4t23Ks;IVP)a}OzNlD4v??fq=gITD_ zsp;tQ3JS;}L+9?h*Acn9uEa?PnEA3C5Olc3b9y35KlaZ*Y9-_R8oNhai=ojV&v`X9 zjNpVD8ymsDXyfq_9doVYB{$R=2%t0}AtBHU0cQtlONJxG#Gyr!*bch{A`M=8 z2jwITzJFkVM^5e{42oBd)FVwG2Kfp)JGnwmPOj3M@7ri~veiLHISk|>A}X3wS&1?z zWQ>iC?fLv!&Dxq1~ zDo6f%zZnF=;6F`m3}ktjSUxT{m)An%Wu+A{$;QS;WtHHQ zcF;JJ(9^w%us99gu9{5P^Y!-P9i5#YHB(;+Rk>wtZLs*A0f$9@+TWLzl#=R5_y${& zQge@20^0ieG2p_D1gU%Zw}X^GwN&$lZNbx=8v=z47!L=;ZgI&yB!nX`FAr8ft6@qP zp&C9oXaI%}vXIl*NCjpU{P<0~Q}65cUq>*Ynu!NI}fwhoR^iw5!Z z^M-TH6;Jh%krAK0mFv(RXf~>2?tDu_LqlWd!2X8?HaWDVP@O`VhalB#{{H?zR)V6U zF>SW0VPRq4$V@-}NLpBM5)l{AgB`8DzJAZpP~j>a1V5nQB?fQA)VZXh%t_Z^wIT3_ z&@tyT3p#6iy3;2<1|l@y5)H-V;Ok5O`6~^9h$xP|z6ww9)fmB2!cUVrOU1&dK3Gz4w`409TBg^qzGp%!%xhXW)J + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/scrub-64.png b/icons-topo/scrub-64.png new file mode 100644 index 0000000000000000000000000000000000000000..bfb9841cf3ca2fa6c306c5acafe0054be9732497 GIT binary patch literal 1637 zcmV-r2AcVaP)#9WK~#90?V3$!6I&RDpEEz1B(=3IZPlhl+uG>HpM?+=)RjL7 zf^Jk?hA((S@7hrXp5QE9kF#G52XIp3+m0BfFN1pP0 z0vIZ_CFX1O&dy{*^a&tMW1FOdtw}iH^*jP+bCV7~Bs0DZc}Lb$Sc+h2&&zTAdygZ1(l^b-w1m zVqnmuHxV%+?5o&^@i_aKHP#ws@XN`|d~iQ-bpBCRn+3CTCv%66R0mg6S8321l=7d9 zJmK>2Wuiuuy%l@0rOoZkZMrkvyv@Bun>MFs7CXJ6{183a9;N*2>Fcx@Eu5@ANz96| zDY%K#HK%DcTIqB8&{R9EluZ56=~Zjh01V}al=8p3-&t%d9=E2W)2J8D7n*9PRioFC zaEqPZclSF03-kp_`C%>0h#VP`big7c2lZ?y|5+MM3?1Ix_X9x@L~Q=LSz5v(D1y}K1*g?ww6M-vharrzRa_!DmH5x; z)tsX%-9=}*6C0ZV0fJhvq|+;xpU5XT_Tw14D|WLrw3U=hasKCdUgcgx=kCdHfaT3RYCF0N}v91LLlF{)qfm@svzaudCyGZh+q^uA1+;0Wm8^-1+Ui{ue6# zx22lzc@`*F#mg<#{7D^#%2DyDD3$zKKM$DR1NjWBxef5U0l#Z*1N?5l@0#@+Fy<6| z&j4wJ#m3-9T8-9{-|$q|L{3-v z9bikFGlOU7cl&A78%fJFKk`4Q)9Tn$v4{26dX;$3B&z7NJEh9!^B0CL@H+RJBUMLO zW-db_=}LETHFXtJn4GLWNt@B8j6e2dsq)~M*&BVbEHjq@AQ0>b z?;xT_7|aiHFLO^R-^=ML%?CKrAt{qA(U%lmtJVunFDaAC_~4yHRhkblgh8!V%X{~I z(X~;h*K9N^CmNR!dT*5Fp9{FoKvX6Cni z!Y@wOGapc{io3WR|8X2kvp7?IhAYEY=(fAD^ELqyAS6PJ^6T|_n)T*_)3es1p_R=q znMB3(&Ie3|if3h(0cU`Avz>mYpCLI^fS33Nqh=I=m=gZ)PB-BuFhzmynGcvq#UI!Y z=yiIzm$^q + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/shelter-64.png b/icons-topo/shelter-64.png new file mode 100644 index 0000000000000000000000000000000000000000..1f82da60c8f33571936b579e3533405544c194a9 GIT binary patch literal 1518 zcmVt<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11#3w}K~#90?VCSLTUQjuf5ym^LgXq&!iv>|D3XUX5<*B06Cp^= zW~2^@D9Du)YMKlzU>XD@CJBN3 zv4c4s-h1#c25g?~3&NjtWxwP5&i%g6_uYH^^lTFm4l{~W6HtwU0jde8M!^7uW0wLy z>#bB337eu_;0!Pbe4-ye0-Aw;^G!s>4EPEdV?#Xp`!W5q{6a+X)tn-7S9Gi`FH1BK zkZ2$vYs<@G!d<;@UU=n8e4U6)846?L<1*{@N(3YVGVAq9Y<%2o!jyi_fj7V=BHxI} zdqdf)o*tQTIM&4*ZN}k{S3Nyu6W)u+RXxXnG@w#M?i*?n(WpH2`?m;BZcqJwNkpS& zBkt?D4wL~GL}cDj@NQ&8o_IXFiZ|L5k4N5(jF?TB*K=l_YqocRV&E3=2T(~O!BTfO z3yqB=W@pn*l9-)kp|O#r?rumV04nvIxAZZxhHTaWCxKsqv!IEHwzLonhb<*7sjsJE zU;w+vV}wS5&w(jRiFIUa23!Ir(8a$U8e*=x+Ol|nSUAjFbv18?hK$fzeXL8ClVjZg zqn!N>a7sMRi_T7-H8ruexM-c7j4 z-~{df7Xf5th3D<+si^-opSM@tvw5Mbu9Px8lfsQ zu2<2x?}*)IKqU|aJ_T(xUTkYKZ%O+KrG7uI{(g#FE+h0O5C9goJ7*^YE&#uvnLsQY zCfd@HQAMW$c8`aOfdNYD>y6L?@C9%`RsU256a(J@R{$G{1WPw>vUKByQI=Xl@re_Z zG&Jx*LjxrZ4FJT3hj}+VOl)|V)kwth7})JNuV2S`?Hbta00P_wz6DmzU6T#SdO4dc zxVWln$NsC+(^)eloif^Nz$LWG$_ZLIyV%y2aYdJb;Avo3f8T(X z{eB~~gjO@$1W51RE_`$q6(gjFvLdUJW_Y(ZNTzZ)0;f zjL;pR(&AyWLwm8FwA$d_GwTg=8%jlO+5jA|c}-`39y5mveN3HC4Nu2A`15eI0AD zn5=|CGUxNH$2Of|LPA7-7m@QB47lG6F!-F;wIosrO}+$P0^b9tfX{#jIiIJw#{)Di zr_i*$G=FFq@DOcf^`QL*n9d)+ykQzmn+HwnL(qQsoCB=izCAd21N@}7`XUaO_+*3& zkI1Mdpc(}OR1;8*f&r=ts7Ap6)dWW z8U+JX6HtvqZ$QS6p6~EHfD%~cbLeM4x=)Ip2I_a|69yhzW|SVX#z)m3C-)8h2g#QW UI`QiE>;M1&07*qoM6N<$g5sj7p8x;= literal 0 HcmV?d00001 diff --git a/icons-topo/shelter-fire-64.png b/icons-topo/shelter-fire-64.png new file mode 100644 index 0000000000000000000000000000000000000000..fe17b566fc07e2fc69f77aad647c512128243733 GIT binary patch literal 2638 zcmV-U3bFNxP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H13Cl@DK~#90?VD|ER8<2lgUr8zLN|!-FjopOZ6%bO;V7FjI z5Q?m|3MEz1EMYOBx=L2Ex|PVX8Wyqr04sjLY@06PiqRUfcE!G|CfhclW)+RZ28wh` z+nILSDQ)NNKKtR`JDpD7W|%vzME}W2XU;w6IsfNB=jA!)+?qs$A2Q4{O<<<+qX08a zV5ae-05jNj5^zjQNi#*lG()SvFMt!k&$aL8fGxn6(@kV1dcY2#i4o_~J~wI4rWYbI zT`5gOPKpM_*1)46TJX>IQuq`CLRyHB_(uk+Jxe8M9)rx;&9NtX%oFAC5XoZ zkfya6br>DZ5EvEC`p%!nzGVxZmX>JvObP4KrDUEsL0opWo)uLXeXTuUR5*LJqJoZP z%VO?4fTyK}j%CZZT2Y~AMHNOz+5^&nH-SO`85p2v*REhP(~6YhV&)tW4D~Ou+NN zztNm`m6y}Ga^;LW59-|0xpF10@^U?EAu8ABQMpfwsd~U%paQrT)Y;gveS7ed^u0n- zem)t;k7Lfr(6jyp6a$?T)tO9ywZPv{MZn$AfW4?FqKIxLV6oZAtg0d*H&@T<1Re!y z#w$Ob0CB*dfIWZ-zn{MS`{{f6Wj!p73ULb-kdT*0VqP8zd3gZ1>*^S;tHWJahp(+I z>Nc=gNPF=`(q4E0EEWI(4g!Awe8ELS1u#6E4Rttf;lfGF`z~HIOptKG=wJaJMvbh} zKn-Ud+qXv?(M90!MW9amoQF2Sh9X))%o!PE96wG{e!ia7hZ-}S1XX|}pbT~WL_8jx zd-ep+s*~ax9M)5)Cks%8(9^hvu2qm(yLU7D;6Y4QtDf~HP+}61bHF0Fws7{Qi6_wu zsE*b%zQa>N7vxvk-5Bo`L) zT|&ZWRwDxZ7WixU{s8JGMnM~o5nv~96vpzt(d}mNop*4Rm*Z(_n$UpGd%{T2WVMpA zZ5vsQjbt@8lCo`E@c1zTIlyt?E#MBLj7H2`KXeP6$7N-Sv$Rxv9UU?rLeKAlurq?Q zxcMRy+`xAt^3w?F?+k-a0KSe6ah8_K<*ckR!NKqP!tYrgo$b2M}gZ~}h;ZUue~)EYH5HS7Zle66k6 z3kncLK@pth=_E{Mq;1$K?!d_H_c)iSd;|(nH=Z`69vDuh&>=@$l;Mlqq z85{)lTw-A?3C3E0gMiif+;jL^TSunA&1Fr!%pLqrp zh1svXf_c`gDJz;%Q<=SgKlpq&pM4h8lifox;u$jm9s`yPzV{x(b#+)*tRQ9U*03w5 zJoXsYB}*7OeVXB#8h};ACN_wf08fe6i*wH&0MZX1hLCNm3=d;}%t_E-=#^koALHv`LtKl%t?Yb(ixg(Td0=codT!$H@E4R{(G0r0lA@{{W7 zQ9~qg%^DK#xrgC1XYhRfIo2gh?gLVRiBD{<0kP--1ptHZz6(I=j?s6PGB7~b`tgi7OH?%>BMGt65y8nJKo9W-bAAfs0 z*5%8|c89s*_?p>OF$7h|z9v zhbW5ll$1!D#iEuqnZkZoSWo}E{dRFTH;afkMC7LHX*YNblW9l#>#sY~Uwcj62S{Y^ zBbw9Gx#5Ekur6Qz0k9b3?;6#1VqsLnn}erL{j%?+mj)tr9@M3?Yvaa`QFnot4QHnz zrYAz(1qDS)Z*S_lx8HESK_{`KlPJVCjf5Sm9hCbDoJpcdz07*qoM6N<$f + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons-topo/swamp.png b/icons-topo/swamp.png new file mode 100644 index 0000000000000000000000000000000000000000..1cd33f8b788d2faa44bae2569503e5869940bb74 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ0#6smkcif|(;WE@C~&Y$pa1jA zzccfEqNgr0(PS^{W7A`v#M>IJ}ut%Xb5_U*ss&Wnt-N(s1}eX%6_S6E22vPyl@ zdxzxIy!dZQy3^Utmu~)L6K*1VKK?=Iw%r$H4)Z>1ESSO0@!7%R4D&++{y8^AqU$S7 SSkD3NX7F_Nb6Mw<&;$T!r9J2X literal 0 HcmV?d00001 diff --git a/icons-topo/tankstelle-64.png b/icons-topo/tankstelle-64.png new file mode 100644 index 0000000000000000000000000000000000000000..3d8611fce38c1292bc8c77eb79257c5329083fee GIT binary patch literal 1624 zcmV-e2B-OnP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11=UGJK~#90?VC+Z6ImR}kzX=t=gsT?tM9#eZ|404S(Zr>6R-tvP9H9y8n7iRAVW+5y?`I^O@F=y zqRISNNFo45Kr3(r$X~RX*(C(r1sZ|j`6@0HfJ4AFpj0>0vdbWF9JoJM1=Aemg}^P~ z9=Yp0D7N22@ue_Pg+wi2FYqT&uDvi72q5+J5cd0#d_E+>dyHrX~c7 zMSJx#uoW1I6=(x+3#f}Pkox)Eq>S%LwPB#0w2>16h z+T5%yJ_48oS(bkS{*IMS1p<6tzux$xeX5wvlz;rFy+!_PLYddANIg9%Id65lDfW8V zAj@eTrG0%^-EMuHO++N3_%;)#31v~E688I3lIys773-co=@|!zRaLBMX-Qkod`Lc@ zw#YTR1OL$_Ofcf{D2td3j1O9S4c7t$n+?a6 zEBx5igoJw5R=1^Q9XtY0DyJhK8l|_A+M|~V>)yn12Qp&OnSWx z?ccA<0!^#WoVG*_YtEfZPe3$EsJ$Jjrw7NiYY3u<)$OLRrG=3*XLPYK9^C_wE&;b2 z>FHAdgn|MbmoEcQ(AdbN*UQ(gE?xB*t_x^?zxK`@Y{!lPAdihP=yDMm7)VLSeEJMG z576ot3JNIo`P2zg_|+={yLKUe{kkamhMxy$k;lgP=jc(@VR73wR@c=n@T=r8d;kE# zFJCfx{=9mgfBH0}*iz>rCjtNvYHvp#9|u6Js9@E>gSyDoJpdmBA*CZ(qQM~JH*c!v z`8wSabEX9Vj9t0}QDxu54+jolvfI;^nNtCXynBcA;)QY|2#7m&q%G5M0a)lEYq+;p z{hr{(XUZ5Z0Bbut5o~jBJBm&ZgnD7P0EpYSv+luzIs3P`WsCYf{N_#C`V1EU0PEhp ztb6%N+w@IEHw#yn=qjy8>m>E zPHm-SLjvD=O(+=_-zvFW+Dgj?adrUwCX_Uj`6?|N68yP7GujqVQv+u6qTj=1bI(so zn9YiBGx3{H5{yEzlDv`<>>f`xP0I>)k4L*pGz7dhp=4Lxol&`|39+&=DZ!K7-Fn$b z`1y0)WW^&^R$^~z(iYzZqOo~EBgz(X00fJLqK*y(QCv{?zh}?tV)$ftH^Eb<(w3t| z5Jkn8nb{+QC_c}~7&(NJV5AaiYh$FbF=w~U3!+G2Yb*IDPiimy!gMCtcb_t|k~FoF zHkbImT?qVvraR;?oxtzJ{qr$aQfC(sa37`5bP_jN$^k+sHg9Df=OMZ5@fgc8eg1!< WM{qV!$(;iL0000 + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/telefon-64.png b/icons-topo/telefon-64.png new file mode 100644 index 0000000000000000000000000000000000000000..6b60ea98852232c244015c0c1c71d76f42a0a8d8 GIT binary patch literal 2075 zcmV+$2;}#PP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12bf7jK~#90-J5M}Q`Z^Cf7duRwt*yLf>s(;QW6AGCP6ZVR@zD{ zRp} zDokFwMC`-~Vkb|MxqjWeR7JtRXAhzNeuDe=ql7|sYiE+Q>@`C^ep>fS&_DLLED72VgTW4(!qwEFM2j z^!ewJa9m@`GNs& zVR)G6i!ZuoU4>D^`9jj=M9WwiALnXMkK=CdYi}pm*GHhY7jILO;XAUt%v><&NKZNE zq4+G&4qO0yIYwe)g87FYax~z1TU%)u8X~ZFuc`Ovd_E+Zw57|8^I$v(u0!3}TVo9YK&pdD1>~ib%m`J96-#edmpFQbd9!45}BAgto%f?(T{?uMfa) zwYB4ulM+@G%g)btbs4pMT11YNGbT%KX)7jYW@OgyuZZ*70Bq7n-p)zRl^r`I9gXUZ zSrm~Tlg=X|Z6cD?3WyF2RK$600FG!SB;J4D`jJ&*qa;80KyOUiqVqW#(F)1LV)F4l z_qgu7HUO8kQf@r=oaF%A8Y!?j2eaMYXe{_lD`@%bSqZOMY3n1AV1kkBEctj z-)-si*4x_3UDMMjbp`p4QQ%3_LRO<6_@$Py`0A^qBN6LUT`7pj9&H%o@4jof^_Nbc z*0;u-lpgKPkRl>~F=}czT0Tb})Z}Cy$*Czz0ra)E8!c~t3@ll0jY0^pANX&HckH)( z7LEfD?CaCBCQ-Y;8<+>Wfm;S;j-3Y=0ubo!)w70BThA>_qvjdY8r}l%bsRbm4g=up z=+N)yW58QZjWWPl;0M|7w}3~0zqujUi+VW$0Ex#*hr^~T`#L)GthaIg+b@P=z?Xp2 zz(3p)>P5}7`vDZcA0-qrz3=yK->zr9=aykb@I$u+dQfxid3d*MFgmn|>dEq#?U`43tm**< z-MT1D9$EJQ7lGHg4Sybrh#Uk?<`}6DKjg~Rtsv#6a^LRV)U~xa%Fovtiip$$p8{)h z%=rf&B!2FkJ%JYU^R|?@2oLHFt20_=sK4K~Kv`O1`OFzUyZ?T!J^Cogix-VvYeO9$ zzH3{NOUNZg9zk7g0U(QueA3iJCK7Q$L+SiH>LsS90T+~6hbQ~_4(cUD0F-s>Xn5rn zcQlBwbm|nJ?%c`J*qG59J-}aa?=qz&^JDwdx|UE4nm*vo-A zgh%sz7|qELib7!DKGzf#%g>{3XrC@GV%`MWDhY(kqIUWrO+U;)itd?gUjpYmG5o!~qa20+lAy;UPW zUu$@D&o{IW-WKb7mpjRMSRH@~?L$@a^EHM(0Kd~d1Rj6fw4giDc>sN;r0V(kT9Xm8 zs`>d^lM%D3`T1H?v4X1Q=W9*H3aXZ$uQin_>|f>Kv16Yr=S52YX-Wak0beUKk=*`M zT?v%CExrl(C(u-+{El`W%Ku@a;`#Ynqg()>!uk1HQVM literal 0 HcmV?d00001 diff --git a/icons-topo/telefon.svg b/icons-topo/telefon.svg new file mode 100644 index 0000000..c58dfda --- /dev/null +++ b/icons-topo/telefon.svg @@ -0,0 +1,119 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/topo_shelter.svg b/icons-topo/topo_shelter.svg new file mode 100644 index 0000000..f3bc9d6 --- /dev/null +++ b/icons-topo/topo_shelter.svg @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons-topo/topo_shelter_fire.svg b/icons-topo/topo_shelter_fire.svg new file mode 100644 index 0000000..98423b7 --- /dev/null +++ b/icons-topo/topo_shelter_fire.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons-topo/trinkwasser-56.png b/icons-topo/trinkwasser-56.png new file mode 100644 index 0000000000000000000000000000000000000000..9c8eff6a8d9101baaed634a509733114b33c8d3f GIT binary patch literal 1855 zcmV-F2f+A=P)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12E0i`K~!jg?V4L`6lE00f4f|I+iq!*Qd$afFQodVtV0LN=pkZZJ~t%z0B_T zaCUaSneNWc%agRQ{q$}%|HVhVSC1FN6PfOkHy`*(O{gZaO* zEWX>C>>8&CUbHC@VTV8GG6B17#kY&eu6aQ40l_;6KLnkMOhSTfSs*wx414~7eTU(% zlTb4YmMxHGzMvXL1rytLL(r)Y76RA%`eEE+Q#fWc3?7F-0D5mgC?7^U&IXes5``VR z;G=EOaw+jSZ3m&E6m%VO9)n@cJl6X+6omi?BYk@WoKo}#&{q_KaL5!6YtR|%I!EhZ z<_qxk_i*#Jdt$l{KkkF-)$qm#$;kx*P+B7Uojq1^;ZU4hA4$kT>eYnfIGi*=oO}CI znDY|sJOG13$(3F2hiyN@BWqyodvNB0v+dz^{4tTsq z2pZ_=g&%%}{5+UG8FI!zQ_S>#XW-vD8E0N1U8YRFvPm#$1WSq_HwOj=CGBnvfTlr5 zH*{Zv^`DsO><5`3m?}kEZOTR%E00An8kOi6H7gJh@_|4K3l5m%j$qPh8u%T9m(Bh1 zQrYVINP>Ae5KM!hC4!f&$1)Ycl+C^(Mlh;F|1Bx~AjI-=(~IDpNYT^;V@sB36#8zM z<1r9`g1md5U>iW?B&+apml>&YrIHQ}OM!_*loU&6rU14i!qSO`~c9E zmjmO+nL^c|x**mf(AFv2KtNhXCL*ZbP!M18ascRx`Ir=L?yqB-8eM1t_uuH%8>-r}`O=t5j~jyZ0P|*8g$+&StY!LSQ?6;UFpWkbT;M-~ zGpEQ_*GEQB@ufbM9=9plUtS8uVN-bXwmCNDPB-O71fx0>75X28v!=$3U$U-1io@}Y z=_f-essjxG^JZEP)W`1ZoQF+WyccRRlkvOSHzHVXkDlp6L(DfvxV^u1HsvBbE88k1 zg3%~kyDrP5U{FZYU~k=4gRWWR{7u%oW3B9KOFL2XbKC= zM-?LqjOjzl@dq9WCQH%cu;~!UoV1}y(ls3N9zi2|stp74vP3XpiZ(V$N&?JLqByL< zxmLJ6AkpwzNezP0sMt`EtrTqsdP_@SLa`~lJqXS1vahD8uFC_a=JwcZ#wWq%cCle# zLUBA(dSgDNgqP$=SV@f{ptcH-G(Fu6xnsSjX!Q)MZUeRo>Iz;=Crndgj4WGVl@AR| zp~>~JCS1P2s@s6q^aO7NrIIF@vMsE(P6V_FT)HAeyD> zh7-XcJpF|A*x-rs+CJ&`!A+&?;-i517O{&@=F(c4_W`OTx#*bHN4Ie_XE7R!r7Tj zz^+rVx!;Q(uLSo2JhRw*>jThu9?m;Hyz_?hZKBh_n9*WW^}#K?5^M%e6&Aqqh1LT< zdAhI8u;51e0NC&Lop$kjfW z@w~jo&L+kcLep>Y3c3`i^>V$R2MV>o;gTZQveC!Y9m*72*`~Iu@{{gz>kfny9=iLAR002ovPDHLkV1ns!Rs#S4 literal 0 HcmV?d00001 diff --git a/icons-topo/trinkwasser.svg b/icons-topo/trinkwasser.svg new file mode 100644 index 0000000..fc4ca93 --- /dev/null +++ b/icons-topo/trinkwasser.svg @@ -0,0 +1,131 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/turm-64.png b/icons-topo/turm-64.png new file mode 100644 index 0000000000000000000000000000000000000000..2992529c941f1e0e963deb4ae6ad42206fcf4fe5 GIT binary patch literal 1168 zcmV;B1aJF^P)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11Pw_R$Bp53X(imVDH%>4j=v) z0{%%Of#1oz&pe))csxmvM1;%($OFh(|LXvb0GvlD!Uf4F%FiUf(wnbVov8HuC{ZN& zmE^aSWgE0qcdg8@l?v+BMm1AZa-z0>JXRaF8QTU1qLr_&+H z@78Bti~-xj;gH2*(X;b*DHe+y4u>SU{Zb6bkYu%5y|?prsaC7B$V6qpn-JI6JOJ$M z?6@v!ng-J}VVWj1O>@N@b@L(WaucGvpGi`dW!GJ9wOaOFW~BT@0iuZw5G_D7u>qn5h$c2bv;fh>28b3Q zn%Dr*0z?xVAX-9W4Z*z5Z#f^=PiEDyI^|=}FmSn$HtKAV@E|>4^xRdMaYtGHhd75B-JM^ub&&Gf+ zB$>@-?+p8g-1$*9H#a>E$Y!&4wJ*=f07KVxmrzNPCeHau^m;uiisEU4uInUe1ZjYu zC&BjsGWB}hb+J?`A)kNRkGy7QXR*4v>KV6kWvtwR;KzV>0MKkUT^FC)d7Jt9dC!<5 z*E?TH9QZLn0f26|>$;fB<;GTiC<_Y5G0vx$y&q|LZ-VXct z>;(WQihDHby6#60&d<+1V~$)GzLGfbV?Y-O-hi{SGtZbKSJ$)BBk^NE8vs;QbzM9< zI`X4u;s|i$YI|0CEU*1RD1H?7*}$vc41uqFI}A?w3z7JhSb%O$dXBA0000 + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/icons-topo/vineyard-64.png b/icons-topo/vineyard-64.png new file mode 100644 index 0000000000000000000000000000000000000000..f8595b79e1d7146a8ded3d3156d8e43924e8c250 GIT binary patch literal 621 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zNUsNB#yF{oGC)De64!{5;QX|b^2DN4hVt@qz0ADq;^f4FRK5J7^x5xhq!<_&UwOJX zhE&XXJ8Qoev!lSV=l?toZ2!Qiv!y4OYwN@-znHE~GMg)NZC}IQi%Pd5ZfU&`=CEs2 zSkY;D{*)+xVN6>2KHiz}+Y9d0NWPmr^K9NVo4Mv&4m`JjEqoe&YaTCeG&9?E z^)F%mFB5XrXLan)s5UcsBW{qLwQ6>M2GfNI-U<5@w(fh^UBTzEcKz=~d5ub^SDxW| zyP?lWd&P-;_1k`_RNT0`^y#L>veyE0-&{PM^XS?uPKT|R^}mIi*RNU2y@0h~i>>sZ zzjv6UwO@a+sqIy3Sp4?pg$PSiSxDvGuzO%cEsBmRHNnyvrX^a79?)_nPzO`+m(| zwDJ6}n#Z$QFE9b^@;JMBVPdvJ);!+cNCsVoUJJ(0aX`Zl?77ZS%6*``{>5*`7kB0V z)H%G_Y222_Zp00i_>zopr05c^I5C8xG literal 0 HcmV?d00001 diff --git a/icons-topo/vineyard.svg b/icons-topo/vineyard.svg new file mode 100644 index 0000000..48d01ed --- /dev/null +++ b/icons-topo/vineyard.svg @@ -0,0 +1,633 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/alpineHut.svg b/icons/alpineHut.svg new file mode 100755 index 0000000..d693a76 --- /dev/null +++ b/icons/alpineHut.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + H + + + diff --git a/icons/areaBeach.svg b/icons/areaBeach.svg new file mode 100755 index 0000000..ce89223 --- /dev/null +++ b/icons/areaBeach.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/icons/areaCemetery.svg b/icons/areaCemetery.svg new file mode 100755 index 0000000..7055f78 --- /dev/null +++ b/icons/areaCemetery.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/areaFieldHospital.svg b/icons/areaFieldHospital.svg new file mode 100755 index 0000000..9492071 --- /dev/null +++ b/icons/areaFieldHospital.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/icons/areaGolf.svg b/icons/areaGolf.svg new file mode 100755 index 0000000..14ce60a --- /dev/null +++ b/icons/areaGolf.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/areaHeath.svg b/icons/areaHeath.svg new file mode 100755 index 0000000..79df661 --- /dev/null +++ b/icons/areaHeath.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/areaHospital.svg b/icons/areaHospital.svg new file mode 100755 index 0000000..adcf0ec --- /dev/null +++ b/icons/areaHospital.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/areaMarsh.svg b/icons/areaMarsh.svg new file mode 100755 index 0000000..73917fe --- /dev/null +++ b/icons/areaMarsh.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/areaMilitary.png b/icons/areaMilitary.png new file mode 100755 index 0000000000000000000000000000000000000000..7a9d4e688eeaeee0d34df83cb934ce4f7873e961 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1SFYWcSQjymUKs7M+SzC{oH>NS%LgmPZ!4! z3;$%Bf(H!DHyKvU%sJq&&2PeFcT-^wMvpm%L + + + + + + + + + + + + image/svg+xml + + + + + + + + P + P + + diff --git a/icons/areaPlayground.svg b/icons/areaPlayground.svg new file mode 100755 index 0000000..7f72631 --- /dev/null +++ b/icons/areaPlayground.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons/areaRefugees.svg b/icons/areaRefugees.svg new file mode 100755 index 0000000..9305c05 --- /dev/null +++ b/icons/areaRefugees.svg @@ -0,0 +1,156 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/areaScree.svg b/icons/areaScree.svg new file mode 100755 index 0000000..f3f6338 --- /dev/null +++ b/icons/areaScree.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons/areaSport.png b/icons/areaSport.png new file mode 100755 index 0000000000000000000000000000000000000000..56d6097f17beb92b9d98078aa3fa629aad17def1 GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1SFYWcSQjy#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBC{gO^ z;uvD#|8{bqP=f-8%gcY^U-VQK?)3C}_(lBj^j~gwV$Hm^1W59&x6ssWpZ_<}-6rkb z)vs68PDxB_@v<#?Fl(Q1Ug001whOnXyEb{=K3=%8e8atMA3}E)Zd-dI?YU%+F@O9F qzpK5@SIh1{SB==~@HA?V(A&I + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/bank.png b/icons/bank.png new file mode 100644 index 0000000000000000000000000000000000000000..520f8b0acbb62597ed3524a54c787a2587641415 GIT binary patch literal 590 zcmV-U0Op=O3x!6OVo3GBlP#Q8cn9ERZ* z>OmnJOu-H0`YJpDc3=w%RpDyg@C02D{F~w8XTFCqNS8wUr0?Z$4D%54)8H@|fIDcC zg4$t&PrM8*s%u#=2N#g}-SBt@>_M><-ppU52wYGKZ_psMbF#oj2>MER0Vmo7Czw3e zvv9?ybMf3bRC{P!3Fj~a(MG``(8DFxsWH)nQLcY6)<=+`(^jx^m_++zt?*b_|LU)(hK?s0#?<_?5u| zC!gY_9X`F7gzGx(Di|y>YQU;B6m_s<5Gp2TFF(6fg{&GBaPQ3u@TP!Cu97Sk;NZAOHXW literal 0 HcmV?d00001 diff --git a/icons/bench.svg b/icons/bench.svg new file mode 100755 index 0000000..28d829b --- /dev/null +++ b/icons/bench.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/icons/binoculars.svg b/icons/binoculars.svg new file mode 100755 index 0000000..435a38e --- /dev/null +++ b/icons/binoculars.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/icons/blitzer.svg b/icons/blitzer.svg new file mode 100755 index 0000000..83c6f93 --- /dev/null +++ b/icons/blitzer.svg @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/buildingCollapsed.svg b/icons/buildingCollapsed.svg new file mode 100755 index 0000000..434d649 --- /dev/null +++ b/icons/buildingCollapsed.svg @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/icons/buoy.svg b/icons/buoy.svg new file mode 100755 index 0000000..c93cbec --- /dev/null +++ b/icons/buoy.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/icons/bus.svg b/icons/bus.svg new file mode 100755 index 0000000..ba08fea --- /dev/null +++ b/icons/bus.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + H + + diff --git a/icons/busstation.svg b/icons/busstation.svg new file mode 100755 index 0000000..e000c77 --- /dev/null +++ b/icons/busstation.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons/cafe.svg b/icons/cafe.svg new file mode 100755 index 0000000..20342e0 --- /dev/null +++ b/icons/cafe.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons/church.svg b/icons/church.svg new file mode 100755 index 0000000..aac12fc --- /dev/null +++ b/icons/church.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/icons/citylimit.svg b/icons/citylimit.svg new file mode 100755 index 0000000..3b09788 --- /dev/null +++ b/icons/citylimit.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + City limit + + diff --git a/icons/cliff.svg b/icons/cliff.svg new file mode 100755 index 0000000..3bf0899 --- /dev/null +++ b/icons/cliff.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/icons/fastfood.svg b/icons/fastfood.svg new file mode 100755 index 0000000..982c331 --- /dev/null +++ b/icons/fastfood.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/icons/fieldHospital.svg b/icons/fieldHospital.svg new file mode 100755 index 0000000..f112387 --- /dev/null +++ b/icons/fieldHospital.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons/firestation.svg b/icons/firestation.svg new file mode 100755 index 0000000..2614e17 --- /dev/null +++ b/icons/firestation.svg @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/icons/fountain.svg b/icons/fountain.svg new file mode 100755 index 0000000..e0b7358 --- /dev/null +++ b/icons/fountain.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons/fuel.png b/icons/fuel.png new file mode 100644 index 0000000000000000000000000000000000000000..14d8f96e39f690a541c2c9d09bcdc330fd4eccae GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSM@i<$9TU z*~Q6;1*v-ZMd`EO*+>BuSqAuoxc>kDAIM~2Vc-A)9u5Hk0SOri1qB674bAGx>gLAg zDHEs6nK@_4!X-bduSLtfNDxsy)4u?H6~*tOJpVyQGkNa?B{>bP0l+XkKJ8WjJ literal 0 HcmV?d00001 diff --git a/icons/fuel.svg b/icons/fuel.svg new file mode 100755 index 0000000..bcf8196 --- /dev/null +++ b/icons/fuel.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons/gate.svg b/icons/gate.svg new file mode 100755 index 0000000..c4c9b8c --- /dev/null +++ b/icons/gate.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons/glasses.svg b/icons/glasses.svg new file mode 100755 index 0000000..9a1c519 --- /dev/null +++ b/icons/glasses.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/icons/guidepost.svg b/icons/guidepost.svg new file mode 100755 index 0000000..3951373 --- /dev/null +++ b/icons/guidepost.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons/hospital.svg b/icons/hospital.svg new file mode 100755 index 0000000..1f6e625 --- /dev/null +++ b/icons/hospital.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/hospital2.png b/icons/hospital2.png new file mode 100644 index 0000000000000000000000000000000000000000..ba72b13b174b9b86e14d66f5d499afe7fcfc4c59 GIT binary patch literal 430 zcmV;f0a5;mP)Ig`}jUrlzK+r>Ci@smI61$;rvg007O+&Cbrw;b&*&=H}?==;`U{ z>+9?6pP%{p`TF|$`}_NYDAor60004WQchCs2cj*2>#T9L~ng8euEH*@jpZ0XrSQ zUO-@n)D{88OR=02NGz@749xk_SqZ4 + + + + + + + + + + + image/svg+xml + + + + + + + + H + + diff --git a/icons/info.svg b/icons/info.svg new file mode 100755 index 0000000..5a417d2 --- /dev/null +++ b/icons/info.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + image/svg+xml + + + + + + + i + + diff --git a/icons/kindergarten.png b/icons/kindergarten.png new file mode 100644 index 0000000000000000000000000000000000000000..388bc8870c4649631df31e1974a9aa0675d3e1f9 GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnL3?x0byx0z;I14-?iy0Ug{Xv-Vi2a-pprB-l zYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt*f;J|V9E|NjRvLl0f915%77 zL4Lsu4$p3+0XeCjE{-7_*OL<**o_np@l9puHf-Yf&CYX%=fAzd2J`>s2?_c0^A8@d z^N(ZFP&|L&z&}Qh5C0j2KB+&EmRNJ1q4N>*Bwn5~{3`qmoSpnUXa4K_GcdRvd4kc` kFo9W+$Ad+ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/landslide.svg b/icons/landslide.svg new file mode 100755 index 0000000..617108a --- /dev/null +++ b/icons/landslide.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/icons/lighthouse.svg b/icons/lighthouse.svg new file mode 100755 index 0000000..9449b96 --- /dev/null +++ b/icons/lighthouse.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/icons/mountainPass.svg b/icons/mountainPass.svg new file mode 100755 index 0000000..5bdd6ce --- /dev/null +++ b/icons/mountainPass.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/parking.png b/icons/parking.png new file mode 100644 index 0000000000000000000000000000000000000000..a5fcf4dd4c42defb175d7756c24f6edb9a018740 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyk|nMYCBgY=CFO}lsSM@i<$9TU z*~Q6;1*v-ZMd`EO*+>BuRRs8ixc>kDpTS@bgUK8Qi#Z%XWHUzqh#Zy(xGa$XB9A2s zK1&QE*EnWw2`b$iRl7H<^JsDJ(c+0`tAS|h+3J~Rn`fTwo_n@?;nnV?SEsDHx@OnC zE&J~6J@xG9xo1Z&y*mp;*WNvQ_3hoeZ{I%s|MulE3A|UN zEEuL6oLI#e^NjggRJJU`@e`A_cXI7g`miR*C1w8lJ@5bBeAs{RoP_aZhXsKSOT^u5 Wk?B!~bIu9CAPr#*!ev zU + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/pharmacy.svg b/icons/pharmacy.svg new file mode 100755 index 0000000..3fabfd0 --- /dev/null +++ b/icons/pharmacy.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/picnic.svg b/icons/picnic.svg new file mode 100755 index 0000000..56df155 --- /dev/null +++ b/icons/picnic.svg @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/pier.svg b/icons/pier.svg new file mode 100755 index 0000000..b4a981d --- /dev/null +++ b/icons/pier.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/icons/playground.png b/icons/playground.png new file mode 100644 index 0000000000000000000000000000000000000000..7772253686d414969b2a24b5cc2eaf94588cfd11 GIT binary patch literal 764 zcmVSYo}QndpP`|lrlzK+r>Cf>sIRZDu&}VPv9YqUva_?Zw6wIgwzj&uy1&1_ z!otGD#KgtL#mLCW%F4>i%gfBn%+Aiv(b3V=)YR40)z;S5*x1+9?9@9*>T^Yrxe_4W1k_V)Mp_xSku`T6HK~xwS&B6CKLSX;~;P+LL zD0`E=C3}zTknFwpCWTbr=Wll%=hRjAc8=dX5AZ)pBo$uopMQfsIc=b!S3uWC3CXMr z=n7_%v>ZZL#6i;TgD!beV=Y}p65?U^>f+8HlF%kM8l4SBQ&pEY1Wns!{;-@t%b|_m zaz@)YAdu*^nCWRGbFYr9g?C7BUCnH7B}wB}sM|Ov5$Cyy`Frp6RE&JSEFDS2IcTms zL$HO4b_qS9Rw~M#p(pI5!o2Jg&=%>WvE0Utyb8x`7#c(mhwj}bD$;N8EquOtD-b37 zI0L`e?OybFo}iE(3ybygcyqV(DCY@~pScmjMvT>t<74`ebJ%mFgiwH`VOWHXio`2{mLJiCzwRdum27ox^s@*`tknj|J4pXzwa+pDnISLzS%YF^y$L~d-gwl m`Zd5!BcjX6+w-FW4+F!E$;*CTO;)G`S>x&I=d#Wzp$PybWJ<39 literal 0 HcmV?d00001 diff --git a/icons/postbox.svg b/icons/postbox.svg new file mode 100755 index 0000000..7fd935a --- /dev/null +++ b/icons/postbox.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/icons/powerPole.svg b/icons/powerPole.svg new file mode 100755 index 0000000..6d0ebca --- /dev/null +++ b/icons/powerPole.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/icons/powerSubStation.svg b/icons/powerSubStation.svg new file mode 100755 index 0000000..aa89e05 --- /dev/null +++ b/icons/powerSubStation.svg @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icons/powerTower.svg b/icons/powerTower.svg new file mode 100755 index 0000000..f934b94 --- /dev/null +++ b/icons/powerTower.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/icons/powerWind.svg b/icons/powerWind.svg new file mode 100755 index 0000000..026a330 --- /dev/null +++ b/icons/powerWind.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons/pub.svg b/icons/pub.svg new file mode 100755 index 0000000..6560efa --- /dev/null +++ b/icons/pub.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/icons/radar.svg b/icons/radar.svg new file mode 100755 index 0000000..9252cbe --- /dev/null +++ b/icons/radar.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/icons/railwaystation.svg b/icons/railwaystation.svg new file mode 100755 index 0000000..9d57b3d --- /dev/null +++ b/icons/railwaystation.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/icons/recycling.png b/icons/recycling.png new file mode 100644 index 0000000000000000000000000000000000000000..ed8434f409121fbb12c6575927fe37593b73a067 GIT binary patch literal 483 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dynE^f_uK)l4XRu&k&|qK?VPNoK zUfkB0V!GnPzmw{m-P(K5M4g*6e14AMMLn8x& z2?Ikb&@={yLIwsK1_l`hhFS)OR0f7#28KwmwUrDE5)7LKU4WJ_FqQ=Q1v5B2yO9Ru za29w(7BevL9R^{>rYtL zGfAw=SqzhraP{jbkp*i(Qjt| zdnrmGvsAnSpLvvBcyVO%zu3jbGlRvd;^y>Un)hZ?&3UW3Rps-7t|yAs&UH<2Uid4Z zz=!XYl4^a>x6VTA)bQhn)lR6mY0ngG)t|}l=D5t)CTE=y?}Jpo>5We6!INdDvYy`@ zFJ+oCb(3jpzJe#0;N+wpksu}O<1=Q&%wpeEo1@m(x%*XKgPVD0bY!=xfQuBUt-rV---uFKmdKI;Vst0DuLl82|tP literal 0 HcmV?d00001 diff --git a/icons/reservoir.svg b/icons/reservoir.svg new file mode 100755 index 0000000..aca7131 --- /dev/null +++ b/icons/reservoir.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/icons/restaurant.svg b/icons/restaurant.svg new file mode 100755 index 0000000..582b2b9 --- /dev/null +++ b/icons/restaurant.svg @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/restaurant1.svg b/icons/restaurant1.svg new file mode 100755 index 0000000..648afde --- /dev/null +++ b/icons/restaurant1.svg @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/restaurant2.svg b/icons/restaurant2.svg new file mode 100755 index 0000000..19ec88a --- /dev/null +++ b/icons/restaurant2.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/school.svg b/icons/school.svg new file mode 100755 index 0000000..a9ce5b2 --- /dev/null +++ b/icons/school.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + abc + + + + diff --git a/icons/shelter.svg b/icons/shelter.svg new file mode 100755 index 0000000..166dbd3 --- /dev/null +++ b/icons/shelter.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/icons/shield_ger_bab.def b/icons/shield_ger_bab.def new file mode 100755 index 0000000..fdc34f4 --- /dev/null +++ b/icons/shield_ger_bab.def @@ -0,0 +1,16 @@ + + + + + + + + + + + + + REPLACELABEL + + + diff --git a/icons/shield_ger_bs.def b/icons/shield_ger_bs.def new file mode 100755 index 0000000..06a6970 --- /dev/null +++ b/icons/shield_ger_bs.def @@ -0,0 +1,16 @@ + + + + + + + + + + + + REPLACELABEL + + + + diff --git a/icons/shield_ger_ls.def b/icons/shield_ger_ls.def new file mode 100755 index 0000000..06bef8e --- /dev/null +++ b/icons/shield_ger_ls.def @@ -0,0 +1,16 @@ + + + + + + + + + + + + REPLACELABEL + + + + diff --git a/icons/soccer.svg b/icons/soccer.svg new file mode 100755 index 0000000..3b0e7f9 --- /dev/null +++ b/icons/soccer.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/icons/speed_camera.png b/icons/speed_camera.png new file mode 100644 index 0000000000000000000000000000000000000000..8400399d427c9093ea6b2e98ea0dc599dd87159c GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9GG z!XV7ZFl&wkP>{XE)7O>#1_u)dtCZTY*ZM#q$r9Iy66gHf+|;}h2Ir#G#FEq$h4Rdj z33I zVDf#ilj*War=i=n++v_92W-w4u|2-YIzw~KYpufu9@k6tCLNAccj6FRaM$7D-@{K1 z{IzC^e|((%$MXYczPp}i{Jp=<@kG;Ct~)Osgc@VA54c~7o#3PJO~ym3;AvI6gYGKs a8iqwTEM^7F<5LFumBG{1&t;ucLK6V*1e1gS literal 0 HcmV?d00001 diff --git a/icons/spring.svg b/icons/spring.svg new file mode 100755 index 0000000..d69415d --- /dev/null +++ b/icons/spring.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/icons/supermarket.svg b/icons/supermarket.svg new file mode 100755 index 0000000..ae94181 --- /dev/null +++ b/icons/supermarket.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/icons/swimming.svg b/icons/swimming.svg new file mode 100755 index 0000000..be6946c --- /dev/null +++ b/icons/swimming.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/icons/telephone.svg b/icons/telephone.svg new file mode 100755 index 0000000..4b9438e --- /dev/null +++ b/icons/telephone.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/toilets.svg b/icons/toilets.svg new file mode 100755 index 0000000..71323ae --- /dev/null +++ b/icons/toilets.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/tower.svg b/icons/tower.svg new file mode 100755 index 0000000..13676d0 --- /dev/null +++ b/icons/tower.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icons/towerRadio.svg b/icons/towerRadio.svg new file mode 100755 index 0000000..44449e8 --- /dev/null +++ b/icons/towerRadio.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/icons/viewpoint.svg b/icons/viewpoint.svg new file mode 100755 index 0000000..4071ee2 --- /dev/null +++ b/icons/viewpoint.svg @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/wall.svg b/icons/wall.svg new file mode 100755 index 0000000..678d706 --- /dev/null +++ b/icons/wall.svg @@ -0,0 +1,190 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/waterpark.svg b/icons/waterpark.svg new file mode 100755 index 0000000..382af69 --- /dev/null +++ b/icons/waterpark.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/mw.pl b/mw.pl new file mode 100644 index 0000000..ddd8490 --- /dev/null +++ b/mw.pl @@ -0,0 +1,184 @@ +# +# PERL mapweaver 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 +# + + + +# 0.03 20110614 -help +# 0.03 20110614 square for nodes +# 0.03 print prg name and version +# 0.03 ruler +# 0.04 ruler positions; ruler background; disc opacity correction; -debug; -verbose +# 0.04 scale, colors and positions; header/footer +# 0.04 triangle and diamond for nodes; labels and icons for nodes +# 0.05 categories for config values +# 0.06 drawArea; area rules; extended help, added valid object properties +# 0.07 way labels; minsizearea implemented; +# 0.08 added coastlines; problems with completeObjects! use option -cie +# 0.09 oneways +# 0.10 pagenumbers; rectangles; comments and empty lines in rule file; config in rule file +# 0.10 coast lines fixed; auto bridge implemented +# 0.11 area icons / patterns added; time; street directory; poi directory; pdf directoriy +# 0.12 way shields +# 0.13 routes, not yet working... +# 0.14 route work +# 0.15 routes working now - finetuning needed; bgbolor implemented; multipolygons +# 0.16 size check for multipolygon areas; scale rule sizes (x:y) +# 0.17 -forcenodes; projection in footer +# 0.18 direxclude options and rule properties +# 0.19 pagenumber bug solved +# 0.20 legend +# 0.21 legend in separate file +# 0.22 help texts for object properties in rule file +# 0.23 latex string sanitize +# 0.24 labels for areas +# 0.25 labels for multipolygons +# 0.26 fix directory bugs +# 0.27 way name substitution, if name is too long for way. incl. legend for map +# 0.28 oceancolor bug fixed +# 0.29 fonts/families +# 0.30 -wns=5 now possible; way name substitutions in separate file +# 0.31 getXXXrule bug fixed; wnsunique +# 0.32 -targetSize +# 0.33 -onewayautosize +# 0.34 pbf support; halo; label transform; bold print of labels +# 0.35 svg text creation bug fixed +# 0.36 font size error wns corrected; box occupy; new place management +# 0.37 -dirprg program to create directory; gpx support +# 0.38 -gpxcolor; -gpxsize +# 0.39 parameter bug dirprg fixed; sanitize bug fixed +# 0.40 draw only items inside drawing area; check for undefined relation nodes, reduce errors +# 0.41 fixed icon space occupy error; eliminated labels drawn outside map area +# 0.42 fixed error with area label svg string +# 0.43 check if areas (simple ways) are closed before drawing +# 0.44 overpass options added +# 0.45 utf-8 encoding error solved for overpass data +# 0.46 out parameter fixed +# 0.47 added srtm option +# 0.48 shield error corrected + + +# TODO +# -different tempfilenames + +my $version = "0.48" ; +my $programName = "mapweaver" ; + +use strict ; +use warnings ; + +use OSM::osm ; +use mwConfig ; +use mwMap ; +use mwRules ; +use mwFile ; +use mwNodes ; +use mwWays ; +use mwRelations ; +use mwMulti ; +use mwMisc ; +use mwOccupy ; +use mwGPX ; + +my $time0 = time() ; + + +print "\n$programName $version by gary68\n\n" ; + +initConfig() ; + +getProgramOptions() ; + +readConfigFile( cv('ini') ) ; + +if ( cv('help') eq "1" ) { + printConfigDescriptions() ; + printValidObjectProperties() ; + die ("quit after help output\n") ; +} + +if ( cv('verbose') eq "1" ) { + printConfig() ; +} + +readRules() ; + +if ( cv('debug') eq "1" ) { + printNodeRules() ; + printWayRules() ; + printAreaRules() ; + printRouteRules() ; +} + +readFile() ; + +my $renderTime0 = time() ; + +adaptRuleSizes() ; + +if ( cv('multionly') eq "0" ) { + + processNodes() ; + + if ( cv('poi') eq "1") { + createPoiDirectory() ; + } + + initOneways() ; + processWays() ; + + if ( cv('dir') eq "1") { + createDirectory() ; + } + + if ( cv('dirpdf') eq "1") { + createDirPdf() ; + } + + processRoutes() ; + +} # multionly + +processMultipolygons() ; + + +if ( cv('legend') ne "0" ) { createLegend() ; } + +if ( cv('pagenumbers') ne "" ) { processPageNumbers() ; } +if ( cv('rectangles') ne "" ) { processRectangles() ; } + +if ( cv ('test') eq "1") { + boxDrawOccupiedAreas() ; +} + +if ( cv ('gpx') ne "") { + processGPXFile() ; +} + + +writeMap() ; + +my $renderTime1 = time() ; + + +my ($paper, $x, $y) = fitsPaper () ; $x = int ($x*10) / 10 ; $y = int ($y*10) / 10 ; +print "map ($x cm x $y cm) fits paper $paper\n\n" ; + +my $time1 = time() ; +print "\nrender time (excluding all file operations) ", stringTimeSpent ($renderTime1-$renderTime0), "\n" ; +print "\n$programName finished after ", stringTimeSpent ($time1-$time0), "\n\n" ; + + diff --git a/mwCoastLines.pm b/mwCoastLines.pm new file mode 100644 index 0000000..741c0cb --- /dev/null +++ b/mwCoastLines.pm @@ -0,0 +1,417 @@ +# +# 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') eq "1") { + 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 ; + if ( cv ('debug') eq "1" ) { print "COAST: way $w will be used unmodified.\n" ; } + } + elsif ( $allOut) { + # do nothing + if ( cv ('debug') eq "1" ) { print "COAST: way $w will NOT be used. outside map.\n" ; } + } + else { + # eliminate all outside nodes at start and end of way, then use new way + + # eliminate outsides at start + while ( (scalar @nodes >= 1) and ( ! pointInMap ($nodes[0]) ) ) { + shift @nodes ; + } + + # eliminate outsides at end + while ( (scalar @nodes >= 1) and ( ! 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 ! + if (cv('debug') eq "1") { print "COAST: " . scalar (@ringNodes) . " rings found.\n" ; } + + # 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 ; + if (cv('debug') eq "1") { print "COAST: initial ring $i - $actualCoords[0]->[0],$actualCoords[0]->[1] -->> $actualCoords[-1]->[0],$actualCoords[-1]->[1] nodes: $num\n" ; } + } + + 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 ; + if (cv('debug') eq "1") { print "COAST: " . scalar (@islandRings) . " islands found.\n" ; } + @ringCoordsClosed = () ; + + # process ringCoordsOpen + # add other rings, corners... + while (scalar @ringCoordsOpen > 0) { # as long as there are open rings + if (cv('debug') eq "1") { print "COAST: building ring...\n" ; } + 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] ; + + if (cv('debug') eq "1") { print "COAST: actual and actualStart $actualX, $actualY - $actualStartX, $actualStartY\n" ; } + + 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 ; + if (cv('debug') eq "1") { print "COAST: matching start other polygon found i= $i\n" ; } + } + } + } + # 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 ; + if (cv('debug') eq "1") { print "COAST: openring $startFromOtherPolygon added to actual ring\n" ; } + } + else { + if ($corner) { # add corner to actual ring + push @actualRing, [$actualX, $actualY] ; + if (cv('debug') eq "1") { print "COAST: corner $actualX, $actualY added to actual ring\n" ; } + } + } + # check if closed + if ( ($actualX == $actualStartX) and ($actualY == $actualStartY) ) { + $closed = 1 ; + push @actualRing, [$actualX, $actualY] ; + push @ringCoordsClosed, [@actualRing] ; + if (cv('debug') eq "1") { print "COAST: ring now closed and moved to closed rings.\n" ; } + } + } # !closed + } # open rings + + my $color = cv('oceancolor') ; + + # build islandRings polygons + if (cv('debug') eq "1") { print "OCEAN: building island polygons\n" ; } + 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 ; + + diff --git a/mwConfig.pm b/mwConfig.pm new file mode 100644 index 0000000..01daa5e --- /dev/null +++ b/mwConfig.pm @@ -0,0 +1,404 @@ +# +# 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 mwConfig ; + +use strict ; +use warnings ; + + +use Getopt::Long ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( cv + initConfig + readConfigFile + setConfigValue + printConfig + printConfigDescriptions + getProgramOptions + ) ; + +my @initial = ( ["verbose",0, "print some more information (CLO)", "misc"], + ["debug",0, "print debug information (CLO)", "misc"], + ["projection", "merc","Used projection", "map"], + ["ellipsoid", "WGS84","Used ellipsoid", "map"], + + ["ruleDefaultNodeSize", "20", "default size of dot for nodes", "nodes"], + ["ruleDefaultNodeColor", "black", "default color of dot for nodes", "nodes"], + ["ruleDefaultNodeShape", "circle", "default shape of node", "nodes"], + ["ruleDefaultNodeLabel", "name", "default key for label", "nodes"], + ["ruleDefaultNodelabelSize", 30, "default size of label text", "nodes"], + ["ruleDefaultNodeLabelFont", "", "DON'T USE", "nodes"], + ["ruleDefaultNodeLabelFontFamily", "sans-serif", "default font family for label", "nodes"], + ["ruleDefaultNodeIconSize", 40, "default size of icon", "nodes"], + ["ruleDefaultNodeFromScale", 0, "default fromScale of node", "nodes"], + ["ruleDefaultNodeToScale", 1000000, "default toScale of node", "nodes"], + + ["ruleDefaultWayLabel","name","default label of way", "ways"], + ["ruleDefaultWayLabelColor","black","default label color of way", "ways"], + ["ruleDefaultWayLabelSize",30,"default label size of way", "ways"], + ["ruleDefaultWayLabelFont","","DON'T USE", "ways"], + ["ruleDefaultWayLabelFontFamily","sans-serif","default label font of way", "ways"], + ["ruleDefaultWayLabelOffset",15,"default label Tspan offset of way", "ways"], + + ["ruleDefaultWayColor","gray","default color of way", "ways"], + ["ruleDefaultWaySize",20,"default size of way", "ways"], + ["ruleDefaultWayBorderColor","black","default color of border of way", "ways"], + ["ruleDefaultWayBorderSize",2,"default size of border of way", "ways"], + ["ruleDefaultWayDash","","default dash style of way", "ways"], + ["ruleDefaultWayDashCap","butt","default cap for dashes of way", "ways"], + ["ruleDefaultWayFromScale",0,"default fromScale of way", "ways"], + ["ruleDefaultWayToScale",1000000,"default toScale of way", "ways"], + + ["ruleDefaultAreaColor","lightgray","default area color", "areas"], + ["ruleDefaultAreaLabelFont","","DON'T USE", "areas"], + ["ruleDefaultAreaLabelFontFamily","sans-serif","default font family for area labels", "areas"], + ["ruleDefaultAreaMinSize",0,"minimum size of area to be drawn", "areas"], + ["ruledefaultAreaFromScale",0,"default fromScale of way", "areas"], + ["ruledefaultAreaToScale",1000000,"default toScale of way", "areas"], + + ["ruledefaultRouteColor","black","default color of route", "routes"], + ["ruledefaultRouteSize",5,"default size of route", "routes"], + ["ruledefaultRouteDash","","default dash of route", "routes"], + ["ruledefaultRouteLinecap","round","default linecap of route", "routes"], + ["ruledefaultRouteOpacity",100,"default opacity of route", "routes"], + ["ruledefaultRouteLabel","ref","default label of route", "routes"], + ["ruledefaultRouteLabelSize",25,"default label size of route", "routes"], + ["ruledefaultRouteNodeSize",10,"default node size of route nodes", "routes"], + + ["ruledefaultRouteFromScale",0,"default fromScale of route", "routes"], + ["ruledefaultRouteToScale",50000,"default toScale of route", "routes"], + + ["elementFont","","DON'T USE", "map"], + ["elementFontFamily","sans-serif","default font family for map elements like title, scale, grid etc.", "map"], + + ["in","map.osm","osm in file (CLO)", "job"], + ["srtm","","srtm in file (CLO)", "job"], + + ["overpass",0,"use overpass servers to get data (CLO)", "job"], + ["near","","search only near this name (when using overpass) (CLO)", "job"], + ["overpassdistance",50000,"overpass distance for near search (CLO)", "job"], + ["overpassserver","http://www.overpass-api.de/api/","overpass server address (CLO)", "job"], + + ["gpx","","gpx file to overlay (CLO)", "map"], + ["gpxColor","black","color for gpx objects (CLO)", "map"], + ["gpxSize",10,"base size of gpx objects (CLO)", "map"], + ["ini","mwconfig.ini","file with configuration values (CLO)", "misc"], + ["out","mapweaver.svg","svg output name (CLO)", "job"], + ["style","mwStandardRules.txt","file with render rules (CLO)", "job"], + ["svgname","mapweaver.svg","output file name for svg graphics (CLO)", "job"], + ["size",2200,"size in pixels x axis, 300dpi (CLO)", "map"], + ["maxTargetSize","","sizes w,h in cm [21,29.7] (CLO)", "map"], + ["legend",0,"appearance and position of legend (CLO)", "map"], + ["bgcolor","white","background color of map (CLO)", "map"], + ["grid",0,"number of grid cells, 0 = no grid (CLO)", "map"], + ["gridcolor","black","color of grid lines (CLO)", "map"], + ["coords",0,"draw coordinate system (CLO)", "map"], + ["coordsexp",-2,"size of grid cells, exp 10 (CLO)", "map"], + ["coordscolor","black","color of coordinates grid lines (CLO)", "map"], + ["clip",0," (CLO)", "job"], + ["clipbbox",""," (CLO)", "job"], + ["pad",0," (CLO)", "job"], + ["ppc",6.5,"points per character (CLO)", "misc", "map"], + ["pdf",0,"convert output to pdf (CLO)", "job"], + ["png",0,"convert output to png (CLO)", "job"], + ["pngdpi",115,"png resolution (CLO)", "job"], + ["dir",0,"add directory (CLO)", "additional information"], + ["dirprg","mwDir.pl","program to create directory (CLO)", "additional information"], + ["direxcludedefault", "no", "object default property for directory entries", "additional information"], + ["poi",0,"add POI directory (CLO)", "additional information"], + ["dirpdf",0,"create directory pdf (CLO)", "additional information"], + ["dircolnum",2,"number of text columns for directory pdf (CLO)", "additional information"], + ["dirtitle","Directory","title for directory (CLO)", "additional information"], + ["tagstat",0,"print tag statistics (CLO)", "misc"], + ["declutter",1," (CLO)", "map"], + ["allowIconMove",0," (CLO)", "map"], + ["forceNodes",0," (CLO)", "map"], + ["lineDist",10,"distance between text lines in pixels", "map"], + ["maxCharPerLine",20,"maximum characters per line in node label", "map"], + ["help",0,"prints help texts (CLO)", "misc"], + ["oneways",0,"add oneway arrows (CLO)", "map"], + ["onewayColor","white","color of oneway arrows (CLO)", "map"], + ["onewaySize",20,"size of oneway arrows (CLO)", "map"], + ["onewayAutoSize",0,"auto size oneway arrows accordind way size; factor 0..100; 0=NOT AUTO; else percent of way size(CLO)", "map"], + ["autobridge",1,"automatically draw bridges and tunnels (CLO)", "map"], + ["noLabel",0,"", "map"], + ["place","","search for place name in osm file and create map (CLO)", "job"], + ["placefile","","name of file containing only place information (CLO)", "job"], + ["lonrad",2,"radius lon in km for place map (CLO)", "job"], + ["latrad",2,"radius lat in km for place map (CLO)", "job"], + ["ruler",0,"draw ruler; positions 1..4 (CLO)", "map"], + ["rulercolor","black","color of ruler (CLO)", "map"], + ["rulerbackground","none","background of ruler, none=transparent (CLO)", "map"], + ["scale",0,"draw scale; positions 1..4 (CLO)", "map"], + ["scalecolor","black","color of scale (CLO)", "map"], + ["scalebackground","none","color of scale background; none=transparent (CLO)", "map"], + ["scaleset",0,"set scale of map (i.e. 10000) (CLO)", "map"], + ["rulescaleset",0,"set assumed scale for rules (CLO)", "map"], + ["routelabelcolor","black","", "routes"], + ["routelabelsize",20,"", "routes"], + ["routelabelfontfamily","sans-serif","font-family for route labels", "routes"], + ["routelabelfont","","DON'T USE", "routes"], + ["routelabeloffset",20,"", "routes"], + ["routeicondist",70,"", "routes"], + ["routeiconscale",1,"", "routes"], + ["routeicondir","./routeicons","", "routes"], + ["poifile","","name of external POI file (CLO)", "job"], + ["relid",0,"relation ID for hikingbook (CLO)", "misc"], + ["rectangles","","draw rectangles for hikingbook (CLO)", "misc"], + ["pagenumbers","","add page numbers to map (CLO)", "misc"], + ["ra",0,"relation analyzer mode (CLO)", "misc"], + ["multionly",0,"draw only multipolygons (CLO)", "misc"], + ["test",0,"test feature (CLO)", "misc"], + ["foot","mapweaver by gary68 - data by www.openstreetmap.org","text for footer (CLO)", "map"], + ["footcolor","black","color for footer (CLO)", "map"], + ["footbackground","none","background color for footer (CLO)", "map"], + ["footsize",40,"font size for footer (CLO)", "map"], + ["head","","text for header (CLO)", "map"], + ["headcolor","black","color for header (CLO)", "map"], + ["headbackground","none","background color for header (CLO)", "map"], + ["headsize",40,"font size for header (CLO)", "map"], + + ["wns",0,"substitute unfitting way names by numbers; 0..4 1..4=positions in map; 5=file (CLO)", "map"], + ["wnssize",20,"size of labels in wns legend", "map"], + ["wnscolor","black","color of labels in wns legend", "map"], + ["wnsbgcolor","white","color of background of wns legend", "map"], + ["wnsunique",0,"wns will label each way only once (CLO)", "map"], + + ["minAreaSize",400,"min size of area to be drawn on map", "map"], + ["minAreaLabelSize",10000,"min size of area to be labeled on map", "map"], + ["oceanColor","lightblue","color of ocean (CLO)", "map"], + ["cIE",0,"osmosis clipIncompleteEntities instead of completeObjects (CLP)", "map"] + + ) ; + +my %cv = () ; +my %explanation = () ; + +# -------------------------------------------------------------------------------- + +sub initConfig { + + # set initial values according to program internal values from array @initial + + foreach my $kv (@initial) { + $cv{ lc( $kv->[0] ) } = $kv->[1] ; + $explanation{ lc( $kv->[0] ) } = $kv->[2] ; + } +} + + +sub setConfigValue { + + # allows any module to change a certain k/v pair + + my ($k, $v) = @_ ; + + $k = lc ( $k ) ; + $cv{$k} = $v ; + if ($cv{"verbose"} > 1) { print "config key $k. value changed to $v\n" ; } +} + +sub cv { + + # access a value by key + + my $k = shift ; + + $k = lc ( $k ) ; + if ( ! defined $cv{ $k } ) { print "WARNING: requested config key $k not defined!\n" ; } + return ( $cv{ $k } ) ; +} + +sub printConfig { + + # print actual config to stdout + + print "\nActual configuration\n" ; + + my %cats = () ; + foreach my $e (@initial) { + $cats{ $e->[3] } = 1 ; + } + + foreach my $cat (sort keys %cats) { + my @entries = () ; + foreach my $e (@initial) { + if ($e->[3] eq $cat) { + push @entries, $e->[0] ; + } + } + print "\nCATEGORY $cat\n" ; + print "--------\n" ; + foreach my $e ( sort { $a cmp $b } @entries ) { + printf "%-30s %-30s\n", $e, cv($e) ; + } + } + + print "\n" ; +} + +sub readConfigFile { + + # read ini file; initial k/v pairs might be changed + + my $fileName = shift ; + my $lc = 0 ; + + print "reading config file $fileName\n" ; + + open (my $file, "<", $fileName) or die ("ERROR: could not open ini file $fileName\n") ; + my $line = "" ; + while ($line = <$file>) { + $lc ++ ; + if ( ! grep /^#/, $line) { + my ($k, $v) = ( $line =~ /(.+?)=(.*)/ ) ; + if ( ( ! defined $k ) or ( ! defined $v ) ) { + print "WARNING: could not parse config line: $line" ; + } + else { + $k = lc ( $k ) ; + $cv{ $k } = $v ; + } + } + } + close ($file) ; + print "$lc lines read.\n\n" ; +} + + +# --------------------------------------------------------------------------------------- + +sub getProgramOptions { + + +my $optResult = GetOptions ( "in=s" => \$cv{'in'}, # the in file, mandatory + "overpass" => \$cv{'overpass'}, + "near:s" => \$cv{'near'}, + "overpassdistance:i" => \$cv{'overpassdistance'}, + "overpassserver:s" => \$cv{'overpassserver'}, + "gpx:s" => \$cv{'gpx'}, + "gpxcolor:s" => \$cv{'gpxcolor'}, + "gpxsize:i" => \$cv{'gpxsize'}, + "ini:s" => \$cv{'ini'}, + "style=s" => \$cv{'style'}, # the style file, mandatory + "out:s" => \$cv{'out'}, # outfile name or default + "srtm:s" => \$cv{'srtm'}, # srtm file name + "size:i" => \$cv{'size'}, # specifies pic size longitude in pixels + "maxtargetsize:s" => \$cv{'maxtargetsize'}, # specifies pic size in cm + "legend:i" => \$cv{'legend'}, # legend? + "bgcolor:s" => \$cv{'bgcolor'}, # background color + "oceancolor:s" => \$cv{'oceancolor'}, # ocean color + "grid:i" => \$cv{'grid'}, # specifies grid, number of parts + "gridcolor:s" => \$cv{'gridcolor'}, # color used for grid and labels + "coords" => \$cv{'coords'}, # + "coordsexp:i" => \$cv{'coordsexp'}, # + "coordscolor:s" => \$cv{'coordscolor'}, # + "clip:i" => \$cv{'clip'}, # specifies how many percent data to clip on each side + "clipbbox:s" => \$cv{'clipbbox'}, # bbox data for clipping map out of data + "pad:i" => \$cv{'pad'}, # specifies how many percent data to pad on each side + "ppc:f" => \$cv{'ppc'}, # pixels needed per label char in font size 10 + "pdf" => \$cv{'pdf'}, # specifies if pdf will be created + "png" => \$cv{'png'}, # specifies if png will be created + "pngdpi:i" => \$cv{'pngdpi'}, # specifies png resolution + "dir" => \$cv{'dir'}, # specifies if directory of streets will be created + "dirprg:s" => \$cv{'dirprg'}, # + "poi" => \$cv{'poi'}, # specifies if directory of pois will be created + "dirpdf" => \$cv{'dirpdf'}, + "dircolnum:i" => \$cv{'dircolnum'}, + "dirtitle:s" => \$cv{'dirtitle'}, + "tagstat" => \$cv{'tagstat'}, # lists k/v used in osm file + "declutter" => \$cv{'declutter'}, + "allowiconmove" => \$cv{'allowiconmove'}, + "help" => \$cv{'help'}, # + "wns:i" => \$cv{'wns'}, # + "wnsunique" => \$cv{'wnsunique'}, # + "oneways" => \$cv{'oneways'}, + "onewaycolor:s" => \$cv{'onewaycolor'}, + "onewaysize:i" => \$cv{'onewaysize'}, + "onewayautosize:i" => \$cv{'onewayautosize'}, + "autobridge:i" => \$cv{'autobridge'}, + "nolabel" => \$cv{'nolabel'}, + "ignorelabels" => \$cv{'ignorelabels'}, + "place:s" => \$cv{'place'}, # place to draw + "placefile:s" => \$cv{'placefile'}, # file to look for places + "lonrad:f" => \$cv{'lonrad'}, + "latrad:f" => \$cv{'latrad'}, + "ruler:i" => \$cv{'ruler'}, + "rulercolor:s" => \$cv{'rulercolor'}, + "rulerbackground:s" => \$cv{'rulerbackground'}, + "scale:i" => \$cv{'scale'}, + "scalecolor:s" => \$cv{'scalecolor'}, + "scalebackground:s" => \$cv{'scalebackground'}, + "scaleset:i" => \$cv{'scaleset'}, + "rulescaleset:i" => \$cv{'rulescaleset'}, + "routelabelcolor:s" => \$cv{'routelabelcolor'}, + "routelabelsize:i" => \$cv{'routelabelsize'}, + "routelabelfont:s" => \$cv{'routelabelfont'}, + "routelabeloffset:i" => \$cv{'routelabeloffset'}, + "routeicondist:i" => \$cv{'routeicondist'}, + "routeiconscale:f" => \$cv{'routeiconscale'}, + "icondir:s" => \$cv{'icondir'}, + "foot:s" => \$cv{'foot'}, + "footcolor:s" => \$cv{'footcolor'}, + "footbackground:s" => \$cv{'footbackground'}, + "footsize:i" => \$cv{'footsize'}, + "head:s" => \$cv{'head'}, + "headcolor:s" => \$cv{'headcolor'}, + "headbackground:s" => \$cv{'headbackground'}, + "headsize:i" => \$cv{'headsize'}, + "poifile:s" => \$cv{'poifile'}, + "relid:i" => \$cv{'relid'}, + "rectangles:s" => \$cv{'rectangles'}, + "pagenumbers:s" => \$cv{'pagenumbers'}, + "multionly" => \$cv{'multionly'}, # draw only areas from multipolygons + "ra:s" => \$cv{'ra'}, # + "debug" => \$cv{'debug'}, # turns debug messages on + "cie" => \$cv{'cie'}, # turns debug messages on + "verbose" => \$cv{'verbose'}, # turns twitter on + "test" => \$cv{'test'} ) ; # test + + +} + +sub printConfigDescriptions { + + my @texts = @initial ; + + @texts = sort {$a->[0] cmp $b->[0]} @texts ; + + print "\nconfig value descriptions\n\n" ; + printf "%-25s %-50s %-20s\n" , "key" , "description", "default" ; + foreach my $t (@texts) { + my $def = $t->[1] ; + if ($def eq "") { $def = "" ; } + printf "%-25s %-50s %-20s\n" , $t->[0] , $t->[2], $def ; + } + print "\n" ; +} + + +1 ; + + diff --git a/mwDir.pl b/mwDir.pl new file mode 100755 index 0000000..a141104 --- /dev/null +++ b/mwDir.pl @@ -0,0 +1,119 @@ + +use strict ; +use warnings ; + +use OSM::osm 8.3 ; + +my $version = "1.00" ; + +my $streetFileName ; +my $poiFileName ; +my $pdfFileName ; +my $texFileName ; +my $titleText ; +my $numColumns ; + +my $streetFile ; +my $poiFile ; +my $texFile ; + + +($streetFileName, $poiFileName, $titleText, $pdfFileName, $numColumns) = @ARGV ; + + +print "mwDir.pl: $streetFileName, $poiFileName, $titleText, $pdfFileName, $numColumns\n" ; + +$texFileName = $pdfFileName ; +$texFileName =~ s/.pdf/.tex/ ; + +open ($texFile, ">", $texFileName) or die ("can't open tex output file") ; +print $texFile "\\documentclass[a4paper,12pt]{book}\n" ; +print $texFile "\\usepackage{multicol}\n" ; +print $texFile "\\usepackage[utf8]{inputenc}\n" ; +print $texFile "\\usepackage[top=2.5cm,bottom=2cm,left=3cm,right=2cm]{geometry}\n" ; +print $texFile "\\columnsep7mm\n" ; +print $texFile "\\begin{document}\n" ; +print $texFile "\\section*{$titleText}\n" ; +print $texFile "\n" ; + +print $texFile "\\tiny\n" ; +print $texFile "Data CC-BY-SA www.openstreetmap.org\n" ; +print $texFile "\\normalsize\n\n" ; + +# streets +if ($streetFileName ne "none") { + my $result = open ($streetFile, "<", $streetFileName) ; + if ($result) { + my $line ; + print $texFile "\\begin{multicols}{$numColumns}[\\subsubsection*{Streets}]\n" ; + print $texFile "\\tiny\n" ; + while ($line = <$streetFile>) { + $line = convertToLatex ($line) ; + my (@entry) = split /\t/, $line ; + print $texFile $entry[0] ; + print $texFile " \\dotfill " ; + print $texFile $entry[1], " \\\\\n" ; + } + close ($streetFile) ; + print $texFile "\\normalsize\n" ; + print $texFile "\\end{multicols}\n" ; + } + else { + print "WARNING: street file $streetFile could not be opened." ; + } +} + + + + +# POIs +if ($poiFileName ne "none") { + my $result = open ($poiFile, "<", $poiFileName) ; + if ($result) { + my $line ; + print $texFile "\\begin{multicols}{$numColumns}[\\subsubsection*{Points of interest}]\n" ; + print $texFile "\\tiny\n" ; + while ($line = <$poiFile>) { + $line = convertToLatex ($line) ; + my @entry = split /\t/, $line ; + print $texFile $entry[0] ; + print $texFile " \\dotfill " ; + print $texFile $entry[1], "\\\\\n" ; + } + close ($poiFile) ; + print $texFile "\\normalsize\n" ; + print $texFile "\\end{multicols}\n" ; + } + else { + print "WARNING: POI file $poiFile could not be opened." ; + } +} + + + + +print $texFile "\\end{document}\n" ; +close ($texFile) ; +print "directory tex file created.\n" ; + + +my $dviFileName = $pdfFileName ; +$dviFileName =~ s/.pdf/.dvi/ ; +my $psFileName = $pdfFileName ; +$psFileName =~ s/.pdf/.ps/ ; + + +`latex $texFileName` ; +print "directory dvi file created.\n" ; +`dvips -D600 $dviFileName -o` ; +print "directory ps file created.\n" ; +`ps2pdf $psFileName $pdfFileName` ; +print "directory pdf file created.\n" ; +`rm *.dvi` ; +`rm *.tex` ; +`rm *.ps` ; +`rm *.aux` ; +`rm *.log` ; +print "directory FINISHED.\n" ; + + diff --git a/mwFile.pm b/mwFile.pm new file mode 100644 index 0000000..23bc85a --- /dev/null +++ b/mwFile.pm @@ -0,0 +1,434 @@ +# +# 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 mwFile ; + +use strict ; +use warnings ; + +use mwConfig ; +use mwMap ; +use mwLabel ; +use LWP::Simple ; + +use OSM::osm ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( readFile + getNodePointers + getWayPointers + getRelationPointers + ) ; + +my %lon ; +my %lat ; +my %memNodeTags ; + +my %memWayNodes ; +my %memWayTags ; + +my %memRelationMembers ; +my %memRelationTags ; + +my $overpassSource0 = "interpreter?data=node%5B%22name%22%3D%22NAME%22%5D%3Bout%20body%3B%0A" ; +my $overpassSource1 = "interpreter?data=node%5B%22name%22%3D%22NEAR%22%5D%3Bnode%28around%3ADIST%29%5B%22name%22%3D%22NAME%22%5D%3Bout%3B" ; +my $overpassSource3 = "interpreter?data=%28node%28BOTTOM%2CLEFT%2CTOP%2CRIGHT%29%3B%3C%3B%3E%3B%29%3Bout%20meta%3B" ; + + +sub readFile { + + my ($nodeId, $nodeLon, $nodeLat, $nodeUser, $aRef1, @nodeTags) ; + my ($wayId, $wayUser, $aRef2, @wayTags, @wayNodes) ; + my ($relationId, $relationUser, @relationTags, @relationMembers) ; + my %invalidWays ; + + + my $osmName ; + if (defined cv('in')) { $osmName = cv('in') ; } + + + my $clipbbox = "" ; + if (defined cv('clipbbox')) { $clipbbox = cv('clipbbox') ; } + + if ( cv('overpass') eq "1" ) { + if ( cv('place') eq "" ) { die ("ERROR: option place not specified.\n") ; } + + my $overpassNear = cv('near') ; + my $overpassDistance = cv('overpassdistance') ; + my $overpassName = cv('place') ; + my $overpassUrl1 = cv('overpassserver') . $overpassSource1 ; + + if ( cv('near') eq "" ) { + $overpassUrl1 = cv('overpassserver') . $overpassSource0 ; + } + + $overpassUrl1 =~ s/NEAR/$overpassNear/ ; + $overpassUrl1 =~ s/DIST/$overpassDistance/ ; + $overpassUrl1 =~ s/NAME/$overpassName/ ; + + if ( cv('debug') eq "1" ) { print "Overpass Query1: $overpassUrl1 ...\n" ; } + print "Send Query 1 to overpass server..\n" ; + my $result1 = get ( $overpassUrl1 ) ; + if ( ! defined $result1 ) { die ("ERROR: bad overpass result!\n") ; } + + if ( cv('debug') eq "1" ) { print "\n$result1\n\n" ; } + + # get lon, lat + + my ($placeLon) = ( $result1 =~ /lon=\"([\d\.\-]+)/ ) ; + my ($placeLat) = ( $result1 =~ /lat=\"([\d\.\-]+)/ ) ; + + if ((! defined $placeLon) or (! defined $placeLat)) { die ("ERROR: lon/lat could not be obtained from 1st overpass result.\n") ; } + + print "place $overpassName found:\n" ; + print "lon= $placeLon\n" ; + print "lat= $placeLat\n" ; + + + # calc bbox + + my $overLeft = $placeLon - cv('lonrad')/(111.11 * cos ( $placeLat / 360 * 3.14 * 2 ) ) ; + my $overRight = $placeLon + cv('lonrad')/(111.11 * cos ( $placeLat / 360 * 3.14 * 2 ) ) ; + my $overTop = $placeLat + cv('latrad')/111.11 ; + my $overBottom = $placeLat - cv('latrad')/111.11 ; + + my $overpassUrl2 = cv('overpassserver') . $overpassSource3 ; + $overpassUrl2 =~ s/LEFT/$overLeft/ ; + $overpassUrl2 =~ s/RIGHT/$overRight/ ; + $overpassUrl2 =~ s/TOP/$overTop/ ; + $overpassUrl2 =~ s/BOTTOM/$overBottom/ ; + + + if ( cv('debug') eq "1" ) { print "Overpass Query2: $overpassUrl2\n" ; } + print "Send Query 2 to overpass server..\n" ; + my $result2 = get ( $overpassUrl2 ) ; + if ( ! defined $result2 ) { die ("ERROR: bad overpass result!\n") ; } + + # save + + + my $opFileName = "overpass.osm" ; + open (my $overFile, ">", $opFileName) ; + binmode($overFile, ":utf8") ; + print $overFile $result2 ; + close ( $overFile ) ; + + setConfigValue ('in', $opFileName) ; + $osmName = $opFileName ; + # setConfigValue ('place', '') ; + + $clipbbox = "$overLeft,$overBottom,$overRight,$overTop" ; + if ( cv('debug') eq "1" ) { print "clipbox: $clipbbox\n" ; } + } + + if ( grep /\.pbf/, $osmName ) { + my $newName = $osmName ; + $newName =~ s/\.pbf/\.osm/i ; + + # osmosis + print "call osmosis to convert pbf file to osm file.\n" ; + `osmosis --read-pbf $osmName --write-xml $newName` ; + + # change config + $osmName = $newName ; + setConfigValue ("in", $newName) ; + } + + + # -place given? look for place and call osmosis + + my $left ; + my $right ; + my $top ; + my $bottom ; + + my $placeFound = 0 ; my $placeLon ; my $placeLat ; + if ( ( cv('place') ne "") and (cv('overpass') ne "1" ) ) { + my ($placeId) = ( cv('place') =~ /([\d]+)/); + if (!defined $placeId) { $placeId = -999999999 ; } + print "looking for place...\n" ; + + my $placeFileName = "" ; + if ( cv('placeFile') ne "" ) { + $placeFileName = cv('placeFile') ; + } + else { + $placeFileName = cv('in') ; + } + + openOsmFile ($placeFileName) ; + ($nodeId, $nodeLon, $nodeLat, $nodeUser, $aRef1) = getNode2 () ; + if ($nodeId != -1) { + @nodeTags = @$aRef1 ; + } + my $place = cv ('place') ; + while ( ($nodeId != -1) and ($placeFound == 0) ) { + my $placeNode = 0 ; my $placeName = 0 ; + foreach my $tag ( @nodeTags ) { + if ($tag->[0] eq "place") { $placeNode = 1 ; } + if ( ($tag->[0] eq "name") and (grep /$place/i, $tag->[1]) ){ $placeName = 1 ; } + } + if ( (($placeNode == 1) and ($placeName == 1)) or ($placeId == $nodeId) ) { + $placeFound = 1 ; + $placeLon = $nodeLon ; + $placeLat = $nodeLat ; + } + ($nodeId, $nodeLon, $nodeLat, $nodeUser, $aRef1) = getNode2 () ; + if ($nodeId != -1) { + @nodeTags = @$aRef1 ; + } + } + + closeOsmFile() ; + + if ($placeFound == 1) { + print "place $place found at " ; + print "lon: $placeLon " ; + print "lat: $placeLat\n" ; + $left = $placeLon - cv('lonrad')/(111.11 * cos ( $placeLat / 360 * 3.14 * 2 ) ) ; + $right = $placeLon + cv('lonrad')/(111.11 * cos ( $placeLat / 360 * 3.14 * 2 ) ) ; + $top = $placeLat + cv('latrad')/111.11 ; + $bottom = $placeLat - cv('latrad')/111.11 ; + + print "call osmosis...\n" ; + + if ( cv('cie') eq "0" ) { + print "OSMOSIS STRING: --bounding-box completeWays=yes completeRelations=yes bottom=$bottom top=$top left=$left right=$right\n" ; + `osmosis --read-xml $osmName --bounding-box completeWays=yes completeRelations=yes bottom=$bottom top=$top left=$left right=$right --write-xml ./temp.osm` ; + } + else { + print "OSMOSIS STRING: --bounding-box clipIncompleteEntities=yes bottom=$bottom top=$top left=$left right=$right\n" ; + `osmosis --read-xml $osmName --bounding-box clipIncompleteEntities=yes bottom=$bottom top=$top left=$left right=$right --write-xml ./temp.osm` ; + } + + print "osmosis done.\n" ; + + $osmName = "./temp.osm" ; + $clipbbox = "$left,$bottom,$right,$top" ; + } + else { + print "ERROR: place $place not found.\n" ; + die() ; + } + } + + + my $srtmFileName = cv('srtm') ; + if ( $srtmFileName ne "" ) { + + my $cmdX = "osmosis --read-xml $osmName --rx file=\"$srtmFileName\" --bounding-box completeWays=yes completeRelations=yes bottom=$bottom top=$top left=$left right=$right --merge --write-xml file=\"./temp2.osm\"" ; + my $cmdP = "osmosis --read-xml $osmName --read-pbf file=\"$srtmFileName\" --bounding-box completeWays=yes completeRelations=yes bottom=$bottom top=$top left=$left right=$right --merge --write-xml file=\"./temp2.osm\"" ; + + my $cmd = "" ; + if (grep /\.pbf/, $srtmFileName) { + $cmd = $cmdP ; + } + else { + $cmd = $cmdX ; + } + + print "call osmosis to merge SRTM data...\n$cmd\n" ; + + `$cmd` ; + + $osmName = "temp2.osm" ; + } + + + # STORE DATA + my $nr = 0 ; my $wr = 0 ; my $rr = 0 ; + print "reading osm file...\n" ; + + openOsmFile ($osmName) ; + ($nodeId, $nodeLon, $nodeLat, $nodeUser, $aRef1) = getNode2 () ; + if ($nodeId != -1) { + @nodeTags = @$aRef1 ; + } + while ($nodeId != -1) { + $nr++ ; + $lon{$nodeId} = $nodeLon ; $lat{$nodeId} = $nodeLat ; + @{$memNodeTags{$nodeId}} = @nodeTags ; + + ($nodeId, $nodeLon, $nodeLat, $nodeUser, $aRef1) = getNode2 () ; + if ($nodeId != -1) { + @nodeTags = @$aRef1 ; + } + } + + ($wayId, $wayUser, $aRef1, $aRef2) = getWay2 () ; + if ($wayId != -1) { + @wayNodes = @$aRef1 ; + @wayTags = @$aRef2 ; + } + while ($wayId != -1) { + $wr++ ; + if (scalar (@wayNodes) > 1) { + @{$memWayTags{$wayId}} = @wayTags ; + @{$memWayNodes{$wayId}} = @wayNodes ; + foreach my $node (@wayNodes) { + if (!defined $lon{$node}) { + print " ERROR: way $wayId references node $node, which is not present!\n" ; + } + } + } + else { + $invalidWays{$wayId} = 1 ; + } + + ($wayId, $wayUser, $aRef1, $aRef2) = getWay2 () ; + if ($wayId != -1) { + @wayNodes = @$aRef1 ; + @wayTags = @$aRef2 ; + } + } + + + ($relationId, $relationUser, $aRef1, $aRef2) = getRelation () ; + if ($relationId != -1) { + @relationMembers = @$aRef1 ; + @relationTags = @$aRef2 ; + } + + while ($relationId != -1) { + $rr++ ; + @{$memRelationTags{$relationId}} = @relationTags ; + @{$memRelationMembers{$relationId}} = @relationMembers ; + + foreach my $member (@relationMembers) { + if ( ($member->[0] eq "node") and (!defined $lon{$member->[1]}) ) { + print " ERROR: relation $relationId references node $member->[1] which is not present!\n" ; + } + if ( ($member->[0] eq "way") and (!defined $memWayNodes{$member->[1]} ) and (!defined $invalidWays{$member->[1]}) ) { + print " ERROR: relation $relationId references way $member->[1] which is not present or invalid!\n" ; + } + } + + #next + ($relationId, $relationUser, $aRef1, $aRef2) = getRelation () ; + if ($relationId != -1) { + @relationMembers = @$aRef1 ; + @relationTags = @$aRef2 ; + } + } + + closeOsmFile () ; + + print "read: $nr nodes, $wr ways and $rr relations.\n\n" ; + + # calc area of pic and init graphics + my $lonMin = 999 ; my $lonMax = -999 ; my $latMin = 999 ; my $latMax = -999 ; + foreach my $key (keys %lon) { + if ($lon{$key} > $lonMax) { $lonMax = $lon{$key} ; } + if ($lon{$key} < $lonMin) { $lonMin = $lon{$key} ; } + if ($lat{$key} > $latMax) { $latMax = $lat{$key} ; } + if ($lat{$key} < $latMin) { $latMin = $lat{$key} ; } + } + + # clip picture if desired + if ($clipbbox ne "") { + my ($bbLeft, $bbBottom, $bbRight, $bbTop) = ($clipbbox =~ /([\d\-\.]+),([\d\-\.]+),([\d\-\.]+),([\d\-\.]+)/ ) ; + # print "$bbLeft, $bbBottom, $bbRight, $bbTop\n" ; + if (($bbLeft > $lonMax) or ($bbLeft < $lonMin)) { print "WARNING -clipbox left parameter outside data.\n" ; } + if (($bbRight > $lonMax) or ($bbRight < $lonMin)) { print "WARNING -clipbox right parameter outside data.\n" ; } + if (($bbBottom > $latMax) or ($bbBottom < $latMin)) { print "WARNING -clipbox bottom parameter outside data.\n" ; } + if (($bbTop > $latMax) or ($bbTop < $latMin)) { print "WARNING -clipbox top parameter outside data.\n" ; } + $lonMin = $bbLeft ; + $lonMax = $bbRight ; + $latMin = $bbBottom ; + $latMax = $bbTop ; + } + else { + if (defined cv('clip')) { + if ( (cv('clip') > 0) and (cv('clip') < 100) ) { + my $clip = cv('clip') ; + $clip = $clip / 100 ; + $lonMin += ($lonMax-$lonMin) * $clip ; + $lonMax -= ($lonMax-$lonMin) * $clip ; + $latMin += ($latMax-$latMin) * $clip ; + $latMax -= ($latMax-$latMin) * $clip ; + } + } + } + + # pad picture if desired + if (defined cv('pad')) { + my $pad = cv('pad') ; + if ( ($pad > 0) and ($pad < 100) ) { + $pad = $pad / 100 ; + $lonMin -= ($lonMax-$lonMin) * $pad ; + $lonMax += ($lonMax-$lonMin) * $pad ; + $latMin -= ($latMax-$latMin) * $pad ; + $latMax += ($latMax-$latMin) * $pad ; + } + } + + my $size = cv('size') ; + + # calc pic size + + if ( cv('scaleSet') != 0 ) { + my $dist = distance ($lonMin, $latMin, $lonMax, $latMin) ; + my $width = $dist / cv('scaleSet') * 1000 * 100 / 2.54 ; # inches + $size = int ($width * 300) ; + } + + if ( cv('maxTargetSize') ne "" ) { + my @a = split /,/, cv('maxTargetSize') ; + my $targetWidth = $a[0] ; + my $targetHeight = $a[1] ; + # print "TS: $targetWidth, $targetHeight [cm]\n" ; + my $distLon = distance ($lonMin, $latMin, $lonMax, $latMin) ; + my $distLat = distance ($lonMin, $latMin, $lonMin, $latMax) ; + # print "TS: $distLon, $distLat [km]\n" ; + my $scaleLon = ($distLon * 1000 * 100) / $targetWidth ; + my $scaleLat = ($distLat * 1000 * 100) / $targetHeight ; + my $targetScale = int $scaleLon ; + if ( $scaleLat > $targetScale ) { $targetScale = int $scaleLat ; } + # print "TS: $targetScale [1:n]\n" ; + + my $width = $distLon / $targetScale * 1000 * 100 / 2.54 ; # inches + $size = int ($width * 300) ; + print "Map width now $size [px] due to maxTargetSize parameter\n" ; + } + + mwMap::initGraph ($size, $lonMin, $latMin, $lonMax, $latMax) ; + +} + +sub getNodePointers { + return ( \%lon, \%lat, \%memNodeTags) ; +} + +sub getWayPointers { + return ( \%memWayNodes, \%memWayTags) ; +} + +sub getRelationPointers { + + return ( \%memRelationMembers, \%memRelationTags) ; +} + + +1 ; + + diff --git a/mwGPX.pm b/mwGPX.pm new file mode 100755 index 0000000..183bfd2 --- /dev/null +++ b/mwGPX.pm @@ -0,0 +1,123 @@ +# +# 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 mwGPX ; + +use strict ; +use warnings ; + +use OSM::gpx ; + +use mwConfig ; +use mwMap ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( processGPXFile + ) ; + + + +sub processGPXFile { + + my ($ref1, $ref2, $ref3) = readGPXFile ( cv('gpx') ) ; + + my %wptHash = %$ref1 ; + my %rteHash = %$ref2 ; + my %trkHash = %$ref3 ; + + my $size = cv('gpxsize') ; + my $color = cv('gpxcolor') ; + + foreach my $wptNr ( sort { $a <=> $b } keys %wptHash) { + # print "WPT $wptNr: $wptHash{$wptNr}{'lon'} $wptHash{$wptNr}{'lat'}\n" ; + if (defined $wptHash{$wptNr}{'name'}) { + # print " name: $wptHash{$wptNr}{'name'}\n" ; + } + if (defined $wptHash{$wptNr}{'ele'}) { + # print " ele: $wptHash{$wptNr}{'ele'}\n" ; + } + + + my $svgString = "fill=\"$color\" stroke=\"none\" " ; + my $lon = $wptHash{$wptNr}{'lon'} ; + my $lat = $wptHash{$wptNr}{'lat'} ; + drawCircle ($lon, $lat, 1, 3*$size, 0, $svgString, 'gpx') ; + + } + + foreach my $rteNr ( sort { $a <=> $b } keys %rteHash) { + # print "RTE $rteNr\n" ; + + my @coords = () ; + + foreach my $rteWptNr ( sort { $a <=> $b } keys %{$rteHash{$rteNr}}) { + # print " wpt $rteWptNr: $rteHash{$rteNr}{$rteWptNr}{'lon'} $rteHash{$rteNr}{$rteWptNr}{'lat'}\n" ; + + my $svgString = "fill=\"$color\" stroke=\"none\" " ; + my $lon = $rteHash{$rteNr}{$rteWptNr}{'lon'} ; + my $lat = $rteHash{$rteNr}{$rteWptNr}{'lat'} ; + drawCircle ($lon, $lat, 1, 2*$size, 0, $svgString, 'gpx') ; + + my ($x, $y) = convert ($lon, $lat) ; + push @coords, $x, $y ; + } + + my $svgString = "" ; + + my $lc = "round" ; + my $lj = "round" ; + + $svgString = "stroke=\"$color\" stroke-width=\"$size\" stroke-linecap=\"$lc\" fill=\"none\" stroke-linejoin=\"$lj\" " ; + + drawWay (\@coords, 0, $svgString, "gpx", undef) ; + } + + foreach my $trkNr ( sort { $a <=> $b } keys %trkHash) { + # print "TRK $trkNr\n" ; + my %seg ; + %seg = %{ $trkHash{$trkNr} } ; + + foreach my $segNr ( sort {$a <=> $b} keys %seg) { + # print " SEG $segNr\n" ; + my %points ; + %points = %{ $seg{$segNr}} ; + foreach my $ptNr ( sort { $a <=> $b } keys %points) { + # print " trkpt $ptNr: $points{$ptNr}{'lon'} $points{$ptNr}{'lat'}\n" ; + + my $svgString = "fill=\"$color\" stroke=\"none\" " ; + my $lon = $points{$ptNr}{'lon'} ; + my $lat = $points{$ptNr}{'lat'} ; + drawCircle ($lon, $lat, 1, $size, 0, $svgString, 'gpx') ; + } + } + } + +} + + + + + +1 ; + + diff --git a/mwInteractive.pl b/mwInteractive.pl new file mode 100644 index 0000000..0373ce9 --- /dev/null +++ b/mwInteractive.pl @@ -0,0 +1,127 @@ + + + +use strict ; +use warnings ; + +# mwInteractive.pl + +my $sStyle = "mwStandardRules.txt" ; +my $tStyle = "mwTopoRules.txt" ; + +my $place = "" ; +my $near = "" ; +my $dist = 50000 ; +my $lonrad = 2 ; +my $latrad = 2 ; +my $scaleset = 10000 ; +my $png = 0 ; +my $pdf = 1 ; +my $outName = "" ; +my $style = "" ; + +print "Mapweaver interactive\n\n" ; + +while ($place eq "") { + print "Please enter exact place name:\n" ; + $place = ; + print "\n" ; + chomp $place ; +} + +# --- + +print "Please enter exact place name of bigger city i.e. in vicinity:\n" ; +$near = ; +print "\n" ; +chomp $near ; + +# --- + +print "Please enter radius in m for vicinity search (defaults to 50.000):\n" ; +$dist = ; +print "\n" ; +chomp $dist ; + +if ($dist eq "") { $dist = 50000 ; } + +# --- + +print "Please enter radius in km for latitude (defaults to 2km):\n" ; +$latrad = ; +print "\n" ; +chomp $latrad ; +if ($latrad eq "") { $latrad=2 ; } + +# --- + +print "Please enter radius in km for longitude (defaults to 2km):\n" ; +$lonrad = ; +print "\n" ; +chomp $lonrad ; +if ($lonrad eq "") { $lonrad=2 ; } + +# --- + +print "Please enter scale of map (i.e. 10000 for 1:10.000):\n" ; +$scaleset = ; +print "\n" ; +chomp $scaleset ; +if ($scaleset eq "") { $scaleset = 10000 ; } + +# --- + +print "Output map in PDF format yes/no (defaults to yes):\n" ; +$pdf = ; +print "\n" ; +chomp $pdf ; +if (($pdf eq "") or (lc $pdf eq "yes")) { $pdf = 1 ; } + +# --- + +print "Output map in PNG format yes/no (defaults to no):\n" ; +$png = ; +print "\n" ; +chomp $png ; +if (($png eq "") or (lc $png eq "no")) { $png = 0 ; } +if (lc $png eq "yes") { $png = 1 ; } + +# --- + +$outName = $place . ".svg" ; + +print "Output name (defaults to $outName):\n" ; +$outName = ; +print "\n" ; +chomp $outName ; + +if ($outName eq "") { $outName = $place . ".svg" ; } +if (! grep /\.svg$/, $outName) { $outName .= ".svg" ; } + +# --- + +print "Select map style from list:\n" ; +print "1 - standard rules (default)\n" ; +print "2 - topo rules\n" ; +$style = ; +print "\n" ; +chomp $style ; + +if ($style eq "2") { $style = "mwTopoRules.txt" ; } +else { $style = "mwStandardRules.txt" ; } + + + + +my $cmd = "perl mw.pl -place=\"$place\" -overpass -style=\"$style\" -out=\"$outName\" -scaleset=$scaleset " ; +if ($near ne "") { $cmd .= "-near=\"$near\" -overpassdistance=$dist " ; } +$cmd .= " -lonrad=$lonrad -latrad=$latrad " ; +if ($png eq "1") { $cmd .= " -png " ; } +if ($pdf eq "1") { $cmd .= " -pdf " ; } + +print "call mw.pl: $cmd\n" ; + +`$cmd` ; + + + diff --git a/mwLabel.pm b/mwLabel.pm new file mode 100644 index 0000000..7a1e6f4 --- /dev/null +++ b/mwLabel.pm @@ -0,0 +1,328 @@ +# +# 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 mwLabel ; + +use strict ; +use warnings ; + +use mwConfig ; +use mwMap ; +# use mwMisc ; +use mwOccupy ; + +my $labelPathId = 0 ; + +my @lines = () ; + +my $numIconsMoved = 0 ; +my $numLabels = 0 ; +my $numIcons = 0 ; +my $numLabelsOmitted = 0 ; +my $numLabelsMoved = 0 ; +my $numIconsOmitted = 0 ; + +my %poiHash = () ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( + placeLabelAndIcon + addToPoiHash + getPoiHash + ) ; + + + + +sub placeLabelAndIcon { +# +# intelligent icon and label placement alg. +# + my ($lon, $lat, $offset, $thickness, $text, $svgText, $icon, $iconSizeX, $iconSizeY, $layer) = @_ ; + + if (cv('debug') eq "1") { print "PLAI: $lon, $lat, $offset, $thickness, $text, $svgText, $icon, $iconSizeX, $iconSizeY, $layer\n" ; } + + my ($x, $y) = mwMap::convert ($lon, $lat) ; # center ! + + if ( ! coordsOut ($x, $y) ) { + + $y = $y + $offset ; + + my ($ref) = splitLabel ($text) ; + my (@lines) = @$ref ; + my $numLines = scalar @lines ; + my $maxTextLenPix = 0 ; + my $orientation = "" ; + my $lineDist = cv ('linedist') ; ; + my $tries = 0 ; + my $allowIconMove = cv ('allowiconmove') ; + + my ($textSize) = ( $svgText =~ /font-size=\"(\d+)\"/ ) ; + if ( ! defined $textSize ) { die ("ERROR: font size could not be determined from svg format string \"$svgText\"\n") ; } + + foreach my $line (@lines) { + my $len = length ($line) * cv('ppc') / 10 * $textSize ; # in pixels + if ($len > $maxTextLenPix) { $maxTextLenPix = $len ; } + } + my $spaceTextX = $maxTextLenPix ; + my $spaceTextY = $numLines * ($lineDist+$textSize) ; + + + if ($icon ne "none") { + $numIcons++ ; + # space for icon? + my $sizeX1 = $iconSizeX ; if ($sizeX1 == 0) { $sizeX1 = 20 ; } + my $sizeY1 = $iconSizeY ; if ($sizeY1 == 0) { $sizeY1 = 20 ; } + my $iconX = $x - $sizeX1/2 ; # top left corner + my $iconY = $y - $sizeY1/2 ; + + my @shifts = (0) ; + if ($allowIconMove eq "1") { + @shifts = ( 0, -15, 15 ) ; + } + my $posFound = 0 ; my $posCount = 0 ; + my ($iconAreaX1, $iconAreaY1, $iconAreaX2, $iconAreaY2) ; + LABAB: foreach my $xShift (@shifts) { + foreach my $yShift (@shifts) { + $posCount++ ; + if ( ( ! boxAreaOccupied ($iconX+$xShift, $iconY+$sizeY1+$yShift, $iconX+$sizeX1+$xShift, $iconY+$yShift) ) or ( cv('forcenodes') eq "1" ) ) { + placeIcon ($iconX+$xShift, $iconY+$yShift, $icon, $sizeX1, $sizeY1, "nodes") ; + $iconAreaX1 = $iconX+$xShift ; + $iconAreaY1 = $iconY+$sizeY1+$yShift ; + $iconAreaX2 = $iconX+$sizeX1+$xShift ; + $iconAreaY2 = $iconY+$yShift ; + + $posFound = 1 ; + if ($posCount > 1) { $numIconsMoved++ ; } + $iconX = $iconX + $xShift ; # for later use with label + $iconY = $iconY + $yShift ; + last LABAB ; + } + } + } + if ($posFound == 1) { + + # label text? + if ($text ne "") { + $numLabels++ ; + + + $sizeX1 += 1 ; $sizeY1 += 1 ; + + my ($x1, $x2, $y1, $y2) ; + # $x, $y centered + # yes, check if space for label, choose position, draw + # no, count omitted text + + my @positions = () ; my $positionFound = 0 ; + # pos 1 centered below + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $sizeY1/2 + $spaceTextY ; $y2 = $y + $sizeY1/2 ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # pos 2/3 to the right, bottom, top + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # pos 4 centered upon + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y - $sizeY1/2 - $spaceTextY ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # pos 5/6 to the right, below and upon + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x + $sizeX1/2 ; $x2 = $x + $sizeX1/2 + $spaceTextX ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # left normal, bottom, top + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y + $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y - $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + # left corners, bottom, top + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y2 = $y + $sizeY1/2 ; $y1 = $y2 + $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - $sizeX1/2 - $spaceTextX ; $x2 = $x - $sizeX1/2 ; $y1 = $y - $sizeY1/2 ; $y2 = $y1 - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + + $tries = 0 ; + LABB: foreach my $pos (@positions) { + $tries++ ; + + $positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], \@lines, $svgText, $layer) ; + + if ($positionFound == 1) { + last LABB ; + } + } + if ($positionFound == 0) { $numLabelsOmitted++ ; } + if ($tries > 1) { $numLabelsMoved++ ; } + } # label + + boxOccupyArea ($iconAreaX1, $iconAreaY1, $iconAreaX2, $iconAreaY2, 0, 2) ; + } # pos found + else { + # no, count omitted + $numIconsOmitted++ ; + } + } + else { # only text + my ($x1, $x2, $y1, $y2) ; + # x1, x2, y1, y2 + # left, right, bottom, top + # choose space for text, draw + # count omitted + + $numLabels++ ; + my @positions = () ; + $x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x + $thickness ; $x2 = $x + $thickness + $spaceTextX ; $y1 = $y + $spaceTextY ; $y2 = $y ; $orientation = "left" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + $x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - ($thickness + $spaceTextX) ; $x2 = $x - $thickness ; $y1 = $y ; $y2 = $y - $spaceTextY ; $orientation = "right" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y - $thickness ; $y2 = $y - ($thickness + $spaceTextY) ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + $x1 = $x - $spaceTextX/2 ; $x2 = $x + $spaceTextX/2 ; $y1 = $y + $thickness + $spaceTextY ; $y2 = $y + $thickness ; $orientation = "centered" ; + push @positions, [$x1, $x2, $y1, $y2, $orientation] ; + + my $positionFound = 0 ; + $tries = 0 ; + LABA: foreach my $pos (@positions) { + $tries++ ; + # print "$lines[0] $pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], $numLines\n" ; + + $positionFound = checkAndDrawText ($pos->[0], $pos->[1], $pos->[2], $pos->[3], $pos->[4], \@lines, $svgText, $layer) ; + + if ($positionFound == 1) { + last LABA ; + } + } + if ($positionFound == 0) { $numLabelsOmitted++ ; } + if ($tries > 1) { $numLabelsMoved++ ; } + } + } +} + + +sub checkAndDrawText { +# +# checks if area available and if so draws text +# + my ($x1, $x2, $y1, $y2, $orientation, $refLines, $svgText, $layer) = @_ ; + + if (cv('debug') eq "1") { print "CADT: $x1, $x2, $y1, $y2, $orientation, $refLines, $svgText, $layer\n" ; } + + my @lines = @$refLines ; + my $numLines = scalar @lines ; + my $lineDist = cv ('linedist') ; + + my ($size) = ( $svgText =~ /font-size=\"(\d+)\"/ ) ; + if ( ! defined $size ) { die ("ERROR: font size could not be determined from svg format string \"$svgText\"\n") ; } + + + # WATCH for variable sequence! + if ( + ( ! boxAreaOccupied ($x1, $y1, $x2, $y2) ) or + ( cv('forcenodes') eq "1" ) + ) { + + for (my $i=0; $i<=$#lines; $i++) { + + my @points = ($x1, $y2+($i+1)*($size+$lineDist), $x2, $y2+($i+1)*($size+$lineDist)) ; + my $pathName = "LabelPath" . $labelPathId ; + $labelPathId++ ; + createPath ($pathName, \@points, "definitions") ; + + if ($orientation eq "centered") { + pathText ($svgText, $lines[$i], $pathName, 0, "middle", 50, $layer) + } + if ($orientation eq "left") { + pathText ($svgText, $lines[$i], $pathName, 0, "start", 0, $layer) + } + if ($orientation eq "right") { + pathText ($svgText, $lines[$i], $pathName, 0, "end", 100, $layer) + } + } + + boxOccupyArea ($x1, $y1, $x2, $y2, 0, 2) ; + + return (1) ; + } + else { + return 0 ; + } +} + +sub splitLabel { +# +# split label text at space locations and then merge new parts if new part will be smaller than XX chars +# + my $text = shift ; + my @lines = split / /, $text ; + my $merged = 1 ; + while ($merged) { + $merged = 0 ; + LAB2: for (my $i=0; $i<$#lines; $i++) { + if (length ($lines[$i] . " " . $lines[$i+1]) <= cv ('maxcharperline') ) { + $lines[$i] = $lines[$i] . " " . $lines[$i+1] ; + splice (@lines, $i+1, 1) ; + $merged = 1 ; + last LAB2 ; + } + } + } + return (\@lines) ; +} + + + + +# ------------------------------------------------------------ + +sub addToPoiHash { + my ($name, $sq) = @_ ; + if (defined $sq) { + $poiHash{$name}{$sq} = 1 ; + } + else { + $poiHash{$name} = 1 ; + } +} + + +sub getPoiHash { + return \%poiHash ; +} + + +1 ; + + diff --git a/mwMap.pm b/mwMap.pm new file mode 100644 index 0000000..e083592 --- /dev/null +++ b/mwMap.pm @@ -0,0 +1,1241 @@ +# +# 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 mwMap ; + +use strict ; +use warnings ; + +use mwConfig ; +# use mwMisc ; +# use mwFile ; +# use mwLabel ; + +use OSM::osm ; + +use Geo::Proj4 ; + + +my $areaNum = 0 ; +my %areaDef = () ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( initGraph + coordsOut + drawCircle + drawSquare + drawTriangle + drawDiamond + drawRect + drawText + writeMap + drawWay + drawArea + fitsPaper + getScale + createPath + pathText + placeIcon + convert + gridSquare + getDimensions + initOneways + addOnewayArrows + addAreaIcon + createShield + getMaxShieldSize + getShieldSizes + getShieldId + addToLayer + createLegendFile + ) ; + + +my @belowWays = ("background", "base", "area", "multi") ; + +my @aboveWays = ( "arealabels", "wayLabels", "routes", "routeStops", "shields", "nodes", "icons", "text", "additional", "occupied") ; + +my @elements = ("scale", "ruler", "legend", "wns", "header", "footer", "rectangles", "title", "gpx") ; + +my %svgLayer = () ; +my %wayLayer = () ; + +my $shieldPathId = 0 ; +my %createdShields = () ; +my %shieldXSize = () ; +my %shieldYSize = () ; + +my $proj ; + +my ($bottom, $left, $right, $top) ; +my ($sizeX, $sizeY) ; +my ($projLeft, $projBottom, $projRight, $projTop) ; +my ($projSizeX, $projSizeY) ; + +sub initGraph { + + # function initializes the picture and projection + + my ($x, $l, $b, $r, $t) = @_ ; + + # my $l0 = int($l) - 1 ; + my $l0 = int(($r+$l) / 2 ) ; + + $proj = Geo::Proj4->new( + proj => cv('projection'), + ellps => cv('ellipsoid'), + lon_0 => $l0 + ) or die "parameter error: ".Geo::Proj4->error. "\n"; + + + ($projLeft, $projBottom) = $proj->forward($b, $l) ; # lat/lon!!! + ($projRight, $projTop) = $proj->forward($t, $r) ; # lat/lon!!! + + $projSizeX = $projRight - $projLeft ; + $projSizeY = $projTop - $projBottom ; + + my $factor = $projSizeY / $projSizeX ; + + $sizeX = int ($x) ; + $sizeY = int ($x * $factor) ; + + $top = $t ; + $left = $l ; + $right = $r ; + $bottom = $b ; + + if ( ( cv('bgcolor') ne "none" ) and ( cv('bgcolor') ne "" ) ) { + my $col = cv('bgcolor') ; + my $svgText = "fill=\"$col\" " ; + drawRect (0, 0, $sizeX, $sizeY, 0, $svgText, "background") ; + } + + if ( cv('ruler') ne "0" ) { + drawRuler() ; + } + + if ( cv('scale') ne "0" ) { + drawScale() ; + } + + if ( cv('grid') != 0) { + drawGrid() ; + } + if ( cv('coords') eq "1") { + drawCoords() ; + } + if ( length cv('foot') > 0 ) { + drawFoot() ; + } + if ( length cv('head') > 0 ) { + drawHead() ; + } + +} + +sub addToLayer { + my ($layer, $text) = @_ ; + + if ( $layer =~ /^[\d\-\.]+$/) { + push @{$wayLayer{$layer}}, $text ; + # print "adding NUMERIC: $text\n" ; + } + else { + push @{$svgLayer{$layer}}, $text ; + # print "adding TEXTUAL: $text\n" ; + } +} + +sub drawWay { + + # accepts list of nodes (plus convert=1) or list of x,y,x,y (convert=0) and draws way/polygon to layerNr if defined or to layerName + + my ($nodesRef, $convert, $svgString, $layerName, $layerNumber) = @_ ; + my @points = () ; + my $valid = 0 ; + + # convert? and expand. + my ($lonRef, $latRef, $tagRef) = mwFile::getNodePointers() ; + if ($convert) { + foreach my $node (@$nodesRef) { + my ($x, $y) = convert ( $$lonRef{$node}, $$latRef{$node}) ; + push @points, $x, $y ; + if (! coordsOut ($x, $y)) { + $valid = 1 ; + } + } + } + else { + @points = @$nodesRef ; + foreach my $node (@$nodesRef) { + my ($x, $y) = ( $$lonRef{$node}, $$latRef{$node}) ; + if (! coordsOut ($x, $y)) { + $valid = 1 ; + } + } + } + + my $refp = simplifyPoints (\@points) ; + @points = @$refp ; + + if ($valid) { + + my $svg = "" ; + + if (defined $layerNumber) { + push @{ $wayLayer{ $layerNumber } }, $svg ; + } + else { + push @{ $svgLayer { $layerName } }, $svg ; + } + } + else { + # if () { print "way not drawn, outside\n" ; } + } +} + + + +sub drawText { + + my ($x, $y, $convert, $text, $svgString, $layerName) = @_ ; + + if ($convert) { + ($x, $y) = convert ($x, $y) ; + } + + if ( ! coordsOut ($x, $y) ) { + + my $svg = "" . $text . "" ; + + push @{ $svgLayer { $layerName } }, $svg ; + } +} + + + + +sub drawCircle { + + # draws circle element to svgLayer given; if convertCoords then lon / lat is converted to x / y + # circleradius either in pixel or in meters (convert=1) + + my ($x, $y, $convertCoords, $radius, $convertRadius, $format, $layerName) = @_ ; + + if ($convertCoords) { + ($x, $y) = convert ($x, $y) ; + } + if ($convertRadius) { + $radius = $radius / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ; + } + + if ( ! coordsOut ($x, $y) ) { + + my $svg = "" ; + + push @{ $svgLayer { $layerName } }, $svg ; + } +} + +sub drawSquare { + + # draws square element to svgLayer given; if convertCoords then lon / lat is converted to x / y + # square size either in pixel or in meters (convert=1) + + my ($x, $y, $convertCoords, $size, $convertSize, $format, $layerName) = @_ ; + + if ($convertCoords) { + ($x, $y) = convert ($x, $y) ; + } + if ($convertSize) { + $size = $size / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ; + } + + my $x1 = $x - $size ; + my $y1 = $y - $size ; + my $dSize = 2 * $size ; + + if ( ! coordsOut ($x, $y) ) { + + my $svg = "" ; + + push @{ $svgLayer { $layerName } }, $svg ; + } +} + +sub drawTriangle { + + # draws triangle element to svgLayer given; if convertCoords then lon / lat is converted to x / y + # square size either in pixel or in meters (convert=1) + + my ($x, $y, $convertCoords, $size, $convertSize, $format, $layerName) = @_ ; + + if ($convertCoords) { + ($x, $y) = convert ($x, $y) ; + } + if ($convertSize) { + $size = $size / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ; + } + + my $h = int ( sqrt ($size * $size / 2) ) ; + + my $x1 = $x ; + # my $y1 = $y - $size ; + my $y1 = $y - $h ; + my $x2 = $x - $h ; + my $y2 = $y + $h ; + my $x3 = $x + $h ; + my $y3 = $y + $h ; + + if ( ! coordsOut ($x1, $y1, $x2, $y2, $x3, $y3) ) { + + my $svg = "" ; + + push @{ $svgLayer { $layerName } }, $svg ; + } +} + +sub drawDiamond { + + # draws diamond element to svgLayer given; if convertCoords then lon / lat is converted to x / y + # square size either in pixel or in meters (convert=1) + + my ($x, $y, $convertCoords, $size, $convertSize, $format, $layerName) = @_ ; + + if ($convertCoords) { + ($x, $y) = convert ($x, $y) ; + } + if ($convertSize) { + $size = $size / (1000 * distance ($left, $bottom, $right, $bottom) ) * $sizeX ; + } + + my $x1 = $x - $size ; # left + my $y1 = $y ; + my $x2 = $x ; # top + my $y2 = $y - $size ; + my $x3 = $x + $size ; #right + my $y3 = $y ; + my $x4 = $x ; # bottom + my $y4 = $y + $size ; + + if ( ! coordsOut ($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4) ) { + my $svg = "" ; + + push @{ $svgLayer { $layerName } }, $svg ; + } +} + +sub drawRect { + + # draws square element to svgLayer given; if convertCoords then lon / lat is converted to x / y + # square size either in pixel or in meters (convert=1) + + my ($x1, $y1, $x2, $y2, $convertCoords, $format, $layerName) = @_ ; + + if ($convertCoords) { + ($x1, $y1) = convert ($x1, $y1) ; + ($x2, $y2) = convert ($x2, $y2) ; + } + + my $sizeX = $x2 - $x1 ; + my $sizeY = $y2 - $y1 ; + + if ( ! coordsOut ($x1, $y1, $x2, $y2) ) { + my $svg = "" ; + + push @{ $svgLayer { $layerName } }, $svg ; + } +} + + +sub createPath { +# +# creates path element for later use with textPath +# + my ($pathName, $refp, $layer) = @_ ; + + my $refp2 = simplifyPoints ($refp) ; + my @points = @$refp2 ; + + my $svg = "\n" ; + + push @{ $svgLayer{ $layer } }, $svg ; +} + + +sub pathText { +# +# draws text to path element; alignment: start, middle, end +# + my ($svgText, $text, $pathName, $tSpan, $alignment, $offset, $layer) = @_ ; + + my $svg = "\n" ; + $svg = $svg . "\n" ; + $svg = $svg . "" . $text . " \n" ; + $svg = $svg . "\n\n" ; + + push @{ $svgLayer{ $layer } }, $svg ; +} + + +sub placeIcon { +# +# create SVG text for icons +# + + + my ($x, $y, $icon, $sizeX, $sizeY, $layer) = @_ ; + + if ( ! coordsOut ($x, $y) ) { + + my ($out) = " 0) { $out .= " width=\"" . $sizeX . "\"" ; } + if ($sizeY > 0) { $out .= " height=\"" . $sizeY . "\"" ; } + $out .= " xlink:href=\"" . $icon . "\" />" ; + + push @{ $svgLayer{ $layer } }, $out ; + } +} + + +sub drawArea { +# +# draws mp in svg ARRAY of ARRAY of nodes/coordinates +# + my ($svgText, $icon, $ref, $convert, $layer) = @_ ; + my @ways = @$ref ; + my $i ; + my $svg = "" ; + my $valid = 1 ; + my @newArray = () ; + + # TODO loop converts original data !!! + + if ($convert) { + my ($lonRef, $latRef, $tagRef) = mwFile::getNodePointers () ; + foreach my $aRef (@ways) { + my @way = @$aRef ; + my @newCoords = () ; + foreach my $n (@way) { + my ($x, $y) = convert ($$lonRef{$n}, $$latRef{$n}) ; + push @newCoords, $x, $y ; + if (coordsOut ($x, $y)) { + $valid = 0 ; + } + } + push @newArray , [@newCoords] ; + } + @ways = @newArray ; + } + + if (defined $areaDef{$icon}) { + $svg = "" ; + + if ($valid) { + push @{ $svgLayer{ $layer } }, $svg ; + } +} + + + +# --------------------------------------------------------------------- + +sub writeMap { + + my $fileName = cv ('out') ; + + open (my $file, ">", $fileName) || die "can't open svg output file $fileName\n"; + + print $file "\n" ; + print $file "\n" ; + + my $w = $sizeX / 300 * 2.54 ; # cm + my $h = $sizeY / 300 * 2.54 ; + + my ($svg) = "\n" ; + print $file $svg ; + + # definitions + if ( defined @{$svgLayer{'definitions'}} ) { + print $file "\n" ; + foreach ( @{$svgLayer{'definitions'}} ) { print $file $_, "\n" ; } + print $file "\n" ; + } + + # below ways + foreach my $layer (@belowWays) { + if ( defined @{$svgLayer{$layer}} ) { + print $file "\n" ; + foreach ( @{$svgLayer{$layer}} ) { print $file $_, "\n" ; } + print $file "\n" ; + } + } + + # ways + foreach my $layer (sort {$a <=> $b} keys %wayLayer) { + if ( defined @{$wayLayer{$layer}} ) { + print $file "\n" ; + foreach ( @{$wayLayer{$layer}} ) { print $file $_, "\n" ; } + print $file "\n" ; + } + } + + + # above of ways + foreach my $layer (@aboveWays) { + if ( defined @{$svgLayer{$layer}} ) { + print $file "\n" ; + foreach ( @{$svgLayer{$layer}} ) { print $file $_, "\n" ; } + print $file "\n" ; + } + } + + + foreach my $layer (@elements) { + if (defined @{$svgLayer{$layer}}) { + print $file "\n" ; + foreach ( @{$svgLayer{$layer}} ) { print $file $_, "\n" ; } + print $file "\n" ; + } + } + + + print $file "\n" ; + + close ($file) ; + + if (cv('pdf') eq "1") { + my ($pdfName) = $fileName ; + $pdfName =~ s/\.svg/\.pdf/ ; + print "creating pdf file $pdfName ...\n" ; + `inkscape -A $pdfName $fileName` ; + } + + if (cv('png') eq "1") { + my ($pngName) = $fileName ; + $pngName =~ s/\.svg/\.png/ ; + my $dpi = cv('pngdpi') ; + print "creating png file $pngName ($dpi dpi)...\n" ; + `inkscape --export-dpi=$dpi -e $pngName $fileName` ; + } + + + +} + +# ----------------------------------------------------------------------------------- + +sub drawGrid { +# +# draw grid on top of map. receives number of parts in x/lon direction +# + + my $number = cv ('grid') ; + my $color = cv ('gridcolor') ; + + my $part = $sizeX / $number ; + my $numY = $sizeY / $part ; + + my $svgStringLine="stroke=\"$color\" stroke-width=\"5\" stroke-dasharray=\"30,30\"" ; + + my $svgStringText = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, 60, $color, undef, undef) ; + + # vertical lines + for (my $i = 1; $i <= $number; $i++) { + my @coords = ($i*$part, 0, $i*$part, $sizeY) ; + drawWay (\@coords, 0, $svgStringLine, "additional", undef) ; + drawText ( ($i-1)*$part+$part/2, 160, 0, chr($i+64), $svgStringText, "additional") ; + + } + + # hor. lines + for (my $i = 1; $i <= $numY; $i++) { + my @coords = (0, $i*$part, $sizeX, $i*$part) ; + drawWay (\@coords, 0, $svgStringLine, "additional", undef) ; + drawText ( 20, ($i-1)*$part+$part/2, 0, $i, $svgStringText, "additional") ; + + } +} + +sub drawCoords { +# +# draws coordinates grid on map +# + my $exp = cv('coordsexp') ; + my $color = cv ('coordscolor'); + my $step = 10 ** $exp ; + + # vert. lines + my $start = int ($left / $step) + 1 ; + my $actual = $start * $step ; + + my $svgStringLine="stroke=\"$color\" stroke-width=\"3\"" ; + my $svgStringText = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, 30, $color, undef, undef) ; + + while ($actual < $right) { + my ($x1, $y1) = convert ($actual, 0) ; + + drawText ( $x1+10, $sizeY-50, 0, $actual, $svgStringText, "additional") ; + + my @coords = ($x1, 0, $x1, $sizeY) ; + drawWay (\@coords, 0, $svgStringLine, "additional", undef) ; + + $actual += $step ; + } + + # hor lines + $start = int ($bottom / $step) + 1 ; + $actual = $start * $step ; + while ($actual < $top) { + # print "actualY: $actual\n" ; + my ($x1, $y1) = convert (0, $actual) ; + + drawText ( $sizeX-180, $y1+30, 0, $actual, $svgStringText, "additional") ; + + my @coords = (0, $y1, $sizeX, $y1) ; + drawWay (\@coords, 0, $svgStringLine, "additional", undef) ; + + $actual += $step ; + } +} + + + + +# ----------------------------------------------------------------------------------- + +sub convert { + + # converts real world coordinates to system graph pixel coordinates + + my ($x, $y) = @_ ; + + my ($x1, $y1) = $proj->forward($y, $x) ; # lat/lon!!! + + my $x2 = int ( ($x1 - $projLeft) / ($projRight - $projLeft) * $sizeX ) ; + my $y2 = $sizeY - int ( ($y1 - $projBottom) / ($projTop - $projBottom) * $sizeY ) ; + + return ($x2, $y2) ; +} + +sub simplifyPoints { + my $ref = shift ; + my @points = @$ref ; + my @newPoints ; + my $maxIndex = $#points ; + + if (scalar @points > 4) { + # push first + push @newPoints, $points[0], $points[1] ; + + # push other + for (my $i=2; $i <= $maxIndex; $i+=2) { + # $simplifyTotal++ ; + if ( ($points[$i]==$points[$i-2]) and ($points[$i+1]==$points[$i-1]) ) { + # same + # $simplified++ ; + } + else { + push @newPoints, $points[$i], $points[$i+1] ; + } + } + return (\@newPoints) ; + } + else { + return ($ref) ; + } + +} + +sub drawRuler { +# +# draws ruler +# + my $col = cv('rulercolor') ; + + my $B ; my $B2 ; + my $L ; my $Lpix ; + my $x ; + my $text ; + + my $lineThickness = 8 ; # at 300dpi + my $textSize = 40 ; # at 300 dpi + my $textDist = 60 ; # at 300 dpi + my $lineLen = 40 ; # at 300 dpi + + my $xOffset = 2 * $lineThickness ; + my $yOffset = 2 * $lineThickness ; + + $B = $right - $left ; # in degrees + $B2 = $B * cos ($top/360*3.14*2) * 111.1 ; # in km + $text = "50m" ; $x = 0.05 ; # default length ruler + + if ($B2 > 0.5) {$text = "100 m" ; $x = 0.1 ; } # enlarge ruler + if ($B2 > 1) {$text = "500 m" ; $x = 0.5 ; } # enlarge ruler + if ($B2 > 5) {$text = "1 km" ; $x = 1 ; } + if ($B2 > 10) {$text = "5 km" ; $x = 5 ; } + if ($B2 > 50) {$text = "10 km" ; $x = 10 ; } + $L = $x / (cos ($top/360*3.14*2) * 111.1 ) ; # length ruler in km + $Lpix = $L / $B * $sizeX ; # length ruler in pixels + + my $rSizeX = int ($Lpix + 2 * $xOffset) ; + my $rSizeY = int ($lineLen + $textSize + 3 * $yOffset) ; + addToLayer ("definitions", "") ; + + if ( cv('rulerbackground') ne "none" ) { + my $color = cv ('rulerbackground') ; + my $svgString = "fill=\"$color\"" ; + drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ; + } + + my $svgString = "stroke=\"$col\" stroke-width=\"$lineThickness\" stroke-linecap=\"round\" " ; + + my @coords = ($xOffset, $yOffset, $xOffset+$Lpix, $yOffset) ; + drawWay (\@coords, 0, $svgString, "definitions", undef) ; + + @coords = ($xOffset, $yOffset, $xOffset, $yOffset+$lineLen) ; + drawWay (\@coords, 0, $svgString, "definitions", undef) ; + + @coords = ($xOffset+$Lpix, $yOffset, $xOffset+$Lpix, $yOffset+$lineLen) ; + drawWay (\@coords, 0, $svgString, "definitions", undef) ; + + @coords = ($xOffset+$Lpix/2, $yOffset, $xOffset+$Lpix/2, $yOffset+$lineLen/2) ; + drawWay (\@coords, 0, $svgString, "definitions", undef) ; + + + my $scale= getScale() ; + $text .= "(1:$scale)" ; + + + $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, 35, $col, undef, undef) ; + drawText ($xOffset, $yOffset+$textDist+30, 0, $text, $svgString, "definitions") ; + + addToLayer ("definitions", "") ; + + my $posX = 40 ; my $posY = 40 ; + + if ( cv('ruler') eq "2") { + $posX = $sizeX - 40 - $rSizeX ; + $posY = 40 ; + } + + if ( cv('ruler') eq "3") { + $posX = 40 ; + $posY = $sizeY - 40 - $rSizeY ; + } + + if ( cv('ruler') eq "4") { + $posX = $sizeX - 40 - $rSizeX ; + $posY = $sizeY - 40 - $rSizeY ; + } + + addToLayer ("ruler", "") ; +} + +sub drawScale { +# +# draws scale value +# + my $col = cv('scalecolor') ; + + my $xOffset = 20 ; + my $yOffset = 20 ; + my $fontSize = 70 ; + my $borderDist = 60 ; + + my $rSizeX = int (350 + 2 * $xOffset) ; + my $rSizeY = int ($fontSize + 2 * $yOffset) ; + addToLayer ("definitions", "") ; + + if ( cv('scalebackground') ne "none" ) { + my $color = cv ('scalebackground') ; + my $svgString = "fill=\"$color\"" ; + drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ; + } + + my $scale= getScale() ; + + my $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef) ; + drawText ($xOffset, $fontSize + $yOffset, 0, "1:$scale", $svgString, "definitions") ; + + addToLayer ("definitions", "") ; + + my $posX = $borderDist ; my $posY = $borderDist ; + + if ( cv('scale') eq "2") { + $posX = $sizeX - $borderDist - $rSizeX ; + $posY = $borderDist ; + } + + if ( cv('scale') eq "3") { + $posX = $borderDist ; + $posY = $sizeY - $borderDist - $rSizeY ; + } + + if ( cv('scale') eq "4") { + $posX = $sizeX - $borderDist - $rSizeX ; + $posY = $sizeY - $borderDist - $rSizeY ; + } + + addToLayer ("scale", "") ; +} + +sub drawFoot { +# +# draws footer +# + my $col = cv('footcolor') ; + my $text = cv('foot') ; + my $len = length $text ; + + my $xOffset = 20 ; + my $yOffset = 20 ; + my $fontSize = cv('footsize') ; + my $borderDistX = 60 ; + my $borderDistY = $fontSize + 50 ; + + my $rSizeX = int ($len*cv('ppc')/10*$fontSize + 2 * $xOffset) ; + my $rSizeY = int ($fontSize + 2 * $yOffset) ; + addToLayer ("definitions", "") ; + + if ( cv('footbackground') ne "none" ) { + my $color = cv ('footbackground') ; + my $svgString = "fill=\"$color\"" ; + drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ; + } + + my $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef) ; + drawText ($xOffset, $fontSize + $yOffset, 0, $text, $svgString, "definitions") ; + + addToLayer ("definitions", "") ; + + my $posX = $borderDistX ; my $posY = $sizeY - $borderDistY ; + + addToLayer ("footer", "") ; +} + +sub drawHead { +# +# draws header +# + my $col = cv('headcolor') ; + my $text = cv('head') ; + my $len = length $text ; + + my $xOffset = 20 ; + my $yOffset = 20 ; + my $fontSize = cv('headsize') ; + my $borderDistX = 60 ; + my $borderDistY = 60 ; + + my $rSizeX = int ($len*cv('ppc')/10*$fontSize + 2 * $xOffset) ; + my $rSizeY = int ($fontSize + 2 * $yOffset) ; + addToLayer ("definitions", "") ; + + if ( cv('headbackground') ne "none" ) { + my $color = cv ('headbackground') ; + my $svgString = "fill=\"$color\"" ; + drawRect (0, 0, $rSizeX, $rSizeY, 0, $svgString, "definitions") ; + } + + my $svgString = mwMisc::createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, $fontSize, $col, undef, undef) ; + drawText ($xOffset, $fontSize + $yOffset, 0, $text, $svgString, "definitions") ; + + addToLayer ("definitions", "") ; + + my $posX = $borderDistX ; my $posY = $borderDistY ; + + addToLayer ("header", "") ; +} + + +sub fitsPaper { +# +# calculates on what paper size the map will fit. sizes are taken from global variables +# + + my $width = $sizeX / 300 * 2.54 ; + my $height = $sizeY / 300 * 2.54 ; + my $paper = "" ; + + my @sizes = () ; + push @sizes, ["4A0", 168.2, 237.8] ; + push @sizes, ["2A0", 118.9, 168.2] ; + push @sizes, ["A0", 84.1, 118.9] ; + push @sizes, ["A1", 59.4, 84.1] ; + push @sizes, ["A2", 42, 59.4] ; + push @sizes, ["A3", 29.7, 42] ; + push @sizes, ["A4", 21, 29.7] ; + push @sizes, ["A5", 14.8, 21] ; + push @sizes, ["A6", 10.5, 14.8] ; + push @sizes, ["A7", 7.4, 10.5] ; + push @sizes, ["none", 0, 0] ; + + foreach my $size (@sizes) { + if ( ( ($width<=$size->[1]) and ($height<=$size->[2]) ) or ( ($width<=$size->[2]) and ($height<=$size->[1]) ) ) { + $paper = $size->[0] ; + } + } + + return ($paper, $width, $height) ; +} + +sub getScale { +# +# calcs scale of map +# + my ($dpi) = 300 ; + + my $dist = distance ($left, $bottom, $right, $bottom) ; + my $inches = $sizeX / $dpi ; + my $cm = $inches * 2.54 ; + my $scale = int ( $dist / ($cm/100/1000) ) ; + $scale = int ($scale / 100) * 100 ; + + return ($scale) ; +} + +sub gridSquare { +# +# returns grid square of given coordinates for directories +# + my ($lon, $lat) = @_ ; + + my $parts = cv('grid') ; + + my ($x, $y) = convert ($lon, $lat) ; + my $xi = int ($x / ($sizeX / $parts)) + 1 ; + my $yi = int ($y / ($sizeX / $parts)) + 1 ; + if ( ($x >= 0) and ($x <= $sizeX) and ($y >= 0) and ($y <= $sizeY) ) { + return (chr($xi+64) . $yi) ; + } + else { + return undef ; + } +} + + +sub getDimensions { + return ($sizeX, $sizeY) ; +} + +# ---------------------------------------------------------------------- + +sub initOneways { +# +# write marker defs to svg +# + my $color = cv('onewaycolor') ; + + my @svgOutputDef = () ; + for (my $markerSize = 5; $markerSize <= 40; $markerSize++) { + push @svgOutputDef, "" ; + push @svgOutputDef, "" ; + push @svgOutputDef, "" ; + } + + + foreach my $line (@svgOutputDef) { + addToLayer ("definitions", $line) ; + } +} + +sub addOnewayArrows { +# +# adds oneway arrows to new pathes +# + my ($wayNodesRef, $direction, $thickness, $layer) = @_ ; + my @wayNodes = @$wayNodesRef ; + my ($lonRef, $latRef) = mwFile::getNodePointers() ; + + if ($direction == -1) { @wayNodes = reverse @wayNodes ; } + + my $minDist = cv('onewaysize') * 1.5 ; + my $markerSize = cv('onewaySize') ; + + if ( cv('onewayAutoSize') != 0 ) { + $markerSize = int ( $thickness / 100 * cv('onewayAutoSize') ) ; + if ( $markerSize < 5 ) { $markerSize = 5 ; } + if ( $markerSize > 40 ) { $markerSize = 40 ; } + $minDist = $markerSize * 1.5 ; + } + + # create new pathes with new nodes + for (my $i = 0; $i < scalar( @wayNodes ) - 1; $i++ ) { + my ($x1, $y1) = convert ($$lonRef{$wayNodes[$i]}, $$latRef{$wayNodes[$i]}) ; + my ($x2, $y2) = convert ($$lonRef{$wayNodes[$i+1]}, $$latRef{$wayNodes[$i+1]}) ; + my $xn = ($x2+$x1) / 2 ; + my $yn = ($y2+$y1) / 2 ; + if (sqrt (($x2-$x1)**2+($y2-$y1)**2) > $minDist) { + # create path + # use path + my $svg = "" ; + + addToLayer ($layer+$thickness/100, $svg) ; + } + } +} + +# ---------------------------------------------------------------------------- + +sub addAreaIcon { +# +# initial collection of area icons +# + my $fileNameOriginal = shift ; + # print "AREA: $fileNameOriginal\n" ; + my $result = open (my $file, "<", $fileNameOriginal) ; + close ($file) ; + if ($result) { + my ($x, $y) ; + if (grep /.svg/, $fileNameOriginal) { + ($x, $y) = mwMisc::sizeSVG ($fileNameOriginal) ; + if ( ($x == 0) or ($y == 0) ) { + $x = 32 ; $y = 32 ; + print "WARNING: size of file $fileNameOriginal could not be determined. Set to 32px x 32px\n" ; + } + } + + if (grep /.png/, $fileNameOriginal) { + ($x, $y) = mwMisc::sizePNG ($fileNameOriginal) ; + } + + if (!defined $areaDef{$fileNameOriginal}) { + + my $x1 = $x ; # scale area icons + my $y1 = $y ; + my $fx = $x1 / $x ; + my $fy = $y1 / $y ; + + # add defs to svg output + my $defName = "A" . $areaNum ; + # print "INFO area icon $fileNameOriginal, $defName, $x, $y --- $x1, $y1 --- $fx, $fy --- processed.\n" ; + $areaNum++ ; + + my $svgElement = "\n" ; + $svgElement .= " \n" ; + $svgElement .= "\n" ; + + addToLayer ("definitions", $svgElement) ; + + $defName = "#" . $defName ; + $areaDef{$fileNameOriginal} = $defName ; + } + } + else { + print "WARNING: area icon $fileNameOriginal not found!\n" ; + } +} + +# ---------------------------------------------------------------------------- + +sub createShield { + my ($name, $targetSize) = @_ ; + my @a = split /:/, $name ; + my $shieldFileName = $a[1] ; + my $shieldText = $a[2] ; + + if (! defined $createdShields{$name}) { + open (my $file, "<", $shieldFileName) or die ("ERROR: shield definition $shieldFileName not found.\n") ; + my @defText = <$file> ; + close ($file) ; + + # get size + # calc scaling + my $sizeX = 0 ; + my $sizeY = 0 ; + foreach my $line (@defText) { + if (grep / $max) { $max = $shieldYSize{$name} ; } + return $max ; +} + +sub getShieldSizes { + my $name = shift ; + my $x = $shieldXSize{$name} ; + my $y = $shieldYSize{$name} ; + return $x, $y ; +} + +sub getShieldId { + my $name = shift ; + return $createdShields{$name} ; +} + +# -------------------------------------------------------------------- + +sub createLegendFile { + my ($x, $y, $extension, $group) = @_ ; + + my $svgName = cv('out') ; + $svgName =~ s/\.svg/$extension\.svg/i ; + my $pngName = $svgName ; + $pngName =~ s/\.svg/\.png/i ; + my $pdfName = $svgName ; + $pdfName =~ s/\.svg/\.pdf/i ; + + + open (my $file, ">", $svgName) || die "can't open legend svg output file $svgName\n"; + + print $file "\n" ; + print $file "\n" ; + + my $w = $x / 300 * 2.54 ; # cm + my $h = $y / 300 * 2.54 ; + + my ($svg) = "\n" ; + print $file $svg ; + + print $file "\n" ; + foreach ( @{$svgLayer{'definitions'}} ) { print $file $_, "\n" ; } + print $file "\n" ; + + print $file "\n" ; + print $file "\n" ; + close $file ; + + if (cv('pdf') eq "1") { + print "creating pdf file $pdfName ...\n" ; + `inkscape -A $pdfName $svgName` ; + } + + if (cv('png') eq "1") { + my $dpi = cv('pngdpi') ; + print "creating png file $pngName ($dpi dpi)...\n" ; + `inkscape --export-dpi=$dpi -e $pngName $svgName` ; + } +} + +sub coordsOut { + my @points = @_ ; + + my $allOut = 0 ; + + my $outLeft = 1 ; + my $outRight = 1 ; + my $outTop = 1 ; + my $outBottom = 1 ; + + for (my $i=0; $i < scalar(@points) - 1; $i += 2) { + my $x = $points[$i] ; + my $y = $points[$i+1] ; + if ($x >= 0) { $outLeft = 0 ; } + if ($x <= $sizeX) { $outRight = 0 ; } + if ($y >= 0) { $outBottom = 0 ; } + if ($y <= $sizeY) { $outTop = 0 ; } + } + + if ( ($outLeft) or ($outRight) or ($outTop) or ($outBottom) ) { $allOut = 1 ; } + + return $allOut ; +} + +1 ; + + diff --git a/mwMisc.pm b/mwMisc.pm new file mode 100644 index 0000000..55f423d --- /dev/null +++ b/mwMisc.pm @@ -0,0 +1,801 @@ +# +# 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 mwMisc ; + +use strict ; +use warnings ; + +use Math::Trig; +use Math::Polygon ; +use List::Util qw[min max] ; + +use mwConfig ; +use mwFile ; +# use mwMap ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( getValue + createLabel + buildRings + angleMapgen + triangleNode + intersection + areaSize + isIn + processPageNumbers + processRectangles + sizePNG + sizeSVG + createDirPdf + getPointOfWay + nodes2Coordinates + areaCenter + createTextSVG + wayVisible + labelTransform + ) ; + + + +sub getValue { + my ($key, $aRef) = @_ ; + my $value = undef ; + foreach my $kv (@$aRef) { + if ($kv->[0] eq $key) { $value = $kv->[1]; } + } + return $value ; +} + +sub createLabel { +# +# takes @tags and labelKey(s) from style file and creates labelTextTotal and array of labels for directory +# takes more keys in one string - using a separator. +# +# § all listed keys will be searched for and values be concatenated +# # first of found keys will be used to select value +# "name§ref" will return all values if given +# "name#ref" will return name, if given. if no name is given, ref will be used. none given, no text +# + my ($ref1, $styleLabelText, $lon, $lat) = @_ ; + my @tags = @$ref1 ; + my @keys ; + my @labels = () ; + my $labelTextTotal = "" ; + + if (grep /!/, $styleLabelText) { # AND + @keys = split ( /!/, $styleLabelText) ; + # print "par found: $styleLabelText; @keys\n" ; + for (my $i=0; $i<=$#keys; $i++) { + if ($keys[$i] eq "_lat") { push @labels, $lat ; } + if ($keys[$i] eq "_lon") { push @labels, $lon ; } + foreach my $tag (@tags) { + if ($tag->[0] eq $keys[$i]) { + push @labels, $tag->[1] ; + } + } + } + $labelTextTotal = "" ; + foreach my $label (@labels) { $labelTextTotal .= $label . " " ; } + } + else { # PRIO + @keys = split ( /#/, $styleLabelText) ; + my $i = 0 ; my $found = 0 ; + while ( ($i<=$#keys) and ($found == 0) ) { + if ($keys[$i] eq "_lat") { push @labels, $lat ; $found = 1 ; $labelTextTotal = $lat ; } + if ($keys[$i] eq "_lon") { push @labels, $lon ; $found = 1 ; $labelTextTotal = $lon ; } + foreach my $tag (@tags) { + if ($tag->[0] eq $keys[$i]) { + push @labels, $tag->[1] ; + $labelTextTotal = $tag->[1] ; + $found = 1 ; + } + } + $i++ ; + } + } + return ( $labelTextTotal, \@labels) ; +} + +sub buildRings { +# +# accepts ref to array of ways and option if unclosed rings shoulf be returned +# closeOpt == 1 returns only closed rings +# +# returns two refs to arrays of arrays: ways and nodes +# + my ($ref, $closeOpt) = @_ ; + my (@allWays) = @$ref ; + my @ringWays = () ; + my @ringNodes = () ; + my $ringCount = 0 ; + + my ($memWayNodesRef, $memWayTagsRef) = mwFile::getWayPointers() ; + + # print "build rings for @allWays\n" ; + if (cv('debug') eq "1" ) { print "BR: called.\n" ; } + while ( scalar @allWays > 0) { + # build new test ring + my (@currentWays) = () ; my (@currentNodes) = () ; + push @currentWays, $allWays[0] ; + if (cv('debug') eq "1" ) { print "BR: initial way for next ring id= $allWays[0]\n" ; } + push @currentNodes, @{$$memWayNodesRef{$allWays[0]}} ; + my $startNode = $currentNodes[0] ; + my $endNode = $currentNodes[-1] ; + if (cv('debug') eq "1" ) { print "BR: initial start and end node $startNode $endNode\n" ; } + my $closed = 0 ; + shift @allWays ; # remove first element + if ($startNode == $endNode) { $closed = 1 ; } + + my $success = 1 ; + while ( ($closed == 0) and ( (scalar @allWays) > 0) and ($success == 1) ) { + # try to find new way + if (cv('debug') eq "1" ) { print "TRY TO FIND NEW WAY\n" ; } + $success = 0 ; + if (cv('debug') eq "1" ) { print "BR: actual start and end node $startNode $endNode\n" ; } + my $i = 0 ; + while ( ($i < (scalar @allWays) ) and ($success == 0) ) { + if (cv('debug') eq "1" ) { print "BR: testing way $i = $allWays[$i]\n" ; } + if (cv('debug') eq "1" ) { print "BR: rev in front?\n" ; } + if ( $$memWayNodesRef{$allWays[$i]}[0] == $startNode ) { + $success = 1 ; + # reverse in front + @currentWays = ($allWays[$i], @currentWays) ; + @currentNodes = (reverse (@{$$memWayNodesRef{$allWays[$i]}}), @currentNodes) ; + splice (@allWays, $i, 1) ; + } + if ($success ==0) { + if (cv('debug') eq "1" ) { print "BR: app at end?\n" ; } + if ( $$memWayNodesRef{$allWays[$i]}[0] == $endNode) { + $success = 1 ; + # append at end + @currentWays = (@currentWays, $allWays[$i]) ; + @currentNodes = (@currentNodes, @{$$memWayNodesRef{$allWays[$i]}}) ; + splice (@allWays, $i, 1) ; + } + } + if ($success ==0) { + if (cv('debug') eq "1" ) { print "BR: app in front?\n" ; } + if ( $$memWayNodesRef{$allWays[$i]}[-1] == $startNode) { + $success = 1 ; + # append in front + @currentWays = ($allWays[$i], @currentWays) ; + @currentNodes = (@{$$memWayNodesRef{$allWays[$i]}}, @currentNodes) ; + splice (@allWays, $i, 1) ; + } + } + if ($success ==0) { + if (cv('debug') eq "1" ) { print "BR: rev at end?\n" ; } + if ( $$memWayNodesRef{$allWays[$i]}[-1] == $endNode) { + $success = 1 ; + # append reverse at the end + @currentWays = (@currentWays, $allWays[$i]) ; + @currentNodes = (@currentNodes, (reverse (@{$$memWayNodesRef{$allWays[$i]}}))) ; + splice (@allWays, $i, 1) ; + } + } + $i++ ; + } # look for new way that fits + + $startNode = $currentNodes[0] ; + $endNode = $currentNodes[-1] ; + if ($startNode == $endNode) { + $closed = 1 ; + if (cv('debug') eq "1" ) { print "BR: ring now closed\n" ;} + } + } # new ring + + # examine ring and act + if ( ($closed == 1) or ($closeOpt == 0) ) { + # eliminate double nodes in @currentNodes + my $found = 1 ; + while ($found) { + $found = 0 ; + LABCN: for (my $i=0; $i<$#currentNodes; $i++) { + if ($currentNodes[$i] == $currentNodes[$i+1]) { + $found = 1 ; + splice @currentNodes, $i, 1 ; + last LABCN ; + } + } + } + # add data to return data + @{$ringWays[$ringCount]} = @currentWays ; + @{$ringNodes[$ringCount]} = @currentNodes ; + $ringCount++ ; + } + } + return (\@ringWays, \@ringNodes) ; +} + +sub angleMapgen { +# +# angle between lines/segments +# + my ($g1x1) = shift ; + my ($g1y1) = shift ; + my ($g1x2) = shift ; + my ($g1y2) = shift ; + my ($g2x1) = shift ; + my ($g2y1) = shift ; + my ($g2x2) = shift ; + my ($g2y2) = shift ; + + my $g1m ; + if ( ($g1x2-$g1x1) != 0 ) { + $g1m = ($g1y2-$g1y1)/($g1x2-$g1x1) ; # steigungen + } + else { + $g1m = 999999999 ; + } + + my $g2m ; + if ( ($g2x2-$g2x1) != 0 ) { + $g2m = ($g2y2-$g2y1)/($g2x2-$g2x1) ; + } + else { + $g2m = 999999999 ; + } + + if ($g1m == $g2m) { # parallel + return (0) ; + } + else { + my $t1 = $g1m -$g2m ; + my $t2 = 1 + $g1m * $g2m ; + if ($t2 == 0) { + return 90 ; + } + else { + my $a = atan (abs ($t1/$t2)) / 3.141592654 * 180 ; + return $a ; + } + } +} + +sub triangleNode { +# +# get segment of segment as coordinates +# from start or from end of segment +# + # 0 = start + # 1 = end + my ($x1, $y1, $x2, $y2, $len, $startEnd) = @_ ; + my ($c) = sqrt ( ($x2-$x1)**2 + ($y2-$y1)**2) ; + my $percent = $len / $c ; + + my ($x, $y) ; + if ($startEnd == 0 ) { + $x = $x1 + ($x2-$x1)*$percent ; + $y = $y1 + ($y2-$y1)*$percent ; + } + else { + $x = $x2 - ($x2-$x1)*$percent ; + $y = $y2 - ($y2-$y1)*$percent ; + } + return ($x, $y) ; +} + +sub intersection { +# +# returns intersection point of two lines, else (0,0) +# + my ($g1x1) = shift ; + my ($g1y1) = shift ; + my ($g1x2) = shift ; + my ($g1y2) = shift ; + + my ($g2x1) = shift ; + my ($g2y1) = shift ; + my ($g2x2) = shift ; + my ($g2y2) = shift ; + + if (($g1x1 == $g2x1) and ($g1y1 == $g2y1)) { # p1 = p1 ? + return ($g1x1, $g1y1) ; + } + if (($g1x1 == $g2x2) and ($g1y1 == $g2y2)) { # p1 = p2 ? + return ($g1x1, $g1y1) ; + } + if (($g1x2 == $g2x1) and ($g1y2 == $g2y1)) { # p2 = p1 ? + return ($g1x2, $g1y2) ; + } + + if (($g1x2 == $g2x2) and ($g1y2 == $g2y2)) { # p2 = p1 ? + return ($g1x2, $g1y2) ; + } + + my $g1m ; + if ( ($g1x2-$g1x1) != 0 ) { + $g1m = ($g1y2-$g1y1)/($g1x2-$g1x1) ; # steigungen + } + else { + $g1m = 999999 ; + } + + my $g2m ; + if ( ($g2x2-$g2x1) != 0 ) { + $g2m = ($g2y2-$g2y1)/($g2x2-$g2x1) ; + } + else { + $g2m = 999999 ; + } + + if ($g1m == $g2m) { # parallel + return (0, 0) ; + } + + my ($g1b) = $g1y1 - $g1m * $g1x1 ; # abschnitte + my ($g2b) = $g2y1 - $g2m * $g2x1 ; + + my ($sx) = ($g2b-$g1b) / ($g1m-$g2m) ; # schnittpunkt + my ($sy) = ($g1m*$g2b - $g2m*$g1b) / ($g1m-$g2m); + + my ($g1xmax) = max ($g1x1, $g1x2) ; + my ($g1xmin) = min ($g1x1, $g1x2) ; + my ($g1ymax) = max ($g1y1, $g1y2) ; + my ($g1ymin) = min ($g1y1, $g1y2) ; + + my ($g2xmax) = max ($g2x1, $g2x2) ; + my ($g2xmin) = min ($g2x1, $g2x2) ; + my ($g2ymax) = max ($g2y1, $g2y2) ; + my ($g2ymin) = min ($g2y1, $g2y2) ; + + if (($sx >= $g1xmin) and + ($sx >= $g2xmin) and + ($sx <= $g1xmax) and + ($sx <= $g2xmax) and + ($sy >= $g1ymin) and + ($sy >= $g2ymin) and + ($sy <= $g1ymax) and + ($sy <= $g2ymax)) { + return ($sx, $sy) ; + } + else { + return (0, 0) ; + } +} + + +sub isIn { +# checks two polygons +# return 0 = neither +# 1 = p1 is in p2 +# 2 = p2 is in p1 + my ($p1, $p2) = @_ ; + + my ($p1In2) = 1 ; + my ($p2In1) = 1 ; + + # p1 in p2 ? + foreach my $pt1 ($p1->points) { + if ($p2->contains ($pt1) ) { + # good + } + else { + $p1In2 = 0 ; + } + } + + # p2 in p1 ? + foreach my $pt2 ($p2->points) { + if ($p1->contains ($pt2) ) { + # good + } + else { + $p2In1 = 0 ; + } + } + + if ($p1In2 == 1) { + return 1 ; + } + elsif ($p2In1 == 1) { + return 2 ; + } + else { + return 0 ; + } +} + +# ------------------------------------------------------------------------------- + +sub processPageNumbers { + if ( cv('pageNumbers') ne "") { + my $pnSize ; my $pnColor ; + my @a = split /,/, cv('pageNumbers') ; + if (scalar @a >= 3) { + $pnSize = $a[0] ; + $pnColor = $a[1] ; + my $pnNumber = $a[2] ; + + if ($pnNumber != 0) { + drawPageNumber ($pnSize, $pnColor, $pnNumber) ; + } + } + if (scalar @a == 7) { + # draw 4 other positions if ne 0!!! + if ($a[3] != 0) { # left + drawPageNumberLeft ($pnSize, $pnColor, $a[3]) ; + } + if ($a[4] != 0) { # bottom + drawPageNumberBottom ($pnSize, $pnColor, $a[4]) ; + } + if ($a[5] != 0) { # right + drawPageNumberRight ($pnSize, $pnColor, $a[5]) ; + } + if ($a[6] != 0) { # top + drawPageNumberTop ($pnSize, $pnColor, $a[6]) ; + } + } + } +} + +sub drawPageNumber { + my ($size, $col, $num) = @_ ; + my ($sizeX, $sizeY) = mwMap::getDimensions() ; + my $x = $sizeX - 2 * $size ; + my $y = $sizeY - 2 * $size ; + my $svgString = "fill=\"$col\" font-size=\"$size\" " ; + mwMap::drawText ($x, $y, 0, $num, $svgString, "text") +} + +sub drawPageNumberLeft { + my ($size, $col, $num) = @_ ; + my ($sizeX, $sizeY) = mwMap::getDimensions() ; + my $x = 2 * $size ; + my $y = $sizeY / 2 ; + my $svgString = "fill=\"$col\" font-size=\"$size\" " ; + mwMap::drawText ($x, $y, 0, $num, $svgString, "text") +} + +sub drawPageNumberBottom { + my ($size, $col, $num) = @_ ; + my ($sizeX, $sizeY) = mwMap::getDimensions() ; + my $x = $sizeX / 2 ; + my $y = $sizeY - 2 * $size ; + my $svgString = "fill=\"$col\" font-size=\"$size\" " ; + mwMap::drawText ($x, $y, 0, $num, $svgString, "text") +} + +sub drawPageNumberRight { + my ($size, $col, $num) = @_ ; + my ($sizeX, $sizeY) = mwMap::getDimensions() ; + my $x = $sizeX - 2 * $size ; + my $y = $sizeY / 2 ; + my $svgString = "fill=\"$col\" font-size=\"$size\" " ; + mwMap::drawText ($x, $y, 0, $num, $svgString, "text") +} + +sub drawPageNumberTop { + my ($size, $col, $num) = @_ ; + my ($sizeX, $sizeY) = mwMap::getDimensions() ; + my $x = $sizeX / 2 ; + my $y = 2 * $size ; + my $svgString = "fill=\"$col\" font-size=\"$size\" " ; + mwMap::drawText ($x, $y, 0, $num, $svgString, "text") +} + +# --------------------------------------------------------------------- + +sub processRectangles { + my $no = 0 ; + + if ( cv('rectangles') ne "") { + my @rects ; + @rects = split /#/, cv('rectangles') ; + foreach my $r (@rects) { + $no++ ; + my @coords ; + @coords = split /,/, $r ; + + my $left = $coords[0] ; + my $bottom = $coords[1] ; + my $right = $coords[2] ; + my $top = $coords[3] ; + + my @nodes ; + push @nodes, convert ($left, $bottom) ; + push @nodes, convert ($right, $bottom) ; + push @nodes, convert ($right, $top) ; + push @nodes, convert ($left, $top) ; + push @nodes, convert ($left, $bottom) ; + + # drawWay (10, "black", 5, "none", @nodes) ; + my $svgString = "fill=\"none\" stroke=\"black\" stroke-width=\"7\" " ; + drawWay (\@nodes, 0, $svgString, "rectangles", undef) ; + # drawRect ($left, $bottom, $right, $top, 1, $svgString, "rectangles") ; + + if ( cv('pagenumbers') ne "") { + my $x = ($right + $left) / 2 ; + my $y = ($bottom + $top) / 2 ; + my $xp ; my $yp ; + ($xp, $yp) = convert ($x, $y) ; + # drawTextPixGrid ($xp, $yp, $no, $pnColor, scalePoints ( scaleBase ($pnSize) ) ) ; + my $svgString = "fill=\"black\" font-size=\"60\" " ; + drawText ($xp, $yp, 0, $no, $svgString, "rectangles") ; + } + + } + } +} + +# -------------------------------------------------------------------- + +sub sizePNG { +# +# evaluates size of png graphics +# + my $fileName = shift ; + + my ($x, $y) ; + my $file ; + my $result = open ($file, "<", $fileName) ; + if ($result) { + my $pic = newFromPng GD::Image($file) ; + ($x, $y) = $pic->getBounds ; + close ($file) ; + } + else { + ($x, $y) = (0, 0) ; + } + return ($x, $y) ; +} + +sub sizeSVG { +# +# evaluates size of svg graphics +# + my $fileName = shift ; + my $file ; + my ($x, $y) ; undef $x ; undef $y ; + + my $result = open ($file, "<", $fileName) ; + if ($result) { + my $line ; + while ($line = <$file>) { + my ($x1) = ( $line =~ /^.*width=\"([\d]+)px\"/ ) ; + my ($y1) = ( $line =~ /^.*height=\"([\d]+)px\"/ ) ; + if (!defined $x1) { + ($x1) = ( $line =~ /^\s*width=\"([\d]+)\"/ ) ; + + } + if (!defined $y1) { + ($y1) = ( $line =~ /^\s*height=\"([\d]+)\"/ ) ; + } + if (defined $x1) { $x = $x1 ; } + if (defined $y1) { $y = $y1 ; } + } + close ($file) ; + } + + if ( (!defined $x) or (!defined $y) ) { + $x = 0 ; $y = 0 ; + print "WARNING: size of file $fileName could not be determined.\n" ; + } + return ($x, $y) ; +} + +# ------------------------------------------------------------------------ + + +sub createDirPdf { + if ((cv('dir') eq "1") or (cv('poi') eq "1")) { + if (cv('grid') > 0) { + my $dirPdfName = cv('out') ; + $dirPdfName =~ s/.svg/_dir.pdf/ ; + my $sName = "none" ; + my $pName = "none" ; + + my $prg = cv ('dirprg') ; + + if (cv('dir') eq "1") { $sName = cv('directoryname') ; } + if (cv('poi') eq "1") { $pName = cv('poiname') ; } + my $dirColNum = cv ('dircolnum') ; + my $dirTitle = cv ('dirtitle') ; + print "\ncalling perl $prg $sName $pName $dirTitle $dirPdfName $dirColNum\n\n" ; + `perl $prg $sName $pName \"$dirTitle\" $dirPdfName $dirColNum > out.txt` ; + } + else { + print "WARNING: directory PDF will not be created because -grid was not specified\n" ; + } + + } + else { + print "WARNING: directory PDF will not be created because neither -dir nor -poi was specified\n" ; + } +} + +# ----------------------------------------------------------------------------- + +sub getPointOfWay { + # + # returns point of way at distance/position + # coordinates and units are pixels + + my ($ref, $position) = @_ ; + my @points = @$ref ; + + my @double = () ; + while (scalar @points > 0) { + my $x = shift @points ; + my $y = shift @points ; + push @double, [$x, $y] ; + } + + my $i = 0 ; my $actLen = 0 ; + while ($actLen < $position) { + $actLen += sqrt ( ($double[$i]->[0]-$double[$i+1]->[0])**2 + ($double[$i]->[1]-$double[$i+1]->[1])**2 ) ; + $i++ ; + } + + my $x = int (($double[$i]->[0] + $double[$i-1]->[0]) / 2) ; + my $y = int (($double[$i]->[1] + $double[$i-1]->[1]) / 2) ; + + # print "POW: $x, $y\n" ; + + return ($x, $y) ; +} + +# ---------------------------------------------------------------- + +sub nodes2Coordinates { +# +# transform list of nodeIds to list of x/y +# straight array in and out +# + my @nodes = @_ ; + my $i ; + + my @result = () ; + + my ($lonRef, $latRef) = mwFile::getNodePointers() ; + + foreach my $n (@nodes) { + my ($x, $y) = mwMap::convert ( $$lonRef{$n}, $$latRef{$n}) ; + push @result, $x, $y ; + } + + return @result ; +} + + +sub areaCenter { +# +# calculate center of area by averageing lons/lats. could be smarter because result could be outside of area! TODO +# + my $ref = shift ; + my @nodes = @$ref ; + + # print "CENTER: @nodes\n" ; + + my $x = 0 ; + my $y = 0 ; + my $num = 0 ; + + my ($lonRef, $latRef) = getNodePointers() ; + + foreach my $n (@nodes) { + $x += $$lonRef{$n} ; + $y += $$latRef{$n} ; + $num++ ; + } + $x = $x / $num ; + $y = $y / $num ; + return ($x, $y) ; + +} + + +sub areaSize { + my $ref = shift ; # nodes + my @nodes = @$ref ; + + # print "SIZE: @nodes\n" ; + + my ($lonRef, $latRef) = mwFile::getNodePointers() ; + + my @poly = () ; + foreach my $node ( @nodes ) { + my ($x, $y) = mwMap::convert ($$lonRef{$node}, $$latRef{$node}) ; + push @poly, [$x, $y] ; + } + my ($p) = Math::Polygon->new(@poly) ; + my $size = $p->area ; + + return $size ; +} + +# --------------------------------------------------------------- + +sub createTextSVG { + my ($fontFamily, $font, $bold, $italic, $size, $color, $strokeWidth, $strokeColor) = @_ ; + + my $svg = "" ; + + if ( (defined $font) and ( $font ne "") ) { + $svg .= "font=\"$font\" " ; + } + if ( (defined $fontFamily) and ( $fontFamily ne "") ) { + $svg .= "font-family=\"$fontFamily\" " ; + } + + if ( (defined $bold) and ( lc ($bold) eq "yes") ) { + $svg .= "font-weight=\"bold\" " ; + } + if ( (defined $italic) and ( lc ($italic) eq "yes") ) { + $svg .= "font-style=\"italic\" " ; + } + + if ( (defined $size) and ( $size ne "") ) { + $svg .= "font-size=\"$size\" " ; + } + if ( (defined $color) and ( $color ne "") ) { + $svg .= "fill=\"$color\" " ; + } + + if ( (defined $strokeColor) and ( $strokeColor ne "") ) { + $svg .= "stroke=\"$strokeColor\" " ; + } + if ( (defined $strokeWidth) and ( $strokeWidth ne "") ) { + $svg .= "stroke-width=\"$strokeWidth\" " ; + } + + + + return $svg ; +} + +# -------------------------------------------------------------------- + +sub wayVisible { + my $ref = shift ; + my @points = @$ref ; + my ($sizeX, $sizeY) = mwMap::getDimensions() ; + + my $result = 0 ; + + for (my $i = 0; $i < $#points; $i += 2) { + my $x = $points[$i] ; + my $y = $points[$i+1] ; + if ( ( $x >= 0 ) and ( $y >= 0 ) and ( $x <= $sizeX ) and ( $y <= $sizeY ) ) { + $result = 1 ; + } + } + return $result ; +} + +# -------------------------------------------------------------------- + +sub labelTransform { + my ($label, $cmd) = @_ ; + if ($cmd ne "") { + eval $cmd ; + if ($@) { print "ERROR processing label '$label' with command: '$cmd'\nERROR: $@\n" ; } + } + return $label ; +} + + +1 ; + + diff --git a/mwMulti.pm b/mwMulti.pm new file mode 100644 index 0000000..68168f5 --- /dev/null +++ b/mwMulti.pm @@ -0,0 +1,420 @@ +# +# 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 mwMulti ; + +use strict ; +use warnings ; + +use mwMap ; +use mwMisc ; +use mwFile ; +use mwLabel ; +use mwConfig ; +use mwRules ; + +use Math::Polygon ; + + + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( processMultipolygons + + ) ; + +my $newId = 0 ; + +my %multiNodes = () ; +my %multiTags = () ; +my %multiPaths = () ; + +my %wayUsed = () ; + +# ------------------------------------------------------------------------- + +sub processMultipolygons { + my $notDrawnMP = 0 ; + my $mp = 0 ; + my $mpLabels = 0 ; + my $mpNotDrawnLabels = 0 ; + print "draw multipolygons...\n" ; + + preprocessMultipolygons() ; + + foreach my $multiId (keys %multiTags) { + + my $ruleRef = getAreaRule ( \@{$multiTags{$multiId}} ) ; + + if (defined $ruleRef) { + + my $svgText = "" ; + my $icon = "" ; + if ($$ruleRef{'icon'} ne "none") { + $icon = $$ruleRef{'icon'} ; + } + else { + my $col = $$ruleRef{'color'} ; + $svgText = "fill=\"$col\" " ; + } + + my $ref = $multiPaths{$multiId}[0] ; # first, outer way + my $size = areaSize ( $ref ) ; + + if ($size >= cv('minareasize') ) { + drawArea ($svgText, $icon, $multiPaths{$multiId}, 1, "multi") ; + $mp++ ; + + + # LABELS + my $name = "" ; my $ref1 ; + if ( cv('ignorelabels') eq "0" ) { + ($name, $ref1) = createLabel ( $multiTags{$multiId}, $$ruleRef{'label'}, 0, 0) ; + + if ( ( $$ruleRef{'label'} ne "none") and + ( cv('nolabel') eq "1" ) and + ($name eq "") ) + { + $name = "NO LABEL" ; + } + } + + if ($name ne "") { + if ($size >= cv('minarealabelsize') ) { + $mpLabels++ ; + if (cv('debug') eq "1") { print "MP LABEL: $name, size: $$ruleRef{'labelsize'}, color: $$ruleRef{'labelcolor'}\n" ; } + + my ($x, $y) = areaCenter ( $multiPaths{$multiId}[0] ) ; + + + my $labelFont = $$ruleRef{'labelfont'} ; + my $labelFontFamily = $$ruleRef{'labelfontfamily'} ; + my $labelSize = $$ruleRef{'labelsize'} ; + my $color = $$ruleRef{'labelcolor'} ; + my $labelBold = $$ruleRef{'labelbold'} ; + my $labelItalic = $$ruleRef{'labelitalic'} ; + my $labelHalo = $$ruleRef{'labelhalo'} ; + my $labelHaloColor = $$ruleRef{'labelhalocolor'} ; + + my $svgText = createTextSVG ( $labelFontFamily, $labelFont, $labelBold, $labelItalic, $labelSize, $color, $labelHalo, $labelHaloColor) ; + + + + + + + # $svgText = createTextSVG ( undef, undef, $$ruleRef{'labelsize'}, $$ruleRef{'labelcolor'}, undef, undef ) ; + if (cv('debug') eq "1") { print "MP LABEL: svg: \"$svgText\"\n" ; } + placeLabelAndIcon ($x, $y, 1, 0, $name, $svgText, "none", 0, 0, "arealabels") ; + } # if size + else { + $mpNotDrawnLabels++ ; + } + } + else { + + } + } + else { + $notDrawnMP++ ; + } + + } # if rule + } # foreach multi + print "$mp multipolygon areas drawn, $notDrawnMP not drawn because they were too small.\n" ; + print "$mpLabels multipolygon labels drawn, $mpNotDrawnLabels not drawn because belonging areas were too small.\n" ; +} + +# ------------------------------------------------------------------------------------------ + +sub preprocessMultipolygons { +# +# preprecess all multipolygons +# + + my ($wayNodesRef, $wayTagsRef) = getWayPointers() ; + my ($relationMembersRef, $relationTagsRef) = getRelationPointers() ; + + foreach my $relId (keys %$relationMembersRef) { + my $isMulti = 0 ; + foreach my $tag (@{$$relationTagsRef{$relId}}) { + if ( ($tag->[0] eq "type") and ($tag->[1] eq "multipolygon") ) { $isMulti = 1 ; } + } + + if ($isMulti) { + if (cv('debug') eq "1") { print "\n---------------------------------------------------\n" ; } + if (cv('debug') eq "1") { print "\nRelation $relId is multipolygon!\n" ; } + + # get inner and outer ways + my (@innerWays) = () ; my (@outerWays) = () ; + foreach my $member ( @{$$relationMembersRef{$relId}} ) { + if ( ($member->[0] eq "way") and ($member->[2] eq "outer") and (defined @{$$wayNodesRef{$member->[1]}} ) ) { push @outerWays, $member->[1] ; } + if ( ($member->[0] eq "way") and ($member->[2] eq "inner") and (defined @{$$wayNodesRef{$member->[1]}} )) { push @innerWays, $member->[1] ; } + } + if (cv('debug') eq "1") { print "OUTER WAYS: @outerWays\n" ; } + if (cv('debug') eq "1") { print "INNER WAYS: @innerWays\n" ; } + + my ($ringsWaysRef, $ringsNodesRef) ; + my @ringWaysInner = () ; my @ringNodesInner = () ; my @ringTagsInner = () ; + # build rings inner + if (scalar @innerWays > 0) { + ($ringsWaysRef, $ringsNodesRef) = buildRings (\@innerWays, 1) ; + @ringWaysInner = @$ringsWaysRef ; + @ringNodesInner = @$ringsNodesRef ; + for (my $ring=0; $ring<=$#ringWaysInner; $ring++) { + if (cv('debug') eq "1") { print "INNER RING $ring: @{$ringWaysInner[$ring]}\n" ; } + my $firstWay = $ringWaysInner[$ring]->[0] ; + if (scalar @{$ringWaysInner[$ring]} == 1) {$wayUsed{$firstWay} = 1 ; } # way will be marked as used/drawn by multipolygon + + @{$ringTagsInner[$ring]} = @{$$wayTagsRef{$firstWay}} ; # ring will be tagged like first contained way + if (cv('debug') eq "1") { + print "tags from first way...\n" ; + foreach my $tag (@{$$wayTagsRef{$firstWay}}) { + print " $tag->[0] - $tag->[1]\n" ; + } + } + if ( (scalar @{$$wayTagsRef{$firstWay}}) == 0 ) { + if (cv('debug') eq "1") { print "tags set to hole in mp.\n" ; } + push @{$ringTagsInner[$ring]}, ["multihole", "yes"] ; + } + } + } + + # build rings outer + my @ringWaysOuter = () ; my @ringNodesOuter = () ; my @ringTagsOuter = () ; + if (scalar @outerWays > 0) { + ($ringsWaysRef, $ringsNodesRef) = buildRings (\@outerWays, 1) ; + @ringWaysOuter = @$ringsWaysRef ; # not necessary for outer + @ringNodesOuter = @$ringsNodesRef ; + for (my $ring=0; $ring<=$#ringWaysOuter; $ring++) { + if (cv('debug') eq "1") { print "OUTER RING $ring: @{$ringWaysOuter[$ring]}\n" ; } + my $firstWay = $ringWaysOuter[$ring]->[0] ; + if (scalar @{$ringWaysOuter[$ring]} == 1) {$wayUsed{$firstWay} = 1 ; } + @{$ringTagsOuter[$ring]} = @{$$relationTagsRef{$relId}} ; # tags from relation + if (cv('debug') eq "1") { + print "tags from relation...\n" ; + foreach my $tag (@{$$relationTagsRef{$relId}}) { + print " $tag->[0] - $tag->[1]\n" ; + } + } + if (scalar @{$$relationTagsRef{$relId}} == 1) { + @{$ringTagsOuter[$ring]} = @{$$wayTagsRef{$firstWay}} ; # ring will be tagged like first way + } + } + } # outer + + my @ringNodesTotal = (@ringNodesInner, @ringNodesOuter) ; + my @ringWaysTotal = (@ringWaysInner, @ringWaysOuter) ; + my @ringTagsTotal = (@ringTagsInner, @ringTagsOuter) ; + + processRings (\@ringNodesTotal, \@ringWaysTotal, \@ringTagsTotal) ; + } # multi + + } # relIds +} + +# ----------------------------------------------------------------------------------------- + +sub processRings { +# +# process rings of multipolygons and create path data for svg +# + my ($ref1, $ref2, $ref3) = @_ ; + my @ringNodes = @$ref1 ; + my @ringWays = @$ref2 ; + my @ringTags = @$ref3 ; + my @polygon = () ; + my @polygonSize = () ; + my @ringIsIn = () ; + my @stack = () ; # all created stacks + my %selectedStacks = () ; # stacks selected for processing + my $actualLayer = 0 ; # for new tags + # rings referenced by array index + + my ($lonRef, $latRef) = getNodePointers() ; + my ($wayNodesRef, $wayTagsRef) = getWayPointers() ; + + # create polygons + if (cv('debug') eq "1") { print "CREATING POLYGONS\n" ; } + for (my $ring = 0 ; $ring <= $#ringWays; $ring++) { + my @poly = () ; + foreach my $node ( @{$ringNodes[$ring]} ) { + push @poly, [$$lonRef{$node}, $$latRef{$node}] ; + } + my ($p) = Math::Polygon->new(@poly) ; + $polygon[$ring] = $p ; + $polygonSize[$ring] = $p->area ; + if (cv('debug') eq "1") { + print " POLYGON $ring - created, size = $polygonSize[$ring] \n" ; + foreach my $tag (@{$ringTags[$ring]}) { + print " $tag->[0] - $tag->[1]\n" ; + } + } + } + + + # create is_in list (unsorted) for each ring + if (cv('debug') eq "1") { print "CALC isIn\n" ; } + for (my $ring1=0 ; $ring1<=$#polygon; $ring1++) { + my $res = 0 ; + for (my $ring2=0 ; $ring2<=$#polygon; $ring2++) { + if ($ring1 < $ring2) { + $res = isIn ($polygon[$ring1], $polygon[$ring2]) ; + if ($res == 1) { + push @{$ringIsIn[$ring1]}, $ring2 ; + if (cv('debug') eq "1") { print " $ring1 isIn $ring2\n" ; } + } + if ($res == 2) { + push @{$ringIsIn[$ring2]}, $ring1 ; + if (cv('debug') eq "1") { print " $ring2 isIn $ring1\n" ; } + } + } + } + } + if (cv('debug') eq "1") { + print "IS IN LIST\n" ; + for (my $ring1=0 ; $ring1<=$#ringNodes; $ring1++) { + if (defined @{$ringIsIn[$ring1]}) { + print " ring $ring1 isIn - @{$ringIsIn[$ring1]}\n" ; + } + } + print "\n" ; + } + + # sort is_in list, biggest first + if (cv('debug') eq "1") { print "SORTING isIn\n" ; } + for (my $ring=0 ; $ring<=$#ringIsIn; $ring++) { + my @isIn = () ; + foreach my $ring2 (@{$ringIsIn[$ring]}) { + push @isIn, [$ring2, $polygonSize[$ring2]] ; + } + @isIn = sort { $a->[1] <=> $b->[1] } (@isIn) ; # sorted array + + my @isIn2 = () ; # only ring numbers + foreach my $temp (@isIn) { + push @isIn2, $temp->[0] ; + } + @{$stack[$ring]} = reverse (@isIn2) ; + push @{$stack[$ring]}, $ring ; # sorted descending and ring self appended + if (cv('debug') eq "1") { print " stack ring $ring sorted: @{$stack[$ring]}\n" ; } + } + + # find tops and select stacks + if (cv('debug') eq "1") { print "SELECTING STACKS\n" ; } + my $actualStack = 0 ; + for (my $stackNumber=0 ; $stackNumber<=$#stack; $stackNumber++) { + # look for top element + my $topElement = $stack[$stackNumber]->[(scalar @{$stack[$stackNumber]} - 1)] ; + my $found = 0 ; + for (my $stackNumber2=0 ; $stackNumber2<=$#stack; $stackNumber2++) { + if ($stackNumber != $stackNumber2) { + foreach my $ring (@{$stack[$stackNumber2]}) { + if ($ring == $topElement) { + $found = 1 ; + if (cv('debug') eq "1") { print " element also found in stack $stackNumber2\n" ; } + } + } + } + } + + if ($found == 0) { + @{$selectedStacks{$actualStack}} = @{$stack[$stackNumber]} ; + $actualStack++ ; + if (cv('debug') eq "1") { print " stack $stackNumber has been selected.\n" ; } + } + + } + + # process selected stacks + + if (cv('debug') eq "1") { print "PROCESS SELECTED STACKS\n" ; } + # while stacks left + while (scalar (keys %selectedStacks) > 0) { + my (@k) = keys %selectedStacks ; + if (cv('debug') eq "1") { print " stacks available: @k\n" ; } + my @nodes = () ; + my @nodesOld ; + my @processedStacks = () ; + + # select one bottom element + my $key = $k[0] ; # key of first stack + if (cv('debug') eq "1") { print " stack nr $key selected\n" ; } + my $ringToDraw = $selectedStacks{$key}[0] ; + if (cv('debug') eq "1") { print " ring to draw: $ringToDraw\n" ; } + + push @nodesOld, @{$ringNodes[$ringToDraw]} ; # outer polygon + push @nodes, [@{$ringNodes[$ringToDraw]}] ; # outer polygon as array + + # and remove ring from stacks; store processed stacks + foreach my $k2 (keys %selectedStacks) { + if ($selectedStacks{$k2}[0] == $ringToDraw) { + shift (@{$selectedStacks{$k2}}) ; + push @processedStacks, $k2 ; + if (scalar @{$selectedStacks{$k2}} == 0) { delete $selectedStacks{$k2} ; } + if (cv('debug') eq "1") { print " removed $ringToDraw from stack $k2\n" ; } + } + } + + # foreach stack in processed stacks + foreach my $k (@processedStacks) { + # if now bottom of a stack is hole, then add this polygon to points + if (defined $selectedStacks{$k}) { + my $tempRing = $selectedStacks{$k}[0] ; + my $temp = $ringTags[$tempRing]->[0]->[0] ; + if (cv('debug') eq "1") { print " testing for hole: stack $k, ring $tempRing, tag $temp\n" ; } + if ($ringTags[$tempRing]->[0]->[0] eq "multihole") { + push @nodesOld, @{$ringNodes[$tempRing]} ; + push @nodes, [@{$ringNodes[$tempRing]}] ; + # print " nodes so far: @nodes\n" ; + # and remove this element from stack + shift @{$selectedStacks{$k}} ; + if (scalar @{$selectedStacks{$k}} == 0) { delete $selectedStacks{$k} ; } + if (cv('debug') eq "1") { print " ring $tempRing identified as hole\n" ; } + } + } + } + + # add way + + @{$multiNodes{$newId}} = @nodesOld ; + @{$multiTags{$newId}} = @{$ringTags[$ringToDraw]} ; + @{$multiPaths{$newId}} = @nodes ; + + push @{$$wayTagsRef{$newId}}, ["layer", $actualLayer] ; + $actualLayer++ ; + + if (cv('debug') eq "1") { + print " DRAWN: $ringToDraw, wayId $newId\n" ; + foreach my $tag (@{$ringTags[$ringToDraw]}) { + print " k/v $tag->[0] - $tag->[1]\n" ; + } + } + + $newId++ ; + + } # (while) +} + + +1 ; + + diff --git a/mwNodes.pm b/mwNodes.pm new file mode 100644 index 0000000..b24039f --- /dev/null +++ b/mwNodes.pm @@ -0,0 +1,187 @@ +# +# 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 mwNodes ; + +use strict ; +use warnings ; + +use OSM::osm 8.21 ; + +use mwConfig ; +use mwFile ; +use mwRules ; +use mwMap ; +use mwMisc ; +use mwLabel ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( processNodes + createPoiDirectory + ) ; + + +sub processNodes { + + print "drawing nodes...\n" ; + + my $lonRef; my $latRef; my $tagRef ; + ($lonRef, $latRef, $tagRef) = getNodePointers () ; + + foreach my $nodeId (keys %$lonRef) { + my @tags = @{ $$tagRef{$nodeId} } ; + my $tagsString = "" ; + + my $ruleRef = getNodeRule (\@tags) ; + if (defined $ruleRef) { + + # draw disc first ! + if (grep /yes/, $$ruleRef{'disc'}) { + my $svgString = "" ; + if ( $$ruleRef{'discsvgstring'} ne "" ) { + $svgString = $$ruleRef{'discsvgstring'} ; + } + else { + $svgString = "fill=\"$$ruleRef{'disccolor'}\" stroke=\"none\" fill-opacity=\"$$ruleRef{'discopacity'}\"" ; + } + drawCircle ($$lonRef{$nodeId}, $$latRef{$nodeId}, 1, $$ruleRef{'discradius'}, 1, $svgString, 'nodes') ; + } + + if (grep /yes/, $$ruleRef{'circle'}) { + my $svgString = "" ; + if ( $$ruleRef{'circlesvgstring'} ne "" ) { + $svgString = $$ruleRef{'circlesvgstring'} ; + } + else { + $svgString = "fill=\"none\" stroke=\"$$ruleRef{'circlecolor'}\" stroke-width=\"$$ruleRef{'circlethickness'}\"" ; + } + drawCircle ($$lonRef{$nodeId}, $$latRef{$nodeId}, 1, $$ruleRef{'circleradius'}, 1, $svgString, 'nodes') ; + } + + if ( ($$ruleRef{'size'} > 0) and ($$ruleRef{'icon'} eq "none") ) { + my $svgString = "" ; + if ( $$ruleRef{'svgstring'} ne "" ) { + $svgString = $$ruleRef{'svgstring'} ; + } + else { + $svgString = "fill=\"$$ruleRef{'color'}\"" ; + } + + if ( $$ruleRef{'shape'} eq "circle") { + drawCircle ($$lonRef{$nodeId}, $$latRef{$nodeId}, 1, $$ruleRef{'size'}, 0, $svgString, 'nodes') ; + } + elsif ( $$ruleRef{'shape'} eq "square") { + drawSquare ($$lonRef{$nodeId}, $$latRef{$nodeId}, 1, $$ruleRef{'size'}, 0, $svgString, 'nodes') ; + } + elsif ( $$ruleRef{'shape'} eq "triangle") { + drawTriangle ($$lonRef{$nodeId}, $$latRef{$nodeId}, 1, $$ruleRef{'size'}, 0, $svgString, 'nodes') ; + } + elsif ( $$ruleRef{'shape'} eq "diamond") { + drawDiamond ($$lonRef{$nodeId}, $$latRef{$nodeId}, 1, $$ruleRef{'size'}, 0, $svgString, 'nodes') ; + } + } + + if ( ($$ruleRef{'label'} ne "none") or ($$ruleRef{'icon'} ne "none") ) { + my ($labelText, $ref) = createLabel (\@tags, $$ruleRef{'label'}, $$lonRef{$nodeId}, $$latRef{$nodeId}) ; + $labelText = labelTransform ($labelText, $$ruleRef{'labeltransform'}) ; + my $labelSize = $$ruleRef{'labelsize'} ; + my $labelColor = $$ruleRef{'labelcolor'} ; + my $labelFont = $$ruleRef{'labelfont'} ; + my $labelFontFamily = $$ruleRef{'labelfontfamily'} ; + my $labelBold = $$ruleRef{'labelbold'} ; + my $labelItalic = $$ruleRef{'labelitalic'} ; + my $labelHalo = $$ruleRef{'labelhalo'} ; + my $labelHaloColor = $$ruleRef{'labelhalocolor'} ; + my $icon = $$ruleRef{'icon'} ; + my $iconSize = $$ruleRef{'iconsize'} ; + + my $svgText = createTextSVG ( $labelFontFamily, $labelFont, $labelBold, $labelItalic, $labelSize, $labelColor, $labelHalo, $labelHaloColor) ; + + placeLabelAndIcon($$lonRef{$nodeId}, $$latRef{$nodeId}, 0, $$ruleRef{'size'}, $labelText, $svgText, $icon, $iconSize, $iconSize, "nodes") ; + } + + # fill poi directory + + my $thing0 = $$ruleRef{'keyvalue'} ; + my ($thing) = ( $thing0 =~ /.+=(.+)/ ) ; + + my $dirName = getValue ("name", $$tagRef{$nodeId} ) ; + if ( ( cv('poi') eq "1" ) and + ( defined $dirName ) and + ( $$ruleRef{'direxclude'} eq "no") + ) { + $dirName .= " ($thing)" ; + if ( cv('grid') > 0) { + my $sq = gridSquare($$lonRef{$nodeId}, $$latRef{$nodeId}, cv('grid')) ; + if (defined $sq) { + addToPoiHash ($dirName, $sq) ; + } + } + else { + # $poiHash{$dirName} = 1 ; + addToPoiHash ($dirName, undef) ; + } + } + + + } # defined ruleref + } + +} + +# ------------------------------------------------------------------------------------ + +sub createPoiDirectory { + my $poiName ; + my $poiFile ; + $poiName = cv ('out') ; + $poiName =~ s/\.svg/\_pois.txt/ ; + setConfigValue("poiname", $poiName) ; + print "creating poi file $poiName ...\n" ; + open ($poiFile, ">", $poiName) or die ("can't open poi file $poiName\n") ; + + my $ref = getPoiHash() ; + my %poiHash = %$ref ; + + if ( cv('grid') eq "0") { + foreach my $poi (sort keys %poiHash) { + $poi = replaceHTMLCode ( $poi ) ; + print $poiFile "$poi\n" ; + } + } + else { + foreach my $poi (sort keys %poiHash) { + $poi = replaceHTMLCode ( $poi ) ; + print $poiFile "$poi\t" ; + foreach my $square (sort keys %{$poiHash{$poi}}) { + print $poiFile "$square " ; + } + print $poiFile "\n" ; + } + } + close ($poiFile) ; +} + +1 ; + + diff --git a/mwOccupy.pm b/mwOccupy.pm new file mode 100644 index 0000000..ceaa0b2 --- /dev/null +++ b/mwOccupy.pm @@ -0,0 +1,332 @@ +# +# 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 mwOccupy ; + +use strict ; +use warnings ; + +use List::Util qw[min max] ; + +use mwMap ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( boxOccupyLines + boxOccupyArea + boxLinesOccupied + boxAreaOccupied + boxDrawOccupiedAreas + ) ; + + +my $boxSize = 5 ; + +my %box = () ; + + +# ------------------------------------------------------------- + +sub boxOccupyLines { + my ($refCoords, $buffer, $value) = @_ ; + my @coordinates = @$refCoords ; + my @lines = () ; + + for ( my $i = 0; $i < $#coordinates-2; $i += 2 ) { + push @lines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ; + } + + foreach my $line ( @lines ) { + my $x1 = $line->[0] ; + my $y1 = $line->[1] ; + my $x2 = $line->[2] ; + my $y2 = $line->[3] ; + + + # print "$x1, $y1, $x2, $y2\n" ; + + if ( $x1 != $x2) { + + my $m = ($y2 - $y1) / ($x2 - $x1) ; + my $b = $y1 - $m * $x1 ; + + if ( abs ( $x1 - $x2 ) > abs ( $y1 - $y2 ) ) { + + # calc points on x axis + my $x = $x1 ; + my $stepX = $boxSize ; + if ( $x2 < $x1 ) { $stepX = - $boxSize ; } + while ( ( $x >= min ($x1, $x2) ) and ( $x <= max ($x1, $x2) ) ) { + + my $y = $m * $x + $b ; + + # ACTUAL COORDINATE $x, $y + my $ax1 = $x - $buffer ; + my $ax2 = $x + $buffer ; + my $ay1 = $y - $buffer ; + my $ay2 = $y + $buffer ; + boxOccupyArea ($ax1, $ay1, $ax2, $ay2, 0, $value) ; + $x += $stepX ; + } + + } + else { + + # calc points on y axis + my $y = $y1 ; + my $stepY = $boxSize ; + if ( $y2 < $y1 ) { $stepY = - $boxSize ; } + while ( ( $y >= min ($y1, $y2) ) and ( $y <= max ($y1, $y2) ) ) { + + my $x = ($y - $b) / $m ; + + # ACTUAL COORDINATE $x, $y + my $ax1 = $x - $buffer ; + my $ax2 = $x + $buffer ; + my $ay1 = $y - $buffer ; + my $ay2 = $y + $buffer ; + boxOccupyArea ($ax1, $ay1, $ax2, $ay2, 0, $value) ; + + $y += $stepY ; + } + + } # abs + + } + else { + my $x = $x1 ; + + # calc points on y axis + my $y = $y1 ; + my $stepY = $boxSize ; + if ( $y2 < $y1 ) { $stepY = - $boxSize ; } + while ( ( $y >= min ($y1, $y2) ) and ( $y <= max ($y1, $y2) ) ) { + + # ACTUAL COORDINATE $x, $y + my $ax1 = $x - $buffer ; + my $ax2 = $x + $buffer ; + my $ay1 = $y - $buffer ; + my $ay2 = $y + $buffer ; + boxOccupyArea ($ax1, $ay1, $ax2, $ay2, 0, $value) ; + + $y += $stepY ; + } + } + + } +} + + +sub boxLinesOccupied { + my ($refCoords, $buffer) = @_ ; + my @coordinates = @$refCoords ; + my @lines = () ; + my $result = 0 ; + + for ( my $i = 0; $i < $#coordinates-2; $i += 2 ) { + push @lines, [$coordinates[$i], $coordinates[$i+1], $coordinates[$i+2], $coordinates[$i+3]] ; + } + + foreach my $line ( @lines ) { + my $x1 = $line->[0] ; + my $y1 = $line->[1] ; + my $x2 = $line->[2] ; + my $y2 = $line->[3] ; + + + # print "$x1, $y1, $x2, $y2\n" ; + + if ( $x1 != $x2) { + + my $m = ($y2 - $y1) / ($x2 - $x1) ; + my $b = $y1 - $m * $x1 ; + + if ( abs ( $x1 - $x2 ) > abs ( $y1 - $y2 ) ) { + + # calc points on x axis + my $x = $x1 ; + my $stepX = $boxSize ; + if ( $x2 < $x1 ) { $stepX = - $boxSize ; } + while ( ( $x >= min ($x1, $x2) ) and ( $x <= max ($x1, $x2) ) ) { + + my $y = $m * $x + $b ; + + # ACTUAL COORDINATE $x, $y + my $ax1 = $x - $buffer ; + my $ax2 = $x + $buffer ; + my $ay1 = $y - $buffer ; + my $ay2 = $y + $buffer ; + my $tmp = boxAreaOccupied ($ax1, $ay1, $ax2, $ay2) ; + if ($tmp > $result) { $result = $tmp ; } + $x += $stepX ; + } + + } + else { + + # calc points on y axis + my $y = $y1 ; + my $stepY = $boxSize ; + if ( $y2 < $y1 ) { $stepY = - $boxSize ; } + while ( ( $y >= min ($y1, $y2) ) and ( $y <= max ($y1, $y2) ) ) { + + my $x = ($y - $b) / $m ; + + # ACTUAL COORDINATE $x, $y + my $ax1 = $x - $buffer ; + my $ax2 = $x + $buffer ; + my $ay1 = $y - $buffer ; + my $ay2 = $y + $buffer ; + my $tmp = boxAreaOccupied ($ax1, $ay1, $ax2, $ay2) ; + if ($tmp > $result) { $result = $tmp ; } + + $y += $stepY ; + } + + } # abs + + } + else { + my $x = $x1 ; + + # calc points on y axis + my $y = $y1 ; + my $stepY = $boxSize ; + if ( $y2 < $y1 ) { $stepY = - $boxSize ; } + while ( ( $y >= min ($y1, $y2) ) and ( $y <= max ($y1, $y2) ) ) { + + # ACTUAL COORDINATE $x, $y + my $ax1 = $x - $buffer ; + my $ax2 = $x + $buffer ; + my $ay1 = $y - $buffer ; + my $ay2 = $y + $buffer ; + my $tmp = boxAreaOccupied ($ax1, $ay1, $ax2, $ay2) ; + if ($tmp > $result) { $result = $tmp ; } + + $y += $stepY ; + } + } + + } + return $result ; +} + + +# ------------------------------------------------------------- + +sub boxOccupyArea { + my ($x1, $y1, $x2, $y2, $buffer, $value) = @_ ; + + if ( $x2 < $x1) { + my $tmp = $x1 ; + $x1 = $x2 ; + $x2 = $tmp ; + } + if ( $y2 < $y1) { + my $tmp = $y1 ; + $y1 = $y2 ; + $y2 = $tmp ; + } + + $x1 -= $buffer ; + $x2 += $buffer ; + $y1 -= $buffer ; + $y2 += $buffer ; + + for ( my $x = $x1; $x <= $x2; $x += $boxSize) { + for ( my $y = $y1; $y <= $y2; $y += $boxSize) { + my $bx = int ( $x / $boxSize ) ; + my $by = int ( $y / $boxSize ) ; + $box{$bx}{$by} = $value ; + # print "box $bx, $by occupied\n" ; + } + } + + return ; +} + + +sub boxAreaOccupied { + my ($x1, $y1, $x2, $y2) = @_ ; + my $result = 0 ; + + if ( $x2 < $x1) { + my $tmp = $x1 ; + $x1 = $x2 ; + $x2 = $tmp ; + } + if ( $y2 < $y1) { + my $tmp = $y1 ; + $y1 = $y2 ; + $y2 = $tmp ; + } + + for ( my $x = $x1; $x <= $x2; $x += $boxSize) { + my $bx = int ($x / $boxSize) ; + for ( my $y = $y1; $y <= $y2; $y += $boxSize) { + my $by = int ($y / $boxSize) ; + # print " $bx, $by\n" ; + if ( defined $box{$bx}{$by} ) { + if ( $box{$bx}{$by} > $result ) { + # print "check box $bx, $by\n" ; + $result = $box{$bx}{$by} ; + } + } + } + } + return $result ; +} + + +# ------------------------------------------------------------- + + +sub boxDrawOccupiedAreas { + my $format1 = "fill=\"red\" fill-opacity=\"0.3\" " ; + my $format2 = "fill=\"blue\" fill-opacity=\"0.3\" " ; + my $format3 = "fill=\"green\" fill-opacity=\"0.5\" " ; + foreach my $bx ( sort {$a <=> $b} keys %box ) { + foreach my $by ( sort {$a <=> $b} keys %{$box{$bx}} ) { + my $x1 = $bx * $boxSize ; + my $x2 = $x1 + $boxSize ; + my $y1 = $by * $boxSize ; + my $y2 = $y1 + $boxSize ; + + if ( $box{$bx}{$by} == 1) { + drawRect ($x1, $y1, $x2, $y2, 0, $format1, "occupied") ; + } + elsif ( $box{$bx}{$by} == 2) { + drawRect ($x1, $y1, $x2, $y2, 0, $format2, "occupied") ; + } + else { + drawRect ($x1, $y1, $x2, $y2, 0, $format3, "occupied") ; + } + # print "occupied $bx, $by\n" ; + } + } + +} + + +1 ; + + diff --git a/mwRelations.pm b/mwRelations.pm new file mode 100644 index 0000000..3ecaf93 --- /dev/null +++ b/mwRelations.pm @@ -0,0 +1,326 @@ +# +# 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 mwRelations ; + +use strict ; +use warnings ; + +use mwMap ; +use mwRules ; +use mwFile ; +use mwMisc ; +use mwLabel ; +use mwConfig ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( processRoutes + + ) ; + +my $pathNumber = 0 ; + +my %iconSizeX = () ; +my %iconSizeY = () ; + + +# -------------------------------------------------------------------------- + +sub processRoutes { +# +# process route data +# + my %routeColors = () ; # will point to arrays of colors per route type + my %actualColorIndex = () ; # which color is next + my %colorNumber = () ; # number of colors per route type + my %wayRouteLabels = () ; # labels to be used per way + my %wayRouteIcons = () ; # icons to be used per way + my (%iconSizeX, %iconSizeY) ; + + print "processing routes...\n" ; + + # init before relation processing + # get colors per type and set actual index + + my $ref = getRouteColors() ; + %routeColors = %$ref ; + foreach my $type (keys %routeColors) { + $colorNumber{$type} = scalar @{$routeColors{$type}} ; + $actualColorIndex{$type} = 0 ; + } + + my ($lonRef, $latRef) = getNodePointers() ; + my ($wayNodesRef, $wayTagsRef) = getWayPointers() ; + my ($relationMembersRef, $relationTagsRef) = getRelationPointers() ; + + foreach my $relId (keys %$relationTagsRef) { + my $relationType = getValue ("type", $$relationTagsRef{$relId} ) ; + if ( ! defined $relationType ) { $relationType = "" ; } + + if ( ( $relationType eq "route" ) and ( (cv('relid') == $relId) or (cv('relid') == 0) ) ) { + + my $ruleRef = getRouteRule( $$relationTagsRef{$relId} ) ; + + if (defined $ruleRef) { + + # new route detected + if (cv('debug') eq "1" ) { print "ROUTE: rule found for $relId, $$ruleRef{'type'}.\n" ; } + + # try to get color from relation tags first + # + my $color = getValue ("color", $$relationTagsRef{$relId} ) ; + if ( ! defined $color) { + $color = getValue ("colour", $$relationTagsRef{$relId} ) ; + } + + # no color yet, then get color from rule + # + if ( ! defined $color) { + if (cv('debug') eq "1" ) { print "ROUTE: actual color index: $actualColorIndex{ $$ruleRef{'type'} }\n" ; } + $color = $routeColors{ $$ruleRef{'type'} }[$actualColorIndex{ $$ruleRef{'type'} }] ; + $actualColorIndex{ $$ruleRef{'type'} } = ($actualColorIndex{ $$ruleRef{'type'} } + 1) % $colorNumber{ $$ruleRef{'type'} } ; + } + if (cv('debug') eq "1" ) { print "ROUTE: $relId final color: $color\n" ; } + + # find icon + my $iconName = getValue ("ref", $$relationTagsRef{$relId} ) ; + if ( ! defined $iconName ) { + getValue ("name", $$relationTagsRef{$relId} ) + } + if ( ! defined $iconName) { $iconName = "" ; } + + # look for route icon. svg first, then png + + my $file ; + $iconName = cv('routeicondir') . $$ruleRef{'type'} . "-" . $iconName . ".svg" ; + my $iconResult = open ($file, "<", $iconName) ; + # print " trying $iconName\n" ; + if ($iconResult) { + if (cv('debug') eq "1") { print "ROUTE: icon $iconName found!\n" ; } + close ($file) ; + } + + if ( ! $iconResult) { + $iconName =~ s/.svg/.png/ ; + # print " trying $iconName\n" ; + $iconResult = open ($file, "<", $iconName) ; + if ($iconResult) { + if (cv('debug') eq "1") { print "ROUTE: icon $iconName found!\n" ; } + close ($file) ; + } + } + + if ($iconResult) { + my ($x, $y) ; undef $x ; undef $y ; + if (grep /.svg/, $iconName) { + ($x, $y) = sizeSVG ($iconName) ; + if ( ($x == 0) or ($y == 0) ) { + $x = 32 ; $y = 32 ; + print "WARNING: size of file $iconName could not be determined. Set to 32px x 32px\n" ; + } + } + + if (grep /.png/, $iconName) { + ($x, $y) = sizePNG ($iconName) ; + } + + $iconSizeX{$iconName} = $x ; + $iconSizeY{$iconName} = $y ; + } + + my ($label, $ref) = createLabel ( $$relationTagsRef{$relId}, $$ruleRef{'label'} ) ; + + my $printIcon = "" ; if ($iconResult) { $printIcon = $iconName ; } + + if (cv('verbose') eq "1" ) { + printf "ROUTE: route %10s %10s %10s %30s %40s\n", $relId, $$ruleRef{'type'}, $color, $label, $printIcon ; + } + + # collect ways + + my $mRef = getAllMembers ($relId, 0) ; + my @tempMembers = @$mRef ; + + my @relWays = () ; + foreach my $member (@tempMembers) { + if ( ( ($member->[2] eq "none") or ($member->[2] eq "route") ) and ($member->[0] eq "way") ) { push @relWays, $member->[1] ; } + if ( ( ($member->[2] eq "forward") or ($member->[2] eq "backward") ) and ($member->[0] eq "way") ) { push @relWays, $member->[1] ; } + + # TODO diversions, shortcuts? + + # stops + if ( (grep /stop/, $member->[2]) and ($member->[0] eq "node") ) { + if ( ( $$ruleRef{'nodesize'} > 0) and (defined $$latRef{$member->[1]}) and (defined $$lonRef{$member->[1]}) ) { + my $svgString = "fill=\"$color\" " ; + drawCircle ($$lonRef{$member->[1]}, $$latRef{$member->[1]}, 1, $$ruleRef{'nodesize'}, 0, $svgString, 'routes') ; + } + } + } + + if (cv('debug') eq "1" ) { print "ROUTE: ways: @relWays\n" ; } + + foreach my $w (@relWays) { + + my $op = $$ruleRef{'opacity'} / 100 ; + my $width = $$ruleRef{'size'} ; + my $linecap = $$ruleRef{'linecap'} ; + my $dashString = "" ; + my $dash = $$ruleRef{'dash'} ; + if ( $dash ne "") { $dashString = "stroke-dasharray=\"$dash\" " ; } + my $svgString = "stroke=\"$color\" stroke-opacity=\"$op\" stroke-width=\"$width\" fill=\"none\" stroke-linejoin=\"round\" stroke-linecap=\"$linecap\" " . $dashString ; + + drawWay ($$wayNodesRef{$w}, 1, $svgString, "routes", undef) ; + + # collect labels and icons per way + # + $wayRouteLabels{$w}{$label} = 1 ; + if ($iconResult) { + $wayRouteIcons{$w}{$iconName} = 1 ; + } + } + + } # rule found + if (cv('debug') eq "1") { print "\n" ; } + } # rel route + } # relation + + # label route ways after all relations have been processed + foreach my $w (keys %wayRouteLabels) { + if ( (defined $$wayNodesRef{$w}) and (scalar @{$$wayNodesRef{$w}} > 1) ) { + my $label = "" ; + foreach my $l (keys %{$wayRouteLabels{$w}}) { + $label .= $l . " " ; + } + + my @way = @{$$wayNodesRef{$w}} ; + if ($$lonRef{$way[0]} > $$lonRef{$way[-1]}) { + @way = reverse (@way) ; + } + + if (labelFitsWay ( \@way, $label, cv('routelabelfont'), cv('routelabelsize') ) ) { + my $pathName = "RoutePath" . $pathNumber ; + $pathNumber++ ; + + my @points = nodes2Coordinates( @way ) ; + + if ( ! coordsOut (@points) ) { + + createPath ($pathName, \@points, "definitions") ; + + my $size = cv('routelabelsize') ; + my $font = cv('routelabelfont') ; + my $fontFamily = cv('routelabelfontfamily') ; + my $color = cv('routelabelcolor') ; + + my $svgText = createTextSVG ( $fontFamily, $font, $size, $color, undef, undef) ; + pathText ($svgText, $label, $pathName, cv('routelabeloffset'), "middle", 50, "routes") ; + } + } + } + } + + # place icons + foreach my $w (keys %wayRouteIcons) { + my $offset = 0 ; + my $nodeNumber = scalar @{$$wayNodesRef{$w}} ; + if ($nodeNumber > 1) { + my $node = $$wayNodesRef{$w}[int ($nodeNumber / 2)] ; + my $num = scalar (keys %{$wayRouteIcons{$w}}) ; + $offset = int (-($num-1)* cv('routeicondist') / 2) ; + + foreach my $iconName (keys %{$wayRouteIcons{$w}}) { + + my $size = 40 ; + placeLabelAndIcon ($$lonRef{$node}, $$latRef{$node}, $offset, $size, "", "", $iconName, $iconSizeX{$iconName}, $iconSizeY{$iconName}, "routes") ; + + $offset += cv('routeicondist') ; + } + } + } +} + +# -------------------------------------------------------------------------- + +sub getAllMembers { +# +# get all members of a relation recursively +# takes rel id and nesting level +# retruns ref to array with all members +# + my ($relId, $nestingLevel) = @_ ; + my @allMembers = () ; + my $maxNestingLevel = 20 ; + + my ($relationMembersRef, $relationTagsRef) = getRelationPointers() ; + + if ($nestingLevel > $maxNestingLevel) { + print "ERROR/WARNING nesting level of relations too deep. recursion stopped at depth $maxNestingLevel! relId=$relId\n" ; + } + else { + foreach my $member ( @{$$relationMembersRef{$relId}} ) { + if ( ($member->[0] eq "way") or ($member->[0] eq "node") ) { + push @allMembers, $member ; + } + if ( $member->[0] eq "relation" ) { + my $ref = getAllMembers ($member->[1], $nestingLevel+1) ; + push @allMembers, @$ref ; + } + } + } + return \@allMembers ; +} + +sub labelFitsWay { + my ($refWayNodes, $text, $font, $size) = @_ ; + my @wayNodes = @$refWayNodes ; + + my ($lonRef, $latRef) = getNodePointers() ; + + # calc waylen + my $wayLength = 0 ; # in pixels + for (my $i=0; $i<$#wayNodes; $i++) { + my ($x1, $y1) = convert ($$lonRef{$wayNodes[$i]}, $$latRef{$wayNodes[$i]}) ; + my ($x2, $y2) = convert ($$lonRef{$wayNodes[$i+1]}, $$latRef{$wayNodes[$i+1]}) ; + $wayLength += sqrt ( ($x2-$x1)**2 + ($y2-$y1)**2 ) ; + } + + + # calc label len + my $labelLength = length ($text) * cv('ppc') / 10 * $size ; # in pixels + + my $fit ; + if ($labelLength < $wayLength) { $fit="fit" ; } else { $fit = "NOFIT" ; } + # print "labelFitsWay: $fit, $text, labelLen = $labelLength, wayLen = $wayLength\n" ; + + if ($labelLength < $wayLength) { + return 1 ; + } + else { + return 0 ; + } +} + + +1 ; + + diff --git a/mwRules.pm b/mwRules.pm new file mode 100644 index 0000000..07b8885 --- /dev/null +++ b/mwRules.pm @@ -0,0 +1,909 @@ +# +# 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 mwRules ; + +use strict ; +use warnings ; + +use mwConfig ; +use mwMap ; +use mwMisc ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( readRules + getNodeRule + printNodeRules + getWayRule + printWayRules + getAreaRule + printAreaRules + printValidObjectProperties + getRouteColors + getRouteRule + printRouteRules + adaptRuleSizes + createLegend + ) ; + +my @validNodeProperties = ( + ["keyValue","key and value like [amenity=hospital]"], + ["color","color of node i.e. [black]"], + ["size","size of node i.e. [50]"], + ["shape","shape of node [circle|disc|triangle|diamond|rectangle]"], + ["svgString","svg format of shape [valid svg string]"], + ["circle","add a circle to the node [yes|no]"], + ["circleColor","color of the circle i.e. [blue]"], + ["circleRadius","circle radius in meters i.e. [1000]"], + ["circleThickness","thickness of the circle i.e. [5]"], + ["circleSVGString","format of the circle []"], + ["disc","add a disc to the node [yes|no]"], + ["discColor","color of the disc i.e. [green]"], + ["discOpacity","opacity of the disc [0..100]"], + ["discRadius","radius of disc in meters i.e. [5000]"], + ["discSVGString","format of the disc []"], + ["label","label for the node like [name|ref]"], + ["labelColor","color for label text i.e. [white]"], + ["labelSize","size of label text i.e. [20]"], + ["labelFont","font for label"], + ["labelFontFamily","font family for label"], + ["labelOffset","distance of label to node i.e. [10]"], + ["labelBold","bold font for label"], + ["labelItalic","italic font for label"], + ["labelHalo","halo for label, width in pixels"], + ["labelHaloColor","color for halo"], + ["labelTransform","perl code for label name transformation"], + ["legend","is this object to be listed in map legend? [yes|no]"], + ["legendLabel","label text of object in legend i.e. [city]"], + ["icon","icon to use for node, overrides shape i.e. [icondir/icon.svg]"], + ["iconSize","size of the icon i.e. [40]"], + ["shieldName","NOT YET IMPLEMENTED"], + ["shieldSize","NOT YET IMPLEMENTED"], + ["shieldLabel","NOT YET IMPLEMENTED"], + ["fromScale","rule will only applied if scale is bigger than fromScale i.e. [5000]"], + ["toScale","rule will only applied if scale is lower than fromScale i.e. [25000]"], + ["direxclude","should these objects be excluded from directory? [yes|no]"] + ) ; + +my @validWayProperties = ( + ["keyValue","key and value like [highway=residential]"], + ["color","color for the way i.e. [gray]"], + ["size","size of the way i.e. [15]"], + ["dash","svg dash array for the way i.e. [20,20]; old mapgen values are also possible"], + ["dashCap","linecap shape for dashes like [butt|round|square]"], + ["borderColor","color of the border of the way i.e. [black]"], + ["borderSize","thickness os the border i.e. [2]"], + ["label","label to be used i.e. [name|ref]"], + ["labelColor","color of label text i.e. [blue]"], + ["labelSize","size of the label i.e. [20]"], + ["labelFont","font for label"], + ["labelFontFamily","font family for label"], + ["labelOffset","distance of label to middle of way i.e. [5]"], + ["labelBold","bold font for label"], + ["labelItalic","italic font for label"], + ["labelHalo","halo for label, width in pixels"], + ["labelHaloColor","color for halo"], + ["labelTransform","perl code for label name transformation"], + ["legend","is this object to be listed in map legend? [yes|no]"], + ["legendLabel","label text of object in legend i.e. [Highway]"], + + ["svgStringBottom","format of lower way part (i.e. border) []"], + ["svgStringTop","format of upper way part []"], + ["bottomBorder","NOT YET IMPLEMENTED"], + + ["fromScale","rule will only applied if scale is bigger than fromScale i.e. [5000]"], + ["toScale","rule will only applied if scale is lower than fromScale i.e. [25000]"], + + ["direxclude","should these objects be excluded from directory? [yes|no]"] + ) ; + +my @validAreaProperties = ( + ["keyValue","key and value of object i.e. [amenity=parking]"], + ["color","color of area i.e. [lightgrey]"], + ["icon","icon for fill pattern to be used i.e. [icondir/parking.svg]"], + ["label", "label text to be rendered i.e. [name]"] , + ["labelFont","font for label"], + ["labelFontFamily","font family for label"], + ["labelColor", "color of label i.e. [green]"] , + ["labelSize", "size of label text i.e. [20]"] , + ["labelBold","bold font for label"], + ["labelItalic","italic font for label"], + ["labelHalo","halo for label, width in pixels"], + ["labelHaloColor","color for halo"], + ["labelTransform","perl code for label name transformation"], + ["base","should this object be drawn underneath other objects? (applies for landuse residential i.e.) [yes|no]"], + ["svgString","format of area []"], + ["legend","is this object to be listed in map legend? [yes|no]"], + ["legendLabel","label text of object in legend i.e. [Parking]"], + ["fromScale","rule will only applied if scale is bigger than fromScale i.e. [5000]"], + ["toScale","rule will only applied if scale is lower than fromScale i.e. [25000]"] + ) ; + + +my @validRouteProperties = ( + ["type","type of route like [bus|hiking]"], + ["color","color of route like [red]"], + ["size","size of route i.e. [10]"], + ["dash","svg dash array style like [20,20]"], + ["linecap","linecap style [butt|round|square]"], + ["opacity","opacity of the route [0..100]"], + ["label","label to be used like [ref]"], + ["labelFont","font for label"], + ["labelFontFamily","font family for label"], + ["labelSize","size of the label i.e. [15]"], + ["nodeSize","size of nodes belonging to route i.e. [20]"], + ["fromScale","rule will only applied if scale is bigger than fromScale i.e. [5000]"], + ["toScale","rule will only applied if scale is lower than fromScale i.e. [25000]"] + ) ; + + +my %nodeRules = () ; +my %areaRules = () ; +my %wayRules = () ; +my %routeRules = () ; +my $nodeNr = 0 ; +my $areaNr = 0 ; +my $wayNr = 0 ; +my $routeNr = 0 ; + +my $line ; +my $ruleFile ; + +# --------------------------------------------------------------------------------------- + +sub printValidObjectProperties { + + print "\nValid Object Properties\n" ; + + print "\nNodes\n-----\n" ; + foreach my $p (sort {$a->[0] cmp $b->[0]} @validNodeProperties) { + printf "%-20s %s\n", $p->[0], $p->[1] ; + } + print "\nWays\n----\n" ; + foreach my $p (sort {$a->[0] cmp $b->[0]} @validWayProperties) { + printf "%-20s %s\n", $p->[0], $p->[1] ; + } + print "\nAreas\n-----\n" ; + foreach my $p (sort {$a->[0] cmp $b->[0]} @validAreaProperties) { + printf "%-20s %s\n", $p->[0], $p->[1] ; + } + print "\nRoutes\n-----\n" ; + foreach my $p (sort {$a->[0] cmp $b->[0]} @validRouteProperties) { + printf "%-20s %s\n", $p->[0], $p->[1] ; + } + print "\n" ; +} + + +# --------------------------------------------------------------------------------------- + + +sub readRules { + + my $fileName = cv('style') ; + my $nrr = 0 ; my $wrr = 0 ; my $arr = 0 ; my $rrr = 0 ; my $crr = 0 ; + + print "reading rule file $fileName\n" ; + + my %vnp = () ; + foreach my $p ( @validNodeProperties ) { $vnp{ lc ( $p->[0] ) } = 1 ; } + + my %vwp = () ; + foreach my $p ( @validWayProperties ) { $vwp{ lc ( $p->[0] ) } = 1 ; } + + my %vap = () ; + foreach my $p ( @validAreaProperties ) { $vap{ lc ( $p->[0] ) } = 1 ; } + + my %vrp = () ; + foreach my $p ( @validRouteProperties ) { $vrp{ lc ( $p->[0] ) } = 1 ; } + + openRuleFile($fileName) ; + while (defined $line) { + if ( grep /^rule node/i, $line ) { + $nodeNr++ ; + $nrr++ ; + getRuleLine() ; + + # set defaults first + $nodeRules{ $nodeNr }{ 'size' } = cv( 'ruleDefaultNodeSize' ) ; + $nodeRules{ $nodeNr }{ 'color' } = cv( 'ruleDefaultNodeColor' ) ; + $nodeRules{ $nodeNr }{ 'shape' } = cv( 'ruleDefaultNodeShape' ) ; + + $nodeRules{ $nodeNr }{ 'label' } = cv( 'ruleDefaultNodeLabel' ) ; + $nodeRules{ $nodeNr }{ 'labelfont' } = cv( 'ruleDefaultNodeLabelFont' ) ; + $nodeRules{ $nodeNr }{ 'labelfontfamily' } = cv( 'ruleDefaultNodeLabelFontFamily' ) ; + $nodeRules{ $nodeNr }{ 'labelsize' } = cv( 'ruleDefaultNodeLabelSize' ) ; + $nodeRules{ $nodeNr }{ 'labelitalic' } = "no" ; + $nodeRules{ $nodeNr }{ 'labelbold' } = "no" ; + $nodeRules{ $nodeNr }{ 'labelhalo' } = 0 ; + $nodeRules{ $nodeNr }{ 'labelhalocolor' } = "white" ; + $nodeRules{ $nodeNr }{ 'labeltransform' } = "" ; + $nodeRules{ $nodeNr }{ 'icon' } = "none" ; + $nodeRules{ $nodeNr }{ 'iconsize' } = cv( 'ruleDefaultNodeIconSize' ) ; + $nodeRules{ $nodeNr }{ 'legend' } = "no" ; + $nodeRules{ $nodeNr }{ 'shieldname' } = "none" ; + $nodeRules{ $nodeNr }{ 'svgstring' } = "" ; + $nodeRules{ $nodeNr }{ 'legend' } = "no" ; + $nodeRules{ $nodeNr }{ 'legendlabel' } = "" ; + + $nodeRules{ $nodeNr }{ 'circle' } = 'no' ; + $nodeRules{ $nodeNr }{ 'circlecolor' } = 'black' ; + $nodeRules{ $nodeNr }{ 'circleradius' } = 1000 ; + $nodeRules{ $nodeNr }{ 'circlethickness' } = 10 ; + $nodeRules{ $nodeNr }{ 'circlesvgstring' } = "" ; + + $nodeRules{ $nodeNr }{ 'disc' } = 'no' ; + $nodeRules{ $nodeNr }{ 'disccolor' } = 'red' ; + $nodeRules{ $nodeNr }{ 'discopacity' } = 50 ; + $nodeRules{ $nodeNr }{ 'discradius' } = 1000 ; + $nodeRules{ $nodeNr }{ 'discradius' } = 1000 ; + $nodeRules{ $nodeNr }{ 'discsvgstring' } = '' ; + + $nodeRules{ $nodeNr }{ 'fromscale' } = cv ('ruledefaultnodefromscale') ; + $nodeRules{ $nodeNr }{ 'toscale' } = cv ('ruledefaultnodetoscale') ; + + $nodeRules{ $nodeNr }{ 'direxclude' } = cv('direxcludedefault') ; + + while ( ( defined $line) and ( ! grep /^rule/i, $line) ) { + my ($k, $v) = ( $line =~ /(.+?)=(.+)/ ) ; + if ( ( ! defined $k ) or ( ! defined $v ) ) { + print "WARNING: could not parse rule line: $line" ; + } + else { + $k = lc ( $k ) ; + $nodeRules{ $nodeNr }{ $k } = $v ; + if ( ! defined $vnp{$k} ) { print "WARNING: $k is not a valid node property!\n" ; } + } + getRuleLine() ; + } + if ( ! defined $nodeRules{ $nodeNr }{ 'keyvalue' } ) { die "ERROR: rule without keyValue detected!\n" ; } + + } # node + + elsif ( grep /^rule way/i, $line ) { + + $wayNr++ ; + $wrr++ ; + getRuleLine() ; + + # set defaults first + $wayRules{ $wayNr }{ 'label' } = cv( 'ruleDefaultWayLabel' ) ; + $wayRules{ $wayNr }{ 'labelfont' } = cv( 'ruleDefaultWayLabelFont' ) ; + $wayRules{ $wayNr }{ 'labelfontfamily' } = cv( 'ruleDefaultWayLabelFontFamily' ) ; + $wayRules{ $wayNr }{ 'labelsize' } = cv( 'ruleDefaultWayLabelSize' ) ; + $wayRules{ $wayNr }{ 'labelcolor' } = cv( 'ruleDefaultWayLabelColor' ) ; + $wayRules{ $wayNr }{ 'labelfont' } = cv( 'ruleDefaultWayLabelFont' ) ; + $wayRules{ $wayNr }{ 'labeloffset' } = cv( 'ruleDefaultWayLabelOffset' ) ; + $wayRules{ $wayNr }{ 'labelitalic' } = "no" ; + $wayRules{ $wayNr }{ 'labelbold' } = "no" ; + $wayRules{ $wayNr }{ 'labelhalo' } = 0 ; + $wayRules{ $wayNr }{ 'labelhalocolor' } = "white" ; + $wayRules{ $wayNr }{ 'labeltransform' } = "" ; + $wayRules{ $wayNr }{ 'legend' } = "no" ; + $wayRules{ $wayNr }{ 'legendlabel' } = "" ; + $wayRules{ $wayNr }{ 'color' } = cv( 'ruleDefaultWayColor' ) ; + $wayRules{ $wayNr }{ 'size' } = cv( 'ruleDefaultWaySize' ) ; + $wayRules{ $wayNr }{ 'bordercolor' } = cv( 'ruleDefaultWayBorderColor' ) ; + $wayRules{ $wayNr }{ 'bordersize' } = cv( 'ruleDefaultWayBorderSize' ) ; + $wayRules{ $wayNr }{ 'dash' } = cv( 'ruleDefaultWayDash' ) ; + $wayRules{ $wayNr }{ 'dashcap' } = cv( 'ruleDefaultWayDashCap' ) ; + + $wayRules{ $wayNr }{ 'svgstringtop' } = "" ; + $wayRules{ $wayNr }{ 'svgstringbottom' } = "" ; + + $wayRules{ $wayNr }{ 'fromscale' } = cv ('ruledefaultwayfromscale') ; + $wayRules{ $wayNr }{ 'toscale' } = cv ('ruledefaultwaytoscale') ; + + $wayRules{ $wayNr }{ 'direxclude' } = cv('direxcludedefault') ; + + while ( ( defined $line) and ( ! grep /^rule/i, $line) ) { + my ($k, $v) = ( $line =~ /(.+?)=(.+)/ ) ; + if ( ( ! defined $k ) or ( ! defined $v ) ) { + print "WARNING: could not parse rule line: $line" ; + } + else { + $k = lc ( $k ) ; + $wayRules{ $wayNr }{ $k } = $v ; + if ( ! defined $vwp{$k} ) { print "WARNING: $k is not a valid way property!\n" ; } + } + getRuleLine() ; + } + if ( ! defined $wayRules{ $wayNr }{ 'keyvalue' } ) { die "ERROR: rule without keyValue detected!\n" ; } + + } # way + + elsif ( grep /^rule area/i, $line ) { + $areaNr++ ; + $arr++ ; + getRuleLine() ; + + # set defaults first + $areaRules{ $areaNr }{ 'label' } = "none" ; + $areaRules{ $areaNr }{ 'labelfont' } = cv( 'ruleDefaultAreaLabelFont' ) ; + $areaRules{ $areaNr }{ 'labelfontfamily' } = cv( 'ruleDefaultAreaLabelFontFamily' ) ; + $areaRules{ $areaNr }{ 'labelcolor' } = "black" ; + $areaRules{ $areaNr }{ 'labelsize' } = 30 ; + $areaRules{ $areaNr }{ 'labelitalic' } = "no" ; + $areaRules{ $areaNr }{ 'labelbold' } = "no" ; + $areaRules{ $areaNr }{ 'labelhalo' } = 0 ; + $areaRules{ $areaNr }{ 'labelhalocolor' } = "white" ; + $areaRules{ $areaNr }{ 'labeltransform' } = "" ; + $areaRules{ $areaNr }{ 'color' } = cv( 'ruleDefaultAreaColor') ; + $areaRules{ $areaNr }{ 'icon' } = "none" ; + $areaRules{ $areaNr }{ 'base' } = "no" ; + $areaRules{ $areaNr }{ 'svgstring' } = "" ; + $areaRules{ $areaNr }{ 'minsize' } = cv ('ruledefaultareaminsize') ; + $areaRules{ $areaNr }{ 'legend' } = "no" ; + $areaRules{ $areaNr }{ 'legendlabel' } = "" ; + $areaRules{ $areaNr }{ 'fromscale' } = cv ('ruledefaultareafromscale') ; + $areaRules{ $areaNr }{ 'toscale' } = cv ('ruledefaultareatoscale') ; + + while ( ( defined $line) and ( ! grep /^rule/i, $line) ) { + my ($k, $v) = ( $line =~ /(.+?)=(.+)/ ) ; + if ( ( ! defined $k ) or ( ! defined $v ) ) { + print "WARNING: could not parse rule line: $line" ; + } + else { + $k = lc ( $k ) ; + $areaRules{ $areaNr }{ $k } = $v ; + if ( ! defined $vap{$k} ) { print "WARNING: $k is not a valid area property!\n" ; } + + if ($k eq "icon") { mwMap::addAreaIcon ($v) ; } + } + getRuleLine() ; + } + if ( ! defined $areaRules{ $areaNr }{ 'keyvalue' } ) { die "ERROR: rule without keyValue detected!\n" ; } + + } # area + + elsif ( grep /^rule route/i, $line ) { + $routeNr++ ; + $rrr++ ; + getRuleLine() ; + + # set defaults first + $routeRules{ $routeNr }{ 'color' } = cv( 'ruleDefaultRouteColor' ) ; + $routeRules{ $routeNr }{ 'size' } = cv( 'ruleDefaultRouteSize' ) ; + $routeRules{ $routeNr }{ 'dash' } = cv( 'ruleDefaultRouteDash' ) ; + $routeRules{ $routeNr }{ 'linecap' } = cv( 'ruleDefaultRouteLinecap' ) ; + $routeRules{ $routeNr }{ 'opacity' } = cv( 'ruleDefaultRouteOpacity' ) ; + $routeRules{ $routeNr }{ 'label' } = cv( 'ruleDefaultRouteLabel' ) ; + # $routeRules{ $routeNr }{ 'labelfont' } = cv( 'ruleDefaultRouteLabelFont' ) ; + # $routeRules{ $routeNr }{ 'labelfontfamily' } = cv( 'ruleDefaultRouteLabelFontFamily' ) ; + # $routeRules{ $routeNr }{ 'labelsize' } = cv( 'ruleDefaultRouteLabelSize' ) ; + $routeRules{ $routeNr }{ 'nodesize' } = cv( 'ruleDefaultRouteNodeSize' ) ; + $routeRules{ $routeNr }{ 'fromscale' } = cv( 'ruleDefaultRouteFromScale' ) ; + $routeRules{ $routeNr }{ 'toscale' } = cv( 'ruleDefaultRouteToScale' ) ; + + while ( ( defined $line) and ( ! grep /^rule/i, $line) ) { + my ($k, $v) = ( $line =~ /(.+?)=(.+)/ ) ; + if ( ( ! defined $k ) or ( ! defined $v ) ) { + print "WARNING: could not parse rule line: $line" ; + } + else { + $k = lc ( $k ) ; + $routeRules{ $routeNr }{ $k } = $v ; + if ( ! defined $vrp{$k} ) { print "WARNING: $k is not a valid route property!\n" ; } + } + getRuleLine() ; + } + if ( ! defined $routeRules{ $routeNr }{ 'type' } ) { die "ERROR: route rule without type detected!\n" ; } + + } # route + + elsif ( grep /^rule config/i, $line ) { + $crr++ ; + my ($key, $value) = ( $line =~ /^rule config\s+(.+)=(.+)/i ) ; + if ( (defined $key) and (defined $value) ) { + setConfigValue ($key, $value) ; + if ( cv('debug') eq "1" ) { + print "RULES: config changed $key=$value\n" ; + } + } + getRuleLine() ; + } # config + + else { + getRuleLine() ; + } + + } + + + close ($ruleFile) ; + + print "rules read: $nrr nodes, $wrr ways, $arr areas, $rrr routes and $crr configs\n\n" ; + +} + +sub getNodeRule { + + # takes tagref and returns hashref to rule properties + + my $tagRef = shift ; + + my $scale = getScale() ; + if ( cv('rulescaleset') != 0 ) { $scale = cv('rulescaleset') ; } + # print "GNR: scale: $scale\n" ; + + my $ruleFound ; undef $ruleFound ; + + # print "\n" ; + + RUL2: foreach my $rule ( sort { $a <=> $b } keys %nodeRules) { + # print "rule $rule\n" ; + if ( ( $nodeRules{$rule}{'fromscale'} <= $scale) and ( $nodeRules{$rule}{'toscale'} >= $scale) ) { + + my @kvs = split /;/, $nodeRules{$rule}{'keyvalue'} ; + my $allValid = 1 ; + RUL1: foreach my $kv1 ( @kvs ) { # for each needed + my ($k, $v) = ( $kv1 =~ /(.+)=(.+)/ ) ; + # print " looking for $k=$v\n" ; + my $found = 0 ; + RUL3: foreach my $tag ( @$tagRef) { + # print " actual kvs: $tag->[0]=$tag->[1]\n" ; + if ( ( $tag->[0] eq $k) and ( ( $tag->[1] eq $v) or ( $v eq "*") ) ) { + $found = 1 ; + # print " FOUND\n" ; + last RUL3 ; + } + } # tags + if ( ! $found ) { + $allValid = 0 ; + last RUL1 ; + } + } # kv1 + + if ( $allValid ) { + # print "ALL VALID\n" ; + # return the first rule found + $ruleFound = \%{ $nodeRules{ $rule } } ; + last RUL2 ; + } + + } # scale + + } # all rules + + return ($ruleFound) ; + +} + +sub printNodeRules { + foreach my $n ( sort { $a <=> $b } keys %nodeRules) { + print "node rule $n\n" ; + foreach my $v (sort keys %{$nodeRules{$n}}) { + print " $v=$nodeRules{$n}{$v}\n" ; + } + print "\n" ; + } +} + +# --------------------------------------------------------------------------------------- + + + +sub getWayRule { + + # takes tagref and returns hashref to rule properties + + my $tagRef = shift ; + + my $scale = getScale() ; + if ( cv('rulescaleset') != 0 ) { $scale = cv('rulescaleset') ; } + + my $ruleFound ; undef $ruleFound ; + + RUL5: foreach my $rule ( sort { $a <=> $b } keys %wayRules) { + # print "rule $rule\n" ; + if ( ( $wayRules{$rule}{'fromscale'} <= $scale) and ( $wayRules{$rule}{'toscale'} >= $scale) ) { + + my @kvs = split /;/, $wayRules{$rule}{'keyvalue'} ; + my $allValid = 1 ; + RUL4: foreach my $kv1 ( @kvs ) { # for each needed + my ($k, $v) = ( $kv1 =~ /(.+)=(.+)/ ) ; + # print " looking for $k=$v\n" ; + my $found = 0 ; + RUL6: foreach my $tag ( @$tagRef) { + # print " actual kvs: $tag->[0]=$tag->[1]\n" ; + if ( ( $tag->[0] eq $k) and ( ( $tag->[1] eq $v) or ( $v eq "*") ) ) { + $found = 1 ; + # print " FOUND\n" ; + last RUL6 ; + } + } # tags + if ( ! $found ) { + $allValid = 0 ; + last RUL4 ; + } + } # kv1 + + if ( $allValid ) { + # print "ALL VALID\n" ; + # return the first rule found + $ruleFound = \%{ $wayRules{ $rule } } ; + last RUL5 ; + } + + } # scale + + } # all rules + + return ($ruleFound) ; + +} + + +sub printWayRules { + foreach my $n ( sort { $a <=> $b } keys %wayRules) { + print "way rule $n\n" ; + foreach my $v (sort keys %{$wayRules{$n}}) { + print " $v=$wayRules{$n}{$v}\n" ; + } + print "\n" ; + } +} + + +# --------------------------------------------------------------------------------------- + + + +sub getAreaRule { + + # takes tagref and returns hashref to rule properties + + my $tagRef = shift ; + + my $scale = getScale() ; + if ( cv('rulescaleset') != 0 ) { $scale = cv('rulescaleset') ; } + + my $ruleFound ; undef $ruleFound ; + + RUL8: foreach my $rule ( sort { $a <=> $b } keys %areaRules) { + # print "rule $rule\n" ; + if ( ( $areaRules{$rule}{'fromscale'} <= $scale) and ( $areaRules{$rule}{'toscale'} >= $scale) ) { + + my @kvs = split /;/, $areaRules{$rule}{'keyvalue'} ; + my $allValid = 1 ; + RUL7: foreach my $kv1 ( @kvs ) { # for each needed + my ($k, $v) = ( $kv1 =~ /(.+)=(.+)/ ) ; + # print " looking for $k=$v\n" ; + my $found = 0 ; + RUL9: foreach my $tag ( @$tagRef) { + # print " actual kvs: $tag->[0]=$tag->[1]\n" ; + if ( ( $tag->[0] eq $k) and ( ( $tag->[1] eq $v) or ( $v eq "*") ) ) { + $found = 1 ; + # print " FOUND\n" ; + last RUL9 ; + } + } # tags + if ( ! $found ) { + $allValid = 0 ; + last RUL7 ; + } + } # kv1 + + if ( $allValid ) { + # print "ALL VALID\n" ; + # return the first rule found + $ruleFound = \%{ $areaRules{ $rule } } ; + last RUL8 ; + } + + } # scale + + } # all rules + + return ($ruleFound) ; + +} + + +sub printAreaRules { + foreach my $n ( sort { $a <=> $b } keys %areaRules) { + print "area rule $n\n" ; + foreach my $v (sort keys %{$areaRules{$n}}) { + print " $v=$areaRules{$n}{$v}\n" ; + } + print "\n" ; + } +} + +# -------------------------------------------------------------------------------- + +sub getRouteRule { + my $tagRef = shift ; + + my $scale = getScale() ; + if ( cv('rulescaleset') != 0 ) { $scale = cv('rulescaleset') ; } + + my $ruleFound ; undef $ruleFound ; + + my $type = getValue ("route", $tagRef) ; + + if (defined $type) { + # print " GRR: $type \n" ; + RULA: foreach my $r ( sort { $a <=> $b } keys %routeRules) { + # print " GRR: $routeRules{$r}{'type'}\n" ; + if ($routeRules{$r}{'type'} eq $type) { + if ( ( $routeRules{$r}{'fromscale'} <= $scale) and ( $routeRules{$r}{'toscale'} >= $scale) ) { + $ruleFound = \%{ $routeRules{ $r } } ; + last RULA ; + } + } + } + + } + + return $ruleFound ; +} + +sub getRouteColors { + my %routeColors = () ; + foreach my $n (keys %routeRules) { + my $type = $routeRules{$n}{'type'} ; + my $color = $routeRules{$n}{'color'} ; + @{$routeColors{$type}} = split ( /;/, $color ) ; + } + return \%routeColors ; +} + +sub printRouteRules { + foreach my $n ( sort { $a <=> $b } keys %routeRules) { + print "route rule $n\n" ; + foreach my $v (sort keys %{$routeRules{$n}}) { + print " $v=$routeRules{$n}{$v}\n" ; + } + print "\n" ; + } +} + +# -------------------------------------------------------------------------------- + + +sub openRuleFile { + my $fileName = shift ; + open ($ruleFile, "<", $fileName) or die ("ERROR: could not open rule file $fileName\n") ; + getRuleLine() ; +} + +sub getRuleLine { + $line = <$ruleFile> ; + if (defined $line) { + $line =~ s/\r//g ; # remove dos/win char at line end + } + while ( (defined $line) and ( (length $line < 2) or ( grep /^comment/i, $line) or ( grep /^\#/i, $line) ) ) { + $line = <$ruleFile> ; + } + return $line ; +} + +sub adaptRuleSizes { + foreach my $r ( keys %nodeRules ) { + foreach my $p ( qw (iconSize labelOffset labelSize shieldSize size) ) { + if ( defined $nodeRules{ $r }{ $p } ) { + if ( grep /:/, $nodeRules{ $r }{ $p } ) { + my $old = $nodeRules{ $r }{ $p } ; + my $new = scaleSize ($nodeRules{ $r }{ $p }, $nodeRules{ $r }{ 'fromscale' }, $nodeRules{ $r }{ 'toscale' }) ; + $nodeRules{ $r }{ $p } = $new ; + if ( cv('debug') eq "1" ) { + print "RULES/scale/node: $old -> $new\n" ; + } + } + } + } + } + foreach my $r ( keys %wayRules ) { + foreach my $p ( qw (bordersize labelsize labeloffset size ) ) { + if ( defined $wayRules{ $r }{ $p } ) { + if ( grep /:/, $wayRules{ $r }{ $p } ) { + my $kv = $wayRules{ $r }{ 'keyvalue' } ; + my $old = $wayRules{ $r }{ $p } ; + my $new = 0 ; + $new = scaleSize ($wayRules{ $r }{ $p }, $wayRules{ $r }{ 'fromscale' }, $wayRules{ $r }{ 'toscale' }) ; + $wayRules{ $r }{ $p } = $new ; + if ( cv('debug') eq "1" ) { + print "RULES/scale/way: $kv $p $old to $new\n" ; + } + } + } + } + } +} + +sub scaleSize { + my ($str, $fromScale, $toScale) = @_ ; + my @tmp = split /:/, $str ; + my $lower = $tmp[0] ; + my $upper = $tmp[1] ; + my $newSize = 0 ; + + my $scale = getScale() ; + if ( cv('rulescaleset') ne "0" ) { $scale = cv('rulescaleset') } ; + + if ( $scale < $fromScale) { + $newSize = $upper ; + } + elsif ( $scale > $toScale ) { + $newSize = $lower ; + } + else { + my $percent = ( $scale - $fromScale ) / ($toScale - $fromScale) ; + $newSize = $upper - $percent * ($upper - $lower) ; + } + $newSize = int ( $newSize * 10 ) / 10 ; + return $newSize ; +} + +sub createLegend { + + # TODO Auto size + + my $nx = 80 ; + my $ny = 80 ; + my $ey = 1.5 * $ny ; + my $sx = 700 ; + my $tx = 200 ; + my $ty = $ey / 2 ; + my $fs = 40 ; + my $actualLine = 0 ; + + my $preCount = 0 ; + foreach my $n (keys %nodeRules) { + if ( $nodeRules{$n}{"legend"} eq "yes" ) { $preCount++ ; } + } + foreach my $n (keys %wayRules) { + if ( $wayRules{$n}{"legend"} eq "yes" ) { $preCount++ ; } + } + foreach my $n (keys %areaRules) { + if ( $areaRules{$n}{"legend"} eq "yes" ) { $preCount++ ; } + } + if ( cv('debug') eq "1" ) { print "LEGEND: $preCount elements found\n" ; } + + my $sy = $preCount * $ey ; + addToLayer ("definitions", "") ; + + my $color = "white" ; + my $svgString = "fill=\"$color\"" ; + drawRect (0, 0, $sx, $sy, 0, $svgString, "definitions") ; + + foreach my $n (keys %nodeRules) { + if ( $nodeRules{$n}{"legend"} eq "yes" ) { + my $x = $nx ; + my $y = $actualLine * $ey + $ny ; + + if ( ($nodeRules{$n}{'size'} > 0) and ($nodeRules{$n}{'icon'} eq "none") ) { + my $svgString = "" ; + if ( $nodeRules{$n}{'svgstring'} ne "" ) { + $svgString = $nodeRules{$n}{'svgstring'} ; + } + else { + $svgString = "fill=\"$nodeRules{$n}{'color'}\"" ; + } + + if ( $nodeRules{$n}{'shape'} eq "circle") { + drawCircle ($x, $y, 0, $nodeRules{$n}{'size'}, 0, $svgString, 'definitions') ; + } + elsif ( $nodeRules{$n}{'shape'} eq "square") { + drawSquare ($x, $y, 0, $nodeRules{$n}{'size'}, 0, $svgString, 'definitions') ; + } + elsif ( $nodeRules{$n}{'shape'} eq "triangle") { + drawTriangle ($x, $y, 0, $nodeRules{$n}{'size'}, 0, $svgString, 'definitions') ; + } + elsif ( $nodeRules{$n}{'shape'} eq "diamond") { + drawDiamond ($x, $y, 0, $nodeRules{$n}{'size'}, 0, $svgString, 'definitions') ; + } + + my $textSvgString = createTextSVG ( cv('elementFontFamily'), cv('elementFont'), $fs, "black", undef, undef ) ; + drawText ($tx, ($actualLine+0.5) * $ey + $fs/2, 0, $nodeRules{$n}{'legendlabel'}, $textSvgString, "definitions") ; + } + else { + # TODO icon + } + $actualLine ++ ; + } + } + + foreach my $w (keys %wayRules) { + if ( $wayRules{$w}{"legend"} eq "yes" ) { + my ($x1, $x2) ; + $x1 = 0.5 * $nx ; + $x2 = 1.5 * $nx ; + my $y = $actualLine * $ey + $ny ; + my ($svg1, $layer1, $svg2, $layer2) = mwWays::createWayParameters ($wayRules{$w}, 0, 0, 0) ; + my @coords = ($x1, $y, $x2, $y) ; + if ($svg2 ne "") { + drawWay ( \@coords, 0, $svg2, "definitions", undef ) ; + } + drawWay ( \@coords, 0, $svg1, "definitions", undef ) ; + + my $textSvgString = createTextSVG ( cv('elementFontFamily'), cv('elementFont'), $fs, "black", undef, undef ) ; + drawText ($tx, ($actualLine+0.5)*$ey + $fs/2, 0, $wayRules{$w}{'legendlabel'}, $textSvgString, "definitions") ; + + $actualLine++ ; + + } + } + + foreach my $a (keys %areaRules) { + if ( $areaRules{$a}{"legend"} eq "yes" ) { + my ($x1, $x2) ; + my ($y1, $y2) ; + $x1 = 0.7 * $nx ; + $x2 = 1.3 * $nx ; + $y1 = $actualLine * $ey + 0.7 * $ny ; + $y2 = $actualLine * $ey + 1.3 * $ny ; + + my $color = $areaRules{$a}{'color'} ; + my $icon = $areaRules{$a}{'icon'} ; + my $base = $areaRules{$a}{'base'} ; + my $svgString = $areaRules{$a}{'svgstring'} ; + + if ( ($svgString eq "") and ($icon eq "none") ) { + $svgString = "fill=\"$color\" " ; + } + + my @coords = ([$x1, $y1, $x2, $y1, $x2, $y2, $x1, $y2, $x1, $y1]) ; + drawArea ($svgString, $icon, \@coords, 0, "definitions") ; + + my $textSvgString = createTextSVG ( cv('elementFontFamily'), cv('elementFont'), $fs, "black", undef, undef ) ; + drawText ($tx, ($actualLine+0.5)*$ey + $fs/2, 0, $areaRules{$a}{'legendlabel'}, $textSvgString, "definitions") ; + $actualLine++ ; + } + } + + + addToLayer ("definitions", "") ; + + my $posX = 0 ; + my $posY = 0 ; + + my ($sizeX, $sizeY) = getDimensions() ; + + if ( cv('legend') eq "2") { + $posX = $sizeX - $sx ; + $posY = 0 ; + } + + if ( cv('legend') eq "3") { + $posX = 0 ; + $posY = $sizeY - $sy ; + } + + if ( cv('legend') eq "4") { + $posX = $sizeX - $sx ; + $posY = $sizeY - $sy ; + } + + if ( (cv('legend') >=1) and (cv('legend')<=4) ) { + addToLayer ("legend", "") ; + } + elsif (cv('legend') == 5) { + # separate file + createLegendFile($sx, $sy, "_legend", "#legenddef") ; + } + +} + +1 ; diff --git a/mwStandardRules.txt b/mwStandardRules.txt new file mode 100644 index 0000000..a5be856 --- /dev/null +++ b/mwStandardRules.txt @@ -0,0 +1,319 @@ +# +# standard ruleset for mapweaver +# + +RULE NODE +keyvalue=amenity=post_box +icon=./icons/postbox.svg +iconsize=40 +toscale=10000 + +RULE NODE +keyvalue=place=city +size=40 +labelsize=60 +shape=square +legend=yes +legendlabel=Großstadt + +RULE NODE +keyvalue=place=town +label=name +labelsize=45 +size=30 +legend=yes +legendlabel=Stadt + +RULE NODE +keyvalue=place=village +size=20 +labelsize=30 +svgString=fill="black" +toscale=50000 +legend=yes +legendlabel=Dorf + +RULE NODE +keyvalue=place=suburb +size=20 +labelsize=30 +toscale=50000 +legend=yes +legendlabel=Vorort + +RULE WAY +keyvalue=highway=motorway +color=blue +size=28 +label=shield:icons/shield_ger_bab.def +labelsize=60 +labeloffset=11 +bordercolor=black +bordersize=2 +direxclude=yes + +RULE WAY +keyvalue=highway=motorway_link +color=blue +size=20 +bordercolor=black +bordersize=2 +direxclude=yes + +RULE WAY +keyvalue=highway=trunk +color=blue +size=26 +label=shield:icons/shield_ger_bs.def +bordercolor=black +bordersize=4 +direxclude=yes + +RULE WAY +keyvalue=highway=trunk_link +color=blue +size=20 +bordercolor=black +bordersize=2 +direxclude=yes + +RULE WAY +keyvalue=highway=primary +label=shield:icons/shield_ger_bs.def +labelsize=50 +labeloffset=10 +color=red +size=26 +bordercolor=black +bordersize=4 +legend=yes +legendlabel=Bundesstraße + +RULE WAY +keyvalue=highway=primary_link +color=red +size=20 +bordercolor=black +bordersize=2 + +RULE WAY +keyvalue=highway=secondary +label=shield:icons/shield_ger_ls.def +labelsize=50 +labeloffset=9 +color=orange +size=24 +bordercolor=black +bordersize=4 +legend=yes +legendlabel=Landstraße + +RULE WAY +keyvalue=highway=secondary_link +color=orange +size=20 +bordercolor=black +bordersize=2 + +RULE WAY +keyvalue=highway=tertiary +color=lightgreen +labelsize=22 +labeloffset=9 +size=22 +bordercolor=black +bordersize=4 +legend=yes +legendlabel=Kreisstraße + +RULE WAY +keyvalue=highway=tertiary_link +color=lightgreen +label=none +size=22 +bordercolor=black +bordersize=4 +legend=no + +RULE WAY +keyvalue=highway=unclassified +color=lightgreen +label=none +size=22 +bordercolor=black +bordersize=4 +legend=no + +RULE WAY +keyvalue=highway=residential +label=name +labelsize=20 +labeloffset=8 +color=grey +size=22 +bordersize=4 +toscale=10000 +legend=yes +legendlabel=Wohnstraße + +RULE WAY +keyvalue=highway=residential +label=none +color=grey +size=5:10 +bordersize=0 +fromscale=10001 +toscale=50000 + +RULE WAY +keyvalue=highway=service +label=none +color=grey +size=20 +bordersize=0 +toscale=10000 + +RULE WAY +keyvalue=highway=track +label=name +labelsize=20 +color=darkgrey +size=3 +labeloffset=-2 +bordersize=0 +toscale=10000 + +RULE WAY +keyvalue=highway=track +label=none +color=darkgrey +size=1 +bordersize=0 +fromscale=10001 +toscale=50000 + +RULE AREA +keyvalue=landuse=residential +color=lightgray +base=yes +legend=yes +legendlabel=Wohngegend + +RULE AREA +keyvalue=building=yes +color=darkgray +legend=yes +legendlabel=Gebäude + +RULE AREA +keyvalue=natural=water +color=lightblue +base=yes +label=name + +RULE AREA +keyvalue=natural=land +color=lightgreen +base=yes + +RULE AREA +keyvalue=leisure=park +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=natural=wood +color=darkgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=forest +color=darkgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=farmland +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=farmyard +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=farm +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=grass +color=lightgreen +base=yes + +RULE AREA +keyvalue=landuse=meadow +color=lightgreen +base=yes + +RULE AREA +keyvalue=landuse=village_green +color=lightgreen +base=yes + +RULE AREA +keyvalue=landuse=industrial +color=lightgray +base=yes + +RULE AREA +keyvalue=landuse=commercial +color=lightgray +base=yes + +RULE AREA +keyvalue=aeroway=runway +color=gray +base=yes + +RULE AREA +keyvalue=aeroway=taxiway +color=gray +base=yes + + + + + +RULE AREA +keyvalue=amenity=parking +icon=icons/areaParking.svg +legend=yes +legendlabel=Parkplatz + +RULE AREA +keyvalue=leisure=golf_course +icon=icons/areaGolf.svg + +RULE AREA +keyvalue=landuse=cemetery +icon=icons/areaCemetery.svg + + +RULE ROUTE +type=hiking +color=blue;green;yellow +opacity=100 +label=name +dash=20,20 + +RULE ROUTE +type=bus +color=black;brown +label=ref diff --git a/mwTopoRules.txt b/mwTopoRules.txt new file mode 100644 index 0000000..54ae15a --- /dev/null +++ b/mwTopoRules.txt @@ -0,0 +1,576 @@ +# +# topo ruleset for mapweaver +# + +RULE CONFIG scaleset=12500 +RULE CONFIG ruler=2 +# RULE CONFIG legend=4 + +RULE NODE +keyvalue=place=city +size=0 +labelsize=60 +shape=square +legend=yes +legendlabel=Großstadt + +RULE NODE +keyvalue=place=town +label=name +labelsize=50 +size=0 +legend=yes +legendlabel=Stadt + +RULE NODE +keyvalue=place=village +size=0 +labelsize=40 +svgString=fill="black" +legend=yes +legendlabel=Dorf + +RULE NODE +keyvalue=place=suburb +size=0 +labelsize=40 +legend=yes +legendlabel=Vorort + +RULE NODE +keyvalue=place=locality +size=10 +label=name +labelsize=30 +shape=circle + +RULE NODE +keyvalue=natural=peak +size=15 +label=name!ele +labelsize=30 +shape=triangle + +RULE NODE +keyvalue=man_made=tower +icon=icons/tower.svg +label=none +labelsize=30 + +RULE NODE +keyvalue=railway=station +label=name +shape=square +size=12 +labelsize=30 + +RULE NODE +keyvalue=amenity=restaurant +icon=icons/restaurant.svg +label=name +labelsize=30 + +RULE WAY +keyvalue=highway=motorway +color=white +size=18 +label=ref +labelsize=30 +labeloffset=-25 +bordercolor=black +bordersize=4 +direxclude=yes + +RULE WAY +keyvalue=highway=motorway_link +label=none +color=white +size=14 +bordercolor=black +bordersize=2 +direxclude=yes + +RULE WAY +keyvalue=highway=trunk +color=white +size=18 +label=ref +labelsize=30 +labeloffset=-25 +bordercolor=black +bordersize=4 +direxclude=yes + +RULE WAY +keyvalue=highway=trunk_link +label=none +color=white +size=14 +bordercolor=black +bordersize=2 +direxclude=yes + +RULE WAY +keyvalue=highway=primary +label=ref +labelsize=25 +labeloffset=-20 +color=yellow +size=14 +bordercolor=black +bordersize=2 +legend=yes +legendlabel=Bundesstraße + +RULE WAY +keyvalue=highway=primary_link +label=none +color=yellow +size=14 +bordercolor=black +bordersize=2 + +RULE WAY +keyvalue=highway=secondary +label=ref +labelsize=25 +labeloffset=-20 +color=yellow +size=14 +bordercolor=black +bordersize=2 +legend=yes +legendlabel=Landstraße + +RULE WAY +keyvalue=highway=secondary_link +label=none +color=yellow +size=14 +bordercolor=black +bordersize=2 + +RULE WAY +keyvalue=highway=tertiary +color=yellow +label=ref +labelsize=25 +labeloffset=-20 +size=14 +bordercolor=black +bordersize=2 +legend=yes +legendlabel=Kreisstraße + +RULE WAY +keyvalue=highway=tertiary_link +color=white +label=none +size=14 +bordercolor=black +bordersize=2 +legend=no + +RULE WAY +keyvalue=highway=residential +label=none +color=white +size=8 +bordersize=2 +legend=yes +legendlabel=Wohnstraße + +RULE WAY +keyvalue=highway=unclassified +label=none +labelsize=18 +labeloffset=8 +color=white +size=8 +bordersize=2 +legend=no + +RULE WAY +keyvalue=highway=service +label=none +color=white +size=6 +bordersize=2 + +RULE WAY +keyvalue=highway=pedestrian +label=none +color=white +size=6 +bordersize=2 + +RULE WAY +keyvalue=highway=track;tracktype=grade1 +label=name +labelsize=25 +labeloffset=-15 +color=white +size=4 +bordersize=2 + +RULE WAY +keyvalue=highway=track;tracktype=grade2 +label=name +labelsize=25 +labeloffset=-15 +color=white +size=4 +bordersize=2 + +RULE WAY +keyvalue=highway=track;tracktype=grade3 +label=name +labelsize=25 +labeloffset=-15 +color=black +size=2 +bordersize=0 + +RULE WAY +keyvalue=highway=track;tracktype=grade4 +label=none +color=black +size=2 +bordersize=0 +dash=20,20 + +RULE WAY +keyvalue=highway=track;tracktype=grade5 +label=none +color=black +size=2 +bordersize=0 +dash=10,20 + +RULE WAY +keyvalue=highway=track +label=name +labelsize=25 +labeloffset=-15 +color=black +size=2 +bordersize=0 + +RULE WAY +keyvalue=highway=footway +label=name +labelsize=25 +labeloffset=-15 +color=black +size=2 +bordersize=0 +dash=5,15 + +RULE WAY +keyvalue=highway=path +label=name +labelsize=25 +labeloffset=-15 +color=black +size=2 +bordersize=0 +dash=5,5 + +RULE WAY +keyvalue=highway=steps +color=black +size=5 +bordersize=0 +dash=4,4 +dashcap=butt + + +RULE WAY +keyvalue=waterway=stream +label=name +labelsize=25 +labelcolor=darkblue +color=lightblue +size=5 +labeloffset=0 +bordersize=0 + +RULE WAY +keyvalue=railway=rail +label=none +color=white +size=4 +bordersize=2 +bordercolor=black +dash=40,40 +dashcap=butt + + +RULE AREA +keyvalue=building=yes +color=grey +legend=yes +legendlabel=Gebäude + +RULE AREA +keyvalue=natural=water +color=lightblue +base=yes +label=name + +RULE AREA +keyvalue=waterway=riverbank +color=lightblue +base=yes + +RULE AREA +keyvalue=natural=land +color=lightgreen +base=yes + +RULE AREA +keyvalue=leisure=park +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=natural=wood +color=darkgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=forest +color=darkgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=farmland +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=farmyard +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=farm +color=lightgreen +base=yes +label=name + +RULE AREA +keyvalue=landuse=grass +color=lightgreen +base=yes + +RULE AREA +keyvalue=landuse=meadow +color=lightgreen +base=yes + +RULE AREA +keyvalue=landuse=village_green +color=lightgreen +base=yes + +RULE AREA +keyvalue=aeroway=runway +color=gray +base=yes + +RULE AREA +keyvalue=aeroway=taxiway +color=gray +base=yes + + + + + +RULE AREA +keyvalue=landuse=cemetery +icon=icons/areaCemetery.svg + + +#RULE ROUTE +#type=hiking +#color=blue;green;yellow +#opacity=100 +#label=name +#dash=20,20 + + + +# +# +# + +RULE NODE +keyvalue=aerialway=station +size=15 +labelsize=25 +shape=circle +legend=no + +RULE NODE +keyvalue=aerialway=pylon +size=10 +shape=circle +legend=no + +RULE WAY +keyvalue=aerialway=cable_car +label=name +labelsize=25 +color=black +size=8 +labeloffset=-10 +bordersize=0 + +RULE WAY +keyvalue=aerialway=gondola +label=name +labelsize=25 +color=black +size=6 +labeloffset=-10 +bordersize=0 + +RULE WAY +keyvalue=aerialway=* +label=name +labelsize=25 +color=black +size=4 +labeloffset=-10 +bordersize=0 + + +#--- + +RULE WAY +keyvalue=contour_ext=elevation_major +label=ele +labelsize=25 +labelcolor=green +color=green +size=3 +labeloffset=-2 +bordersize=0 + +RULE WAY +keyvalue=contour_ext=elevation_medium +label=ele +labelsize=20 +labelcolor=green +color=green +size=2 +labeloffset=-2 +bordersize=0 + +RULE WAY +keyvalue=contour_ext=elevation_minor +label=none +labelcolor=green +color=green +size=1 +labeloffset=-2 +bordersize=0 + + + +# MOUNTAINS + +RULE NODE +keyvalue=mountain_pass=yes +icon=icons/mountainPass.svg +label=name!ele +labelsize=30 + +RULE NODE +keyvalue=tourism=viewpoint +icon=icons/viewpoint.svg +label=name +labelsize=30 + +RULE NODE +keyvalue=tourism=alpine_hut +icon=icons/alpineHut.svg +label=name +labelsize=30 + +RULE NODE +keyvalue=amenity=shelter +icon=icons/shelter.svg +label=name +labelsize=30 + + +RULE WAY +keyvalue=natural=ridge +color=grey +size=4 +label=name +labelsize=30 +labeloffset=3 + +RULE WAY +keyvalue=natural=arete +color=grey +size=4 +label=name +labelsize=30 +labeloffset=3 + +RULE WAY +keyvalue=natural=cliff +color=red +size=6 +label=name +labelsize=30 +labeloffset=-20 +bordercolor=white +bordersize=0 +dash=8,8 + + +# POWER + +RULE NODE +keyvalue=power=tower +size=8 +label=none +shape=square + +RULE NODE +keyvalue=power=pole +size=6 +label=none +shape=square + +RULE WAY +keyvalue=power=line +color=orange +size=2 +bordersize=0 +label=none + +RULE AREA +keyvalue=power=station +color=gray + +RULE AREA +keyvalue=power=sub_station +color=gray + +RULE NODE +keyvalue=power=generator;source=wind +icon=icons/powerWind.svg +label=none + diff --git a/mwWayLabel.pm b/mwWayLabel.pm new file mode 100644 index 0000000..0ff6dbd --- /dev/null +++ b/mwWayLabel.pm @@ -0,0 +1,578 @@ +# +# 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 mwWayLabel ; + +use strict ; +use warnings ; + +use mwConfig ; +use mwFile ; +use mwMisc ; +use mwMap ; +use mwLabel ; +use mwOccupy ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( addToDirectory + getDirectory + addWayLabel + preprocessWayLabels + createWayLabels + ) ; + +my %directory = () ; +my %wayLabels = () ; +my @labelCandidates = () ; +my %ruleRefs = () ; +my $pathNumber = 0 ; + +my $numWayLabelsOmitted = 0 ; +my $wnsNumber = 1 ; +my @wns =() ; + + +# ------------------------------------------------------------------------ + +sub addToDirectory { + my ($name, $square) = @_ ; + if ( ! defined $square ) { + $directory { $name } = 1 ; + } + else { + $directory { $name } { $square } = 1 ; + } +} + +sub getDirectory { + return \%directory ; +} + +sub addWayLabel { +# +# collect all way label data before actual labeling +# + my ($wayId, $name, $ruleRef) = @_ ; + push @{ $wayLabels{$ruleRef}{$name} }, $wayId ; + $ruleRefs{$ruleRef} = $ruleRef ; + if ( cv ('debug') eq "1" ) { + print "AWL: $wayId, $name, $ruleRef\n" ; + } +} + +sub preprocessWayLabels { +# +# preprocess way labels collected so far +# combine ways with same rule and name +# split ways where direction in longitude changes so labels will be readable later +# store result in @labelCandidates +# + + my ($lonRef, $latRef) = getNodePointers() ; + my ($memWayNodesRef, $memWayTagsRef) = getWayPointers() ; + + foreach my $rule (keys %wayLabels) { + my $ruleRef = $ruleRefs{ $rule } ; + # print "PPWL: ruleNum $rule\n" ; + foreach my $name (keys %{$wayLabels{$rule}}) { + my (@ways) = @{$wayLabels{$rule}{$name}} ; + # print "PPWL: processing name $name, " . scalar (@ways) . " ways\n" ; + my ($waysRef, $nodesRef) = buildRings (\@ways, 0) ; + my @segments = @$nodesRef ; + # print "PPWL: processing name $name, " . scalar (@segments) . " segments\n" ; + + if ( ! grep /shield:/i, $name) { + + my @newSegments = () ; + foreach my $segment (@segments) { + my @actual = @$segment ; + # print "PPWL: Actual segment @actual\n" ; + my $found = 1 ; + while ($found) { + $found = 0 ; my $sp = 0 ; + # look for splitting point + LABSP: for (my $i=1; $i<$#actual; $i++) { + if ( (($$lonRef{$actual[$i-1]} > $$lonRef{$actual[$i]}) and ($$lonRef{$actual[$i+1]} > $$lonRef{$actual[$i]})) or + (($$lonRef{$actual[$i-1]} < $$lonRef{$actual[$i]}) and ($$lonRef{$actual[$i+1]} < $$lonRef{$actual[$i]})) ) { + $found = 1 ; + $sp = $i ; + last LABSP ; + } + } + if ($found == 1) { + # print "\nname $name --- sp: $sp\n" ; + # print "ACTUAL BEFORE: @actual\n" ; + # create new seg + my @newSegment = @actual[0..$sp] ; + push @newSegments, [@newSegment] ; + # print "NEW: @newSegment\n" ; + + # splice actual + splice @actual, 0, $sp ; + # print "ACTUAL AFTER: @actual\n\n" ; + } + } + @$segment = @actual ; + } + + push @segments, @newSegments ; + + } + + foreach my $segment (@segments) { + my (@wayNodes) = @$segment ; + my @points = () ; + + if ($$lonRef{$wayNodes[0]} > $$lonRef{$wayNodes[-1]}) { + if ( ( ! grep /motorway/, $$ruleRef{'keyvalue'}) and ( ! grep /trunk/, $$ruleRef{'keyvalue'} ) ) { + @wayNodes = reverse @wayNodes ; + } + } + + foreach my $node (@wayNodes) { + push @points, convert ($$lonRef{$node}, $$latRef{$node}) ; + } + # print "PPWL: segment @wayNodes\n" ; + # print "PPWL: segment @points\n" ; + + my ($segmentLengthPixels) = 0 ; + + + for (my $i=0; $i<$#wayNodes; $i++) { + my ($x1, $y1) = convert ($$lonRef{$wayNodes[$i]}, $$latRef{$wayNodes[$i]}) ; + my ($x2, $y2) = convert ($$lonRef{$wayNodes[$i+1]}, $$latRef{$wayNodes[$i+1]}) ; + $segmentLengthPixels += sqrt ( ($x2-$x1)**2 + ($y2-$y1)**2 ) ; + } + # print "$rule, $wayIndexLabelSize\n" ; + + my $labelLengthPixels = 0 ; + + if (grep /shield/i, $$ruleRef{'label'} ) { + $labelLengthPixels = $$ruleRef{'labelsize'} ; + # print "PPWL: len = $labelLengthPixels\n" ; + } + else { + $labelLengthPixels = length ($name) * cv('ppc') / 10 * $$ruleRef{'labelsize'} ; + } + + # print "\nPPWL: name $name - ppc $ppc - size $ruleArray[$wayIndexLabelSize]\n" ; + # print "PPWL: wayLen $segmentLengthPixels\n" ; + # print "PPWL: labLen $labelLengthPixels\n" ; + + push @labelCandidates, [$rule, $name, $segmentLengthPixels, $labelLengthPixels, [@points]] ; + if ( cv('debug') eq "1") { + print "PLC: $rule, $name, $segmentLengthPixels, $labelLengthPixels\n" ; + } + } + } + } +} + +sub subWay { +# +# takes coordinates and label information and creates new way/path +# also calculates total angles / bends +# + my ($ref, $labLen, $alignment, $position) = @_ ; + my @coordinates = @$ref ; + my @points ; + my @dists ; + my @angles = () ; + + for (my $i=0; $i < $#coordinates; $i+=2) { + push @points, [$coordinates[$i],$coordinates[$i+1]] ; + } + + $dists[0] = 0 ; + my $dist = 0 ; + if (scalar @points > 1) { + for (my $i=1;$i<=$#points; $i++) { + $dist = $dist + sqrt ( ($points[$i-1]->[0]-$points[$i]->[0])**2 + ($points[$i-1]->[1]-$points[$i]->[1])**2 ) ; + $dists[$i] = $dist ; + } + } + + # calc angles at nodes + if (scalar @points > 2) { + for (my $i=1;$i<$#points; $i++) { + $angles[$i] = angleMapgen ($points[$i-1]->[0], $points[$i-1]->[1], $points[$i]->[0], $points[$i]->[1], $points[$i]->[0], $points[$i]->[1], $points[$i+1]->[0], $points[$i+1]->[1]) ; + } + } + + my $wayLength = $dist ; + my $refPoint = $wayLength / 100 * $position ; + my $labelStart ; my $labelEnd ; + if ($alignment eq "start") { # left + $labelStart = $refPoint ; + $labelEnd = $labelStart + $labLen ; + } + if ($alignment eq "end") { # right + $labelEnd = $refPoint ; + $labelStart = $labelEnd - $labLen ; + } + if ($alignment eq "middle") { # center + $labelEnd = $refPoint + $labLen / 2 ; + $labelStart = $refPoint - $labLen / 2 ; + } + + # find start and end segments + my $startSeg ; my $endSeg ; + for (my $i=0; $i<$#points; $i++) { + if ( ($dists[$i]<=$labelStart) and ($dists[$i+1]>=$labelStart) ) { $startSeg = $i ; } + if ( ($dists[$i]<=$labelEnd) and ($dists[$i+1]>=$labelEnd) ) { $endSeg = $i ; } + } + + my @finalWay = () ; + my $finalAngle = 0 ; + my ($sx, $sy) = triangleNode ($coordinates[$startSeg*2], $coordinates[$startSeg*2+1], $coordinates[$startSeg*2+2], $coordinates[$startSeg*2+3], $labelStart-$dists[$startSeg], 0) ; + push @finalWay, $sx, $sy ; + + if ($startSeg != $endSeg) { + for (my $i=$startSeg+1; $i<=$endSeg; $i++) { + push @finalWay, $coordinates[$i*2], $coordinates[$i*2+1] ; + $finalAngle += abs ($angles[$i]) ; + } + } + + my ($ex, $ey) = triangleNode ($coordinates[$endSeg*2], $coordinates[$endSeg*2+1], $coordinates[$endSeg*2+2], $coordinates[$endSeg*2+3], $labelEnd-$dists[$endSeg], 0) ; + push @finalWay, $ex, $ey ; + + return (\@finalWay, $finalAngle) ; +} + +sub createWayLabels { +# +# finally take all way label candidates and try to label them +# + + my %wnsUnique = () ; + print "placing way labels...\n" ; + + my %notDrawnLabels = () ; + my %drawnLabels = () ; + + # calc ratio to label ways first where label just fits + # these will be drawn first + foreach my $candidate (@labelCandidates) { + my $wLen = $candidate->[2] ; + my $lLen = $candidate->[3] ; + if ($wLen == 0) { $wLen = 1 ; } + if ($lLen == 0) { $lLen = 1 ; } + $candidate->[5] = $lLen / $wLen ; + } + @labelCandidates = sort { $b->[5] <=> $a->[5] } @labelCandidates ; + + foreach my $candidate (@labelCandidates) { + my $ruleRef = $ruleRefs{ $candidate->[0] } ; + my $name = $candidate->[1] ; + my $wLen = $candidate->[2] ; + my $lLen = $candidate->[3] ; + my @points = @{$candidate->[4]} ; + + my $toLabel = 1 ; + if ( ( cv('declutter') eq "1") and ($points[0] > $points[-2]) and + ( ( grep /motorway/i, $$ruleRef{'keyvalue'}) or (grep /trunk/i, $$ruleRef{'keyvalue'}) ) ) { + $toLabel = 0 ; + } + + + # wns? + if ( ($lLen > $wLen * 0.95) and ( cv('wns') > 0 ) ) { + if ( ( $toLabel != 0 ) and ( ! grep /shield:/i, $name) and ( wayVisible( \@points ) ) ) { + if ( ! defined $wnsUnique{$name} ) { + my $oldName = $name ; + $wnsUnique{$name} = 1 ; + push @wns, [ $wnsNumber, $name] ; + $name = $wnsNumber ; + $lLen = cv('ppc') / 10 * $$ruleRef{'labelsize'} * length ($name) ; + # print "WNS: $oldName - $name\n" ; + $wnsNumber++ ; + } + } + } + + + if ( ($lLen > $wLen*0.95) or ($toLabel == 0) ) { + # label too long + $numWayLabelsOmitted++ ; + $notDrawnLabels { $name } = 1 ; + + } + else { + + if (grep /shield:/i, $name) { + + createShield ($name, $$ruleRef{'labelsize'} ) ; + + my $shieldMaxSize = getMaxShieldSize ($name) ; + + my $numShields = int ($wLen / ($shieldMaxSize * 12) ) ; + # if ($numShields > 4) { $numShields = 4 ; } + + if ($numShields > 0) { + my $step = $wLen / ($numShields + 1) ; + my $position = $step ; + while ($position < $wLen) { + my ($x, $y) = getPointOfWay (\@points, $position) ; + # print "XY: $x, $y\n" ; + + if ( ! coordsOut ($x, $y) ) { + + # place shield if not occupied + + my ($ssx, $ssy) = getShieldSizes($name) ; + + my $x2 = int ($x - $ssx / 2) ; + my $y2 = int ($y - $ssy / 2) ; + + # print "AREA: $x2, $y2, $x2+$lLen, $y2+$lLen\n" ; + + if ( ! mwLabel::boxAreaOccupied ($x2, $y2+$ssy, $x2+$ssx, $y2) ) { + + my $id = getShieldId ($name) ; + addToLayer ("shields", "") ; + + mwLabel::boxOccupyArea ($x2, $y2+$ssy, $x2+$ssx, $y2, 0, 3) ; + } + } + + $position += $step ; + } + } + + } # shield + + else { + + # print "$wLen - $name - $lLen\n" ; + my $numLabels = int ($wLen / (4 * $lLen)) ; + if ($numLabels < 1) { $numLabels = 1 ; } + if ($numLabels > 4) { $numLabels = 4 ; } + + if ($numLabels == 1) { + # print "LA: $name *1*\n" ; + my $spare = 0.95 * $wLen - $lLen ; + my $sparePercentHalf = $spare / ($wLen*0.95) *100 / 2 ; + my $startOffset = 50 - $sparePercentHalf ; + my $endOffset = 50 + $sparePercentHalf ; + # five possible positions per way + my $step = ($endOffset - $startOffset) / 5 ; + my @positions = () ; + my $actual = $startOffset ; + my $size = $$ruleRef{'labelsize'} ; + while ($actual <= $endOffset) { + my ($ref, $angle) = subWay (\@points, $lLen, "middle", $actual) ; + my @way = @$ref ; + # my ($col) = lineCrossings (\@way) ; + my ($col) = boxLinesOccupied (\@way, $size/2) ; + # calc quality of position. distance from middle and bend angles + my $quality = $angle + abs (50 - $actual) ; + if ($col == 0) { push @positions, ["middle", $actual, $quality] ; } + $actual += $step ; + } + if (scalar @positions > 0) { + $drawnLabels { $name } = 1 ; + # sort by quality and take best one + @positions = sort {$a->[2] <=> $b->[2]} @positions ; + my ($pos) = shift @positions ; + my ($ref, $angle) = subWay (\@points, $lLen, $pos->[0], $pos->[1]) ; + my @finalWay = @$ref ; + + # TODO IF INSIDE + # print "final way @finalWay\n" ; + + if ( ! coordsOut (@finalWay) ) { + my $pathName = "Path" . $pathNumber ; $pathNumber++ ; + createPath ($pathName, \@finalWay, "definitions") ; + + my $size = $$ruleRef{'labelsize'} ; + my $color = $$ruleRef{'labelcolor'} ; + my $font = $$ruleRef{'labelfont'} ; + my $fontFamily = $$ruleRef{'labelfontfamily'} ; + my $labelBold = $$ruleRef{'labelbold'} ; + my $labelItalic = $$ruleRef{'labelitalic'} ; + my $labelHalo = $$ruleRef{'labelhalo'} ; + my $labelHaloColor = $$ruleRef{'labelhalocolor'} ; + + my $svgText = createTextSVG ( $fontFamily, $font, $labelBold, $labelItalic, $size, $color, $labelHalo, $labelHaloColor) ; + # pathText ($svgText, $name, $pathName, $$ruleRef{'labeloffset'}, $pos->[0], $pos->[1], "text") ; + pathText ($svgText, $name, $pathName, $$ruleRef{'labeloffset'}, $pos->[0], 50, "text") ; + + boxOccupyLines (\@finalWay, $size/2, 3) ; + } + } + else { + $numWayLabelsOmitted++ ; + } + } + else { # more than one label + # print "LA: $name *X*\n" ; + my $labelDrawn = 0 ; + my $interval = int (100 / ($numLabels + 1)) ; + my @positions = () ; + for (my $i=1; $i<=$numLabels; $i++) { + push @positions, $i * $interval ; + } + + foreach my $position (@positions) { + my ($refFinal, $angle) = subWay (\@points, $lLen, "middle", $position) ; + my (@finalWay) = @$refFinal ; + # my ($collision) = lineCrossings (\@finalWay) ; + + my $size = $$ruleRef{'labelsize'} ; + my ($collision) = boxLinesOccupied (\@finalWay, $size/2 ) ; + + if ($collision == 0) { + $labelDrawn = 1 ; + $drawnLabels { $name } = 1 ; + my $pathName = "Path" . $pathNumber ; $pathNumber++ ; + + # createPath ($pathName, \@points, "definitions") ; + createPath ($pathName, \@finalWay, "definitions") ; + + my $size = $$ruleRef{'labelsize'} ; + my $color = $$ruleRef{'labelcolor'} ; + my $font = $$ruleRef{'labelfont'} ; + my $fontFamily = $$ruleRef{'labelfontfamily'} ; + my $labelBold = $$ruleRef{'labelbold'} ; + my $labelItalic = $$ruleRef{'labelitalic'} ; + my $labelHalo = $$ruleRef{'labelhalo'} ; + my $labelHaloColor = $$ruleRef{'labelhalocolor'} ; + + my $svgText = createTextSVG ( $fontFamily, $font, $labelBold, $labelItalic, $size, $color, $labelHalo, $labelHaloColor) ; + pathText ($svgText, $name, $pathName, $$ruleRef{'labeloffset'}, "middle", 50, "text") ; + + boxOccupyLines (\@finalWay, $size/2, 3) ; + + + + } + else { + # print "INFO: $name labeled less often than desired.\n" ; + } + } + if ($labelDrawn == 0) { + $notDrawnLabels { $name } = 1 ; + } + } + } + } + } + my $labelFileName = cv('out') ; + $labelFileName =~ s/\.svg/_NotDrawnLabels.txt/ ; + my $labelFile ; + open ($labelFile, ">", $labelFileName) or die ("couldn't open label file $labelFileName") ; + print $labelFile "Not drawn labels\n\n" ; + foreach my $labelName (sort keys %notDrawnLabels) { + if (!defined $drawnLabels { $labelName } ) { + print $labelFile "$labelName\n" ; + } + } + close ($labelFile) ; + + + # way name substitutes legend? + + if ( cv('wns') > 0 ) { + createWNSLegend() ; + } + +} + +# ------------------------------------------------------------ + +sub createWNSLegend { + my $size = cv('wnssize') ; + my $color = cv('wnscolor') ; + + # TODO max len auto size + my $maxLen = 0 ; + foreach my $e ( @wns ) { + if ( length $e->[1] > $maxLen ) { $maxLen = length $e->[1] ; } + } + + my $sy = 2 * $size ; + my $sx = (4 + $maxLen) * $size / 10 * cv('ppc') ; + my $tx = 4 * $size / 10 * cv('ppc') ; + my $nx = 1 * $size / 10 * cv('ppc') ; + my $ty = 1.5 * $size ; + + my $sizeX = $sx ; + my $sizeY = $sy * scalar @wns ; + + # defs + + my $actualLine = 0 ; + + addToLayer ("definitions", "") ; + + # bg + my $bg = cv('wnsbgcolor') ; + my $svgString = "fill=\"$bg\"" ; + drawRect (0, 0, $sizeX, $sizeY, 0, $svgString, "definitions") ; + + $svgString = createTextSVG ( cv('elementFontFamily'), cv('elementFont'), undef, undef, cv('wnssize'), cv('wnscolor'), undef, undef) ; + foreach my $e ( @wns ) { + my $y = $actualLine * $sy + $ty ; + drawText ($nx, $y, 0, $e->[0], $svgString, "definitions") ; + drawText ($tx, $y, 0, $e->[1], $svgString, "definitions") ; + + $actualLine++ ; + } + + addToLayer ("definitions", "") ; + + my $posX = 0 ; + my $posY = 0 ; + + # reset some variables + ($sizeX, $sizeY) = getDimensions() ; + $sy = $sy * scalar @wns ; + + if ( cv('wns') eq "2") { + $posX = $sizeX - $sx ; + $posY = 0 ; + } + + if ( cv('wns') eq "3") { + $posX = 0 ; + $posY = $sizeY - $sy ; + } + + if ( cv('wns') eq "4") { + $posX = $sizeX - $sx ; + $posY = $sizeY - $sy ; + } + + if ( ( cv('wns') >=1 ) and ( cv('wns') <= 4 ) ) { + addToLayer ("wns", "") ; + } + + if ( cv('wns') eq "5") { + createLegendFile ($sx, $sy, "_wns", "#wnsdef") ; + } +} + +1 ; + + diff --git a/mwWays.pm b/mwWays.pm new file mode 100644 index 0000000..8e20af6 --- /dev/null +++ b/mwWays.pm @@ -0,0 +1,394 @@ +# +# 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 mwWays ; + +use strict ; +use warnings ; + +use OSM::osm 8.3 ; + +use mwConfig ; +use mwFile ; +use mwRules ; +use mwMap ; +use mwMisc ; +use mwWayLabel ; +use mwCoastLines ; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +require Exporter ; + +@ISA = qw ( Exporter AutoLoader ) ; + +@EXPORT = qw ( processWays + getCoastWays + createDirectory + ) ; + +my $areasOmitted = 0 ; +my $areasDrawn = 0 ; + +my $areaLabels = 0 ; +my $areaLabelsOmitted = 0 ; + +my @coastWays = () ; + +sub processWays { + + print "drawing ways/areas...\n" ; + + my $nodesRef; my $tagRef ; + ($nodesRef, $tagRef) = getWayPointers () ; + my ($lonRef, $latRef, $nodeTagRef) = getNodePointers() ; + + foreach my $wayId (keys %$nodesRef) { + my @tags = @{ $$tagRef{$wayId} } ; + my $tagsString = "" ; + + # coast + my $v = getValue ("natural", \@tags) ; + if ( (defined $v) and ($v eq "coastline") ) { + push @coastWays, $wayId ; + } + + # WAYS + + my $ruleRef = getWayRule (\@tags) ; + if (defined $ruleRef) { + my @nodes = @{ $$nodesRef{ $wayId } } ; + + my $layer = getValue ("layer", $$tagRef{$wayId}) ; + if ( ! defined $layer ) { $layer = 0 ; } + + # TODO check for numeric!!! + + my $direction = 0 ; + my $ow = getValue("oneway", $$tagRef{$wayId}) ; + if (defined $ow) { + if (($ow eq "yes") or ($ow eq "true") or ($ow eq "1")) { $direction = 1 ; } + if ($ow eq "-1") { $direction = -1 ; } + } + + my $bridge = getValue("bridge", $$tagRef{$wayId}) ; + if (defined $bridge) { + if (($bridge eq "yes") or ($bridge eq "true")) { $bridge = 1 ; } else { $bridge = 0 ; } + } + else { $bridge = 0 ; } + + my $tunnel = getValue("tunnel", $$tagRef{$wayId}) ; + if (defined $tunnel) { + if (($tunnel eq "yes") or ($tunnel eq "true")) { $tunnel = 1 ; } else { $tunnel = 0 ; } + } + else { $tunnel = 0 ; } + + my ($svg1, $layer1, $svg2, $layer2) = createWayParameters ($ruleRef, $layer, $bridge, $tunnel) ; + + drawWay ( \@nodes, 1, $svg1, undef, $layer1 ) ; + if ($svg2 ne "") { + drawWay ( \@nodes, 1, $svg2, undef, $layer2 ) ; + } + + my $size = $$ruleRef{'size'} ; + if ( ( cv('oneways') eq "1" ) and ($direction != 0) ) { + addOnewayArrows (\@nodes, $direction, $size, $layer) ; + } + + + # LABEL WAY + if ( cv('ignorelabels') eq "0" ) { + if ($$ruleRef{'label'} ne "none") { + + my $name = "" ; my $ref1 ; my @names ; + + if (grep /shield/i, $$ruleRef{'label'} ) { + ($name, $ref1) = createLabel (\@tags, "ref",0, 0) ; + my $ref = $name ; + + if (grep /;/, $ref) { + my @a = split /;/, $ref ; + $ref = $a[0] ; + } + + if ($ref ne "") { + @names = ($ref) ; + $name = $$ruleRef{'label'} . ":$ref" ; + # print "DRAW WAY: name set to $name\n" ; + } + else { + @names = () ; + $name = "" ; + } + + # print "WAY: name for shield >$name<\n" ; + } + else { + ($name, $ref1) = createLabel (\@tags, $$ruleRef{'label'}, 0, 0) ; + @names = @$ref1 ; + $name = labelTransform ($name, $$ruleRef{'labeltransform'}) ; + } + + if ( ( cv('nolabel') eq "1") and ($name eq "") ) { $name = "NO LABEL" ; } + + if ($name ne "") { + addWayLabel ($wayId, $name, $ruleRef) ; + } + if ( ( cv('dir') eq "1") and ( $$ruleRef{'direxclude'} eq "no") ) { + if ( cv('grid') > 0) { + foreach my $node ( @nodes ) { + foreach my $name (@names) { + my $sq = gridSquare($$lonRef{$node}, $$latRef{$node}, cv('grid') ) ; + if (defined $sq) { + addToDirectory($name, $sq) ; + } + } + } + } + else { + foreach my $name (@names) { + addToDirectory ($name, undef) ; + } + } + } + } # label + } # ignorelabels + } + + # AREAS + + $ruleRef = getAreaRule (\@tags) ; + my @nodes = @{ $$nodesRef{ $wayId } } ; + if ( (defined $ruleRef) and ($nodes[0] == $nodes[-1]) ) { + my $color = $$ruleRef{'color'} ; + my $icon = $$ruleRef{'icon'} ; + my $base = $$ruleRef{'base'} ; + my $svgString = $$ruleRef{'svgstring'} ; + my $size = areaSize (\@nodes) ; + my @ways = [@nodes] ; + + if ( ($svgString eq "") and ($icon eq "none") ) { + $svgString = "fill=\"$color\" " ; + } + + if ($size > cv('minareasize') ) { + if ($base eq "yes") { + drawArea ($svgString, $icon, \@ways, 1, "base") ; + } + else { + drawArea ($svgString, $icon, \@ways, 1, "area") ; + } + $areasDrawn++ ; + + + # DRAW label + if ( $$ruleRef{'label'} ne "none" ) { + $areaLabels++ ; + if ($size > cv('minarealabelsize') ) { + # text + my ($name, $ref1) = createLabel (\@tags, $$ruleRef{'label'},0, 0) ; + $name = labelTransform ($name, $$ruleRef{'labeltransform'}) ; + + # pos + my ($lon, $lat) = areaCenter ( $$nodesRef{$wayId} ) ; + + # draw + my $labelFont = $$ruleRef{'labelfont'} ; + my $labelFontFamily = $$ruleRef{'labelfontfamily'} ; + my $labelSize = $$ruleRef{'labelsize'} ; + my $color = $$ruleRef{'labelcolor'} ; + my $labelBold = $$ruleRef{'labelbold'} ; + my $labelItalic = $$ruleRef{'labelitalic'} ; + my $labelHalo = $$ruleRef{'labelhalo'} ; + my $labelHaloColor = $$ruleRef{'labelhalocolor'} ; + + my $svgText = createTextSVG ( $labelFontFamily, $labelFont, $labelBold, $labelItalic, $labelSize, $color, $labelHalo, $labelHaloColor) ; + + mwLabel::placeLabelAndIcon ($lon, $lat, 0, 0, $name, $svgText, "none", 0, 0, "arealabels") ; + } + else { + $areaLabelsOmitted++ ; + } + } + + + } + else { + $areasOmitted++ ; + } + + } # Area + } + + print "$areasDrawn areas drawn, $areasOmitted omitted because they are too small\n" ; + print "$areaLabels area labels total, $areaLabelsOmitted omitted because belonging areas were too small\n" ; + + my $cw = scalar @coastWays ; + if ( cv('verbose')) { print "$cw coast line ways found.\n" ; } + + preprocessWayLabels() ; + createWayLabels() ; + + if ($cw > 0) { + processCoastLines (\@coastWays) ; + } +} + +# ---------------------------------------------------------------------------- + +sub createWayParameters { + my ($ruleRef, $layer, $bridge, $tunnel) = @_ ; + + my $svg1 = "" ; my $layer1 = 0 ; + my $svg2 = "" ; my $layer2 = 0 ; + + my %dashDefinition = () ; + @{$dashDefinition {1} } = ("round", "20,20") ; + @{$dashDefinition {2} } = ("round", "44,20") ; + @{$dashDefinition {3} } = ("round", "28,20") ; + @{$dashDefinition {4} } = ("round", "12,20") ; + + @{$dashDefinition {10} } = ("round", "8,8") ; + @{$dashDefinition {11} } = ("round", "16,16") ; + @{$dashDefinition {12} } = ("round", "24,24") ; + @{$dashDefinition {13} } = ("round", "32,32") ; + @{$dashDefinition {14} } = ("round", "40,40") ; + + @{$dashDefinition {20} } = ("round", "0,8,0,16") ; + @{$dashDefinition {21} } = ("round", "0,16,0,32") ; + @{$dashDefinition {22} } = ("round", "0,24,0,48") ; + @{$dashDefinition {23} } = ("round", "0,32,0,48") ; + + @{$dashDefinition {30} } = ("butt", "4,4") ; + @{$dashDefinition {31} } = ("butt", "8,8") ; + @{$dashDefinition {32} } = ("butt", "12,12") ; + @{$dashDefinition {33} } = ("butt", "4,12") ; + @{$dashDefinition {34} } = ("butt", "4,20") ; + @{$dashDefinition {35} } = ("butt", "8,20") ; + + if ( cv ('autobridge') eq "0" ) { + $layer = 0 ; + } + + if ( ( $$ruleRef{'svgstringtop'} ne "" ) or ( $$ruleRef{'svgstringbottom'} ne "" ) ) { + + $svg1 = $$ruleRef{'svgstringtop'} ; + $svg2 = $$ruleRef{'svgstringbottom'} ; + + # TODO layer + $layer1 = $layer ; + $layer2 = $layer ; + + } + else { + + my $size = $$ruleRef{'size'} ; + my $color = $$ruleRef{'color'} ; + + my $lc = "round" ; + my $lj = "round" ; + + my $dash = "" ; + if ( $$ruleRef{'dash'} ne "" ) { + if ( ! grep /,/, $$ruleRef{'dash'}) { + my @ds = @{$dashDefinition{ $$ruleRef{'dash'} } } ; + $lc = $ds[0] ; + my $style = $ds[1] ; + $dash = "stroke-dasharray=\"$style\" " ; + } + else { + $lc = $$ruleRef{'dashcap'} ; + my $style = $$ruleRef{'dash'} ; + $dash = "stroke-dasharray=\"$style\"" ; + } + } + + # top (actual way) + $svg1 = "stroke=\"$color\" stroke-width=\"$size\" stroke-linecap=\"$lc\" fill=\"none\" stroke-linejoin=\"$lj\" " . $dash ; + $layer1 = $layer + $size / 100 ; + + my $bs = $$ruleRef{'bordersize'} ; + $lc = "round" ; + $dash = "" ; + + if ( cv ('autobridge') eq "1" ) { + # TODO bridge/tunnel + if ( $bridge == 1) { + $lc = "butt" ; + $bs += 3 ; # TODO config value + } + elsif ( $tunnel == 1) { + $lc = "butt" ; + $dash = "stroke-dasharray=\"10,10\" " ; + $bs += 3 ; + } + } + + # bottom (border) + if ( $bs > 0 ) { + $size = 2 * $bs + $$ruleRef{'size'} ; + $color = $$ruleRef{'bordercolor'} ; + $svg2 = "stroke=\"$color\" stroke-width=\"$size\" stroke-linecap=\"$lc\" fill=\"none\" stroke-linejoin=\"$lj\" " . $dash ; + $layer2 = $layer - 0.3 + $size / 100 ; + } + else { + $svg2 = "" ; + $layer2 = 0 ; + } + + } + + return ($svg1, $layer1, $svg2, $layer2) ; +} + +# --------------------------------------------------------------------------------- + +sub createDirectory { + my $directoryName ; + my $dirFile ; + $directoryName = cv ('out') ; + $directoryName =~ s/\.svg/\_streets.txt/ ; + setConfigValue("directoryname", $directoryName) ; + print "creating dir file $directoryName ...\n" ; + open ($dirFile, ">", $directoryName) or die ("can't open dir file $directoryName\n") ; + + my $ref = getDirectory() ; + my %directory = %$ref ; + + if ( cv('grid') eq "0") { + foreach my $street (sort keys %directory) { + $street = replaceHTMLCode ( $street ) ; + print $dirFile "$street\n" ; + } + } + else { + foreach my $street (sort keys %directory) { + my $streetSanitized = replaceHTMLCode ( $street ) ; + print $dirFile "$streetSanitized\t" ; + foreach my $square (sort keys %{$directory{$street}}) { + print $dirFile "$square " ; + } + print $dirFile "\n" ; + } + } + close ($dirFile) ; +} + +1 ; + +