214 """Translate GDAL data type to WKT Raster pixel type."""
216 gdalc.GDT_Byte : { 'name': 'PT_8BUI', 'id': 4 },
217 gdalc.GDT_Int8 : { 'name': 'PT_8BSI', 'id': 3 },
218 gdalc.GDT_Int16 : { 'name': 'PT_16BSI', 'id': 5 },
219 gdalc.GDT_UInt16 : { 'name': 'PT_16BUI', 'id': 6 },
220 gdalc.GDT_Int32 : { 'name': 'PT_32BSI', 'id': 7 },
221 gdalc.GDT_UInt32 : { 'name': 'PT_32BUI', 'id': 8 },
222 gdalc.GDT_Float32 : { 'name': 'PT_32BF', 'id': 10 },
223 gdalc.GDT_Float64 : { 'name': 'PT_64BF', 'id': 11 }
226 # XXX: Uncomment these logs to debug types translation
227 #logit('MSG: Input GDAL pixel type: %s (%d)\n' % (gdal.GetDataTypeName(gdt), gdt))
228 #logit('MSG: Output WKTRaster pixel type: %(name)s (%(id)d)\n' % (pixtypes.get(gdt, 13)))
230 return pixtypes.get(gdt, 13)
385def make_sql_addrastercolumn(options, pixeltypes, nodata, pixelsize, blocksize, extent):
386 assert len(pixeltypes) > 0, "No pixel types given"
387 ts = make_sql_schema_table_names(options.table)
388 pt = make_sql_value_array(pixeltypes)
391 if nodata is not None and len(nodata) > 0:
392 nd = make_sql_value_array(nodata)
400 bs = ( 'null', 'null' )
401 # Check if regular blocking mode requested
402 if options.block_size is not None:
403 assert pixelsize is not None, "Pixel size is none, but regular blocking requested"
404 assert blocksize is not None, "Block size is none, but regular blocking requested"
405 assert extent is not None, "Extent is none, but regular blocking requested"
406 assert len(pixelsize) == 2, "Invalid pixel size, two values expected"
407 assert len(blocksize) == 2, "Invalid block size, two values expected"
408 assert len(extent) == 4, "Invalid extent, four coordinates expected"
409 assert len(extent[0]) == len(extent[3]) == 2, "Invalid extent, pair of X and Y pair expected"
411 bs = ( blocksize[0], blocksize[1] )
412 extgeom = "ST_Envelope(ST_SetSRID('POLYGON((%.15f %.15f,%.15f %.15f,%.15f %.15f,%.15f %.15f,%.15f %.15f))'::geometry, %d))" % \
413 (extent[0][0], extent[0][1], extent[1][0], extent[1][1],
414 extent[2][0], extent[2][1], extent[3][0], extent[3][1],
415 extent[0][0], extent[0][1], options.srid)
417 sql = "SELECT AddRasterColumn('%s','%s','%s',%d, %s, %s, %s, %s, %.15f, %.15f, %s, %s, %s);\n" % \
418 (ts[0], ts[1], options.column, options.srid, pt, odb, rb, nd,
419 pixelsize[0], pixelsize[1], bs[0], bs[1], extgeom)
421 logit("SQL: %s" % sql)
437def make_sql_create_raster_overviews(options):
438 schema = make_sql_schema_table_names(options.table)[0]
439 table = make_sql_full_table_name(schema + '.raster_overviews')
440 sql = 'CREATE TABLE ' + table + ' ( ' \
441 'o_table_catalog character varying(256) NOT NULL, ' \
442 'o_table_schema character varying(256) NOT NULL, ' \
443 'o_table_name character varying(256) NOT NULL, ' \
444 'o_column character varying(256) NOT NULL, ' \
445 'r_table_catalog character varying(256) NOT NULL, ' \
446 'r_table_schema character varying(256) NOT NULL, ' \
447 'r_table_name character varying(256) NOT NULL, ' \
448 'r_column character varying(256) NOT NULL, ' \
449 'out_db boolean NOT NULL, ' \
450 'overview_factor integer NOT NULL, ' \
451 'CONSTRAINT raster_overviews_pk ' \
452 'PRIMARY KEY (o_table_catalog, o_table_schema, o_table_name, o_column, overview_factor));\n'
457def make_sql_register_overview(options, ov_table, ov_factor):
458 assert len(ov_table) > 0
461 catalog = quote_sql_value('')
462 schema = make_sql_schema_table_names(options.table)[0]
463 r_table = make_sql_table_name(options.table)
465 sql = "INSERT INTO public.raster_overviews( " \
466 "o_table_catalog, o_table_schema, o_table_name, o_column, " \
467 "r_table_catalog, r_table_schema, r_table_name, r_column, out_db, overview_factor) " \
468 "VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', FALSE, %d);\n" % \
469 (catalog, schema, ov_table, options.column, catalog, schema, r_table, options.column, ov_factor)
543def calculate_block_size(ds, band_from, band_to):
544 """Size of natural block reported by GDAL for bands of given dataset"""
547 for i in range(band_from, band_to):
548 band = ds.GetRasterBand(i)
549 assert band is not None, "Cannot access raster band %d" % i
550 dims = band.GetBlockSize()
552 # Assume bands with common block size
556 # Validate dimensions of bands block
557 if block_dims != dims:
558 logit("MSG: Block sizes don't match: %s != %s\n" % (str(block_dims), str(dims)))
560 assert block_dims is not None, "Failed to calculate block size"
561 return (int(block_dims[0]), int(block_dims[1]))
670def wkblify_raster_header(options, ds, level, ulp, xsize = None, ysize = None):
671 """Writes WKT Raster header based on given GDAL into HEX-encoded WKB."""
672 assert ds is not None, "Error: Missing GDAL dataset"
674 assert len(ulp) == 2, "Error: invalid upper-left corner"
676 if xsize is None or ysize is None:
677 assert xsize is None and ysize is None
678 xsize = ds.RasterXSize
679 ysize = ds.RasterYSize
681 # Collect GeoReference information
682 gt = get_gdal_geotransform(ds)
683 ul = calculate_geoxy(gt, (ulp[0], ulp[1]))
684 rt_ip = ( ul[0], ul[1] )
685 rt_skew = ( gt[2], gt[4] )
686 rt_scale = ( gt[1] * level, gt[5] * level )
688 # TODO: Any way to lookup for SRID based on SRS in WKT?
689 #srs = osr.SpatialReference()
690 #srs.ImportFromWkt(ds.GetProjection())
692 # Burn input raster as WKTRaster WKB format
695 hexwkb += wkblify('B', options.endian)
697 hexwkb += wkblify('H', options.version)
699 if options.band is not None and options.band > 0:
700 hexwkb += wkblify('H', 1)
702 hexwkb += wkblify('H', ds.RasterCount)
705 hexwkb += wkblify('d', rt_scale[0])
706 hexwkb += wkblify('d', rt_scale[1])
707 hexwkb += wkblify('d', rt_ip[0])
708 hexwkb += wkblify('d', rt_ip[1])
709 hexwkb += wkblify('d', rt_skew[0])
710 hexwkb += wkblify('d', rt_skew[1])
711 hexwkb += wkblify('i', options.srid)
712 check_hex(hexwkb, 57)
713 ### Number of columns and rows
714 hexwkb += wkblify('H', xsize)
715 hexwkb += wkblify('H', ysize)
716 check_hex(hexwkb, 61)
718 logit("MSG: Georeference: px = %s -> ul = %s \tresolution = %s \trotation = %s\n" \
719 % (str(ulp), str(rt_ip), str(rt_scale), str(rt_skew)))
751def wkblify_band(options, band, level, xoff, yoff, read_block_size, block_size, infile, bandidx):
752 """Writes band of given GDAL dataset into HEX-encoded WKB for WKT Raster output."""
753 assert band is not None, "Error: Missing GDAL raster band"
759 # TODO: Do we want to handle options.overview_level? --mloskot
761 # TODO: Where bandidx and ds come from? --mloskot
762 # ANSWER: Provided by caller method --jorgearevalo
763 hexwkb += wkblify('B', bandidx - 1)
764 filepath = os.path.abspath(infile.replace('\\', '\\\\'))
765 logit('MSG: Out-db raster path=%s\n' % filepath)
766 hexwkb += wkblify(str(len(filepath)) + 's', filepath)
767 hexwkb += wkblify('B', 0)
771 # Right most column and bottom most row of blocks have
772 # portions that extend beyond the raster
773 read_padding_size = calculate_block_pad_size(band, xoff, yoff, read_block_size)
774 valid_read_block_size = ( read_block_size[0] - read_padding_size[0],
775 read_block_size[1] - read_padding_size[1] )
778 if read_padding_size[0] > 0 or read_padding_size[1] > 0:
779 target_block_size = (valid_read_block_size[0] / level, valid_read_block_size[1] / level)
780 target_padding_size = (read_padding_size[0] / level, read_padding_size[1] / level)
782 target_block_size = block_size
783 target_padding_size = ( 0, 0 )
785 logit('MSG: Normalize read_block=%s for level=%d to valid_read_block=%s with padding=%s\n' % \
786 (read_block_size, level, valid_read_block_size, read_padding_size))
787 logit('MSG: Normalize target_block=%s for level=%d to valid_target_block=%s with padding=%s\n' % \
788 (block_size, level, target_block_size, target_padding_size))
789 logit('MSG: ReadAsArray( %d, %d, %s, %s)\n' % \
790 (xoff, yoff, str(valid_read_block_size), str(target_block_size)))
792 assert valid_read_block_size[0] > 0 and valid_read_block_size[1] > 0
793 assert target_block_size[0] > 0 and target_block_size[1] > 0
795 pixels = band.ReadAsArray(xoff, yoff, valid_read_block_size[0], valid_read_block_size[1],
796 target_block_size[0], target_block_size[1])
798 # XXX: Use for debugging only
799 #dump_block_numpy(pixels)
801 out_pixels = numpy.zeros((block_size[1], block_size[0]), pt2numpy(band.DataType))
803 logit('MSG: Read valid source:\t%d x %d\n' % (len(pixels[0]), len(pixels)))
804 logit('MSG: Write into block:\t%d x %d\n' % (len(out_pixels[0]), len(out_pixels)))
806 if target_padding_size[0] > 0 or target_padding_size[1] > 0:
808 ysize_read_pixels = len(pixels)
809 nodata_value = fetch_band_nodata(band)
811 # Apply columns padding
812 pad_cols = numpy.array([nodata_value] * target_padding_size[0])
813 for row in range (0, ysize_read_pixels):
814 out_line = numpy.append(pixels[row], pad_cols)
815 out_pixels[row] = out_line
817 # Fill rows padding with nodata value
818 for row in range(ysize_read_pixels, ysize_read_pixels + target_padding_size[1]):
819 out_pixels[row].fill(nodata_value)
823 # XXX: Use for debugging only
824 #dump_block_numpy(out_pixels)
826 hexwkb = binascii.hexlify(out_pixels)
831def wkblify_raster_level(options, ds, level, band_range, infile, i):
832 assert ds is not None
834 assert len(band_range) == 2
836 band_from = band_range[0]
837 band_to = band_range[1]
839 # Collect raster and block dimensions
840 raster_size = ( ds.RasterXSize, ds.RasterYSize )
841 if options.block_size is not None:
842 block_size = parse_block_size(options)
843 read_block_size = ( block_size[0] * level, block_size[1] * level)
844 grid_size = calculate_grid_size(raster_size, read_block_size)
846 block_size = raster_size # Whole raster as a single block
847 read_block_size = block_size
850 logit("MSG: Processing raster=%s using read_block_size=%s block_size=%s of grid=%s in level=%d\n" % \
851 (str(raster_size), str(read_block_size), str(block_size), str(grid_size), level))
853 # Register base raster in RASTER_COLUMNS - SELECT AddRasterColumn();
855 if i == 0 and options.create_table:
856 gt = get_gdal_geotransform(ds)
857 pixel_size = ( gt[1], gt[5] )
858 pixel_types = collect_pixel_types(ds, band_from, band_to)
859 nodata_values = collect_nodata_values(ds, band_from, band_to)
860 extent = calculate_bounding_box(ds, gt)
861 sql = make_sql_addrastercolumn(options, pixel_types, nodata_values,
862 pixel_size, block_size, extent)
863 options.output.write(sql)
864 gen_table = options.table
867 # Create overview table and register in RASTER_OVERVIEWS
869 # CREATE TABLE o_<LEVEL>_<NAME> ( rid serial, options.column RASTER )
870 schema_table_names = make_sql_schema_table_names(options.table)
871 level_table_name = 'o_' + str(level) + '_' + schema_table_names[1]
872 level_table = schema_table_names[0] + '.' + level_table_name
874 sql = make_sql_create_table(options, level_table, True)
875 options.output.write(sql)
876 sql = make_sql_register_overview(options, level_table_name, level)
877 options.output.write(sql)
878 gen_table = level_table
880 # Write (original) raster to hex binary output
884 for ycell in range(0, grid_size[1]):
885 for xcell in range(0, grid_size[0]):
887 xoff = xcell * read_block_size[0]
888 yoff = ycell * read_block_size[1]
890 logit("MSG: --------- CELL #%04d\tindex = %d x %d\tdim = (%d x %d)\t(%d x %d) \t---------\n" % \
891 (tile_count, xcell, ycell, xoff, yoff, xoff + read_block_size[0], yoff + read_block_size[1]))
893 if options.block_size is not None:
894 hexwkb = '' # Reset buffer as single INSERT per tile is generated
895 hexwkb += wkblify_raster_header(options, ds, level, (xoff, yoff),
896 block_size[0], block_size[1])
898 hexwkb += wkblify_raster_header(options, ds, level, (xoff, yoff))
900 for b in range(band_from, band_to):
901 band = ds.GetRasterBand(b)
902 assert band is not None, "Missing GDAL raster band %d" % b
903 logit("MSG: Band %d\n" % b)
905 hexwkb += wkblify_band_header(options, band)
906 hexwkb += wkblify_band(options, band, level, xoff, yoff, read_block_size, block_size, infile, b)
909 check_hex(hexwkb) # TODO: Remove to not to decrease performance
910 sql = make_sql_insert_raster(gen_table, options.column, hexwkb, options.filename, infile)
911 options.output.write(sql)
913 tile_count = tile_count + 1
915 return (gen_table, tile_count)
917def wkblify_raster(options, infile, i, previous_gt = None):
918 """Writes given raster dataset using GDAL features into HEX-encoded of
919 WKB for WKT Raster output."""
921 assert infile is not None, "Input file is none, expected file name"
922 assert options.version == g_rt_version, "Error: invalid WKT Raster protocol version"
923 assert options.endian == NDR, "Error: invalid endianness, use little-endian (NDR) only"
924 assert options.srid >= -1, "Error: do you really want to specify SRID = %d" % options.srid
926 # Open source raster file
927 ds = gdal.Open(infile, gdalc.GA_ReadOnly);
929 sys.exit('Error: Cannot open input file: ' + str(infile))
931 # By default, translate all raster bands
933 # Calculate range for single-band request
934 if options.band is not None and options.band > 0:
935 band_range = ( options.band, options.band + 1 )
937 band_range = ( 1, ds.RasterCount + 1 )
939 # Compare this px size with previous one
940 current_gt = get_gdal_geotransform(ds)
941 if previous_gt is not None:
942 if previous_gt[1] != current_gt[1] or previous_gt[5] != current_gt[5]:
943 sys.exit('Error: Cannot load raster with different pixel size in the same raster table')
945 # Generate requested overview level (base raster if level = 1)
946 summary = wkblify_raster_level(options, ds, options.overview_level, band_range, infile, i)
947 SUMMARY.append( summary )
958 (opts, args) = parse_command_line()
961 VERBOSE = opts.verbose
966 saved_out = sys.stdout
967 if isinstance(opts.output, str):
968 filename = opts.output
969 opts.output = open(filename, "w")
972 opts.output.write('BEGIN;\n')
974 # If overviews requested, CREATE TABLE raster_overviews
975 if opts.create_raster_overviews_table:
976 sql = make_sql_create_raster_overviews(opts)
977 opts.output.write(sql)
980 if opts.overview_level == 1:
983 sql = make_sql_drop_raster_table(opts.table)
984 opts.output.write(sql)
987 if opts.create_table and opts.overview_level == 1:
988 sql = make_sql_create_table(opts)
989 opts.output.write(sql)
994 # Burn all specified input raster files into single WKTRaster table
996 for infile in opts.raster:
997 filelist = glob.glob(infile)
998 assert len(filelist) > 0, "No input raster files found for '" + str(infile) + "'"
1000 for filename in filelist:
1001 logit("MSG: Dataset #%d: %s\n" % (i + 1, filename))
1003 # Write raster data to WKB and send it to opts.output
1004 gt = wkblify_raster(opts, filename.replace( '\\', '/') , i, gt)
1008 if opts.index and SUMMARY is not None:
1009 sql = make_sql_create_gist(SUMMARY[0][0], opts.column)
1010 opts.output.write(sql)
1013 opts.output.write('END;\n')
1016 if opts.vacuum and SUMMARY is not None:
1017 sql = make_sql_vacuum(SUMMARY[0][0])
1018 opts.output.write(sql)
1021 if opts.output != sys.stdout:
1022 sys.stdout = saved_out
1024 print("------------------------------------------------------------")
1025 print(" Summary of GDAL to PostGIS Raster processing:")
1026 print("------------------------------------------------------------")
1028 m = '%d (%s)' % (i, infile)
1031 print("Number of processed raster files: " + m)
1032 print("List of generated tables (number of tiles):")
1036 print("%d\t%s (%d)" % (i, s[0], s[1]))