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

Last change on this file since 30595 was 26563, checked in by gary68, 8 years ago

useractivity; some bug fixes regarding uninitialized values and encoding errors

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