aletrn commited on
Commit
0aa759d
·
1 Parent(s): 7a8b84d

[refactor] remove unused code from tms2geotiff.py

Browse files
Files changed (1) hide show
  1. src/io/tms2geotiff.py +8 -404
src/io/tms2geotiff.py CHANGED
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
  """
4
- download geo-referenced raster tiles images.
5
  Modified from https://github.com/gumblex/tms2geotiff/
6
 
7
  BSD 2-Clause License
@@ -31,21 +29,19 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
  """
33
 
 
34
  import io
35
- import os
36
- import re
37
  import math
38
- import time
39
  import sqlite3
40
- import argparse
41
- import itertools
42
- import concurrent.futures
43
 
44
  from PIL import Image
45
- from PIL import TiffImagePlugin
46
 
47
- from src import PROJECT_ROOT_FOLDER, app_logger
48
- from src.utilities.constants import EARTH_EQUATORIAL_RADIUS, WKT_3857, DEFAULT_TMS
 
49
 
50
  Image.MAX_IMAGE_PIXELS = None
51
 
@@ -161,38 +157,6 @@ def print_progress(progress, total, done=False):
161
  app_logger.info('Downloaded image %d/%d, %.2f%%' % (progress, total, progress * 100 / total))
162
 
163
 
164
- class ProgressBar:
165
- def __init__(self, use_tqdm=True):
166
- self._tqdm_fn = None
167
- self.tqdm_bar = None
168
- self.tqdm_progress = 0
169
- if use_tqdm:
170
- try:
171
- import tqdm
172
- self._tqdm_fn = lambda total: tqdm.tqdm(
173
- total=total, unit='img')
174
- except ImportError:
175
- pass
176
-
177
- def print_progress(self, progress, total, done=False):
178
- if self.tqdm_bar is None and self._tqdm_fn:
179
- self.tqdm_bar = self._tqdm_fn(total)
180
- if not done:
181
- return
182
- if self.tqdm_bar is None:
183
- print_progress(progress, total, done)
184
- elif progress > self.tqdm_progress:
185
- delta = progress - self.tqdm_progress
186
- self.tqdm_bar.update(delta)
187
- self.tqdm_progress = progress
188
-
189
- def close(self):
190
- if self.tqdm_bar:
191
- self.tqdm_bar.close()
192
- else:
193
- app_logger.info('\nDone.')
194
-
195
-
196
  def mbtiles_save(db, img_data, xy, zoom, img_format):
197
  if not img_data:
198
  return
