├── .gitignore ├── www ├── robots.txt ├── kml.php ├── wld.php ├── tiles.txt ├── python.php ├── perl.php ├── bigmap.php ├── ozimap.php ├── panel.php ├── index.html └── queue.php ├── LICENSE ├── scripts ├── purge_images.pl ├── tiles2html.pl └── bigmap_download.pl └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | queue/ 2 | result/ 3 | -------------------------------------------------------------------------------- /www/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this software, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /www/kml.php: -------------------------------------------------------------------------------- 1 | '."\n"; 5 | ?> 6 | 7 | 8 | 9 | .png 10 | 11 | 12 | 13 | 14 | 15 | 16 | 0 17 | 18 | 19 | -------------------------------------------------------------------------------- /www/wld.php: -------------------------------------------------------------------------------- 1 | [1] <=> $a->[1] } map { ["$path/$_", (stat "$path/$_")[9], (stat "$path/$_")[7]] } grep {/\.png$/} readdir $dh; 15 | closedir $dh; 16 | 17 | my $size = 0; 18 | foreach(@images) { 19 | $size += $_->[2]; 20 | #printf "%s: ctime=%d, size=%d, diff=%.1f MB\n", $_->[0], $_->[1], $_->[2], ($limit-$size)/1024/1024; 21 | unlink $_->[0] if $size > $limit; 22 | } 23 | -------------------------------------------------------------------------------- /scripts/tiles2html.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Converts tiles.txt to a javascript code to be included in index.html 4 | # Written by Ilya Zverev, licensed WTFPL. 5 | 6 | use strict; 7 | use Cwd 'abs_path'; 8 | use File::Basename; 9 | 10 | my $wwwroot = abs_path(dirname(__FILE__)).'/../www'; 11 | 12 | open INDEX, '<'.$wwwroot.'/index.html' or die "No index.html found: $!"; 13 | my @index; 14 | push @index, $_ while ; 15 | close INDEX; 16 | 17 | open TILES, '<'.$wwwroot.'/tiles.txt' or die "No tiles.txt found: $!"; 18 | my @base; 19 | my @overlay; 20 | while() { 21 | next if /^\s*#/; 22 | chomp; 23 | my @arr = split /\s*,\s*/; 24 | push @{ $arr[1] ? \@overlay : \@base }, \@arr if $#arr >= 4; 25 | } 26 | close TILES; 27 | 28 | my $res = "\tbase = {\n"; 29 | $res .= print_code($_) foreach @base; 30 | $res =~ s/,\n$/\n/; 31 | $res .= "\t};\n"; 32 | 33 | $res .= "\toverlay = {\n"; 34 | $res .= print_code($_) foreach @overlay; 35 | $res =~ s/,\n$/\n/; 36 | $res .= "\t};\n"; 37 | 38 | open INDEX, '>'.$wwwroot.'/index.html' or die "Cannot write to index.html: $!"; 39 | my $print = 1; 40 | foreach my $line (@index) { 41 | $print = 1 if $line =~ m#// END GENERATED#; 42 | print INDEX $line if $print; 43 | if( $line =~ m#// GENERATED# ) { 44 | $print = 0; 45 | print INDEX $res; 46 | } 47 | } 48 | close INDEX; 49 | 50 | sub print_code { 51 | my $line = shift; 52 | my $url = $line->[4]; 53 | my $sub = ''; 54 | if( $url =~ /{([a-z0-9]+)}/ ) { 55 | $sub = ", subdomains: '$1'"; 56 | $url =~ s/$1/s/; 57 | } 58 | $url =~ s/!([xyz])/{\1}/g; 59 | my $attr = $line->[5]; 60 | $attr = 'Map data © OpenStreetMap'.($attr ? ' | ' : '').$attr if !$line->[1]; 61 | $attr =~ s/"/\\"/g; 62 | 63 | my $res = ''; 64 | $res .= "\t\t'".$line->[0]."': L.tileLayer('$url', {\n"; 65 | $res .= "\t\t\tname: '".$line->[0]."', minZoom: ".$line->[2].", maxZoom: ".$line->[3].$sub.",\n"; 66 | $res .= "\t\t\tattribution: '$attr'\n"; 67 | $res .= "\t\t}),\n"; 68 | return $res; 69 | } 70 | -------------------------------------------------------------------------------- /www/tiles.txt: -------------------------------------------------------------------------------- 1 | #name,overlay?,minzoom,maxzoom,url,tile attribution 2 | 3 | mapnik,0,0,19,https://tile.openstreetmap.org/!z/!x/!y.png, 4 | osm-de,0,0,18,https://{abc}.tile.openstreetmap.de/tiles/osmde/!z/!x/!y.png,Tiles © OSM DE 5 | osm-fr,0,0,20,https://{abc}.tile.openstreetmap.fr/osmfr/!z/!x/!y.png,Tiles © OSM France 6 | 7 | humanitarian,0,0,19,https://{abc}.tile.openstreetmap.fr/hot/!z/!x/!y.png,Tiles © Humanitarian OSM Team 8 | veloroad,0,6,15,https://tile.osmz.ru/veloroad/!z/!x/!y.png,Tiles © Ilya Zverev 9 | topomap,0,0,17,https://{abc}.tile.opentopomap.org/!z/!x/!y.png,Tiles © OpenTopoMap 10 | 11 | seamarks,1,0,18,https://tiles.openseamap.org/seamark/!z/!x/!y.png,Sea Marks © OpenSeaMap 12 | harbours,1,0,18,https://t2.openseamap.org/tile/!z/!x/!y.png,Harbours © OpenSeaMap 13 | fires,1,0,17,http://www.openfiremap.org/hytiles/!z/!x/!y.png,Fire Hydrants © OpenFireMap 14 | 15 | wmhiking,1,0,18,https://tile.waymarkedtrails.org/hiking/!z/!x/!y.png,Trails © Waymarked Trails 16 | wmcycling,1,0,18,https://tile.waymarkedtrails.org/cycling/!z/!x/!y.png,Trails © Waymarked Trails 17 | wmmtb,1,0,18,https://tile.waymarkedtrails.org/mtb/!z/!x/!y.png,Trails © Waymarked Trails 18 | wmriding,1,0,18,https://tile.waymarkedtrails.org/riding/!z/!x/!y.png,Trails © Waymarked Trails 19 | wmskating,1,0,18,https://tile.waymarkedtrails.org/skating/!z/!x/!y.png,Trails © Waymarked Trails 20 | wmslopes,1,0,18,https://tile.waymarkedtrails.org/slopemap/!z/!x/!y.png,Trails © Waymarked Trails 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BigMap 2 2 | 3 | This is a successor to [BigMap](http://wiki.openstreetmap.org/wiki/Bigmap) script: a tool to 4 | stitch map tiles and produce a PNG image. Among improvements are: 5 | 6 | * Better landing page with Leaflet and URL parsing. 7 | * More than fifteen popular tile layers to choose from. 8 | * KML, WLD and OziExplorer MAP meta files generation. 9 | * Map downloading script can be produced in Python language. 10 | * Fixed user agent and small pauses while downloading. 11 | * Attribution on generated images. 12 | * Server-side stitching with a queue. 13 | 14 | ## Installation 15 | 16 | The following directory structure is recommended: 17 | 18 | | Path | Mode | Description 19 | |---|---|--- 20 | | `./` | `0755` | Place scripts from `scripts` directory here, with `0755` mode on them. 21 | | `./queue/` | `0777` | Working directory that should be outside wwwroot. 22 | | `./queue/tasks/` | `0777` | Task files will be put here by PHP scripts. 23 | | `./queue/queue` | `0666` | A queue file that will be modified by PHP scripts. 24 | | `./www/` | `0755` | WWWRoot. Place PHP scripts here and point HTTP server to it. 25 | | `./www/result/` | `0777` | A directory for generated image files. 26 | 27 | Then modify paths to be absolute in `bigmap_download.pl` and `purge_images.pl` scripts, so they can be 28 | called by cron. Also change server address in `bigmap_download.pl` and size limit in `purge_images.pl`. 29 | And add those two lines in `crontab -e` editor (your intervals may vary): 30 | 31 | */2 * * * * /var/www/.../bigmap_download.pl 32 | 2 */6 * * * /var/www/.../purge_images.pl 33 | 34 | Of all PHP scripts only `queue.php` needs configuring: you should change e-mail in there, and may want to alter limits. 35 | Tile limit is specified in `$max_tiles` variable in `bigmap.php`. 36 | 37 | After changing `tiles.txt` you would want to update Leaflet layers in `index.html`. To do this, just check paths 38 | in `scripts/tiles2html.pl` and run it. 39 | 40 | ## License 41 | 42 | All scripts were written by Ilya Zverev, partly based on public domain code by Frederik Ramm. 43 | Published under WTFPL license. 44 | -------------------------------------------------------------------------------- /www/python.php: -------------------------------------------------------------------------------- 1 | 12 | #!/usr/bin/env python 13 | # Generated by BigMap 2. Permalink: 14 | 15 | 16 | import io, urllib.request, datetime, time, re, random 17 | from PIL import Image, ImageDraw 18 | # ^^^^^^ install "python-pillow" package | pip install Pillow | easy_install Pillow 19 | 20 | (zoom, xmin, ymin, xmax, ymax) = (, , , , ) 21 | layers = [] 22 | attribution = '' 23 | xsize = xmax - xmin + 1 24 | ysize = ymax - ymin + 1 25 | tilesize = 256 26 | 27 | resultImage = Image.new("RGBA", (xsize * tilesize, ysize * tilesize), (0,0,0,0)) 28 | counter = 0 29 | for x in range(xmin, xmax+1): 30 | for y in range(ymin, ymax+1): 31 | for layer in layers: 32 | url = layer.replace("!x", str(x)).replace("!y", str(y)).replace("!z", str(zoom)) 33 | match = re.search("{([a-z0-9]+)}", url) 34 | if match: 35 | url = url.replace(match.group(0), random.choice(match.group(1))) 36 | print(url, "... "); 37 | try: 38 | req = urllib.request.Request(url, headers={'User-Agent': 'BigMap/2.0'}) 39 | tile = urllib.request.urlopen(req).read() 40 | except Exception as e: 41 | print("Error", e) 42 | continue; 43 | image = Image.open(io.BytesIO(tile)) 44 | resultImage.paste(image, ((x-xmin)*tilesize, (y-ymin)*tilesize), image.convert("RGBA")) 45 | counter += 1 46 | if counter == 10: 47 | time.sleep(2); 48 | counter = 0 49 | 50 | draw = ImageDraw.Draw(resultImage) 51 | draw.text((5, ysize*tilesize-15), attribution, (0,0,0)) 52 | del draw 53 | 54 | now = datetime.datetime.now() 55 | outputFileName = "map%02d-%02d%02d%02d-%02d%02d.png" % (zoom, now.year % 100, now.month, now.day, now.hour, now.minute) 56 | resultImage.save(outputFileName) 57 | -------------------------------------------------------------------------------- /www/perl.php: -------------------------------------------------------------------------------- 1 | 12 | #!/usr/bin/perl 13 | # Generated by BigMap 2. Permalink: 14 | 15 | 16 | use strict; 17 | use LWP; 18 | use GD; 19 | 20 | my ($zoom, $xmin, $ymin, $xmax, $ymax) = (); 21 | my @layers = (); 22 | my $attribution = ''; 23 | my $xsize = $xmax - $xmin + 1; 24 | my $ysize = $ymax - $ymin + 1; 25 | 26 | my $img = GD::Image->new($xsize*256, $ysize*256, 1); 27 | my $white = $img->colorAllocate(248,248,248); 28 | $img->filledRectangle(0,0,$xsize*256,$ysize*256,$white); 29 | my $ua = LWP::UserAgent->new(); 30 | $ua->env_proxy; 31 | $ua->agent('BigMap/2.0'); 32 | my $count = 0; 33 | for (my $x=0;$x<$xsize;$x++) 34 | { 35 | for (my $y=0;$y<$ysize;$y++) 36 | { 37 | my $xx = $x + $xmin; 38 | my $yy = $y + $ymin; 39 | foreach my $base(@layers) 40 | { 41 | my $url = $base; 42 | $url =~ s/!z/$zoom/g; 43 | $url =~ s/!x/$xx/g; 44 | $url =~ s/!y/$yy/g; 45 | $url =~ s/{([a-z0-9]+)}/substr($1,int(rand(length($1))),1)/e; 46 | print STDERR "$url... "; 47 | my $resp = $ua->get($url); 48 | print STDERR $resp->status_line; 49 | print STDERR "\n"; 50 | next unless $resp->is_success; 51 | my $tile = GD::Image->new($resp->content); 52 | next if ($tile->width == 1); 53 | if ($base =~ /seamark/) { 54 | my $black=$tile->colorClosest(0,0,0); 55 | $tile->transparent($black); 56 | } 57 | $img->copy($tile, $x*256,$y*256,0,0,256,256); 58 | if( ++$count == 10 ) { sleep 2; $count = 0; } 59 | } 60 | } 61 | } 62 | my $black = $img->colorClosest(0,0,0); 63 | $img->string(gdSmallFont, 5, $ysize*256 - 15, $attribution, $black); 64 | 65 | my @t = localtime(); 66 | open PIC, sprintf('>map%02d-%02d%02d%02d-%02d%02d.png', $zoom, $t[5]%100, $t[4]+1, $t[3], $t[2], $t[1]); 67 | binmode PIC; 68 | print PIC $img->png(); 69 | close PIC; 70 | -------------------------------------------------------------------------------- /www/bigmap.php: -------------------------------------------------------------------------------- 1 | OpenStreetMap'; 59 | $file = @fopen('tiles.txt', 'r'); 60 | if( $file ) { 61 | while( ($line = fgets($file)) !== false ) { 62 | $layer = explode(',', chop($line)); 63 | if( in_array($layer[0], $needed) && (count($result) ? $layer[1] : !$layer[1]) && $layer[2] <= $zoom && $layer[3] >= $zoom ) { 64 | $result[] = $layer[4]; 65 | if( strlen($layer[5]) ) 66 | $attribution .= ', '.$layer[5]; 67 | } 68 | if( count($result) >= 4 ) 69 | break; 70 | } 71 | fclose($file); 72 | } 73 | if( !count($result) ) 74 | $result[] = 'https://tile.openstreetmap.org/!z/!x/!y.png'; 75 | $attrib_plain = str_replace('©', '(c)', preg_replace('/<[^>]+>/', '', $attribution)); 76 | return $result; 77 | } 78 | 79 | function tile2latlon($x, $y) { 80 | global $zoom2; 81 | // taken from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Perl 82 | $relY1 = M_PI * (1 - 2 * $y / $zoom2); 83 | $relY2 = M_PI * (1 - 2 * ($y + 1) / $zoom2); 84 | $lat1 = rad2deg(atan(sinh($relY1))); 85 | $lat2 = rad2deg(atan(sinh($relY2))); 86 | $lon1 = 360 * ($x / $zoom2 - 0.5); 87 | $lon2 = $lon1 + 360 / $zoom2; 88 | return array($lat2, $lon1, $lat1, $lon2); 89 | } 90 | 91 | ?> 92 | 93 | 94 | 95 | BigMap 2 96 | 97 | 98 | 99 | 100 | "; 111 | } 112 | } 113 | } 114 | echo "
$attribution
\n"; 115 | 116 | require('panel.php'); 117 | ?> 118 | 119 | 120 | -------------------------------------------------------------------------------- /www/ozimap.php: -------------------------------------------------------------------------------- 1 | 17 | OziExplorer Map Data File Version 2.2 18 | BigMap 19 | .png 20 | 1 ,Map Code, 21 | WGS 84,WGS 84, 0.0000, 0.0000,WGS 84 22 | Reserved 1 23 | Reserved 2 24 | Magnetic Variation,,,E 25 | Map Projection,Mercator,PolyCal,No,AutoCalOnly,No,BSBUseWPX,No 26 | Point01,xy, 0, 0,in, deg,,, grid, , , ,N 27 | Point02,xy, , ,in, deg,,, grid, , , ,N 28 | Point03,xy, , ,in, deg, , ,N, , ,E, grid, , , ,N 29 | Point04,xy, , ,in, deg, , ,N, , ,E, grid, , , ,N 30 | Point05,xy, , ,in, deg, , ,N, , ,E, grid, , , ,N 31 | Point06,xy, , ,in, deg, , ,N, , ,E, grid, , , ,N 32 | Point07,xy, , ,in, deg, , ,N, , ,E, grid, , , ,N 33 | Point08,xy, , ,in, deg, , ,N, , ,E, grid, , , ,N 34 | Point09,xy, , ,in, deg, , ,N, , ,E, grid, , , ,N 35 | Point10,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 36 | Point11,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 37 | Point12,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 38 | Point13,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 39 | Point14,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 40 | Point15,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 41 | Point16,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 42 | Point17,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 43 | Point18,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 44 | Point19,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 45 | Point20,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 46 | Point21,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 47 | Point22,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 48 | Point23,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 49 | Point24,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 50 | Point25,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 51 | Point26,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 52 | Point27,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 53 | Point28,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 54 | Point29,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 55 | Point30,xy, , ,in, deg, , ,N, , ,W, grid, , , ,N 56 | Projection Setup,,,,,,,,,, 57 | Map Feature = MF ; Map Comment = MC These follow if they exist 58 | Track File = TF These follow if they exist 59 | Moving Map Parameters = MM? These follow if they exist 60 | MM0,Yes 61 | MMPNUM,4 62 | MMPXY,1,0,0 63 | $work_path/working"; 24 | close TTT; 25 | open(my $log, ">>$work_path/log"); 26 | binmode($log, ":unix"); # autoflush 27 | #print $log "\n"; 28 | 29 | # "finally" equivalent 30 | eval { 31 | # find the task 32 | open QUEUE, "<$work_path/queue" or die 'cannot open queue file: '.$!; 33 | if( open DONE, "<$work_path/done" ) { 34 | # skipping all finished tasks 35 | while(my $taskid = ) { 36 | chomp $taskid; 37 | while() { 38 | last if /^$taskid,/; 39 | } 40 | } 41 | close DONE; 42 | } 43 | my $task = ; 44 | close QUEUE; 45 | 46 | if( $task && $task =~ /^([a-z]+\d+),/ ) { 47 | # found task, great! Save task id and read data 48 | my $taskid = $1; 49 | open TASK, "<$work_path/tasks/$taskid" or die "Cannot open task file $taskid: $!"; 50 | my @contents; 51 | while() { 52 | chomp; 53 | push @contents, $_; 54 | } 55 | close TASK; 56 | 57 | my ($status) = split /,/, $contents[1]; 58 | if( $status >= 2 ) { 59 | write_task($taskid, \@contents, $status); 60 | die "Task $taskid with status $status was not marked as done"; 61 | } 62 | 63 | wlog("Found task $taskid, status $status"); 64 | write_task($taskid, \@contents, 1); 65 | clear_tiles() if $status >= 0; 66 | my $ua = LWP::UserAgent->new(); 67 | $ua->env_proxy; 68 | $ua->agent('BigMap/2.0 ('.$address.')'); 69 | $ua->timeout(15); 70 | if( download_tiles($ua, $contents[2], $contents[3]) ) { 71 | if( create_image($taskid, $contents[2], $contents[4]) ) { 72 | $status = 2; 73 | } else { 74 | $status = 3; 75 | } 76 | clear_tiles(); 77 | } else { 78 | $status = $status < 0 ? $status-1 : -1; 79 | $status = 3 if $status <= -3; 80 | } 81 | write_task($taskid, \@contents, $status); 82 | wlog("Task done with status $status"); 83 | } 84 | }; 85 | wlog('Error: '.$@) if $@; 86 | close $log; 87 | 88 | # remove mutex 89 | unlink "$work_path/working"; 90 | 91 | sub wlog { 92 | my $text = shift; 93 | my @t = localtime; 94 | printf $log "[%02d.%02d.%04d %02d:%02d:%02d] %s\n", $t[3], $t[4]+1, $t[5]+1900, $t[2], $t[1], $t[0], $text; 95 | } 96 | 97 | sub write_task { 98 | my($taskid, $contents, $status) = @_; 99 | open TASK, ">$work_path/tasks/$taskid" or die "Cannot open task file: $!"; 100 | print TASK $contents->[0]."\n"; 101 | print TASK $status.','.time()."\n"; 102 | print TASK $contents->[$_]."\n" for 2..$#{$contents}; 103 | close TASK; 104 | if( $status >= 2 && open DONE, ">>$work_path/done") { 105 | print DONE "$taskid\n"; 106 | close DONE; 107 | } 108 | } 109 | 110 | sub clear_tiles { 111 | return if !-d "$work_path/tiles"; 112 | opendir(my $dh, "$work_path/tiles") or die "Cannot open tile directory: $!"; 113 | my @tiles = grep {/\.png/} readdir $dh; 114 | closedir $dh; 115 | wlog("Removing cached tiles") if scalar @tiles; 116 | unlink "$work_path/tiles/$_" foreach @tiles; 117 | } 118 | 119 | sub download_tiles { 120 | my ($ua, $task, $urls) = @_; 121 | my ($zoom, $xmin, $ymin, $xmax, $ymax) = split /,/, $task; 122 | my @layers = split /\|/, $urls; 123 | my $count = 0; 124 | my $good = 1; 125 | wlog('Downloading tiles...'); 126 | my %stat; 127 | my $stat_ar; 128 | for( my $x = $xmin; $x <= $xmax; $x++ ) { 129 | for( my $y = $ymin; $y <= $ymax; $y++ ) { 130 | my $img = GD::Image->new(256, 256, 1); 131 | for( my $l = 0; $l <= $#layers; $l++ ) { 132 | my $url = $layers[$l]; 133 | $url =~ s/!z/$zoom/g; 134 | $url =~ s/!x/$x/g; 135 | $url =~ s/!y/$y/g; 136 | $url =~ s/{([a-z0-9]+)}/substr($1,int(rand(length($1))),1)/e; 137 | 138 | if( exists $stat{$layers[$l]} ) { $stat_ar = $stat{$layers[$l]}; } else { 139 | $stat_ar = [0, 0]; 140 | $stat{$layers[$l]} = $stat_ar; 141 | } 142 | my $resp = $ua->get($url); 143 | #wlog($url.' - '.$resp->status_line); 144 | if( !$resp->is_success ) { 145 | wlog($url.' - '.$resp->status_line); 146 | $img = 0; 147 | $good = 0; 148 | $stat_ar->[1]++; 149 | last; 150 | } 151 | my $tile = GD::Image->new($resp->content); 152 | next if !$tile || $tile->width == 1; 153 | $stat_ar->[0]++; 154 | if( $url =~ /seamark/ ) { 155 | my $black = $tile->colorClosest(0,0,0); 156 | $tile->transparent($black); 157 | } 158 | $img->copy($tile,0,0,0,0,256,256); 159 | if( ++$count == 5 ) { sleep 2; $count = 0; } 160 | } 161 | if( $img ) { 162 | mkdir "$work_path/tiles" if !-d "$work_path/tiles"; 163 | open PIC, sprintf(">$work_path/tiles/%d_%06d_%06d.png", $zoom, $x, $y) or return 0; 164 | binmode PIC; 165 | print PIC $img->png(); 166 | close PIC; 167 | } 168 | } 169 | } 170 | wlog($_.': '.$stat{$_}->[0].' good, '.$stat{$_}->[1].' failed') foreach keys %stat; 171 | return $good; 172 | } 173 | 174 | sub create_image { 175 | my ($taskid, $task, $attribution) = @_; 176 | my ($zoom, $xmin, $ymin, $xmax, $ymax) = split /,/, $task; 177 | my @t = gmtime(); 178 | my $filename = sprintf('%s/%s-%02d%02d%02d-%02d%02d.png', $result_path, $taskid, $t[5]%100, $t[4]+1, $t[3], $t[2], $t[1]); 179 | wlog("Creating image $filename"); 180 | my $xsize = $xmax - $xmin + 1; 181 | my $ysize = $ymax - $ymin + 1; 182 | my $img = GD::Image->new($xsize*256, $ysize*256, 1); 183 | my $white = $img->colorAllocate(248,248,248); 184 | $img->filledRectangle(0,0,$xsize*256,$ysize*256,$white); 185 | for( my $x = $xmin; $x <= $xmax; $x++ ) { 186 | for( my $y = $ymin; $y <= $ymax; $y++ ) { 187 | my $fn = sprintf("$work_path/tiles/%d_%06d_%06d.png", $zoom, $x, $y); 188 | my $tile = GD::Image->new($fn); 189 | return 0 if !$tile; 190 | $img->copy($tile,($x-$xmin)*256,($y-$ymin)*256,0,0,256,256); 191 | } 192 | } 193 | my $black = $img->colorAllocate(0,0,0); 194 | $img->string(gdSmallFont, 5, $ysize*256 - 15, $attribution, $black); 195 | open PIC, '>'.$filename; 196 | binmode PIC; 197 | print PIC $img->png(); 198 | close PIC; 199 | return 1; 200 | } 201 | -------------------------------------------------------------------------------- /www/panel.php: -------------------------------------------------------------------------------- 1 | '; 5 | $widtiles = $xmax-$xmin+1; 6 | $heitiles = $ymax-$ymin+1; 7 | $widpix = $widtiles*256; 8 | $heipix = $heitiles*256; 9 | $asp="1:1"; 10 | if ($widpix>$heipix) 11 | { 12 | $asp = sprintf("%.2f:1", $widpix/$heipix); 13 | } 14 | elseif($widpix<$heipix) 15 | { 16 | $asp = sprintf("1:%.2f", $heipix/$widpix); 17 | } 18 | 19 | printf("Map is %dx%d tiles (%dx%d px) at zoom %d, aspect %s
", 20 | $widtiles,$heitiles,$widpix,$heipix,$zoom,$asp); 21 | echo ''; 22 | echo tde(); // td("tl", "right", $xmin-1, $xmax, $ymin-1, $ymax, $zoom); 23 | echo $ymin <= 0 ? tde('top') : td("top", "center", $xmin, $xmax, $ymin-1, $ymax, $zoom); 24 | echo tde(); // td("tr", "left", $xmin, $xmax+1, $ymin-1, $ymax, $zoom); 25 | echo ""; 26 | echo tde(); // td("ul", "right", $xmin-1, $xmax-1, $ymin-1, $ymax-1, $zoom); 27 | echo $ymin <= 0 ? tde('up') : td("up", "center", $xmin, $xmax, $ymin-1, $ymax-1, $zoom); 28 | echo tde(); // td("ur", "left", $xmin+1, $xmax+1, $ymin-1, $ymax-1, $zoom); 29 | echo ""; 30 | echo tde(); // td("tl", "right", $xmin+1, $xmax, $ymin+1, $ymax, $zoom); 31 | echo $ymin == $ymax ? tde('top') : td("top", "center", $xmin, $xmax, $ymin+1, $ymax, $zoom); 32 | echo tde(); // td("tr", "left", $xmin, $xmax-1, $ymin+1, $ymax, $zoom); 33 | echo ""; 34 | echo $xmin <= 0 ? tde('left', 'right') : td("left", "right", $xmin-1, $xmax, $ymin, $ymax, $zoom); 35 | echo ""; 36 | echo $xmax >= $zoom2-1 ? tde('right', 'left') : td("right", "left", $xmin, $xmax+1, $ymin, $ymax, $zoom); 37 | echo ""; 38 | echo $xmin <= 0 ? tde('left', 'right') : td("left", "right", $xmin-1, $xmax-1, $ymin, $ymax, $zoom); 39 | echo ""; 40 | echo $xmax >= $zoom2-1 ? tde('right', 'left') : td("right", "left", $xmin+1, $xmax+1, $ymin, $ymax, $zoom); 41 | echo ""; 42 | echo $xmin == $xmax ? tde('left', 'right') : td("left", "right", $xmin+1, $xmax, $ymin, $ymax, $zoom); 43 | echo ""; 44 | echo $xmin == $xmax ? tde('right', 'left') : td("right", "left", $xmin, $xmax-1, $ymin, $ymax, $zoom); 45 | echo ""; 46 | echo tde(); // td("bl", "right", $xmin-1, $xmax, $ymin, $ymax+1, $zoom); 47 | echo $ymax >= $zoom2-1 ? tde('bottom') : td("bottom", "center", $xmin, $xmax, $ymin, $ymax+1, $zoom); 48 | echo tde(); // td("br", "left", $xmin, $xmax+1, $ymin, $ymax+1, $zoom); 49 | echo ""; 50 | echo tde(); // td("dl", "right", $xmin-1, $xmax-1, $ymin+1, $ymax+1, $zoom); 51 | echo $ymax >= $zoom2-1 ? tde('down') : td("down", "center", $xmin, $xmax, $ymin+1, $ymax+1, $zoom); 52 | echo tde(); // td("dr", "left", $xmin+1, $xmax+1, $ymin+1, $ymax+1, $zoom); 53 | echo ""; 54 | echo tde(); // td("bl", "right", $xmin+1, $xmax, $ymin, $ymax-1, $zoom); 55 | echo $ymin == $ymax ? tde('bottom') : td("bottom", "center", $xmin, $xmax, $ymin, $ymax-1, $zoom); 56 | echo tde(); // td("br", "left", $xmin, $xmax-1, $ymin, $ymax-1, $zoom); 57 | echo ""; 58 | echo ""; 70 | echo "
  
EXPAND SHIFT SHRINK
  
"; 59 | echo ""; 60 | echo td("in/double size", "left", $xmin*2,$xmax*2+1,$ymin*2,$ymax*2+1,$zoom+1); 61 | echo ""; 62 | echo td("in/keep size", "left", $xmin*2+($xmax-$xmin)/2,$xmax*2-($xmax-$xmin)/2,$ymin*2+($ymax-$ymin)/2,$ymax*2-($ymax-$ymin)/2,$zoom+1); 63 | echo ""; 64 | echo ""; 65 | echo ""; 66 | echo td("out/keep size", "left", $xmin/2-($xmax-$xmin)/4,$xmax/2+($xmax-$xmin)/4,$ymin/2-($ymax-$ymin)/4,$ymax/2+($ymax-$ymin)/4,$zoom-1); 67 | echo ""; 68 | echo td("out/halve size", "left", $xmin/2,$xmax/2,$ymin/2,$ymax/2,$zoom-1); 69 | echo "
   ZOOM  
"; 71 | echo ""; 72 | echo preg_replace('/\?[^"]+/', sprintf('index.html#map=%d/%f/%f',$zoom,($lat_min+$lat_max)/2,($lon_min+$lon_max)/2), td("BigMap", "left", $xmin,$xmax,$ymin,$ymax,$zoom)); 73 | echo ""; 74 | echo td("Permalink", "left", $xmin,$xmax,$ymin,$ymax,$zoom); 75 | echo ""; 76 | echo str_replace('?', '?action=ozimap&', td("OZI", "left", $xmin,$xmax,$ymin,$ymax,$zoom)); 77 | echo ""; 78 | echo str_replace('?', '?action=wld&', td("WLD", "left", $xmin,$xmax,$ymin,$ymax,$zoom)); 79 | echo ""; 80 | echo str_replace('?', '?action=perl&', td("Perl", "left", $xmin,$xmax,$ymin,$ymax,$zoom)); 81 | echo ""; 82 | echo str_replace('?', '?action=python&', td("Py", "left", $xmin,$xmax,$ymin,$ymax,$zoom)); 83 | echo ""; 84 | echo ($xmax-$xmin+1) * ($ymax-$ymin+1) > $max_tiles ? tde("Enqueue", "left") : str_replace('?', '?action=enqueue&', td("Enqueue", "left", $xmin,$xmax,$ymin,$ymax,$zoom)); 85 | echo ""; 86 | echo td("100", "left", $xmin,$xmax,$ymin,$ymax,$zoom,256); 87 | echo ""; 88 | echo td("50", "left", $xmin,$xmax,$ymin,$ymax,$zoom,128); 89 | echo ""; 90 | echo td("25%", "left", $xmin,$xmax,$ymin,$ymax,$zoom,64); 91 | echo ""; 92 | echo ""; 93 | echo "
        // hide this
