|
12 | 12 | import sys |
13 | 13 |
|
14 | 14 |
|
15 | | -def export(world, export_type, export_bpp, export_signed, export_normalize): |
| 15 | +''' |
| 16 | +Whenever a GDAL short-format (http://www.gdal.org/formats_list.html) is given |
| 17 | +and a unique mapping to a file suffix exists, it is looked up in gdal_mapper. |
| 18 | +
|
| 19 | +Trivial ones (i.e. a call to lower() does the job) are not handled: |
| 20 | + BAG, BMP, BT, ECW, ERS, FITS, GIF, GTA, PNG, RIK, VRT, XPM |
| 21 | +
|
| 22 | +All other formats (>100) currently end up with their respective GDAL short-format |
| 23 | +converted to lower-case and might need to be renamed by the user. |
| 24 | +''' |
| 25 | +gdal_mapper = { # TODO: Find a way to make GDAL provide this mapping. |
| 26 | + "aig" : "adf", |
| 27 | + "bsb" : "kap", |
| 28 | + "doq1" : "doq", |
| 29 | + "doq2" : "doq", |
| 30 | + "esat" : "n1", |
| 31 | + "grib" : "grb", |
| 32 | + "gtiff" : "tif", |
| 33 | + "hfa" : "img", |
| 34 | + "jdem" : "mem", |
| 35 | + "jpeg" : "jpg", |
| 36 | + "msgn" : "nat", |
| 37 | + "terragen": "ter", |
| 38 | + "usgsdem" : "dem", |
| 39 | +} |
| 40 | + |
| 41 | + |
| 42 | +def export(world, export_filetype = 'GTiff', export_datatype = 'float32', path = 'seed_output'): |
16 | 43 | try: |
17 | 44 | gdal |
18 | 45 | except NameError: |
19 | 46 | print("Cannot export: please install pygdal.") |
20 | 47 | sys.exit(1) |
21 | 48 |
|
22 | | - final_driver = gdal.GetDriverByName(export_type) |
| 49 | + final_driver = gdal.GetDriverByName(export_filetype) |
23 | 50 | if final_driver is None: |
24 | | - print("%s driver not registered." % export_type) |
| 51 | + print("%s driver not registered." % export_filetype) |
25 | 52 | sys.exit(1) |
26 | 53 |
|
27 | | - if export_bpp == 8 and export_signed: |
28 | | - numpy_type = numpy.int8 |
29 | | - gdal_type = gdal.GDT_Byte |
30 | | - elif export_bpp == 8 and not export_signed: |
| 54 | + # try to find the proper file-suffix |
| 55 | + export_filetype = export_filetype.lower() |
| 56 | + if export_filetype in gdal_mapper: |
| 57 | + export_filetype = gdal_mapper[export_filetype] |
| 58 | + |
| 59 | + # Note: GDAL will throw informative errors on its own whenever file type and data type cannot be matched. |
| 60 | + |
| 61 | + # translate export_datatype; http://www.gdal.org/gdal_8h.html#a22e22ce0a55036a96f652765793fb7a4 |
| 62 | + export_datatype = export_datatype.lower() |
| 63 | + if export_datatype in ['gdt_byte', 'uint8', 'int8', 'byte', 'char']: # GDAL does not support int8 |
| 64 | + bpp, signed, normalize = (8, False, True) |
31 | 65 | numpy_type = numpy.uint8 |
32 | | - gdal_type = gdal.GDT_Byte |
33 | | - elif export_bpp == 16 and export_signed: |
34 | | - numpy_type = numpy.int16 |
35 | | - gdal_type = gdal.GDT_Int16 |
36 | | - elif export_bpp == 16 and not export_signed: |
| 66 | + gdal_type = gdal.GDT_Byte |
| 67 | + elif export_datatype in ['gdt_uint16', 'uint16']: |
| 68 | + bpp, signed, normalize = (16, False, True) |
37 | 69 | numpy_type = numpy.uint16 |
38 | | - gdal_type = gdal.GDT_UInt16 |
39 | | - elif export_bpp == 32 and export_signed: |
40 | | - numpy_type = numpy.int32 |
41 | | - gdal_type = gdal.GDT_Int32 |
42 | | - elif export_bpp == 32 and not export_signed: |
| 70 | + gdal_type = gdal.GDT_UInt16 |
| 71 | + elif export_datatype in ['gdt_uint32', 'uint32']: |
| 72 | + bpp, signed, normalize = (32, False, True) |
43 | 73 | numpy_type = numpy.uint32 |
44 | | - gdal_type = gdal.GDT_UInt32 |
| 74 | + gdal_type = gdal.GDT_UInt32 |
| 75 | + elif export_datatype in ['gdt_int16', 'int16']: |
| 76 | + bpp, signed, normalize = (16, True, True) |
| 77 | + numpy_type = numpy.int16 |
| 78 | + gdal_type = gdal.GDT_Int16 |
| 79 | + elif export_datatype in ['gdt_int32', 'int32', 'int']: # fallback for 'int' |
| 80 | + bpp, signed, normalize = (32, True, True) |
| 81 | + numpy_type = numpy.int32 |
| 82 | + gdal_type = gdal.GDT_Int32 |
| 83 | + elif export_datatype in ['gdt_float32', 'float32', 'float']: # fallback for 'float' |
| 84 | + bpp, signed, normalize = (32, True, False) |
| 85 | + numpy_type = numpy.float32 |
| 86 | + gdal_type = gdal.GDT_Float32 |
| 87 | + elif export_datatype in ['gdt_float64', 'float64']: |
| 88 | + bpp, signed, normalize = (64, True, False) |
| 89 | + numpy_type = numpy.float64 |
| 90 | + gdal_type = gdal.GDT_Float64 |
45 | 91 | else: |
46 | | - print ("BPP %d is not valid, we only support 8, 16 and 32." % export_bpp) |
47 | | - sys.exit(1) |
| 92 | + raise TypeError("Type of data not recognized or not supported by GDAL: %s" % export_datatype) |
48 | 93 |
|
49 | 94 | # massage data to scale between the absolute min and max |
50 | | - elevation = numpy.array(world.elevation['data']) |
| 95 | + elevation = numpy.copy(world.elevation['data']) |
51 | 96 |
|
52 | | - if not export_signed and elevation.min() < 0.0: |
53 | | - elevation += abs(elevation.min()) # TODO: need better way to handle negative numbers |
| 97 | + # shift data according to minimum possible value |
| 98 | + if signed: |
| 99 | + elevation = elevation - world.sea_level() # elevation 0.0 now refers to sea-level |
| 100 | + else: |
| 101 | + elevation -= elevation.min() # lowest point at 0.0 |
54 | 102 |
|
55 | | - if export_normalize: |
56 | | - if export_signed: |
57 | | - elevation *= (((2**export_bpp)/2)-1)/elevation.max() |
| 103 | + # rescale data (currently integer-types only) |
| 104 | + if normalize: |
| 105 | + # elevation maps usually have a range of 0 to 10, maybe 15 - rescaling for integers is essential |
| 106 | + if signed: |
| 107 | + elevation *= (2**(bpp - 1) - 1) / max(abs(elevation.min(), abs(elevation.max()))) |
58 | 108 | else: |
59 | | - elevation *= (2**export_bpp)/elevation.max() |
| 109 | + elevation *= (2**bpp - 1) / abs(elevation.max()) |
| 110 | + |
| 111 | + # round data (integer-types only) |
| 112 | + if numpy_type != numpy.float32 and numpy_type != numpy.float64: |
| 113 | + elevation = elevation.round() |
60 | 114 |
|
| 115 | + # switch to final data type; no rounding performed |
61 | 116 | elevation = elevation.astype(numpy_type) |
62 | 117 |
|
63 | | - # take elevation data and push it into an intermediate GTiff format |
| 118 | + # take elevation data and push it into an intermediate GTiff format (some formats don't support being written by Create()) |
64 | 119 | inter_driver = gdal.GetDriverByName("GTiff") |
65 | | - _, inter_file = tempfile.mkstemp() |
66 | | - initial_ds = inter_driver.Create(inter_file, world.height, world.width, 1, gdal_type) |
| 120 | + _, inter_file = tempfile.mkstemp() # returns: (file-handle, absolute path) |
| 121 | + initial_ds = inter_driver.Create(inter_file, world.width, world.height, 1, gdal_type) |
67 | 122 | band = initial_ds.GetRasterBand(1) |
| 123 | + |
68 | 124 | band.WriteArray(elevation) |
69 | 125 | band = None # dereference band |
70 | 126 | initial_ds = None # save/flush and close |
71 | 127 |
|
72 | 128 | # take the intermediate GTiff format and convert to final format |
73 | 129 | initial_ds = gdal.Open(inter_file) |
74 | | - final_driver.CreateCopy('seed_output-%d.%s' % (export_bpp, export_type), initial_ds) |
| 130 | + final_driver.CreateCopy('%s-%d.%s' % (path, bpp, export_filetype), initial_ds) |
75 | 131 |
|
76 | 132 | os.remove(inter_file) |
0 commit comments