@@ -363,365 +327,5 @@ def download_extent(
363
  return retim, matrix
364
 
365
 
366
- def generate_tiffinfo(matrix):
367
- ifd = TiffImagePlugin.ImageFileDirectory_v2()
368
- # GeoKeyDirectoryTag
369
- gkdt = [
370
- 1, 1,
371
- 0, # GeoTIFF 1.0
372
- 0, # NumberOfKeys
373
- ]
374
- # KeyID, TIFFTagLocation, KeyCount, ValueOffset
375
- geokeys = [
376
- # GTModelTypeGeoKey
377
- (1024, 0, 1, 1), # 2D projected coordinate reference system
378
- # GTRasterTypeGeoKey
379
- (1025, 0, 1, 1), # PixelIsArea
380
- # GTCitationGeoKey
381
- (1026, 34737, 25, 0),
382
- # GeodeticCitationGeoKey
383
- (2049, 34737, 7, 25),
384
- # GeogAngularUnitsGeoKey
385
- (2054, 0, 1, 9102), # degree
386
- # ProjectedCRSGeoKey
387
- (3072, 0, 1, 3857),
388
- # ProjLinearUnitsGeoKey
389
- (3076, 0, 1, 9001), # metre
390
- ]
391
- gkdt[3] = len(geokeys)
392
- ifd.tagtype[34735] = 3 # short
393
- ifd[34735] = tuple(itertools.chain(gkdt, *geokeys))
394
- # GeoDoubleParamsTag
395
- ifd.tagtype[34736] = 12 # double
396
- # GeoAsciiParamsTag
397
- ifd.tagtype[34737] = 1 # byte
398
- ifd[34737] = b'WGS 84 / Pseudo-Mercator|WGS 84|\x00'
399
- a, b, c, d, e, f = matrix
400
- # ModelPixelScaleTag
401
- ifd.tagtype[33550] = 12 # double
402
- # ModelTiepointTag
403
- ifd.tagtype[33922] = 12 # double
404
- # ModelTransformationTag
405
- ifd.tagtype[34264] = 12 # double
406
- # This matrix tag should not be used
407
- # if the ModelTiepointTag and the ModelPixelScaleTag are already defined
408
- if c == 0 and e == 0:
409
- ifd[33550] = (b, -f, 0.0)
410
- ifd[33922] = (0.0, 0.0, 0.0, a, d, 0.0)
411
- else:
412
- ifd[34264] = (
413
- b, c, 0.0, a,
414
- e, f, 0.0, d,
415
- 0.0, 0.0, 0.0, 0.0,
416
- 0.0, 0.0, 0.0, 1.0
417
- )
418
- return ifd
419
-
420
-
421
- def img_memorysize(img):
422
- return img.size[0] * img.size[1] * len(img.getbands())
423
-
424
-
425
- def save_image_fn(img, filename, matrix, **params):
426
- wld_ext = {
427
- '.gif': '.gfw',
428
- '.jpg': '.jgw',
429
- '.jpeg': '.jgw',
430
- '.jp2': '.j2w',
431
- '.png': '.pgw',
432
- '.tif': '.tfw',
433
- '.tiff': '.tfw',
434
- }
435
- basename, ext = os.path.splitext(filename)
436
- ext = ext.lower()
437
- wld_name = basename + wld_ext.get(ext, '.wld')
438
- img_params = params.copy()
439
- if ext == '.jpg':
440
- img_params['quality'] = 92
441
- img_params['optimize'] = True
442
- elif ext == '.png':
443
- img_params['optimize'] = True
444
- elif ext.startswith('.tif'):
445
- if img_memorysize(img) >= 4 * 1024 * 1024 * 1024:
446
- # BigTIFF
447
- return save_geotiff_gdal(img, filename, matrix)
448
- img_params['compression'] = 'tiff_adobe_deflate'
449
- img_params['tiffinfo'] = generate_tiffinfo(matrix)
450
- img.save(filename, **img_params)
451
- if not ext.startswith('.tif'):
452
- with open(wld_name, 'w', encoding='utf-8') as f_wld:
453
- a, b, c, d, e, f = matrix
454
- f_wld.write('\n'.join(map(str, (b, e, c, f, a, d, ''))))
455
- return img
456
-
457
-
458
- def save_geotiff_gdal(img, filename, matrix):
459
- if 'GDAL_DATA' in os.environ:
460
- del os.environ['GDAL_DATA']
461
- if 'PROJ_LIB' in os.environ:
462
- del os.environ['PROJ_LIB']
463
-
464
- import numpy
465
- from osgeo import gdal
466
- gdal.UseExceptions()
467
-
468
- imgbands = len(img.getbands())
469
- driver = gdal.GetDriverByName('GTiff')
470
- gdal_options = ['COMPRESS=DEFLATE', 'PREDICTOR=2', 'ZLEVEL=9', 'TILED=YES']
471
- if img_memorysize(img) >= 4 * 1024 * 1024 * 1024:
472
- gdal_options.append('BIGTIFF=YES')
473
- if img_memorysize(img) >= 50 * 1024 * 1024:
474
- gdal_options.append('NUM_THREADS=%d' % max(1, os.cpu_count()))
475
-
476
- gtiff = driver.Create(filename, img.size[0], img.size[1],
477
- imgbands, gdal.GDT_Byte,
478
- options=gdal_options)
479
- gtiff.SetGeoTransform(matrix)
480
- gtiff.SetProjection(WKT_3857)
481
- for band in range(imgbands):
482
- array = numpy.array(img.getdata(band), dtype='u8')
483
- array = array.reshape((img.size[1], img.size[0]))
484
- band = gtiff.GetRasterBand(band + 1)
485
- band.WriteArray(array)
486
- gtiff.FlushCache()
487
- return img
488
-
489
-
490
- def save_image_auto(img, filename, matrix, use_gdal=False, **params):
491
- ext = os.path.splitext(filename)[1].lower()
492
- if ext in ('.tif', '.tiff') and use_gdal:
493
- return save_geotiff_gdal(img, filename, matrix)
494
- else:
495
- return save_image_fn(img, filename, matrix, **params)
496
-
497
-
498
  class TaskCancelled(RuntimeError):
499
  pass
500
-
501
-
502
- def parse_extent(s):
503
- try:
504
- coords_text = re_coords_split.split(s)
505
- return (float(coords_text[1]), float(coords_text[0]),
506
- float(coords_text[3]), float(coords_text[2]))
507
- except (IndexError, ValueError):
508
- raise ValueError("Invalid extent, should be: min_lon,min_lat,max_lon,max_lat")
509
-
510
-
511
- def gui():
512
- import tkinter as tk
513
- import tkinter.ttk as ttk
514
- import tkinter.messagebox
515
-
516
- root_tk = tk.Tk()
517
-
518
- def cmd_get_save_file():
519
- result = root_tk.tk.eval("""tk_getSaveFile -filetypes {
520
- {{GeoTIFF} {.tiff}}
521
- {{JPG} {.jpg}}
522
- {{PNG} {.png}}
523
- {{All Files} *}
524
- } -defaultextension .tiff""")
525
- if result:
526
- v_output.set(result)
527
-
528
- def cmd_get_save_mbtiles():
529
- result = root_tk.tk.eval("""tk_getSaveFile -filetypes {
530
- {{MBTiles} {.mbtiles}}
531
- {{All Files} *}
532
- } -defaultextension .tiff""")
533
- if result:
534
- v_mbtiles.set(result)
535
-
536
- frame = ttk.Frame(root_tk, padding=8)
537
- frame.grid(column=0, row=0, sticky='nsew')
538
- frame.master.title('Download TMS image')
539
- frame.master.resizable(0, 0)
540
- l_url = ttk.Label(frame, width=50, text="URL: (with {x}, {y}, {z})")
541
- l_url.grid(column=0, row=0, columnspan=3, sticky='w', pady=(0, 2))
542
- v_url = tk.StringVar()
543
- e_url = ttk.Entry(frame, textvariable=v_url)
544
- e_url.grid(column=0, row=1, columnspan=3, sticky='we', pady=(0, 5))
545
- l_extent = ttk.Label(frame, text="Extent: (min_lon,min_lat,max_lon,max_lat)")
546
- l_extent.grid(column=0, row=2, columnspan=3, sticky='w', pady=(0, 2))
547
- v_extent = tk.StringVar()
548
- e_extent = ttk.Entry(frame, width=50, textvariable=v_extent)
549
- e_extent.grid(column=0, row=3, columnspan=3, sticky='we', pady=(0, 5))
550
- l_zoom = ttk.Label(frame, width=5, text="Zoom:")
551
- l_zoom.grid(column=0, row=4, sticky='w')
552
- v_zoom = tk.StringVar()
553
- v_zoom.set('13')
554
- e_zoom = ttk.Spinbox(frame, width=10, textvariable=v_zoom, **{
555
- 'from': 1, 'to': 19, 'increment': 1
556
- })
557
- e_zoom.grid(column=1, row=4, sticky='w')
558
- l_output = ttk.Label(frame, width=10, text="Output:")
559
- l_output.grid(column=0, row=5, sticky='w')
560
- v_output = tk.StringVar()
561
- e_output = ttk.Entry(frame, width=30, textvariable=v_output)
562
- e_output.grid(column=1, row=5, sticky='we')
563
- b_output = ttk.Button(frame, text='...', width=3, command=cmd_get_save_file)
564
- b_output.grid(column=2, row=5, sticky='we')
565
- l_mbtiles = ttk.Label(frame, width=10, text="MBTiles:")
566
- l_mbtiles.grid(column=0, row=6, sticky='w')
567
- v_mbtiles = tk.StringVar()
568
- e_mbtiles = ttk.Entry(frame, width=30, textvariable=v_mbtiles)
569
- e_mbtiles.grid(column=1, row=6, sticky='we')
570
- b_mbtiles = ttk.Button(frame, text='...', width=3, command=cmd_get_save_mbtiles)
571
- b_mbtiles.grid(column=2, row=6, sticky='we')
572
- p_progress = ttk.Progressbar(frame, mode='determinate')
573
- p_progress.grid(column=0, row=7, columnspan=3, sticky='we', pady=(5, 2))
574
-
575
- started = False
576
- stop_download = False
577
-
578
- def reset():
579
- b_download.configure(
580
- text='Download', state='normal', command=cmd_download)
581
- root_tk.update()
582
-
583
- def update_progress(progress, total, done):
584
- nonlocal started, stop_download
585
- if not started:
586
- if done:
587
- p_progress.configure(maximum=total, value=progress)
588
- else:
589
- p_progress.configure(maximum=total)
590
- started = True
591
- elif done:
592
- p_progress.configure(value=progress)
593
- root_tk.update()
594
- if stop_download:
595
- raise TaskCancelled()
596
-
597
- def cmd_download():
598
- nonlocal started, stop_download
599
- started = False
600
- stop_download = False
601
- b_download.configure(text='Cancel', command=cmd_cancel)
602
- root_tk.update()
603
- try:
604
- url = v_url.get().strip()
605
- args = [url]
606
- args.extend(parse_extent(v_extent.get()))
607
- args.append(int(v_zoom.get()))
608
- filename = v_output.get()
609
- mbtiles = v_mbtiles.get()
610
- kwargs = {'mbtiles': mbtiles, 'save_image': bool(filename)}
611
- if not all(args) or not any((filename, mbtiles)):
612
- raise ValueError("Empty input")
613
- except (TypeError, ValueError, IndexError) as ex:
614
- reset()
615
- tkinter.messagebox.showerror(
616
- title='tms2geotiff',
617
- message="Invalid input: %s: %s" % (type(ex).__name__, ex),
618
- master=frame
619
- )
620
- return
621
- root_tk.update()
622
- try:
623
- img, matrix_projection = download_extent(
624
- *args, progress_callback=update_progress, **kwargs)
625
- b_download.configure(text='Saving...', state='disabled')
626
- root_tk.update()
627
- if filename:
628
- save_image_auto(img, filename, matrix_projection)
629
- reset()
630
- except TaskCancelled:
631
- reset()
632
- tkinter.messagebox.showwarning(
633
- title='tms2geotiff',
634
- message="Download cancelled.",
635
- master=frame
636
- )
637
- return
638
- except Exception as ex:
639
- reset()
640
- tkinter.messagebox.showerror(
641
- title='tms2geotiff',
642
- message="%s: %s" % (type(ex).__name__, ex),
643
- master=frame
644
- )
645
- return
646
- tkinter.messagebox.showinfo(
647
- title='tms2geotiff',
648
- message="Download complete.",
649
- master=frame
650
- )
651
-
652
- def cmd_cancel():
653
- nonlocal started, stop_download
654
- started = False
655
- stop_download = True
656
- reset()
657
-
658
- b_download = ttk.Button(
659
- width=15, text='Download', default='active', command=cmd_download)
660
- b_download.grid(column=0, row=6, columnspan=3, pady=2)
661
-
662
- root_tk.mainloop()
663
-
664
-
665
- def downloader(input_args, input_parser):
666
- download_args = [input_args.source]
667
- try:
668
- if input_args.extent:
669
- download_args.extend(parse_extent(input_args.extent))
670
- else:
671
- coords0 = tuple(map(float, getattr(input_args, 'from').split(',')))
672
- print("coords0:", coords0, "#")
673
- coords1 = tuple(map(float, getattr(input_args, 'to').split(',')))
674
- print("coords1:", coords1, "#")
675
- download_args.extend((coords0[0], coords0[1], coords1[0], coords1[1]))
676
- except Exception as e_downloader:
677
- print(f"e_downloader:", e_downloader, "#")
678
- input_parser.print_help()
679
- return 1
680
- download_args.append(input_args.zoom)
681
- download_args.append(input_args.mbtiles)
682
- download_args.append(bool(input_args.output))
683
- progress_bar = ProgressBar()
684
- download_args.append(progress_bar.print_progress)
685
- img_geo, matrix = download_extent(*download_args)
686
- progress_bar.close()
687
- if input_args.output:
688
- print(f"Saving image to {input_args.output}.")
689
- save_image_auto(img_geo, input_args.output, matrix)
690
- return 0
691
-
692
-
693
- def main():
694
- parser = argparse.ArgumentParser(
695
- description="Merge TMS tiles to a big image.",
696
- epilog="If no parameters are specified, it will open the GUI.")
697
- parser.add_argument(
698
- "-s", "--source", metavar='URL', default=DEFAULT_TMS,
699
- help="TMS server url (default is OpenStreetMap: %s)" % DEFAULT_TMS)
700
- parser.add_argument("-f", "--from", metavar='LAT,LON', help="one corner")
701
- parser.add_argument("-t", "--to", metavar='LAT,LON', help="the other corner")
702
- parser.add_argument("-e", "--extent",
703
- metavar='min_lon,min_lat,max_lon,max_lat',
704
- help="extent in one string (use either -e, or -f and -t)")
705
- parser.add_argument("-z", "--zoom", type=int, help="zoom level")
706
- parser.add_argument("-m", "--mbtiles", help="save MBTiles file")
707
- parser.add_argument("-g", "--gui", action='store_true', help="show GUI")
708
- parser.add_argument("output", nargs='?', help="output image file (can be omitted)")
709
- args = parser.parse_args()
710
- if args.gui or not getattr(args, 'zoom', None):
711
- gui()
712
- # parser.print_help()
713
- return 1
714
-
715
- downloader(args, parser)
716
-
717
-
718
- if __name__ == '__main__':
719
- # import sys
720
- # sys.exit(main())
721
- pt0 = 45.699, 127.1
722
- pt1 = 30.1, 148.492
723
- geo_img_output_filename = PROJECT_ROOT_FOLDER / "tmp" / "japan_out_main.png"
724
- geo_img, projection_matrix = download_extent(DEFAULT_TMS, pt0[0], pt0[1], pt1[0], pt1[1], 6)
725
-
726
- print(f"Saving image to {geo_img_output_filename}.")
727
- save_image_auto(geo_img, geo_img_output_filename, projection_matrix)
 
 
 
1
  """
2
+ Download geo-referenced raster tiles images.
3
  Modified from https://github.com/gumblex/tms2geotiff/
4
 
5
  BSD 2-Clause License
 
29
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
  """
31
 
32
+ import concurrent.futures
33
  import io
34
+ import itertools
 
35
  import math
36
+ import re
37
  import sqlite3
38
+ import time
 
 
39
 
40
  from PIL import Image
 
41
 
42
+ from src import app_logger
43
+ from src.utilities.constants import EARTH_EQUATORIAL_RADIUS
44
+
45
 
46
  Image.MAX_IMAGE_PIXELS = None
47
 
 
157
  app_logger.info('Downloaded image %d/%d, %.2f%%' % (progress, total, progress * 100 / total))
158
 
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  def mbtiles_save(db, img_data, xy, zoom, img_format):
161
  if not img_data:
162
  return
 
327
  return retim, matrix
328
 
329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  class TaskCancelled(RuntimeError):
331
  pass