"; 94 | echo ""; 95 | 96 | # helper to display a table cell with a parametrized link inside 97 | function td($what, $align, $xmi, $xma, $ymi, $yma, $zm, $scl = 0) { 98 | global $scale, $tiles; 99 | if( !$scl ) $scl = $scale; 100 | return sprintf('%s', 101 | $align, 102 | $xmi, $xma, 103 | $ymi, $yma, 104 | $zm, $scl, $tiles, $what); 105 | } 106 | 107 | function tde($text = '', $align = 'center') { 108 | return ''.$text.''; 109 | } 110 | ?> 111 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BigMap 2 5 | 6 | 7 | 8 | 9 | 32 | 33 | 34 |

BigMap 2

35 |

Zoom to an area you want to have an image of, or paste map URL here: 36 |

37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 |

Download queue

48 | 49 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /www/queue.php: -------------------------------------------------------------------------------- 1 | (count($layers) > 1 ? $max_tiles / 1.5 : $max_tiles) ) { 15 | echo '

Too many tiles for this server. Sorry.

Return to BigMap

'; 16 | exit; 17 | } 18 | // 1. read queue and check that there were no tasks with given params 19 | $ident = "$zoom,$xmin,$ymin,$xmax,$ymax,$tiles"; 20 | $taskid = false; 21 | $queue = @fopen($workpath.'/queue', 'r'); 22 | if( $queue ) { 23 | while( !$taskid && ($line = fgets($queue)) !== false ) { 24 | $p = strpos($line, ','.$ident.','); 25 | if( $p > 0 ) 26 | $taskid = substr($line, 0, $p); 27 | } 28 | fclose($queue); 29 | } 30 | // 2. if there weren't, add task to queue 31 | if( !$taskid ) { 32 | if( get_queue_pos('_') >= $max_queue ) { 33 | echo '

Too many tasks in the queue already. Sorry.

Return to BigMap

'; 34 | exit; 35 | } 36 | $taskid = get_taskid(); 37 | if( !is_dir($workpath.'/tasks') ) 38 | mkdir($workpath.'/tasks'); 39 | $task = fopen($workpath.'/tasks/'.$taskid, 'w'); 40 | if( $task ) { 41 | $place = nominatim(($lat_min + $lat_max) / 2, ($lon_min + $lon_max) / 2); 42 | // start time; status; last time; ident; layer urls; attribution; nominatim response 43 | fwrite($task, time()."\n0,".time()."\n"); 44 | fwrite($task, "$ident\n"); 45 | fwrite($task, implode('|', $layers)."\n"); 46 | fwrite($task, "$attrib_plain\n"); 47 | fwrite($task, "$place\n"); 48 | fclose($task); 49 | chmod($workpath.'/tasks/'.$taskid, 0666); 50 | $queue = fopen($workpath.'/queue', 'a'); 51 | fwrite($queue, "$taskid,$ident,$place\n"); 52 | fclose($queue); 53 | } 54 | } 55 | // 3. redirect to the task page 56 | header("Location: ${redirect}queue.php?task=$taskid"); 57 | exit; 58 | } 59 | 60 | function get_taskid() { 61 | $t = ''; 62 | for( $i = 0; $i < 3; $i++ ) 63 | $t .= chr(rand(97,122)); 64 | return $t.(time() - 1390000000); 65 | } 66 | 67 | function nominatim($lat, $lon) { 68 | global $zoom, $email; 69 | $url = 'https://nominatim.openstreetmap.org/reverse?format=json&zoom='.min($zoom, 8).'&addressdetails=0' 70 | .'&email='.urlencode($email).'&lat='.$lat.'&lon='.$lon; 71 | $response = json_decode(file_get_contents($url), true); 72 | if( isset($response) && isset($response['display_name']) ) 73 | return $response['display_name']; 74 | return sprintf('Unknown place (%.1f°%s, %.1f°%s)', abs($lat), $lat < 0 ? 'S' : 'N', abs($lon), $lon < 0 ? 'W' : 'E'); 75 | } 76 | 77 | function get_queue_pos($taskid) { 78 | global $workpath; 79 | $queue = fopen($workpath.'/queue', 'r'); 80 | if( !$queue ) 81 | return 999; 82 | $found_done = false; 83 | $done = @fopen($workpath.'/done', 'r'); 84 | if( $done ) { 85 | while( ($donetask = fgets($done)) !== false ) { 86 | $donetask = chop($donetask); 87 | if( $donetask == $taskid ) 88 | $found_done = true; 89 | while( ($line = fgets($queue)) !== false ) { 90 | if( substr($line, 0, strlen($donetask)+1 ) == $donetask.',' ) 91 | break; 92 | } 93 | } 94 | fclose($done); 95 | } 96 | $cnt = 0; 97 | while( ($line = fgets($queue)) !== false ) { 98 | $cnt++; 99 | if( substr($line, 0, strlen($taskid)+1 ) == $taskid.',' ) { 100 | $found_done = false; 101 | break; 102 | } 103 | } 104 | fclose($queue); 105 | return $found_done ? 0 : $cnt; 106 | } 107 | 108 | ?> 109 | 110 | 111 | 112 | BigMap 2 113 | 114 | 115 | 116 | 117 | $min_interval ) { 126 | $task[1] = '0,'.time()."\n"; 127 | if( $ftask = fopen($workpath.'/tasks/'.$taskid, 'w') ) { 128 | foreach( $task as $taskline ) 129 | fwrite($ftask, $taskline); 130 | fclose($ftask); 131 | $queue = fopen($workpath.'/queue', 'a'); 132 | fwrite($queue, $taskid.','.chop($task[2]).','.chop($task[5])."\n"); 133 | fclose($queue); 134 | } 135 | } 136 | $statusid = $statusar[0]; 137 | if( $statusid == 0 ) $status = 'queued'; 138 | elseif( $statusid == 1 ) $status = 'processing'; 139 | elseif( $statusid == 2 ) $status = 'ready'; 140 | elseif( $statusid == 3 ) $status = 'failed'; 141 | elseif( $statusid < 0 ) $status = 'error, waiting'; 142 | else $status = 'unknown status'; 143 | $queuepos = get_queue_pos($taskid); 144 | $ident = explode(',', chop($task[2])); 145 | $link = $redirect.'bigmap.php?xmin='.$ident[1].'&ymin='.$ident[2].'&xmax='.$ident[3].'&ymax='.$ident[4].'&zoom='.$ident[0].'&tiles='.$ident[5]; 146 | ?> 147 |

Task :

148 |

149 |

x tiles at zoom ; layers: .

150 |

Opened on , last updated on .

151 | 0 ) { 153 | echo "

Queue position: $queuepos. Refresh this page in ".($queuepos*2+1)."-".($queuepos*3+1)." minutes to download your image.

"; 154 | } elseif( time() - $statusar[1] > $min_interval ) { 155 | echo '

Restart task

'; 156 | } else { 157 | echo '

You can return here later to refresh the image.

'; 158 | } 159 | 160 | // list all generated images 161 | if( $dh = opendir($imgpath) ) { 162 | $taskidlen = strlen($taskid); 163 | $images = array(); 164 | while( false !== ($filename = readdir($dh)) ) { 165 | if( substr($filename, 0, $taskidlen) == $taskid ) 166 | $images[] = $filename; 167 | } 168 | closedir($dh); 169 | 170 | if( count($images) ) { 171 | echo '

Generated images

'; 172 | rsort($images); 173 | foreach( $images as $image ) { 174 | $name = substr($image, $taskidlen + 5, 2).'.'.substr($image, $taskidlen + 3, 2).'.'.substr($image, $taskidlen + 1, 2) 175 | .' '.substr($image, $taskidlen + 8, 2).':'.substr($image, $taskidlen + 10, 2); 176 | $meta = $link.'&basename='.preg_replace('/\.\w+$/', '', $image).'&action'; 177 | echo "$name .map .wld .kml
\n"; 178 | } 179 | echo '

Note that images will be purged in a couple of weeks.

'; 180 | } 181 | } 182 | } else { 183 | echo '

Task was not found

'; 184 | } 185 | echo '

List all tasks

'; 186 | } else { 187 | // list all tasks 188 | echo '

BigMap 2 Download Queue

'; 189 | $queue = fopen($workpath.'/queue', 'r'); 190 | if( !$queue ) { 191 | echo '

Error: queue was not found.

'; 192 | exit; 193 | } 194 | $tasks_done = array(); 195 | $tasks_pending = array(); 196 | 197 | $done = @fopen($workpath.'/done', 'r'); 198 | if( $done ) { 199 | while( ($donetask = fgets($done)) !== false ) { 200 | $donetask = chop($donetask); 201 | while( ($line = fgets($queue)) !== false ) { 202 | if( substr($line, 0, strlen($donetask)+1 ) == $donetask.',' ) { 203 | $tasks_done[] = explode(',', chop($line), 8); 204 | break; 205 | } 206 | } 207 | } 208 | fclose($done); 209 | } 210 | while( ($line = fgets($queue)) !== false ) { 211 | $tasks_pending[] = explode(',', chop($line), 8); 212 | } 213 | fclose($queue); 214 | 215 | if( count($tasks_pending) ) { 216 | // check for active task 217 | $task = @file($workpath.'/tasks/'.$tasks_pending[0][0]); 218 | if( $task && substr($task[1], 0, 1) != '0' ) { 219 | echo "

Now processing

\n"; 220 | echo '

'.get_task_line($tasks_pending[0])."

\n"; 221 | array_shift($tasks_pending); 222 | } 223 | } 224 | 225 | if( count($tasks_pending) ) { 226 | echo "

Pending tasks

\n

\n"; 227 | foreach( $tasks_pending as $task ) 228 | echo get_task_line($task)."
\n"; 229 | echo "

\n"; 230 | } 231 | 232 | $cnt = count($tasks_done); 233 | if( $cnt ) { 234 | $limit = 50; 235 | echo "

Finished tasks

\n

\n"; 236 | for( $i = $cnt - 1; $i >= max(0, $cnt - $limit); $i-- ) 237 | echo get_task_line($tasks_done[$i])."
\n"; 238 | echo "

\n"; 239 | if( $cnt > $limit ) 240 | echo "

".($cnt-$limit)." older tasks were not included.

\n"; 241 | } 242 | } 243 | 244 | function get_task_line($task) { 245 | return '' 246 | .($task[4]-$task[2]+1).'x'.($task[5]-$task[3]+1).' tiles at zoom '.$task[1].': '.htmlspecialchars($task[7]); 247 | } 248 | ?> 249 | 250 | 251 | --------------------------------------------------------------------------------