source: subversion/applications/utils/gary68/useractivity.pl @ 24457

Last change on this file since 24457 was 24457, checked in by gary68, 9 years ago

new useractivity.pl - display cities in map

  • Property svn:executable set to *
File size: 49.9 KB
Line 
1#!/usr/bin/perl
2#
3# useractivity.pl by gary68
4#
5#
6#
7# Copyright (C) 2009, Gerhard Schwanz
8#
9# 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
10# Free Software Foundation; either version 3 of the License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>
16#
17#
18# Version 2
19# - map support added
20# - way support added
21#
22# Version 2.1
23# - separate files for time slots (privacy)
24#
25# Version 2.2
26# - some bugs fixed
27#
28# Version 2.3
29# - html print program version number
30# - draw renamed ways
31#
32# Version 2.4
33# - user "age" added
34#
35# Version 3.0
36# - black and white lists
37#
38# Version 3.1
39# - bug fix
40#
41# Version 3.2
42# - Uids added
43#
44# Version 4.2
45# - display cities
46#
47
48use strict ;
49use warnings ;
50
51use OSM::osm ;
52use OSM::osmgraph ;
53use File::stat ;
54#use Time::localtime ;
55use Compress::Bzip2 ;
56
57my $program = "useractivity.pl" ;
58my $usage = $program . " file1.osm file2.osm out.htm Mode [numTopUsers] [picSize] (Mode = [N|P|D|S], picSize x in pixels)\n" ;
59$usage .= "N = normal\nP = with picture\nPD = with detailed picture\nPS/PDS = also write SVG file\nout.white.txt and out.black.txt (white and black lists) can be given (enter one user name per line)\n" ;
60my $version = "4.2" ;
61
62my $topMax = 10 ;
63
64my %lon1 ; my %lat1 ; my %lon2 ; my %lat2 ;
65my $wayId1 ; my $wayVersion1 ; my $wayTimestamp1 ; my $wayUid1 ; my $wayUser1 ; my $wayChangeset1 ; my @wayNodes1 ; my @wayTags1 ;
66my $wayId2 ; my $wayVersion2 ; my $wayTimestamp2 ; my $wayUid2 ; my $wayUser2 ; my $wayChangeset2 ; my @wayNodes2 ; my @wayTags2 ;
67
68my $nodeId1 ; my $nodeUser1 ; my $nodeLat1 ; my $nodeLon1 ; my @nodeTags1 ; my $nodeVersion1 ; my $nodeTimestamp1 ; my $nodeUid1 ; my $nodeChangeset1 ;
69my $nodeId2 ; my $nodeUser2 ; my $nodeLat2 ; my $nodeLon2 ; my @nodeTags2 ; my $nodeVersion2 ; my $nodeTimestamp2 ; my $nodeUid2 ; my $nodeChangeset2 ;
70my $aRef1 ; my $aRef2 ;
71
72my $time0 = time() ; 
73
74my $mode = "" ; my $sizeX = 1024 ;
75my $html ; my $htmlName ;
76my $osm1Name ; my $osm2Name ;
77
78my $file1 ; my $file2 ; my $bz1 ; my $bz2 ; my $isBz21 ; my $isBz22 ; my $line1 ; my $line2 ; my $bzerrno ;
79my $file1Name ; my $file2Name ;
80
81my %minLon ; my %minLat ; my %maxLon ; my %maxLat ; # per user
82my $deletedNodes = 0 ; my $deletedNodesWithTags = 0 ;
83my $deletedWays = 0 ; my $deletedWaysWithTags = 0 ;
84my %nodesAdded ; my %nodesMovedNumber ; my %nodesMovedDistance ; my %nodesMovedMax ;
85my %waysAdded ;
86my %tagsAdded ; my %tagsDeleted ; my %tagsRenamed ; my %tagsReclassified ; my %tagsRereffed ;
87my %tagsDeletedName ;
88my %renames ; my %reclassifications ; my %rerefs ;
89my @deletedNodesIds = () ; my %deletedNodesTags = () ;
90my @deletedWaysIds = () ; my %deletedWaysTags = () ;
91my %versionJumpsNodes = () ;
92my %versionJumpsWays = () ;
93my %opTime ;
94my %neededNodes = () ;
95my %age = () ;
96my $localDay ; my $localMonth ; my $localYear ;
97
98my $reName ; my $reClass ; my $reRef ;
99my $name1 ; my $name2 ; my $ref1 ; my $ref2 ; my $class1 ; my $class2 ;
100my %white ; my %black ; my %blackActive ; my %blackUid ; my %whiteUid ; my %blackUidActive ;
101my %activeUid ;
102
103my @cities = () ;
104
105my $objectProcessed = 0 ; # 0=node, 1=way
106
107###############
108# get parameter
109###############
110$osm1Name = shift||'';
111if (!$osm1Name) { die (print $usage, "\n") ; }
112
113$osm2Name = shift||'';
114if (!$osm2Name) { die (print $usage, "\n") ; }
115
116$htmlName = shift||'';
117if (!$htmlName) { die (print $usage, "\n") ; }
118
119$mode = shift||'';
120if (!$mode) {   $mode = "N" ; }
121
122$topMax = shift||'';
123if (!$topMax) { $topMax = 10 ; }
124
125$sizeX = shift||'';
126if (!$sizeX) {  $sizeX = 1024 ; }
127
128print "\n$program $version for files:\n\n" ;
129print stringFileInfo ($osm1Name), "\n"  ;
130print stringFileInfo ($osm2Name), "\n\n"  ;
131
132#------------------------------------------------------------------------------------
133# Main
134#------------------------------------------------------------------------------------
135
136initLocaltime() ;
137print "local time: $localDay $localMonth $localYear\n" ;
138
139populateCities() ;
140
141if (grep /P/, $mode) { initializeMap() ; }
142
143readLists() ;
144
145openOsm1File ($osm1Name) ;
146moveNodeFile1() ;
147
148openOsm2File ($osm2Name) ;
149moveNodeFile2() ;
150
151processNodes() ;
152
153moveWayFile1() ;
154moveWayFile2() ;
155
156processWays() ;
157
158closeOsm1File () ;
159closeOsm2File () ;
160
161if ( ! (grep /P/, $mode) )  { 
162        processNeededWayNodes() ; 
163}
164
165if (grep /P/, $mode) { print "paint areas and save map...\n" ; }
166if (grep /P/, $mode) { paintAreas() ; }
167if (grep /P/, $mode) { saveMap() ; }
168if (grep /P/, $mode) { print "done.\n" ; }
169
170removeWhiteListData() ;
171getBlackListData() ;
172output() ;
173
174#-------
175# FINISH
176#-------
177
178print "\n$program finished after ", stringTimeSpent (time()-$time0), "\n\n" ;
179
180
181#------------------------------------------------------------------------------------
182# Node procesing
183#------------------------------------------------------------------------------------
184
185sub processNodes {
186        print "processing nodes...\n" ;
187        while ( ($nodeId1 > -1) or ($nodeId2 > -1) ) {
188                # print "while $nodeId1     $nodeId2\n" ;
189                if ($nodeId1 == -1) {
190                        # get rest from file 2, new nodes
191                        while ( $nodeId2 != -1 ) {
192                                # print "$nodeId1     $nodeId2   2-NEW\n" ;
193                                $nodesAdded{$nodeUser2}++ ;
194                                addOperationTime ($nodeUser2, $nodeTimestamp2) ;
195                                if (grep /P/, $mode) { paintNewNode() ; }
196                                userArea ($nodeUser2, $nodeLon2, $nodeLat2) ;
197                                $activeUid{$nodeUid2} = $nodeUser2 ;
198                                moveNodeFile2() ;
199                        }
200                }
201
202                if ($nodeId2 == -1) {
203                        # get rest from file 1, deleted nodes
204                        while ( $nodeId1 != -1 ) {
205                                $deletedNodes++ ;
206                                if (scalar @nodeTags1 > 0) { 
207                                        $deletedNodesWithTags++ ; 
208                                        if (grep /P/, $mode) { paintDeletedNodeWithTag() ; }
209                                        push @deletedNodesIds, $nodeId1 ;
210                                        @{$deletedNodesTags{$nodeId1}} = @nodeTags1 ;
211                                }
212                                else {
213                                        if (grep /P/, $mode) { paintDeletedNode() ; }
214                                }
215                                moveNodeFile1() ;
216                        }
217                }
218
219                if ( ($nodeId1 == $nodeId2) and ($nodeId1 != -1) ) {
220                        # print "equal $nodeId1     $nodeId2\n" ;
221
222                        # place?
223                        if (grep /P/, $mode) {
224                                my $place = 0 ; my $placeName = "" ; my $placeNameGiven = 0 ;
225                                foreach my $t (@nodeTags1) {
226                                        if ($t->[0] eq "place") { $place = 1 ; }
227                                        if ($t->[0] eq "name") { $placeNameGiven = 1 ; $placeName = $t->[1] ; }
228                                }
229                                if ( ($place == 1) and ($placeNameGiven == 1) ) { paintPlace ($nodeLon1, $nodeLat1, $placeName) ; } 
230                        }
231
232                        # position...
233                        if ( ($nodeLon1 != $nodeLon2) or ($nodeLat1 != $nodeLat2) ) {
234                                $nodesMovedNumber{$nodeUser2}++ ;
235                                addOperationTime ($nodeUser2, $nodeTimestamp2) ;
236                                if (grep /P/, $mode) { paintMovedNode() ; }
237                                my ($d) = distance ($nodeLon1, $nodeLat1, $nodeLon2, $nodeLat2) ;
238                                $nodesMovedDistance{$nodeUser2} += $d ;
239                                if (defined $nodesMovedMax{$nodeUser2}) {
240                                        if ($nodesMovedMax{$nodeUser2} < $d) { $nodesMovedMax{$nodeUser2} = $d ; }
241                                }
242                                else {
243                                        $nodesMovedMax{$nodeUser2} = $d ;
244                                }
245                                userArea ($nodeUser2, $nodeLon1, $nodeLat1) ;
246                                userArea ($nodeUser2, $nodeLon2, $nodeLat2) ;
247                                $activeUid{$nodeUid2} = $nodeUser2 ;
248                        } # moved
249
250                        if ($nodeVersion2 - $nodeVersion1 > 2) { 
251                                push @{$versionJumpsNodes{$nodeUser2}}, [$nodeId2, $nodeVersion2 - $nodeVersion1] ;
252                        }
253
254                        # process tags
255                        my ($added, $deleted, $renamed, $reclassified) = compareTags (\@nodeTags1, \@nodeTags2) ; 
256                        if ($added) { $tagsAdded{$nodeUser2} += $added ; }
257                        if ($deleted) { $tagsDeleted{$nodeUser2} += $deleted ; }
258                        if ($renamed) { 
259                                $tagsRenamed{$nodeUser2} += $renamed ; 
260                                if (grep /P/, $mode) { paintRenamedNode() ; }
261                        }
262                        if ($added or $deleted or $renamed or $reclassified) {
263                                addOperationTime ($nodeUser2, $nodeTimestamp2) ;
264                                userArea ($nodeUser2, $nodeLon2, $nodeLat2) ;
265                                $activeUid{$nodeUid2} = $nodeUser2 ;
266                        }
267                        moveNodeFile1() ;
268                        moveNodeFile2() ;
269                }
270
271                if ( ($nodeId1 > $nodeId2) and ($nodeId2 != -1) ) {
272                        # print "1 > 2 $nodeId1     $nodeId2   2-NEW\n" ;
273                        # id 2 not found in file 1, nodeId2 new
274                        $nodesAdded{$nodeUser2}++ ;
275                        addOperationTime ($nodeUser2, $nodeTimestamp2) ;
276                        if (grep /P/, $mode) { paintNewNode() ; }
277                        userArea ($nodeUser2, $nodeLon2, $nodeLat2) ;
278                        $activeUid{$nodeUid2} = $nodeUser2 ;
279                        # move file2 until id2>=id1
280                        while ( ($nodeId2 < $nodeId1) and ($nodeId2 != -1) ) {
281                                moveNodeFile2() ;
282                        }
283                }
284
285                if ( ($nodeId1 < $nodeId2) and ($nodeId1 != -1) ) {
286                        # print "1 < 2 $nodeId1     $nodeId2   1-DELETED\n" ;
287                        # id 1 not found in file 2, nodeId1 deleted
288                        $deletedNodes++ ;
289                        if (scalar @nodeTags1 > 0) { 
290                                $deletedNodesWithTags++ ; 
291                                if (grep /P/, $mode) { paintDeletedNodeWithTag() ; }
292                                push @deletedNodesIds, $nodeId1 ;
293                                @{$deletedNodesTags{$nodeId1}} = @nodeTags1 ;
294                        }
295                        else {
296                                if (grep /P/, $mode) { paintDeletedNode() ; }
297                        }
298                        # move file1 until id1>=id2
299                        while ( ($nodeId1 < $nodeId2) and ($nodeId1 != -1) ) {
300                                moveNodeFile1() ;
301                        }
302                }
303        }
304        print "finished.\n" ;
305}
306
307#------------------------------------------------------------------------------------
308# Way procesing
309#------------------------------------------------------------------------------------
310
311sub processWays {
312        $objectProcessed = 1 ;
313        print "processing ways...\n" ;
314        while ( ($wayId1 > -1) or ($wayId2 > -1) ) {
315                if ($wayId1 == -1) {
316                        # get rest from file 2, new ways
317                        while ( $wayId2 != -1 ) {
318                                # print "$wayId1     $wayId2   2-NEW\n" ;
319                                $waysAdded{$wayUser2}++ ;
320                                addOperationTime ($wayUser2, $wayTimestamp2) ;
321                                if (scalar @wayNodes2 >= 2) {
322                                        #userArea ($wayUser2, $lon2{$wayNodes2[0]}, $lat2{$wayNodes2[0]}) ;
323                                        #userArea ($wayUser2, $lon2{$wayNodes2[-1]}, $lat2{$wayNodes2[-1]}) ;
324                                        userAreaWay ($wayUser2, $wayNodes2[0]) ;
325                                        userAreaWay ($wayUser2, $wayNodes2[-1]) ;
326                                        $activeUid{$wayUid2} = $wayUser2 ;
327                                        if (grep /P/, $mode) { paintNewWay() ; }
328                                }
329                                #next
330                                moveWayFile2() ;
331                        }
332                }
333
334                if ($wayId2 == -1) {
335                        # get rest from file 1, deleted ways
336                        while ( $wayId1 != -1 ) {
337                                $deletedWays++ ;
338                                if (scalar @wayTags1 > 0) { 
339                                        $deletedWaysWithTags++ ; 
340                                        if (scalar @wayNodes1 >= 2) {
341                                                if (grep /P/, $mode) { paintDeletedWayWithTag() ; }
342                                        }
343                                        push @deletedWaysIds, $wayId1 ;
344                                        @{$deletedWaysTags{$wayId1}} = @wayTags1 ;
345                                }
346                                else {
347                                        if (scalar @wayNodes1 >= 2) {
348                                                if (grep /P/, $mode) { paintDeletedWay() ; }
349                                        }
350                                }
351                                #next
352                                moveWayFile1() ;
353                        }
354                }
355
356                if ( ($wayId1 == $wayId2) and ($wayId1 != -1) ) {
357                        # print "equal $wayId1     $wayId2\n" ;
358                        # TODO position
359                        # TODO nodes number
360
361                        if ($wayVersion2 - $wayVersion1 > 2) { 
362                                push @{$versionJumpsWays{$wayUser2}}, [$wayId2, $wayVersion2 - $wayVersion1] ;
363                        }
364
365                        # process tags
366                        my ($added, $deleted, $renamed, $reclassified, $rereffed) = compareTags (\@wayTags1, \@wayTags2) ; 
367                        if ($added) { $tagsAdded{$wayUser2} += $added ; }
368                        if ($deleted) { $tagsDeleted{$wayUser2} += $deleted ; }
369                        if ($renamed) { 
370                                $tagsRenamed{$wayUser2} += $renamed ; 
371                                if (grep /P/, $mode) { paintRenamedWay() ; }
372                        }
373                        if ($reclassified) { 
374                                $tagsReclassified{$wayUser2} += $reclassified ; 
375                                if (scalar @wayNodes1 >= 2) {
376                                        if (grep /P/, $mode) { paintReclassifiedWay() ; }
377                                }
378                        } 
379                        if ($rereffed) { 
380                                $tagsRereffed{$wayUser2} += $rereffed ; 
381                                if (scalar @wayNodes1 >= 2) {
382                                        if (grep /P/, $mode) { paintRereffedWay() ; }
383                                }
384                        } 
385                        if ($added or $deleted or $renamed or $reclassified or $rereffed) { 
386                                addOperationTime ($wayUser2, $wayTimestamp2) ; 
387                                if ( (scalar @wayNodes1 >= 2) and (scalar @wayNodes2 >= 2) ) {
388                                        # userArea ($wayUser2, $lon2{$wayNodes2[0]}, $lat2{$wayNodes2[0]}) ;
389                                        # userArea ($wayUser2, $lon2{$wayNodes2[-1]}, $lat2{$wayNodes2[-1]}) ;
390                                        userAreaWay ($wayUser2, $wayNodes2[0]) ;
391                                        userAreaWay ($wayUser2, $wayNodes2[-1]) ;
392                                        $activeUid{$wayUid2} = $wayUser2 ;
393                                }
394                        }
395                        moveWayFile1() ;
396                        moveWayFile2() ;
397                }
398
399                if ( ($wayId1 > $wayId2) and ($wayId2 != -1) ) {
400                        # print "1 > 2 $wayId1     $wayId2   2-NEW\n" ;
401                        # id 2 not found in file 1, wayId2 new
402                        $waysAdded{$wayUser2}++ ;
403                        addOperationTime ($wayUser2, $wayTimestamp2) ; 
404                        if (scalar @wayNodes2 >= 2) {
405                                # userArea ($wayUser2, $lon2{$wayNodes2[0]}, $lat2{$wayNodes2[0]}) ;
406                                # userArea ($wayUser2, $lon2{$wayNodes2[-1]}, $lat2{$wayNodes2[-1]}) ;
407                                userAreaWay ($wayUser2, $wayNodes2[0]) ;
408                                userAreaWay ($wayUser2, $wayNodes2[-1]) ;
409                                $activeUid{$wayUid2} = $wayUser2 ;
410                                if (grep /P/, $mode) { paintNewWay() ; }
411                        }
412                        # move file2 until id2>=id1
413                        while ( ($wayId2 < $wayId1) and ($wayId2 != -1) ) {
414                                moveWayFile2() ;
415                        }
416                }
417
418                if ( ($wayId1 < $wayId2) and ($wayId1 != -1) ) {
419                        # print "1 < 2 $wayId1     $wayId2   1-DELETED\n" ;
420                        # id 1 not found in file 2, wayId1 deleted
421                        $deletedWays++ ;
422                        if (scalar @wayTags1 > 0) { 
423                                $deletedWaysWithTags++ ; 
424                                if (scalar @wayNodes1 >= 2) {
425                                        if (grep /P/, $mode) { paintDeletedWayWithTag() ; }
426                                }
427                                push @deletedWaysIds, $wayId1 ;
428                                @{$deletedWaysTags{$wayId1}} = @wayTags1 ;
429                        }
430                        else {
431                                if (scalar @wayNodes1 >= 2) {
432                                        if (grep /P/, $mode) { paintDeletedWay() ; }
433                                }
434                        }
435                        # move file1 until id1>=id2
436                        while ( ($wayId1 < $wayId2) and ($wayId1 != -1) ) {
437                                moveWayFile1() ;
438                        }
439                }
440        }
441        print "finished.\n" ;
442}
443
444
445#------------------------------------------------------------------------------------
446# Output functions
447#------------------------------------------------------------------------------------
448
449sub output {
450        print "outputting html...\n" ;
451        my @a ; my @list ;
452        open ($html, ">", $htmlName) or die ("can't open html output file") ;
453        printHTMLHeader ($html, "UserActvity by Gary68") ;
454        print $html "<H1>UserActvity by gary68</H1>" ;
455        print $html "<p>Version: ", $version, "</p>\n"  ;
456        print $html "<p>", stringFileInfo ($osm1Name), "</p>\n"  ;
457        print $html "<p>", stringFileInfo ($osm2Name), "</p>\n"  ;
458        print $html "<p>A deleted tag can result out of a change of a tag. The same is true for the addition of a tag. Real changes are not counted.</p>" ;
459
460        print $html "<H1>Black and white lists</H1>" ;
461        print $html "<H2>WHITE listed users</H2>\n<p>" ;
462        foreach my $u (sort keys %white) { print $html $u, "; " ; }
463        print $html "</p>\n" ;
464        print $html "<H2>BLACK listed users</H2>\n<p>" ;
465        foreach my $u (sort keys %black) { print $html $u, "; " ; }
466        print $html "</p>\n" ;
467        print $html "<H2>ACTIVE BLACK listed users</H2>\n<p><strong>" ;
468        foreach my $u (sort keys %blackActive) { print $html $u, "; " ; }
469        print $html "</strong></p>\n" ;
470        print $html "<H2>BLACK listed uids</H2>\n<p>" ;
471        foreach my $u (sort keys %blackUid) { print $html $u, "; " ; }
472        print $html "</p>\n" ;
473        print $html "<H2>ACTIVE BLACK listed uids</H2>\n<p><strong>" ;
474        foreach my $u (sort keys %blackUidActive) { print $html $u . " - " . $activeUid{$u}, " <br>\n" ; }
475        print $html "</strong></p>\n" ;
476       
477
478        print $html "<H1>Results</H1>" ;
479        print $html "<p>DELETED NODES: $deletedNodes</p>\n" ;
480        print $html "<p>DELETED NODES WITH TAGS: $deletedNodesWithTags (details see further down)</p>\n" ;
481       
482        @a = () ;
483        foreach my $u (keys %age) { push @a, [$u, $age{$u}] ; }
484        printBottom ("BOTTOM age users (in days, derived from timestamps)", 0, @a) ;
485        @a = () ;
486        foreach my $e (keys %nodesMovedNumber) { push @a, [$e, $nodesMovedNumber{$e}] ; }
487        printTop ("TOP moved nodes number", 0, @a) ;
488        @a = () ;
489        foreach my $e (keys %nodesMovedDistance) { push @a, [$e, $nodesMovedDistance{$e}] ; }
490        printTop ("TOP moved nodes total distance (km)", 1, @a) ;
491        @a = () ;
492        foreach my $e (keys %nodesMovedDistance) { push @a, [$e, $nodesMovedDistance{$e}/$nodesMovedNumber{$e}] ; }
493        printTop ("TOP moved nodes average distance (km)", 1, @a) ;
494        @a = () ;
495        foreach my $e (keys %nodesMovedMax) { push @a, [$e, $nodesMovedMax{$e}] ; }
496        printTop ("TOP moved nodes maximum distance (km)", 1, @a) ;
497
498        @a = () ;
499        foreach my $u (keys %maxLon) {
500                # print "calc area for user: $u\n" ;
501                push @a, [$u, distance ($minLon{$u}, $minLat{$u}, $minLon{$u}, $maxLat{$u}) * distance ($minLon{$u}, $minLat{$u}, $maxLon{$u}, $minLat{$u})] ;
502        }
503        printTop ("TOP work areas (km²)", 1, @a) ;
504
505        @a = () ;
506        foreach my $e (keys %tagsAdded) { push @a, [$e, $tagsAdded{$e}] ; }
507        printTop ("TOP tags added", 0, @a) ;
508
509        @a = () ;
510        foreach my $e (keys %tagsRenamed) { push @a, [$e, $tagsRenamed{$e}] ; }
511        printTop ("TOP objects renamed", 0, @a) ;
512
513        @list = @a ; @list = reverse (sort {$a->[1]<=>$b->[1]} @list) ;
514        # if (scalar @list > $topMax) { @list = @list[0..$topMax-1] ; }
515        print $html "<h2>Renames by ALL users</h2>\n" ;
516        printHTMLTableHead ($html) ;
517        printHTMLTableHeadings ($html, "User", "Tag", "Number") ; 
518        foreach my $e (@list) {
519                foreach my $t (keys %{$renames{$e->[0]}}) {
520                        # printf $html "%-25s %-80s %5i<br>\n" , $e->[0], $t, $renames{$e->[0]}{$t} ;
521                        printHTMLRowStart ($html) ;
522                        printHTMLCellLeft ($html, userLink ($e->[0])) ;                 
523                        printHTMLCellLeft ($html, $t) ;                 
524                        printHTMLCellRight ($html, $renames{$e->[0]}{$t}) ;                     
525                        printHTMLRowEnd ($html) ;
526                }
527        }
528        printHTMLTableFoot ($html) ;
529
530        @a = () ;
531        foreach my $e (keys %tagsRereffed) { push @a, [$e, $tagsRereffed{$e}] ; }
532        printTop ("TOP objects rereffed", 0, @a) ;
533
534        @list = @a ; @list = reverse (sort {$a->[1]<=>$b->[1]} @list) ;
535        # if (scalar @list > $topMax) { @list = @list[0..$topMax-1] ; }
536        print $html "<h2>Rerefs by ALL users</h2>\n" ;
537        printHTMLTableHead ($html) ;
538        printHTMLTableHeadings ($html, "User", "Tag", "Number") ; 
539        foreach my $e (@list) {
540                foreach my $t (keys %{$rerefs{$e->[0]}}) {
541                        # printf $html "%-25s %-80s %5i<br>\n" , $e->[0], $t, $rerefs{$e->[0]}{$t} ;
542                        printHTMLRowStart ($html) ;
543                        printHTMLCellLeft ($html, userLink ($e->[0])) ;                 
544                        printHTMLCellLeft ($html, $t) ;                 
545                        printHTMLCellRight ($html, $rerefs{$e->[0]}{$t}) ;                     
546                        printHTMLRowEnd ($html) ;
547                }
548        }
549        printHTMLTableFoot ($html) ;
550
551        @a = () ;
552        foreach my $e (keys %tagsReclassified) { push @a, [$e, $tagsReclassified{$e}] ; }
553        printTop ("TOP ways reclassified", 0, @a) ;
554
555        @list = @a ; @list = reverse (sort {$a->[1]<=>$b->[1]} @list) ;
556        # if (scalar @list > $topMax) { @list = @list[0..$topMax-1] ; }
557        print $html "<h2>Reclassifications by ALL users</h2>\n" ;
558        printHTMLTableHead ($html) ;
559        printHTMLTableHeadings ($html, "User", "Tag", "Number") ; 
560        foreach my $e (@list) {
561                foreach my $t (keys %{$reclassifications{$e->[0]}}) {
562                        # printf $html "%-25s %-80s %5i<br>\n" , $e->[0], $t, $reclassifications{$e->[0]}{$t} ;
563                        printHTMLRowStart ($html) ;
564                        printHTMLCellLeft ($html, userLink ($e->[0])) ;                 
565                        printHTMLCellLeft ($html, $t) ;                 
566                        printHTMLCellRight ($html, $reclassifications{$e->[0]}{$t}) ;                   
567                        printHTMLRowEnd ($html) ;
568                }
569        }
570        printHTMLTableFoot ($html) ;
571
572#       print $html "<h2>Version jumps nodes by ALL users</h2>\n" ;
573#       printHTMLTableHead ($html) ;
574#       printHTMLTableHeadings ($html, "User", "Nodes") ;
575#       foreach my $u (keys %versionJumpsNodes) {
576#               printHTMLRowStart ($html) ;
577#               printHTMLCellLeft ($html, userLink ($u)) ;                     
578#               my $jumps = "" ;
579#               foreach my $v (@{$versionJumpsNodes{$u}}) {
580#                       $jumps = $jumps . historyLink ("node", $v->[0]) . " (" . $v->[1] . ") " ;
581#               }
582#               printHTMLCellLeft ($html, $jumps) ;                     
583#               printHTMLRowEnd ($html) ;
584#       }
585#       printHTMLTableFoot ($html) ;
586
587#       print $html "<h2>Version jumps ways by ALL users</h2>\n" ;
588#       printHTMLTableHead ($html) ;
589#       printHTMLTableHeadings ($html, "User", "Ways") ;
590#       foreach my $u (keys %versionJumpsWays) {
591#               printHTMLRowStart ($html) ;
592#               printHTMLCellLeft ($html, userLink ($u)) ;                     
593#               my $jumps = "" ;
594#               foreach my $v (@{$versionJumpsWays{$u}}) {
595#                       $jumps = $jumps . historyLink ("node", $v->[0]) . " (" . $v->[1] . ") " ;
596#               }
597#               printHTMLCellLeft ($html, $jumps) ;                     
598#               printHTMLRowEnd ($html) ;
599#       }
600#       printHTMLTableFoot ($html) ;
601
602        @a = () ;
603        foreach my $e (keys %tagsDeleted) { push @a, [$e, $tagsDeleted{$e}] ; }
604        printTop ("TOP tags deleted", 0, @a) ;
605
606        @list = @a ; @list = reverse (sort {$a->[1]<=>$b->[1]} @list) ;
607        if (scalar @list > $topMax) { @list = @list[0..$topMax-1] ; }
608        print $html "<h2>Tags removed by TOP users</h2>\n" ;
609        printHTMLTableHead ($html) ;
610        printHTMLTableHeadings ($html, "User", "Removed", "Number") ; 
611        foreach my $e (@list) {
612                foreach my $t (keys %{$tagsDeletedName{$e->[0]}}) {
613                        # printf $html "%-25s %-50s %5i<br>\n" , $e->[0], $t, $tagsDeletedName{$e->[0]}{$t} ;
614                        printHTMLRowStart ($html) ;
615                        printHTMLCellLeft ($html, userLink ($e->[0])) ;                 
616                        printHTMLCellLeft ($html, $t) ;                 
617                        printHTMLCellRight ($html, $tagsDeletedName{$e->[0]}{$t}) ;                     
618                        printHTMLRowEnd ($html) ;
619                }
620        }
621        printHTMLTableFoot ($html) ;
622
623        print $html "<h2>ALL deleted Nodes with tags (details)</h2>\n" ;
624        printHTMLTableHead ($html) ;
625        printHTMLTableHeadings ($html, "NodeId", "Tags") ; 
626        foreach my $n (@deletedNodesIds) {
627                printHTMLRowStart ($html) ;
628                printHTMLCellLeft ($html, historyLink ("node", $n) ) ;                 
629                my $tagText = "" ;
630                foreach my $t (@{$deletedNodesTags{$n}}) { $tagText = $tagText . $t->[0] . ":" . $t->[1] . "<br>\n" ; }
631                printHTMLCellLeft ($html, $tagText) ;   
632                printHTMLRowEnd ($html) ;
633        }
634        printHTMLTableFoot ($html) ;
635
636        print $html "<h2>ALL deleted ways with tags (details)</h2>\n" ;
637        printHTMLTableHead ($html) ;
638        printHTMLTableHeadings ($html, "WayId", "Tags") ; 
639        foreach my $n (@deletedWaysIds) {
640                printHTMLRowStart ($html) ;
641                printHTMLCellLeft ($html, historyLink ("way", $n) ) ;                   
642                my $tagText = "" ;
643                foreach my $t (@{$deletedWaysTags{$n}}) { $tagText = $tagText . $t->[0] . ":" . $t->[1] . "<br>\n" ; }
644                printHTMLCellLeft ($html, $tagText) ;   
645                printHTMLRowEnd ($html) ;
646        }
647        printHTMLTableFoot ($html) ;
648
649        print $html "<p>$program finished after ", stringTimeSpent (time()-$time0), "</p>\n" ;
650        printHTMLFoot ($html) ;
651        close ($html) ;
652
653        my ($html2Name) = $htmlName ;
654        $html2Name =~ s/.htm/.time.htm/ ;
655        open ($html, ">", $html2Name) or die ("can't open html2 output file") ;
656        printHTMLHeader ($html, "UserActvity (TIME) by Gary68") ;
657        print $html "<H1>UserActvity (TIME) by gary68</H1>" ;
658        print $html "<p>", stringFileInfo ($osm1Name), "</p>\n"  ;
659        print $html "<p>", stringFileInfo ($osm2Name), "</p>\n"  ;
660
661        @a = () ;
662        foreach my $u (keys %opTime) { push @a, [$u, numOpHours ($u)] ; }
663        printTop ("TOP operation hour slots", 0, @a) ;
664
665        @list = reverse (sort {$a->[1]<=>$b->[1]} @a) ;
666        if (scalar @list > $topMax) { @list = @list[0..$topMax-1] ; }
667        print $html "<h2>Operation time slots TOP users</h2>\n" ;
668        printHTMLTableHead ($html) ;
669        printHTMLTableHeadings ($html, "User", "00","01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23") ; 
670        foreach my $e (@list) {
671                printHTMLRowStart ($html) ;
672                printHTMLCellLeft ($html, userLink ($e->[0])) ;                 
673                my $max = 0 ;
674                foreach my $h ("00","01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23") {
675                        if ( (defined $opTime{$e->[0]}{$h}) and ($opTime{$e->[0]}{$h} > $max) )  { $max = $opTime{$e->[0]}{$h} ; } 
676                }
677                # print "$e->[0] $max\n" ;
678                my $value = 0 ;
679                foreach my $h ("00","01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23") {
680                        if (defined $opTime{$e->[0]}{$h}) { $value = $opTime{$e->[0]}{$h} ; } else { $value = 0 ; }
681                        my ($colorValue) = 255 - ( int ($value / $max * 255) / 2) ; my ($colorString) = sprintf "%02x", $colorValue ;
682                        # print "$value $colorValue $colorString\n" ;
683                        my ($htmlString) = "<td align=\"right\" bgcolor=\"#" . $colorString . $colorString . $colorString . "\">" . $value . "</td>" ;
684                        print $html $htmlString ;
685                }
686                printHTMLRowEnd ($html) ;
687        }
688        printHTMLTableFoot ($html) ;
689
690
691        printHTMLFoot ($html) ;
692        close ($html) ;
693
694
695        print "done.\n" ;
696}
697
698sub printTop {
699        my ($heading, $decimal, @list) = @_ ;
700        print $html "<h2>$heading</h2>\n" ;
701        printHTMLTableHead ($html) ;
702        printHTMLTableHeadings ($html, "User", "Data") ; 
703        @list = reverse (sort {$a->[1]<=>$b->[1]} @list) ;
704        if (scalar @list > $topMax) { @list = @list[0..$topMax-1] ; }
705        foreach my $e (@list) {
706                printHTMLRowStart ($html) ;
707                my $s ;
708                if ($decimal) { 
709                        $s = sprintf "%8.3f", $e->[1] ;
710                }
711                else {
712                        $s = sprintf "%8i", $e->[1] ;
713                }
714                printHTMLCellLeft ($html, userLink ($e->[0]) ) ;                       
715                printHTMLCellRight ($html, $s) ;                       
716                printHTMLRowEnd ($html) ;
717        } 
718        printHTMLTableFoot ($html) ;
719}
720
721
722sub printBottom {
723        my ($heading, $decimal, @list) = @_ ;
724        print $html "<h2>$heading</h2>\n" ;
725        printHTMLTableHead ($html) ;
726        printHTMLTableHeadings ($html, "User", "Data") ; 
727        @list = sort {$a->[1]<=>$b->[1]} @list ;
728        if (scalar @list > $topMax) { @list = @list[0..$topMax-1] ; }
729        foreach my $e (@list) {
730                printHTMLRowStart ($html) ;
731                my $s ;
732                if ($decimal) { 
733                        $s = sprintf "%8.3f", $e->[1] ;
734                }
735                else {
736                        $s = sprintf "%8i", $e->[1] ;
737                }
738                printHTMLCellLeft ($html, userLink ($e->[0]) ) ;                       
739                printHTMLCellRight ($html, $s) ;                       
740                printHTMLRowEnd ($html) ;
741        } 
742        printHTMLTableFoot ($html) ;
743}
744
745
746#------------------------------------------------------------------------------------
747# some functions
748#------------------------------------------------------------------------------------
749
750sub addOperationTime {
751        my ($user, $timestamp) = @_ ;
752        # timestamp="2008-04-13T13:23:55+01:00"
753        my ($hour) = substr ($timestamp, 11, 2) ;
754        $opTime{$user}{$hour}++ ;
755}
756
757sub numOpHours {
758        my ($user) = shift ;
759        my $hours = 0 ;
760        foreach my $hour ("00","01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23") {
761                if (defined $opTime{$user}{$hour}) { $hours++ ; }
762        }
763        return ($hours) ;
764}
765
766sub userAreaWay {
767        my ($u, $n) = @_ ;
768        if (grep /P/, $mode) {
769                userArea ($u, $lon2{$n}, $lat2{$n}) ;
770        }
771        else {
772                $neededNodes{$n} = $u ;
773        }
774}
775
776sub processNeededWayNodes {
777        print "get needed nodes for touched ways...\n" ;
778        openOsm2File ($osm2Name) ;
779        moveNodeFile2() ;
780        while ( $nodeId2 != -1 ) {
781                if (defined $neededNodes{$nodeId2}) {
782                        userArea ($neededNodes{$nodeId2}, $nodeLon2, $nodeLat2) ;
783                }
784                moveNodeFile2() ;
785        }
786        closeOsm2File () ;
787        print "done.\n" ;
788}
789
790sub userArea {
791        my ($u, $lon, $lat) = @_ ;
792
793        if ( (!defined $lon) or (! defined $lat) ) {
794                print "userArea ERROR user $u nodes $nodeId1 $nodeId2 ways $wayId1 $wayId2\n" ;
795        }
796
797        if (! defined $maxLon{$u}) {
798                $minLon{$u} = $lon ;
799                $minLat{$u} = $lat ;
800                $maxLon{$u} = $lon ;
801                $maxLat{$u} = $lat ;
802        }
803        else {
804                if ($lon > $maxLon{$u}) { $maxLon{$u} = $lon ; }
805                if ($lon < $minLon{$u}) { $minLon{$u} = $lon ; }
806                if ($lat > $maxLat{$u}) { $maxLat{$u} = $lat ; }
807                if ($lat < $minLat{$u}) { $minLat{$u} = $lat ; }
808        }
809}
810
811sub compareTags {
812        my ($aRef1, $aRef2) = @_ ;
813        my $added = 0 ; my $deleted = 0 ; my $renamed = 0 ; my $reclassified = 0 ; my $rereffed = 0 ;
814        my (@tags1) = @$aRef1 ; my (@tags2) = @$aRef2 ;
815
816        $reName = 0 ; $reClass = 0 ; $reRef = 0 ;
817
818        # RENAMED?
819        my $nameGiven = 0 ;
820        my $nameOld = "" ; 
821        my $nameNew = "" ;
822        foreach my $t (@tags1) { 
823                if ($t->[0] eq "name") { $nameGiven = 1 ; $nameOld = $t->[1] ; }
824        }
825        foreach my $t (@tags2) { 
826                if ($t->[0] eq "name") { $nameNew = $t->[1] ; }
827        }
828        if ( ($nameGiven == 1) and ($nameNew ne $nameOld) ) { 
829                $renamed = 1 ; 
830                $renames{$nodeUser2}{$nameOld . " > " . $nameNew} ++ ;
831                $reName = 1 ; $name1 = $nameOld ; $name2 = $nameNew ;
832        }
833       
834        if ($objectProcessed == 1 ) {
835                # REREF?
836                my $refGiven = 0 ;
837                my $refOld = "" ; 
838                my $refNew = "" ;
839                foreach my $t (@tags1) { 
840                        if ($t->[0] eq "ref") { $refGiven = 1 ; $refOld = $t->[1] ; }
841                }
842                foreach my $t (@tags2) { 
843                        if ($t->[0] eq "ref") { $refNew = $t->[1] ; }
844                }
845                if ( ($refGiven == 1) and ($refNew ne $refOld) ) { 
846                        $rereffed = 1 ; 
847                        $rerefs{$wayUser2}{$refOld . " > " . $refNew} ++ ;
848                        $reRef = 1 ; $ref1 = $refOld ; $ref2 = $refNew ;
849                }
850                # RECLASSIFIED?
851                my $highwayGiven = 0 ;
852                my $highwayOld = "" ;
853                my $highwayNew = "" ;
854                foreach my $t (@tags1) { 
855                        if ($t->[0] eq "highway") { $highwayGiven = 1 ; $highwayOld = $t->[1] ; }
856                }
857                foreach my $t (@tags2) { 
858                        if ($t->[0] eq "highway") { $highwayNew = $t->[1] ; }
859                }
860                if ( ($highwayGiven == 1) and ($highwayNew ne $highwayOld) ) { 
861                        $reclassified = 1 ; 
862                        $reclassifications{$wayUser2}{$highwayOld . " > " . $highwayNew} ++ ;
863                        $reClass = 1 ; $class1 = $highwayOld ; $class2 = $highwayNew ;
864                }
865        } # objectProcessed
866
867        # ADDED?
868        foreach my $t2 (@tags2) {
869                my $found = 0 ;
870                foreach my $t1 (@tags1) {
871                        if ( ($t1->[0] eq $t2->[0]) and ($t1->[1] eq $t2->[1]) ) { $found = 1 ; }
872                }
873                if ($found == 0) { $added++ ; }
874        }
875
876        # DELETED?
877        foreach my $t1 (@tags1) {
878                my $found = 0 ;
879                foreach my $t2 (@tags2) {
880                        if ( ($t1->[0] eq $t2->[0]) and ($t1->[1] eq $t2->[1]) ) { $found = 1 ; }
881                }
882                # if ($found == 0) {
883                if ( ($found == 0) and ($t1->[0] ne "created_by") ) { 
884                        $deleted++ ; 
885                        $tagsDeletedName{$nodeUser2}{$t1->[0].":".$t1->[1]}++ ;
886                }
887        }
888
889        return ($added, $deleted, $renamed, $reclassified, $rereffed) ;
890} # compareTags
891
892sub userLink {
893        my ($user) = shift ;
894        return "<a href=\"http://www.openstreetmap.org/user/" . $user . "\">" . $user . "</a>" ;
895}
896
897
898
899#------------------------------------------------------------------------------------
900# Map functions
901#------------------------------------------------------------------------------------
902
903sub paintNewWay {
904        drawWay ("black", 2, nodes2Coordinates2 (@wayNodes2) ) ;
905        if (grep /D/, $mode) { drawTextPos ($lon2{$wayNodes2[0]}, $lat2{$wayNodes2[0]}, 0, 0, $wayUser2, "black", 2) ; }
906}
907
908sub paintDeletedWay {
909        drawWay ("red", 2, nodes2Coordinates1 (@wayNodes1) ) ;
910}
911
912sub paintDeletedWayWithTag {
913        drawWay ("red", 3, nodes2Coordinates1 (@wayNodes1) ) ;
914}
915
916sub paintRenamedWay {
917        drawWay ("orange", 3, nodes2Coordinates1 (@wayNodes1) ) ;
918        if (grep /D/, $mode) { 
919                drawTextPos ($lon1{$wayNodes1[0]}, $lat1{$wayNodes1[0]}, 0, 0, $wayUser2, "orange", 2) ; 
920                drawTextPos ($lon1{$wayNodes1[0]}, $lat1{$wayNodes1[0]}, 0, -8, $name1 . "->" . $name2, "black", 2) ; 
921        }
922}
923
924sub paintRereffedWay {
925        drawWay ("orange", 3, nodes2Coordinates1 (@wayNodes1) ) ;
926        if (grep /D/, $mode) { 
927                drawTextPos ($lon1{$wayNodes1[0]}, $lat1{$wayNodes1[0]}, 0, 0, $wayUser2, "orange", 2) ; 
928                drawTextPos ($lon1{$wayNodes1[0]}, $lat1{$wayNodes1[0]}, 0, -8, $ref1 . "->" . $ref2, "black", 2) ; 
929        }
930}
931
932sub paintReclassifiedWay {
933        drawWay ("pink", 3, nodes2Coordinates1 (@wayNodes1) ) ;
934        if (grep /D/, $mode) { 
935                drawTextPos ($lon1{$wayNodes1[0]}, $lat1{$wayNodes1[0]}, 0, 0, $wayUser2, "pink", 2) ; 
936                drawTextPos ($lon1{$wayNodes1[0]}, $lat1{$wayNodes1[0]}, 0, -8, $class1 . "->" . $class2, "black", 2) ; 
937        }
938}
939
940sub paintMovedWay {
941}
942
943
944sub paintNewNode {
945        drawNodeDot ($nodeLon2, $nodeLat2, "black", 4) ;
946        if (grep /D/, $mode) { drawTextPos ($nodeLon2, $nodeLat2, 0, 0, $nodeUser2, "black", 2) ; }
947}
948
949sub paintDeletedNode {
950        drawNodeDot ($nodeLon1, $nodeLat1, "red", 4) ;
951}
952
953sub paintDeletedNodeWithTag {
954        drawNodeDot ($nodeLon1, $nodeLat1, "red", 5) ;
955}
956
957sub paintRenamedNode {
958        drawNodeDot ($nodeLon1, $nodeLat1, "orange", 4) ;
959        if (grep /D/, $mode) { 
960                drawTextPos ($nodeLon1, $nodeLat1, 0, 0, $nodeUser2, "orange", 2) ; 
961                drawTextPos ($nodeLon1, $nodeLat1, 0, -8, $name1 . "->" . $name2, "black", 2) ; 
962        }
963}
964
965sub paintMovedNode {
966        # blue
967        drawNodeDot ($nodeLon1, $nodeLat1, "lightblue", 4) ;
968        drawWay ("lightblue", 1, ($nodeLon1, $nodeLat1, $nodeLon2, $nodeLat2) ) ;
969        drawNodeDot ($nodeLon2, $nodeLat2, "blue", 4) ;
970        if (grep /D/, $mode) { drawTextPos ($nodeLon1, $nodeLat1, 0, 0, $nodeUser2, "blue", 2) ; }
971}
972
973sub paintAreas {
974        foreach my $user (keys %minLon) {
975                drawWay ("tomato", 1, $minLon{$user}, $minLat{$user}, $minLon{$user}, $maxLat{$user}, $maxLon{$user}, $maxLat{$user}, $maxLon{$user}, $minLat{$user}, $minLon{$user}, $minLat{$user} ) ;
976                drawTextPos ($minLon{$user}, $minLat{$user}, 0, 0, $user, "black", 2) ;
977        }
978}
979
980sub paintPlace {
981        my ($lon, $lat, $name) = @_ ;
982        drawTextPos ($lon, $lat, 0, 0, $name, "black", 4) ;     
983        drawNodeDot ($lon, $lat, "black", 2) ;
984}
985
986sub initializeMap {
987        print "initializing map...\n" ;
988        print "- parsing nodes file1...\n" ;
989        my $lonMax = -999 ; my $lonMin = 999 ; my $latMax = -999 ; my $latMin = 999 ;
990        openOsm1File ($osm1Name) ;
991        moveNodeFile1() ;       
992
993        # get all node information from file 1         
994        while ($nodeId1 != -1) {
995                $lon1{$nodeId1} = $nodeLon1 ; $lat1{$nodeId1} = $nodeLat1 ;
996                if ($nodeLon1 > $lonMax) { $lonMax = $nodeLon1 ; }
997                if ($nodeLat1 > $latMax) { $latMax = $nodeLat1 ; }
998                if ($nodeLon1 < $lonMin) { $lonMin = $nodeLon1 ; }
999                if ($nodeLat1 < $latMin) { $latMin = $nodeLat1 ; }
1000                # next
1001                moveNodeFile1() ;       
1002        }
1003        initGraph ($sizeX, $lonMin, $latMin, $lonMax, $latMax) ;
1004        if (grep /S/, $mode) {
1005                enableSVG() ;
1006        }
1007
1008        # ways
1009        print "- parsing ways file1...\n" ;
1010        moveWayFile1() ;
1011        while ($wayId1 != -1) {
1012                drawWay ("lightgray", 1, nodes2Coordinates1 (@wayNodes1) ) ;
1013                moveWayFile1() ;
1014        }
1015        closeOsm1File() ;
1016
1017        print "- parsing nodes file2...\n" ;
1018        openOsm2File ($osm2Name) ;
1019        moveNodeFile2() ;
1020
1021        # get all node information from file 2
1022        while ($nodeId2 != -1) {
1023                $lon2{$nodeId2} = $nodeLon2 ; $lat2{$nodeId2} = $nodeLat2 ;
1024                # next
1025                moveNodeFile2() ;
1026        }
1027        closeOsm2File() ;
1028
1029        foreach my $c (@cities) {
1030                drawNodeDot ($c->[1], $c->[2], "black", 2) ;
1031                drawTextPos ($c->[1], $c->[2], 0, 0, $c->[0], "black", 3) ;
1032        }
1033
1034        print "done.\n" ;
1035}
1036
1037sub saveMap {
1038        print "saving map...\n" ;
1039        drawHead ($program . " ". $version . " by Gary68", "black", 3) ;
1040        drawFoot ("data by openstreetmap.org" . " " . stringFileInfo ($osm1Name) . stringFileInfo ($osm2Name), "black", 3) ;
1041        drawLegend (3, "New", "black", "Deleted", "red", "Moved", "blue", "Renamed", "orange", "Reclassified", "pink", "User Area", "tomato") ;
1042        drawRuler ("black") ;
1043        my ($pngName) = $htmlName ;
1044        $pngName =~ s/.htm/.png/ ;
1045        writeGraph ($pngName) ; 
1046        if (grep /S/, $mode) {
1047                my ($svgName) = $htmlName ;
1048                $svgName =~ s/.htm/.svg/ ;
1049                writeSVG ($svgName) ; 
1050        }
1051        print "done.\n" ;
1052}
1053
1054sub nodes2Coordinates1 {
1055        my @nodes = @_ ;
1056        my $i ;
1057        my @result = () ;
1058        for ($i=0; $i<=$#nodes; $i++) {
1059                if (!defined $lon1{$nodes[$i]}) { 
1060                        print "WARNING: node info $nodes[$i] missing\n" ; 
1061                }
1062                else {
1063                        push @result, $lon1{$nodes[$i]} ;
1064                        push @result, $lat1{$nodes[$i]} ;
1065                }
1066        }
1067        return @result ;
1068}
1069
1070sub nodes2Coordinates2 {
1071        my @nodes = @_ ;
1072        my $i ;
1073        my @result = () ;
1074        for ($i=0; $i<=$#nodes; $i++) {
1075                if (!defined $lon2{$nodes[$i]}) { 
1076                        print "WARNING: node info $nodes[$i] missing\n" ; 
1077                }
1078                else {
1079                        push @result, $lon2{$nodes[$i]} ;
1080                        push @result, $lat2{$nodes[$i]} ;
1081                }
1082        }
1083        return @result ;
1084}
1085
1086
1087
1088#------------------------------------------------------------------------------------
1089# Basic object operations
1090#------------------------------------------------------------------------------------
1091
1092sub getNode1 {
1093        my ($id, $version, $timestamp, $uid, $user, $changeset, $lat, $lon) ;
1094        my @gTags = () ;
1095        if($line1 =~ /^\s*\<node/) {
1096
1097                ($id) = ($line1 =~ / id=[\'\"](.+?)[\'\"]/ ) ;
1098                ($user) = ($line1 =~ / user=[\'\"](.+?)[\'\"]/ ) ;
1099                ($lon) = ($line1 =~ / lon=[\'\"](.+?)[\'\"]/ ) ;
1100                ($lat) = ($line1 =~ / lat=[\'\"](.+?)[\'\"]/ ) ;
1101                ($version) = ($line1 =~ / version=[\'\"](.+?)[\'\"]/ ) ;
1102                ($timestamp) = ($line1 =~ / timestamp=[\'\"](.+?)[\'\"]/ ) ;
1103                ($uid) = ($line1 =~ / uid=[\'\"](.+?)[\'\"]/ ) ;
1104                ($changeset) = ($line1 =~ / changeset=[\'\"](.+?)[\'\"]/ ) ;
1105
1106                if (!defined $user) { $user = "unknown" ; }
1107                if (!defined $uid) { $uid = 0 ; }
1108
1109                if (!$id or (! (defined ($lat))) or ( ! (defined ($lon))) ) {
1110                        print "WARNING reading osm file1, line follows (expecting id, lon, lat and user for node):\n", $line1, "\n" ; 
1111                }
1112                else {
1113                        if ( (grep (/">/, $line1)) or (grep (/'>/, $line1)) ) {                  # more lines, get tags
1114                                nextLine1() ;
1115                                while (!grep(/<\/node>/, $line1)) {
1116                                        my ($k, $v) = ($line1 =~ /^\s*\<tag k=[\'\"](.+)[\'\"]\s*v=[\'\"](.+)[\'\"]/) ;
1117                                        if ( (defined ($k)) and (defined ($v)) ) {
1118                                                my $tag = [$k, $v] ; push @gTags, $tag ;
1119                                        }
1120                                        else { 
1121                                                #print "WARNING tag not recognized file1: ", $line1, "\n" ;
1122                                        }
1123                                        nextLine1() ;
1124                                }
1125                                nextLine1() ;
1126                        }
1127                        else {
1128                                nextLine1() ;
1129                        }
1130                }
1131        }
1132        else {
1133                return (-1, -1, -1, -1, -1) ; 
1134        } # node
1135        return ($id, $version, $timestamp, $uid, $user, $changeset, $lat, $lon, \@gTags) ; # in main @array = @$ref
1136} # getNode1
1137
1138sub getNodeFile2 {
1139        my ($id, $version, $timestamp, $uid, $user, $changeset, $lat, $lon) ;
1140        my @gTags = () ;
1141        if($line2 =~ /^\s*\<node/) {
1142                ($id) = ($line2 =~ / id=[\'\"](.+?)[\'\"]/ ) ;
1143                ($user) = ($line2 =~ / user=[\'\"](.+?)[\'\"]/ ) ;
1144                ($lon) = ($line2 =~ / lon=[\'\"](.+?)[\'\"]/ ) ;
1145                ($lat) = ($line2 =~ / lat=[\'\"](.+?)[\'\"]/ ) ;
1146                ($version) = ($line2 =~ / version=[\'\"](.+?)[\'\"]/ ) ;
1147                ($timestamp) = ($line2 =~ / timestamp=[\'\"](.+?)[\'\"]/ ) ;
1148                ($uid) = ($line2 =~ / uid=[\'\"](.+?)[\'\"]/ ) ;
1149                ($changeset) = ($line2 =~ / changeset=[\'\"](.+?)[\'\"]/ ) ;
1150                if (!defined $user) { $user = "unknown" ; }
1151                if (!defined $uid) { $uid = 0 ; }
1152
1153                if ( (! defined $id) or (! (defined ($lat))) or ( ! (defined ($lon))) ) {
1154                        print "WARNING reading osm file 2, line follows (expecting id, lon, lat and user for node):\n", $line2, "\n" ; 
1155                }
1156                else {
1157                        if ( (grep (/">/, $line2)) or (grep (/'>/, $line2)) ) {                  # more lines, get tags
1158                                nextLine2() ;
1159                                while (!grep(/<\/node>/, $line2)) {
1160                                        my ($k, $v) = ($line2 =~ /^\s*\<tag k=[\'\"](.+)[\'\"]\s*v=[\'\"](.+)[\'\"]/) ;
1161                                        if ( (defined ($k)) and (defined ($v)) ) {
1162                                                my $tag = [$k, $v] ; push @gTags, $tag ;
1163                                        }
1164                                        else { 
1165                                                #print "WARNING tag not recognized file 2: ", $line2, "\n" ;
1166                                        }
1167                                        nextLine2() ;
1168                                }
1169                                nextLine2() ;
1170                        }
1171                        else {
1172                                nextLine2() ;
1173                        }
1174                }
1175        }
1176        else {
1177                return (-1, -1, -1, -1, -1) ; 
1178        } # node
1179        return ($id, $version, $timestamp, $uid, $user, $changeset, $lat, $lon, \@gTags) ; # in main @array = @$ref
1180} # getNodeFile2
1181
1182
1183
1184sub getWay1 {
1185        my $id ; my $u ; my @tags ; my @nodes ; my $version ; my $timestamp ; my $uid ; my $changeset ;
1186        if($line1 =~ /^\s*\<way/) {
1187
1188                ($id) = ($line1 =~ / id=[\'\"](.+?)[\'\"]/ ) ;
1189                ($uid) = ($line1 =~ / uid=[\'\"](.+?)[\'\"]/ ) ;
1190                ($u) = ($line1 =~ / user=[\'\"](.+?)[\'\"]/ ) ;
1191                ($timestamp) = ($line1 =~ / timestamp=[\'\"](.+?)[\'\"]/ ) ;
1192                ($version) = ($line1 =~ / version=[\'\"](.+?)[\'\"]/ ) ;
1193
1194                if (! defined $u) { $u = "unknown" ; }
1195                if (! defined $uid) { $uid = 0 ; }
1196                if (! defined $id) { print "ERROR reading osm file1, line follows (expecting way id):\n", $line1, "\n" ; }
1197                unless ($id) { next; }
1198                nextLine1() ;
1199                while (not($line1 =~ /\/way>/)) { # more way data
1200                        my ($node) = ($line1 =~ /^\s*\<nd ref=[\'\"](\d+)[\'\"]/); # get node id
1201                        my ($k, $v) = ($line1 =~ /^\s*\<tag k=[\'\"](.+)[\'\"]\s*v=[\'\"](.+)[\'\"]/) ;
1202                        if ($node) {
1203                                push @nodes, $node ;
1204                        }
1205                        if ($k and defined($v)) { my $tag = [$k, $v] ; push @tags, $tag ; }
1206                        nextLine1() ;
1207                }
1208                nextLine1() ;
1209        }
1210        else {
1211                return (-1, -1, -1, -1, -1, -1, -1, -1) ;
1212        }
1213        return ($id, $version, $timestamp, $uid, $u, $changeset, \@nodes, \@tags) ;
1214} # getWay1
1215
1216sub getWayFile2 {
1217        my $id ; my $u ; my @tags ; my @nodes ; my $version ; my $timestamp ; my $uid ; my $changeset ;
1218        if($line2 =~ /^\s*\<way/) {
1219
1220                ($id) = ($line2 =~ / id=[\'\"](.+?)[\'\"]/ ) ;
1221                ($uid) = ($line2 =~ / uid=[\'\"](.+?)[\'\"]/ ) ;
1222                ($u) = ($line2 =~ / user=[\'\"](.+?)[\'\"]/ ) ;
1223                ($timestamp) = ($line2 =~ / timestamp=[\'\"](.+?)[\'\"]/ ) ;
1224                ($version) = ($line2 =~ / version=[\'\"](.+?)[\'\"]/ ) ;
1225
1226                if (! defined $u) { $u = "unknown" ; }
1227                if (! defined $uid) { $uid = 0 ; }
1228                if (! defined $id) { print "ERROR reading osm file2, line follows (expecting way id):\n", $line1, "\n" ; }
1229                unless ($id) { next; }
1230                nextLine2() ;
1231                while (not($line2 =~ /\/way>/)) { # more way data
1232                        my ($node) = ($line2 =~ /^\s*\<nd ref=[\'\"](\d+)[\'\"]/); # get node id
1233                        my ($k, $v) = ($line2 =~ /^\s*\<tag k=[\'\"](.+)[\'\"]\s*v=[\'\"](.+)[\'\"]/) ;
1234                        if ($node) {
1235                                push @nodes, $node ;
1236                        }
1237                        if ($k and defined($v)) { my $tag = [$k, $v] ; push @tags, $tag ; }
1238                        nextLine2() ;
1239                }
1240                nextLine2() ;
1241        }
1242        else {
1243                return (-1, -1, -1, -1, -1, -1, -1, -1) ;
1244        }
1245        return ($id, $version, $timestamp, $uid, $u, $changeset, \@nodes, \@tags) ;
1246} # getWayFile2
1247
1248
1249sub moveNodeFile1 {
1250        ($nodeId1, $nodeVersion1, $nodeTimestamp1, $nodeUid1, $nodeUser1, $nodeChangeset1, $nodeLat1, $nodeLon1, $aRef1) = getNode1 () ;
1251        if ($nodeId1 != -1) {
1252                @nodeTags1 = @$aRef1 ;
1253        }
1254}
1255
1256sub moveNodeFile2 {
1257        ($nodeId2, $nodeVersion2, $nodeTimestamp2, $nodeUid2, $nodeUser2, $nodeChangeset2, $nodeLat2, $nodeLon2, $aRef1) = getNodeFile2 () ;
1258        if ($nodeId2 != -1) {
1259                @nodeTags2 = @$aRef1 ;
1260                userTimestamp ($nodeUser2, $nodeTimestamp2) ;
1261        }
1262}
1263
1264sub moveWayFile1 {
1265        ($wayId1, $wayVersion1, $wayTimestamp1, $wayUid1, $wayUser1, $wayChangeset1, $aRef1, $aRef2) = getWay1() ;
1266        if ($wayId1 != -1) {
1267                @wayNodes1 = @$aRef1 ;
1268                @wayTags1 = @$aRef2 ;
1269        }
1270}
1271
1272sub moveWayFile2 {
1273        ($wayId2, $wayVersion2, $wayTimestamp2, $wayUid2, $wayUser2, $wayChangeset2, $aRef1, $aRef2) = getWayFile2() ;
1274        if ($wayId2 != -1) {
1275                @wayNodes2 = @$aRef1 ;
1276                @wayTags2 = @$aRef2 ;
1277                userTimestamp ($wayUser2, $wayTimestamp2) ;
1278        }
1279}
1280
1281
1282#------------------------------------------------------------------------------------
1283# Basic file operations
1284#------------------------------------------------------------------------------------
1285
1286sub openOsm1File {
1287        $file1Name = shift ;
1288        if (grep /.bz2/, $file1Name) { $isBz21 = 1 ; } else { $isBz21 = 0 ; }
1289        if ($isBz21) {
1290                $bz1 = bzopen($file1Name, "rb") or die "Cannot open $file1Name: $bzerrno\n" ;
1291        }
1292        else {
1293                open ($file1, "<", $file1Name) || die "can't open osm file1" ;
1294        }
1295        nextLine1() ;           
1296        while ( ! (grep /\<node/, $line1) ) {
1297                nextLine1() ;
1298        }
1299        return 1 ;
1300}
1301
1302sub closeOsm1File {
1303        if ($isBz21) { $bz1->bzclose() ; }
1304        else { close ($file1) ; }
1305}
1306
1307sub nextLine1 {
1308        if ($isBz21) { $bz1->bzreadline($line1) ; }
1309        else { $line1 = <$file1> ; }
1310}
1311
1312sub openOsm2File {
1313        $file2Name = shift ;
1314        if (grep /.bz2/, $file2Name) { $isBz22 = 1 ; } else { $isBz22 = 0 ; }
1315        if ($isBz22) {
1316                $bz2 = bzopen($file2Name, "rb") or die "Cannot open $file2Name: $bzerrno\n" ;
1317        }
1318        else {
1319                open ($file2, "<", $file2Name) || die "can't open osm file2" ;
1320        }
1321        nextLine2() ;           
1322        while ( ! (grep /\<node/, $line2) ) {
1323                nextLine2() ;
1324        }
1325        return 1 ;
1326}
1327
1328sub closeOsm2File {
1329        if ($isBz22) { $bz2->bzclose() ; }
1330        else { close ($file2) ; }
1331}
1332
1333sub nextLine2 {
1334        if ($isBz22) { $bz2->bzreadline($line2) ; }
1335        else { $line2 = <$file2> ; }
1336}
1337
1338#------------------------------------------------------------------------------------
1339# Basic date operations
1340#------------------------------------------------------------------------------------
1341
1342
1343sub diffDates {
1344        my ($d1, $m1, $y1, $d2, $m2, $y2) = @_ ;
1345        my $res ;
1346        $res = ($y2-$y1) * 365 ;
1347        $res += ($m2-$m1) * 30 ;
1348        $res += ($d2-$d1) ;
1349        return ($res) ;
1350}
1351
1352
1353sub extractDateFromTimestamp {
1354        my ($str) = shift ;
1355        my $d ; my $m; my $y ;
1356
1357        $d = substr ($str, 8, 2) ;
1358        $m = substr ($str, 5, 2) ;
1359        $y = substr ($str, 0, 4) ;
1360        return ($d, $m, $y) ;
1361}
1362
1363
1364sub initLocaltime {
1365        my ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = localtime() ;
1366        $localYear = 1900 + $yearOffset ;
1367        $localDay  = $dayOfMonth ;
1368        $localMonth = $month + 1 ;
1369}
1370
1371
1372
1373sub userTimestamp {
1374        my ($u, $timestamp) = @_ ;
1375        my $diff ;
1376        $diff = diffDates (extractDateFromTimestamp ($timestamp), $localDay, $localMonth, $localYear) ;
1377        if (defined $age{$u}) {
1378                if ($diff > $age{$u}) {
1379                        $age{$u} = $diff ;
1380                }
1381        }
1382        else {
1383                $age{$u} = $diff ;
1384        }
1385}
1386
1387
1388
1389#------------------------------------------------------------------------------------
1390# Black and white lists
1391#------------------------------------------------------------------------------------
1392sub readLists {
1393        my $file ; my $success ; my $line ;
1394        my ($whiteName) = $htmlName ;
1395        my ($blackName) = $htmlName ;
1396        $whiteName =~ s/.htm/.white.txt/ ;
1397        $blackName =~ s/.htm/.black.txt/ ;
1398
1399        $success = open ($file, "<", $whiteName) ;
1400        if ($success) {
1401                while ($line = <$file>) {
1402                        my ($uid, $user) = ($line =~ /^(\d+);"(.+)"$/) ;
1403                        if ( (defined $user) and (defined $uid) )  {
1404                                $white{$user} = 1 ;
1405                                $whiteUid{$uid} = 1 ;
1406                        }
1407                }
1408                close ($file) ;
1409                print "\nWHITE listed users:\n" ;
1410                foreach my $u (sort keys %white) { print $u, "; " ; }
1411                print "\n\n" ;
1412        }
1413        else {
1414                print "no white list found.\n" ;
1415        }
1416
1417        $success = open ($file, "<", $blackName) ;
1418        if ($success) {
1419                while ($line = <$file>) {
1420                        my ($uid, $user) = ($line =~ /^(\d+);"(.+)"$/) ;
1421                        if ( (defined $user) and (defined $uid) )  {
1422                                $black{$user} = 1 ;
1423                                $blackUid{$uid} = 1 ;
1424                        }
1425                }
1426                close ($file) ;
1427                print "\nBLACK listed users:\n" ;
1428                foreach my $u (sort keys %black) { print $u, "; " ; }
1429                print "\n\n" ;
1430                print "\nBLACK listed uids:\n" ;
1431                foreach my $u (sort keys %blackUid) { print $u, " " ; }
1432                print "\n\n" ;
1433        }
1434        else {
1435                print "no black list found.\n" ;
1436        }
1437
1438}
1439
1440sub removeWhiteListData {
1441        foreach my $u (keys %white) {
1442                delete $age{$u} ;
1443                delete $nodesMovedNumber{$u} ;
1444                delete $nodesMovedDistance{$u} ;
1445                delete $nodesMovedMax{$u} ;
1446                delete $minLon{$u} ;
1447                delete $minLat{$u} ;
1448                delete $maxLon{$u} ;
1449                delete $maxLat{$u} ;
1450                delete $tagsAdded{$u} ;
1451                delete $tagsDeleted{$u} ;
1452                delete $tagsRenamed{$u} ;
1453                delete $tagsRereffed{$u} ;
1454                delete $tagsReclassified{$u} ;
1455                delete $versionJumpsNodes{$u} ;
1456                delete $versionJumpsWays{$u} ;
1457                delete $opTime{$u} ;
1458        }
1459}
1460
1461sub getBlackListData {
1462        foreach my $u (keys %black) {
1463                if (defined  $nodesMovedNumber{$u}) { $blackActive{$u} = 1 ; }
1464                if (defined  $nodesMovedDistance{$u}) { $blackActive{$u} = 1 ; }
1465                if (defined  $nodesMovedMax{$u}) { $blackActive{$u} = 1 ; }
1466                if (defined  $minLon{$u}) { $blackActive{$u} = 1 ; }
1467                if (defined  $minLat{$u}) { $blackActive{$u} = 1 ; }
1468                if (defined  $maxLon{$u}) { $blackActive{$u} = 1 ; }
1469                if (defined  $maxLat{$u}) { $blackActive{$u} = 1 ; }
1470                if (defined  $tagsAdded{$u}) { $blackActive{$u} = 1 ; }
1471                if (defined  $tagsDeleted{$u}) { $blackActive{$u} = 1 ; }
1472                if (defined  $tagsRenamed{$u}) { $blackActive{$u} = 1 ; }
1473                if (defined  $tagsRereffed{$u}) { $blackActive{$u} = 1 ; }
1474                if (defined  $tagsReclassified{$u}) { $blackActive{$u} = 1 ; }
1475                if (defined  $versionJumpsNodes{$u}) { $blackActive{$u} = 1 ; }
1476                if (defined  $versionJumpsWays{$u}) { $blackActive{$u} = 1 ; }
1477                if (defined  $opTime{$u}) { $blackActive{$u} = 1 ; }
1478        }
1479
1480        foreach my $id (keys %activeUid) {
1481                if (defined $blackUid{$id}) { $blackUidActive{$id} = 1 ; }
1482        }
1483}
1484
1485sub populateCities {
1486push @cities, ["Erlangen", 11.0037436, 49.598038] ;
1487push @cities, ["München", 11.5754815, 48.1372719] ;
1488push @cities, ["Hildesheim", 9.9523243, 52.1527898] ;
1489push @cities, ["Chemnitz", 12.9252977, 50.8322608] ;
1490push @cities, ["Hamburg", 10.000654, 53.5503414] ;
1491push @cities, ["Köln", 6.9569468, 50.9412323] ;
1492push @cities, ["Bremen", 8.80727, 53.0757681] ;
1493push @cities, ["Herne", 7.2196765, 51.5377786] ;
1494push @cities, ["Bayreuth", 11.5763079, 49.9427202] ;
1495push @cities, ["Schwerin", 11.4148038, 53.6288297] ;
1496push @cities, ["Kiel", 10.1371858, 54.3216753] ;
1497push @cities, ["Dortmund", 7.4651736, 51.5113709] ;
1498push @cities, ["Hannover", 9.7385632, 52.3744809] ;
1499push @cities, ["Lübeck", 10.6847384, 53.8664436] ;
1500push @cities, ["Rostock", 12.1287241, 54.0924328] ;
1501push @cities, ["Konstanz", 9.1751732, 47.6589856] ;
1502push @cities, ["Bamberg", 10.8876283, 49.892691] ;
1503push @cities, ["Würzburg", 9.9329662, 49.79245] ;
1504push @cities, ["Moers", 6.6352091, 51.4504762] ;
1505push @cities, ["Bonn", 7.0999274, 50.7344839] ;
1506push @cities, ["Leverkusen", 7.0175786, 51.049718] ;
1507push @cities, ["Heilbronn", 9.2186549, 49.1422908] ;
1508push @cities, ["Essen", 7.012273, 51.4552058] ;
1509push @cities, ["Frankfurt am Main", 8.6805975, 50.1432793] ;
1510push @cities, ["Ulm", 9.9910464, 48.398312] ;
1511push @cities, ["Saarbrücken", 6.996567, 49.2350486] ;
1512push @cities, ["Siegen", 8.0153315, 50.8705296] ;
1513push @cities, ["Neuss", 6.6900832, 51.1958431] ;
1514push @cities, ["Cottbus", 14.3391578, 51.7596392] ;
1515push @cities, ["Braunschweig", 10.5251064, 52.2643004] ;
1516push @cities, ["Recklinghausen", 7.2007542, 51.6118188] ;
1517push @cities, ["Wolfsburg", 10.7861682, 52.4205588] ;
1518push @cities, ["Halle (Saale)", 11.970473, 51.4820941] ;
1519push @cities, ["Trier", 6.6402058, 49.7557338] ;
1520push @cities, ["Reutlingen", 9.2105667, 48.4963326] ;
1521push @cities, ["Oberhausen", 6.859351, 51.4980457] ;
1522push @cities, ["Mülheim an der Ruhr", 6.8787875, 51.4283922] ;
1523push @cities, ["Magdeburg", 11.6399609, 52.1315889] ;
1524push @cities, ["Stuttgart", 9.1829087, 48.7763496] ;
1525push @cities, ["Salzgitter", 10.3489635, 52.1480205] ;
1526push @cities, ["Bottrop", 6.9292036, 51.5215805] ;
1527push @cities, ["Wiesbaden", 8.2499998, 50.0832999] ;
1528push @cities, ["Bielefeld", 8.5313701, 52.0191887] ;
1529push @cities, ["Erfurt", 11.033629, 50.9774188] ;
1530push @cities, ["Aachen", 6.0816445, 50.7742933] ;
1531push @cities, ["Pforzheim", 8.7029532, 48.8908846] ;
1532push @cities, ["Aschaffenburg", 9.14917, 49.9739] ;
1533push @cities, ["Krefeld", 6.5592502, 51.3340369] ;
1534push @cities, ["Gelsenkirchen", 7.0711688, 51.5431133] ;
1535push @cities, ["Duisburg", 6.7497693, 51.4334338] ;
1536push @cities, ["Osnabrück", 8.0499998, 52.2667002] ;
1537push @cities, ["Heidelberg", 8.6948125, 49.4093608] ;
1538push @cities, ["Mannheim", 8.4672976, 49.4897239] ;
1539push @cities, ["Mönchengladbach", 6.4419995, 51.1910666] ;
1540push @cities, ["Remscheid", 7.194881, 51.1796081] ;
1541push @cities, ["Landau in der Pfalz", 8.1132884, 49.2075151] ;
1542push @cities, ["Solingen", 7.08333, 51.1833] ;
1543push @cities, ["Potsdam", 13.0666999, 52.4] ;
1544push @cities, ["Speyer", 8.4336151, 49.3165553] ;
1545push @cities, ["Darmstadt", 8.6511775, 49.8727746] ;
1546push @cities, ["Dresden", 13.7381437, 51.0493286] ;
1547push @cities, ["Augsburg", 10.8837144, 48.3665283] ;
1548push @cities, ["Jena", 11.5833091, 50.9331401] ;
1549push @cities, ["Gera", 12.0799792, 50.8760398] ;
1550push @cities, ["Wuppertal", 7.1832976, 51.2666575] ;
1551push @cities, ["Freiburg im Breisgau", 7.8646903, 47.9949985] ;
1552push @cities, ["Kaiserslautern", 7.7689951, 49.4432174] ;
1553push @cities, ["Bochum", 7.2166699, 51.4833001] ;
1554push @cities, ["Koblenz", 7.5943348, 50.3532028] ;
1555push @cities, ["Berlin", 13.3888548, 52.5170397] ;
1556push @cities, ["Hagen", 7.4610436, 51.3573015] ;
1557push @cities, ["Leipzig", 12.3746816, 51.3405087] ;
1558push @cities, ["Hamm", 7.8108895, 51.67894] ;
1559push @cities, ["Paderborn", 8.752653, 51.7177044] ;
1560push @cities, ["Göttingen", 9.934507, 51.5336849] ;
1561push @cities, ["Mainz", 8.2710237, 49.9999952] ;
1562push @cities, ["Karlsruhe", 8.4044366, 49.0140679] ;
1563push @cities, ["Regensburg", 12.0956268, 49.0159295] ;
1564push @cities, ["Ludwigshafen", 8.4396699, 49.4792564] ;
1565push @cities, ["Kempten", 10.3169236, 47.7264273] ;
1566push @cities, ["Düsseldorf", 6.7637565, 51.2235376] ;
1567push @cities, ["Witten", 7.335124, 51.4370171] ;
1568push @cities, ["Kassel", 9.4770164, 51.3092659] ;
1569push @cities, ["Münster", 7.6251879, 51.9625101] ;
1570push @cities, ["Oldenburg", 8.2146017, 53.1389753] ;
1571push @cities, ["Nürnberg", 11.0773238, 49.4538501] ;
1572push @cities, ["Fürth", 10.9896011, 49.477271] ;
1573push @cities, ["Ingolstadt", 11.4317222, 48.7659636] ;
1574push @cities, ["Bremerhaven", 8.5865508, 53.5522265] ;
1575}
1576
Note: See TracBrowser for help on using the repository browser.