214 """Translate GDAL data type to WKT Raster pixel type."""
216 gdalc.GDT_Byte : { 'name': 'PT_8BUI', 'id': 4 },
217 gdalc.GDT_Int16 : { 'name': 'PT_16BSI', 'id': 5 },
218 gdalc.GDT_UInt16 : { 'name': 'PT_16BUI', 'id': 6 },
219 gdalc.GDT_Int32 : { 'name': 'PT_32BSI', 'id': 7 },
220 gdalc.GDT_UInt32 : { 'name': 'PT_32BUI', 'id': 8 },
221 gdalc.GDT_Float32 : { 'name': 'PT_32BF', 'id': 10 },
222 gdalc.GDT_Float64 : { 'name': 'PT_64BF', 'id': 11 }
225 # XXX: Uncomment these logs to debug types translation
226 #logit('MSG: Input GDAL pixel type: %s (%d)\n' % (gdal.GetDataTypeName(gdt), gdt))
227 #logit('MSG: Output WKTRaster pixel type: %(name)s (%(id)d)\n' % (pixtypes.get(gdt, 13)))
229 return pixtypes.get(gdt, 13)
384def make_sql_addrastercolumn(options, pixeltypes, nodata, pixelsize, blocksize, extent):
385 assert len(pixeltypes) > 0, "No pixel types given"
386 ts = make_sql_schema_table_names(options.table)
387 pt = make_sql_value_array(pixeltypes)
390 if nodata is not None and len(nodata) > 0:
391 nd = make_sql_value_array(nodata)
399 bs = ( 'null', 'null' )
400 # Check if regular blocking mode requested
401 if options.block_size is not None:
402 assert pixelsize is not None, "Pixel size is none, but regular blocking requested"
403 assert blocksize is not None, "Block size is none, but regular blocking requested"
404 assert extent is not None, "Extent is none, but regular blocking requested"
405 assert len(pixelsize) == 2, "Invalid pixel size, two values expected"
406 assert len(blocksize) == 2, "Invalid block size, two values expected"
407 assert len(extent) == 4, "Invalid extent, four coordinates expected"
408 assert len(extent[0]) == len(extent[3]) == 2, "Invalid extent, pair of X and Y pair expected"
410 bs = ( blocksize[0], blocksize[1] )
411 extgeom = "ST_Envelope(ST_SetSRID('POLYGON((%.15f %.15f,%.15f %.15f,%.15f %.15f,%.15f %.15f,%.15f %.15f))'::geometry, %d))" % \
412 (extent[0][0], extent[0][1], extent[1][0], extent[1][1],
413 extent[2][0], extent[2][1], extent[3][0], extent[3][1],
414 extent[0][0], extent[0][1], options.srid)
416 sql = "SELECT AddRasterColumn('%s','%s','%s',%d, %s, %s, %s, %s, %.15f, %.15f, %s, %s, %s);\n" % \
417 (ts[0], ts[1], options.column, options.srid, pt, odb, rb, nd,
418 pixelsize[0], pixelsize[1], bs[0], bs[1], extgeom)
420 logit("SQL: %s" % sql)
436def make_sql_create_raster_overviews(options):
437 schema = make_sql_schema_table_names(options.table)[0]
438 table = make_sql_full_table_name(schema + '.raster_overviews')
439 sql = 'CREATE TABLE ' + table + ' ( ' \
440 'o_table_catalog character varying(256) NOT NULL, ' \
441 'o_table_schema character varying(256) NOT NULL, ' \
442 'o_table_name character varying(256) NOT NULL, ' \
443 'o_column character varying(256) NOT NULL, ' \
444 'r_table_catalog character varying(256) NOT NULL, ' \
445 'r_table_schema character varying(256) NOT NULL, ' \
446 'r_table_name character varying(256) NOT NULL, ' \
447 'r_column character varying(256) NOT NULL, ' \
448 'out_db boolean NOT NULL, ' \
449 'overview_factor integer NOT NULL, ' \
450 'CONSTRAINT raster_overviews_pk ' \
451 'PRIMARY KEY (o_table_catalog, o_table_schema, o_table_name, o_column, overview_factor));\n'
456def make_sql_register_overview(options, ov_table, ov_factor):
457 assert len(ov_table) > 0
460 catalog = quote_sql_value('')
461 schema = make_sql_schema_table_names(options.table)[0]
462 r_table = make_sql_table_name(options.table)
464 sql = "INSERT INTO public.raster_overviews( " \
465 "o_table_catalog, o_table_schema, o_table_name, o_column, " \
466 "r_table_catalog, r_table_schema, r_table_name, r_column, out_db, overview_factor) " \
467 "VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', FALSE, %d);\n" % \
468 (catalog, schema, ov_table, options.column, catalog, schema, r_table, options.column, ov_factor)
542def calculate_block_size(ds, band_from, band_to):
543 """Size of natural block reported by GDAL for bands of given dataset"""
546 for i in range(band_from, band_to):
547 band = ds.GetRasterBand(i)
548 assert band is not None, "Cannot access raster band %d" % i
549 dims = band.GetBlockSize()
551 # Assume bands with common block size
555 # Validate dimensions of bands block
556 if block_dims != dims:
557 logit("MSG: Block sizes don't match: %s != %s\n" % (str(block_dims), str(dims)))
559 assert block_dims is not None, "Failed to calculate block size"
560 return (int(block_dims[0]), int(block_dims[1]))
669def wkblify_raster_header(options, ds, level, ulp, xsize = None, ysize = None):
670 """Writes WKT Raster header based on given GDAL into HEX-encoded WKB."""
671 assert ds is not None, "Error: Missing GDAL dataset"
673 assert len(ulp) == 2, "Error: invalid upper-left corner"
675 if xsize is None or ysize is None:
676 assert xsize is None and ysize is None
677 xsize = ds.RasterXSize
678 ysize = ds.RasterYSize
680 # Collect GeoReference information
681 gt = get_gdal_geotransform(ds)
682 ul = calculate_geoxy(gt, (ulp[0], ulp[1]))
683 rt_ip = ( ul[0], ul[1] )
684 rt_skew = ( gt[2], gt[4] )
685 rt_scale = ( gt[1] * level, gt[5] * level )
687 # TODO: Any way to lookup for SRID based on SRS in WKT?
688 #srs = osr.SpatialReference()
689 #srs.ImportFromWkt(ds.GetProjection())
691 # Burn input raster as WKTRaster WKB format
694 hexwkb += wkblify('B', options.endian)
696 hexwkb += wkblify('H', options.version)
698 if options.band is not None and options.band > 0:
699 hexwkb += wkblify('H', 1)
701 hexwkb += wkblify('H', ds.RasterCount)
704 hexwkb += wkblify('d', rt_scale[0])
705 hexwkb += wkblify('d', rt_scale[1])
706 hexwkb += wkblify('d', rt_ip[0])
707 hexwkb += wkblify('d', rt_ip[1])
708 hexwkb += wkblify('d', rt_skew[0])
709 hexwkb += wkblify('d', rt_skew[1])
710 hexwkb += wkblify('i', options.srid)
711 check_hex(hexwkb, 57)
712 ### Number of columns and rows
713 hexwkb += wkblify('H', xsize)
714 hexwkb += wkblify('H', ysize)
715 check_hex(hexwkb, 61)
717 logit("MSG: Georeference: px = %s -> ul = %s \tresolution = %s \trotation = %s\n" \
718 % (str(ulp), str(rt_ip), str(rt_scale), str(rt_skew)))
750def wkblify_band(options, band, level, xoff, yoff, read_block_size, block_size, infile, bandidx):
751 """Writes band of given GDAL dataset into HEX-encoded WKB for WKT Raster output."""
752 assert band is not None, "Error: Missing GDAL raster band"
758 # TODO: Do we want to handle options.overview_level? --mloskot
760 # TODO: Where bandidx and ds come from? --mloskot
761 # ANSWER: Provided by caller method --jorgearevalo
762 hexwkb += wkblify('B', bandidx - 1)
763 filepath = os.path.abspath(infile.replace('\\', '\\\\'))
764 logit('MSG: Out-db raster path=%s\n' % filepath)
765 hexwkb += wkblify(str(len(filepath)) + 's', filepath)
766 hexwkb += wkblify('B', 0)
770 # Right most column and bottom most row of blocks have
771 # portions that extend beyond the raster
772 read_padding_size = calculate_block_pad_size(band, xoff, yoff, read_block_size)
773 valid_read_block_size = ( read_block_size[0] - read_padding_size[0],
774 read_block_size[1] - read_padding_size[1] )
777 if read_padding_size[0] > 0 or read_padding_size[1] > 0:
778 target_block_size = (valid_read_block_size[0] / level, valid_read_block_size[1] / level)
779 target_padding_size = (read_padding_size[0] / level, read_padding_size[1] / level)
781 target_block_size = block_size
782 target_padding_size = ( 0, 0 )
784 logit('MSG: Normalize read_block=%s for level=%d to valid_read_block=%s with padding=%s\n' % \
785 (read_block_size, level, valid_read_block_size, read_padding_size))
786 logit('MSG: Normalize target_block=%s for level=%d to valid_target_block=%s with padding=%s\n' % \
787 (block_size, level, target_block_size, target_padding_size))
788 logit('MSG: ReadAsArray( %d, %d, %s, %s)\n' % \
789 (xoff, yoff, str(valid_read_block_size), str(target_block_size)))
791 assert valid_read_block_size[0] > 0 and valid_read_block_size[1] > 0
792 assert target_block_size[0] > 0 and target_block_size[1] > 0
794 pixels = band.ReadAsArray(xoff, yoff, valid_read_block_size[0], valid_read_block_size[1],
795 target_block_size[0], target_block_size[1])
797 # XXX: Use for debugging only
798 #dump_block_numpy(pixels)
800 out_pixels = numpy.zeros((block_size[1], block_size[0]), pt2numpy(band.DataType))
802 logit('MSG: Read valid source:\t%d x %d\n' % (len(pixels[0]), len(pixels)))
803 logit('MSG: Write into block:\t%d x %d\n' % (len(out_pixels[0]), len(out_pixels)))
805 if target_padding_size[0] > 0 or target_padding_size[1] > 0:
807 ysize_read_pixels = len(pixels)
808 nodata_value = fetch_band_nodata(band)
810 # Apply columns padding
811 pad_cols = numpy.array([nodata_value] * target_padding_size[0])
812 for row in range (0, ysize_read_pixels):
813 out_line = numpy.append(pixels[row], pad_cols)
814 out_pixels[row] = out_line
816 # Fill rows padding with nodata value
817 for row in range(ysize_read_pixels, ysize_read_pixels + target_padding_size[1]):
818 out_pixels[row].fill(nodata_value)
822 # XXX: Use for debugging only
823 #dump_block_numpy(out_pixels)
825 hexwkb = binascii.hexlify(out_pixels)
830def wkblify_raster_level(options, ds, level, band_range, infile, i):
831 assert ds is not None
833 assert len(band_range) == 2
835 band_from = band_range[0]
836 band_to = band_range[1]
838 # Collect raster and block dimensions
839 raster_size = ( ds.RasterXSize, ds.RasterYSize )
840 if options.block_size is not None:
841 block_size = parse_block_size(options)
842 read_block_size = ( block_size[0] * level, block_size[1] * level)
843 grid_size = calculate_grid_size(raster_size, read_block_size)
845 block_size = raster_size # Whole raster as a single block
846 read_block_size = block_size
849 logit("MSG: Processing raster=%s using read_block_size=%s block_size=%s of grid=%s in level=%d\n" % \
850 (str(raster_size), str(read_block_size), str(block_size), str(grid_size), level))
852 # Register base raster in RASTER_COLUMNS - SELECT AddRasterColumn();
854 if i == 0 and options.create_table:
855 gt = get_gdal_geotransform(ds)
856 pixel_size = ( gt[1], gt[5] )
857 pixel_types = collect_pixel_types(ds, band_from, band_to)
858 nodata_values = collect_nodata_values(ds, band_from, band_to)
859 extent = calculate_bounding_box(ds, gt)
860 sql = make_sql_addrastercolumn(options, pixel_types, nodata_values,
861 pixel_size, block_size, extent)
862 options.output.write(sql)
863 gen_table = options.table
866 # Create overview table and register in RASTER_OVERVIEWS
868 # CREATE TABLE o_<LEVEL>_<NAME> ( rid serial, options.column RASTER )
869 schema_table_names = make_sql_schema_table_names(options.table)
870 level_table_name = 'o_' + str(level) + '_' + schema_table_names[1]
871 level_table = schema_table_names[0] + '.' + level_table_name
873 sql = make_sql_create_table(options, level_table, True)
874 options.output.write(sql)
875 sql = make_sql_register_overview(options, level_table_name, level)
876 options.output.write(sql)
877 gen_table = level_table
879 # Write (original) raster to hex binary output
883 for ycell in range(0, grid_size[1]):
884 for xcell in range(0, grid_size[0]):
886 xoff = xcell * read_block_size[0]
887 yoff = ycell * read_block_size[1]
889 logit("MSG: --------- CELL #%04d\tindex = %d x %d\tdim = (%d x %d)\t(%d x %d) \t---------\n" % \
890 (tile_count, xcell, ycell, xoff, yoff, xoff + read_block_size[0], yoff + read_block_size[1]))
892 if options.block_size is not None:
893 hexwkb = '' # Reset buffer as single INSERT per tile is generated
894 hexwkb += wkblify_raster_header(options, ds, level, (xoff, yoff),
895 block_size[0], block_size[1])
897 hexwkb += wkblify_raster_header(options, ds, level, (xoff, yoff))
899 for b in range(band_from, band_to):
900 band = ds.GetRasterBand(b)
901 assert band is not None, "Missing GDAL raster band %d" % b
902 logit("MSG: Band %d\n" % b)
904 hexwkb += wkblify_band_header(options, band)
905 hexwkb += wkblify_band(options, band, level, xoff, yoff, read_block_size, block_size, infile, b)
908 check_hex(hexwkb) # TODO: Remove to not to decrease performance
909 sql = make_sql_insert_raster(gen_table, options.column, hexwkb, options.filename, infile)
910 options.output.write(sql)
912 tile_count = tile_count + 1
914 return (gen_table, tile_count)
916def wkblify_raster(options, infile, i, previous_gt = None):
917 """Writes given raster dataset using GDAL features into HEX-encoded of
918 WKB for WKT Raster output."""
920 assert infile is not None, "Input file is none, expected file name"
921 assert options.version == g_rt_version, "Error: invalid WKT Raster protocol version"
922 assert options.endian == NDR, "Error: invalid endianness, use little-endian (NDR) only"
923 assert options.srid >= -1, "Error: do you really want to specify SRID = %d" % options.srid
925 # Open source raster file
926 ds = gdal.Open(infile, gdalc.GA_ReadOnly);
928 sys.exit('Error: Cannot open input file: ' + str(infile))
930 # By default, translate all raster bands
932 # Calculate range for single-band request
933 if options.band is not None and options.band > 0:
934 band_range = ( options.band, options.band + 1 )
936 band_range = ( 1, ds.RasterCount + 1 )
938 # Compare this px size with previous one
939 current_gt = get_gdal_geotransform(ds)
940 if previous_gt is not None:
941 if previous_gt[1] != current_gt[1] or previous_gt[5] != current_gt[5]:
942 sys.exit('Error: Cannot load raster with different pixel size in the same raster table')
944 # Generate requested overview level (base raster if level = 1)
945 summary = wkblify_raster_level(options, ds, options.overview_level, band_range, infile, i)
946 SUMMARY.append( summary )
957 (opts, args) = parse_command_line()
960 VERBOSE = opts.verbose
965 saved_out = sys.stdout
966 if isinstance(opts.output, str):
967 filename = opts.output
968 opts.output = open(filename, "w")
971 opts.output.write('BEGIN;\n')
973 # If overviews requested, CREATE TABLE raster_overviews
974 if opts.create_raster_overviews_table:
975 sql = make_sql_create_raster_overviews(opts)
976 opts.output.write(sql)
979 if opts.overview_level == 1:
982 sql = make_sql_drop_raster_table(opts.table)
983 opts.output.write(sql)
986 if opts.create_table and opts.overview_level == 1:
987 sql = make_sql_create_table(opts)
988 opts.output.write(sql)
993 # Burn all specified input raster files into single WKTRaster table
995 for infile in opts.raster:
996 filelist = glob.glob(infile)
997 assert len(filelist) > 0, "No input raster files found for '" + str(infile) + "'"
999 for filename in filelist:
1000 logit("MSG: Dataset #%d: %s\n" % (i + 1, filename))
1002 # Write raster data to WKB and send it to opts.output
1003 gt = wkblify_raster(opts, filename.replace( '\\', '/') , i, gt)
1007 if opts.index and SUMMARY is not None:
1008 sql = make_sql_create_gist(SUMMARY[0][0], opts.column)
1009 opts.output.write(sql)
1012 opts.output.write('END;\n')
1015 if opts.vacuum and SUMMARY is not None:
1016 sql = make_sql_vacuum(SUMMARY[0][0])
1017 opts.output.write(sql)
1020 if opts.output != sys.stdout:
1021 sys.stdout = saved_out
1023 print("------------------------------------------------------------")
1024 print(" Summary of GDAL to PostGIS Raster processing:")
1025 print("------------------------------------------------------------")
1027 m = '%d (%s)' % (i, infile)
1030 print("Number of processed raster files: " + m)
1031 print("List of generated tables (number of tiles):")
1035 print("%d\t%s (%d)" % (i, s[0], s[1]))