Commit beaa0851 authored by Andrey Filippov's avatar Andrey Filippov
Browse files

Exported GPS and resolution OK

parent 6894dccd
Loading
Loading
Loading
Loading
+266 −26
Original line number Diff line number Diff line
@@ -58,9 +58,14 @@ public class ElphelTiffWriter {
	public static String TIFF_FIELD_TAG =                   "TIFFField";
	public static String TIFF_ASCIIS_TAG =                  "TIFFAsciis";
	public static String TIFF_ASCII_TAG =                   "TIFFAscii";
	public static String TIFF_BYTES_TAG =                   "TIFFBytes";
	public static String TIFF_BYTE_TAG =                    "TIFFByte";
	public static String TIFF_SHORTS_TAG =                  "TIFFShorts";
	public static String TIFF_SHORT_TAG =                   "TIFFShort";
	public static String TIFF_RATIONALS_TAG =               "TIFFRationals";
	public static String TIFF_RATIONAL_TAG =                "TIFFRational";
	public static String TIFF_FIELD_VALUE_ATTRIBUTE =       "value";
	public static String TIFF_FIELD_DESCRIPTION_ATTRIBUTE = "description";
	
	
	public static void saveTiffARGB32(
@@ -146,7 +151,7 @@ Sets an array of integer pixels in the default RGB color model (TYPE_INT_ARGB) a
	    double [] lla = {	49.9476514222895,28.5547846609957,323.750316821841};
	    LocalDateTime dt =  Did_ins.getLocalDateTime(2286, 491072.634526699);
	    double pix_in_meters = 0.01;
	    
	    int cm_dig_after = 9;
	    /*
	    ExifGPSTagSet exif_tag_set = ExifGPSTagSet.getInstance();
	    
@@ -181,18 +186,27 @@ Sets an array of integer pixels in the default RGB color model (TYPE_INT_ARGB) a
		}
	    TIFFTagSet tag_set = ExifGPSTagSet.getInstance();
	    TIFFTagSet eptts =   ExifParentTIFFTagSet.getInstance();
	    // Try changing node attributes w/o need for directory
	    directory.addTagSet(tag_set); // ExifGPSTagSet.getInstance());
	    directory.addTagSet(eptts);   // ExifParentTIFFTagSet.getInstance());
	    //ExifGPSTagSet
	    imageMetadata = directory.getAsMetadata();
	    /* */
	    
	    Node root_gps = imageMetadata.getAsTree(TIFF_METADATA_FORMAT);//?
	    IIOMetadataNode root_new = (IIOMetadataNode) imageMetadata.getAsTree(TIFF_METADATA_FORMAT);//?
	    
	    System.out.println("\n=== root_new ===:");
	    DumpImageMetadata.displayMetadataNode(root_new, 1);
	    
//	    createGpsMetadata(double [] lla, LocalDateTime dt)
	    
	    System.out.println("\n=== root_gps ===:");
	    DumpImageMetadata.displayMetadataNode(root_gps, 1);
	    
	    Node ifd =      root_gps.getFirstChild();	    
	    IIOMetadataNode ifd =    (IIOMetadataNode)  root_new.getFirstChild();
	    String ifd_attr =        ifd.getAttribute("tagSets");
	    // set it to ifd_attr	"javax.imageio.plugins.tiff.BaselineTIFFTagSet,javax.imageio.plugins.tiff.ExifGPSTagSet,javax.imageio.plugins.tiff.ExifParentTIFFTagSet" (id=187)
	    // remove directory above and addTagSet
	    IIOMetadataNode gpsRootNode = createGpsMetadata(lla, dt);
	    /*
	    IIOMetadataNode gpsRootNode = new IIOMetadataNode("TIFFIFD");
	    gpsRootNode.setAttribute("parentTagNumber", "34853");
	    gpsRootNode.setAttribute("parentTagName",   "GPSInfoIFDPointer");
@@ -207,7 +221,14 @@ Sets an array of integer pixels in the default RGB color model (TYPE_INT_ARGB) a
	    asciiNode.appendChild(childAsciiNode);
	    childNode.appendChild(asciiNode);
	    gpsRootNode.appendChild(childNode);
	    */
	    System.out.println("\n=== gpsRootNode ===:");
	    DumpImageMetadata.displayMetadataNode(gpsRootNode, 1);
	    
	    
	    addResolutionMetadata(ifd,pix_in_meters,cm_dig_after);
	    ifd.appendChild(gpsRootNode);
	    
	    System.out.println("\n=== ifd ===:");
	    DumpImageMetadata.displayMetadataNode(ifd, 1);

@@ -238,33 +259,29 @@ outstr.close();
	public static String TIFF_RATIONALS_TAG =   "TIFFRationals";
	public static String TIFF_RATIONAL_TAG =    "TIFFRational";
	public static String TIFF_FIELD_VALUE_ATTRIBUTE = "value";




	     */
	    
	    
	    
	    IIOMetadataNode dimension = createResolutionMetadata(pix_in_meters);
//	    imageMetadata.appendChild(dimension);
	    
	    
////		IIOMetadataNode root_dimension = new IIOMetadataNode(TIFF_METADATA_FORMAT); // IIOMetadataFormatImpl.standardMetadataFormatName);
//		root_dimension.appendChild(dimension);
////		root_dimension.appendChild(root_gps);
	    System.out.println("\n=== root_gps ===:"); // should have 2 tagSets, but has only baseline
////		root_dimension.appendChild(root_new);
////	    System.out.println("\n=== root_new ===:"); // should have 2 tagSets, but has only baseline
	    /*
	    <javax_imageio_tiff_image_1.0>
        <TIFFIFD tagSets='javax.imageio.plugins.tiff.BaselineTIFFTagSet,javax.imageio.plugins.tiff.GeoTIFFTagSet'>
	    */
	    DumpImageMetadata.displayMetadataNode(root_gps, 1);
	    System.out.println("\n=== root_new ===:");
	    DumpImageMetadata.displayMetadataNode(root_new, 1);
	    System.out.println("\n=== imageMetadata - before merge ===:");
	    DumpImageMetadata.displayMetadataNode(imageMetadata.getAsTree(TIFF_METADATA_FORMAT), 1);
		
		// lost GPS data after merge
		try {
			imageMetadata.mergeTree(TIFF_METADATA_FORMAT, root_gps); // dimension); // Root must have "TIFFIFD" child
			imageMetadata.mergeTree(TIFF_METADATA_FORMAT, root_new); // dimension); // Root must have "TIFFIFD" child
//			imageMetadata.setFromTree(TIFF_METADATA_FORMAT, root_dimension); // dimension);
		} catch (IIOInvalidTreeException e1) {
			// TODO Auto-generated catch block
@@ -304,8 +321,162 @@ outstr.close();
	    System.out.println();
        
	}
	private static IIOMetadataNode createRationalDegrees(double d, int digits_after) { // 8
		int denom = 1;
		for (int i = 0; i < digits_after; i++) denom *= 10;
		int degs =  (int) Math.floor(d);
		d = 60* (d - degs);
		int mins =  (int) Math.floor(d);
		d = 60* (d - mins);
		int fsec =  (int) Math.round(d*denom);
	    IIOMetadataNode node_rationals = new IIOMetadataNode(TIFF_RATIONALS_TAG);
	    IIOMetadataNode node_degs =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_degs.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, degs+"/1");
	    IIOMetadataNode node_mins =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_mins.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, mins+"/1");
	    IIOMetadataNode node_secs =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_secs.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, fsec+"/"+denom);
	    node_rationals.appendChild(node_degs);
	    node_rationals.appendChild(node_mins);
	    node_rationals.appendChild(node_secs);
	    return node_rationals;
	}
	
	private static IIOMetadataNode createRationalMeters(double d, int digits_after) { // 3
		int denom = 1;
		for (int i = 0; i < digits_after; i++) denom *= 10;
		int fmeters =  (int) Math.round(d*denom);
	    IIOMetadataNode node_rationals = new IIOMetadataNode(TIFF_RATIONALS_TAG);
	    IIOMetadataNode node_meters =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_meters.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, fmeters+"/"+denom);
	    node_rationals.appendChild(node_meters);
	    return node_rationals;
	}

	private static IIOMetadataNode createTimeStamp(LocalDateTime dt, int digits_after) { // 3
		int denom = 1;
		for (int i = 0; i < digits_after; i++) denom *= 10;
		int fsec = dt.getSecond()*denom+((int) Math.round(denom * dt.getNano()*1E-9));
		
	    IIOMetadataNode node_rationals = new IIOMetadataNode(TIFF_RATIONALS_TAG);
	    IIOMetadataNode node_hrs =       new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_hrs.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, dt.getHour()+"/1");
	    IIOMetadataNode node_mins =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_mins.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, dt.getMinute()+"/1");
	    IIOMetadataNode node_secs =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_secs.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, fsec+"/"+denom);
	    node_rationals.appendChild(node_hrs);
	    node_rationals.appendChild(node_mins);
	    node_rationals.appendChild(node_secs);
	    return node_rationals;
	}
	
	private static IIOMetadataNode createDateStamp(LocalDateTime dt) {
	    IIOMetadataNode node_date = new IIOMetadataNode(TIFF_ASCIIS_TAG);
	    IIOMetadataNode node_date_ascii = new IIOMetadataNode(TIFF_ASCII_TAG);
	    node_date_ascii.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE,
	    		String.format("%04d:%02d:%02d", dt.getYear(), dt.getMonthValue(), dt.getDayOfMonth()));
	    node_date.appendChild(node_date_ascii);
	    return node_date;
	}
	
	private static IIOMetadataNode createResolutionMetadata(double resolutionPixelInMeters) {
	private static IIOMetadataNode createGpsMetadata(double [] lla, LocalDateTime dt) {
		int ang_sec_after = 5;
		int meters_after =  3;
		int seconds_after = 3;
		
		// WGS84 latitude, longitude, height above ellipsoid (degrees,degrees,meters)
	    IIOMetadataNode gpsRootNode = new IIOMetadataNode("TIFFIFD");
	    gpsRootNode.setAttribute("parentTagNumber", "34853");
	    gpsRootNode.setAttribute("parentTagName",   "GPSInfoIFDPointer");
	    gpsRootNode.setAttribute("tagSets", "javax.imageio.plugins.tiff.ExifGPSTagSet");
        boolean lat_south = lla[0] < 0;
        double lat_abs = Math.abs(lla[0]);
        boolean long_west = lla[1] < 0;
        double long_abs = Math.abs(lla[1]);
        boolean alt_down = lla[2] < 0;
        double alt_abs = Math.abs(lla[2]);
        
        // Latitude
	    IIOMetadataNode node_lat_ref = new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_lat_ref.setAttribute("number", "1");
	    node_lat_ref.setAttribute("name", "GPSLatitudeRef");
	    IIOMetadataNode node_lat_ref_asciis = new IIOMetadataNode(TIFF_ASCIIS_TAG);
	    IIOMetadataNode node_lat_ref_ascii = new IIOMetadataNode(TIFF_ASCII_TAG);
	    node_lat_ref_ascii.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, lat_south?"S":"N");
	    node_lat_ref_asciis.appendChild(node_lat_ref_ascii);
	    node_lat_ref.appendChild(node_lat_ref_asciis);
	    gpsRootNode.appendChild(node_lat_ref);
	    IIOMetadataNode node_lat =  new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_lat.setAttribute("number", "2");
	    node_lat.setAttribute("name", "GPSLatitude");
	    node_lat.appendChild(createRationalDegrees(lat_abs, ang_sec_after));
	    gpsRootNode.appendChild(node_lat);

	    System.out.println("\n=== gpsRootNode after latitude ===:");
	    DumpImageMetadata.displayMetadataNode(gpsRootNode, 1);

        // Longitude
	    IIOMetadataNode node_long_ref = new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_long_ref.setAttribute("number", "3");
	    node_long_ref.setAttribute("name", "GPSLongitudeRef");
	    IIOMetadataNode node_long_ref_asciis = new IIOMetadataNode(TIFF_ASCIIS_TAG);
	    IIOMetadataNode node_long_ref_ascii = new IIOMetadataNode(TIFF_ASCII_TAG);
	    node_long_ref_ascii.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, long_west?"W":"E");
	    node_long_ref_asciis.appendChild(node_long_ref_ascii);
	    node_long_ref.appendChild(node_long_ref_asciis);
	    gpsRootNode.appendChild(node_long_ref);
	    IIOMetadataNode node_long =  new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_long.setAttribute("number", "4");
	    node_long.setAttribute("name", "GPSLongitude");
	    node_long.appendChild(createRationalDegrees(long_abs, ang_sec_after));
	    gpsRootNode.appendChild(node_long);

	    System.out.println("\n=== gpsRootNode after longitude ===:");
	    DumpImageMetadata.displayMetadataNode(gpsRootNode, 1);
	    
        // Altitude
	    IIOMetadataNode node_alt_ref = new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_alt_ref.setAttribute("number", "5");
	    node_alt_ref.setAttribute("name", "GPSAltitudeRef");
	    IIOMetadataNode node_alt_ref_asciis = new IIOMetadataNode(TIFF_BYTES_TAG);
	    IIOMetadataNode node_alt_ref_ascii =  new IIOMetadataNode(TIFF_BYTE_TAG);
	    node_alt_ref_ascii.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, alt_down?"1":"0");
	    node_alt_ref_ascii.setAttribute(TIFF_FIELD_DESCRIPTION_ATTRIBUTE, "Sea level");
	    node_alt_ref_asciis.appendChild(node_alt_ref_ascii);
	    node_alt_ref.appendChild(node_alt_ref_asciis);
	    gpsRootNode.appendChild(node_alt_ref);
	    IIOMetadataNode node_alt =  new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_alt.setAttribute("number", "6");
	    node_alt.setAttribute("name", "GPSAltitude");
	    node_alt.appendChild(createRationalMeters(alt_abs, meters_after));
	    gpsRootNode.appendChild(node_alt);
	    
	    System.out.println("\n=== gpsRootNode after altitude ===:");
	    DumpImageMetadata.displayMetadataNode(gpsRootNode, 1);
	    
	    if (dt != null) {
		    // Time
		    IIOMetadataNode node_time = new IIOMetadataNode(TIFF_FIELD_TAG);
		    node_time.setAttribute("number", "7");
		    node_time.setAttribute("name", "GPSTimeStamp");
		    node_time.appendChild(createTimeStamp(dt,seconds_after));
		    gpsRootNode.appendChild(node_time);
		    System.out.println("\n=== gpsRootNode after time ===:");
		    DumpImageMetadata.displayMetadataNode(gpsRootNode, 1);
		    // Date
		    IIOMetadataNode node_date = new IIOMetadataNode(TIFF_FIELD_TAG);
		    node_date.setAttribute("number", "29");
		    node_date.setAttribute("name", "GPSDateStamp");
		    node_date.appendChild(createDateStamp(dt));
		    gpsRootNode.appendChild(node_date);
	    }
	    System.out.println("\n=== gpsRootNode after date ===:");
	    DumpImageMetadata.displayMetadataNode(gpsRootNode, 1);
	    return gpsRootNode;
	}
	
	private static IIOMetadataNode createResolutionMetadataStd(double resolutionPixelInMeters) {
//		String pixelSize = Double.toString(25.4 / resolutionDPI);
		String pixelSize = Double.toString(1000*resolutionPixelInMeters); // millimeters
		IIOMetadataNode horizontal = new IIOMetadataNode("HorizontalPixelSize");
@@ -320,13 +491,82 @@ outstr.close();
//		root.appendChild(dimension);
//		return root;
	}
	private static IIOMetadataNode createGpsMetadata(double [] lla, LocalDateTime dt) {
		IIOMetadataNode gpsRootNode = new IIOMetadataNode("TIFFIFD");
//		gpsRootNode.setAttribute(FileConstants.TIFF_FIELD_NUMBER_ATTRIBUTE, "34853");		
	
		return gpsRootNode;
	private static void addResolutionMetadata(
			IIOMetadataNode ifd,
			double          resolutionPixelInMeters,
			int             digits_after) {
		int denom = 1;
		for (int i = 0; i < digits_after; i++) denom *= 10;
		int fppcm = (int) Math.round(denom * 0.01/resolutionPixelInMeters); // pixels per cm 
		
	    IIOMetadataNode node_resolution_unit = new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_resolution_unit.setAttribute("number", "296");
	    node_resolution_unit.setAttribute("name", "ResolutionUnit");
    	IIOMetadataNode node_ru_shorts = new IIOMetadataNode(TIFF_SHORTS_TAG);
    	IIOMetadataNode node_ru_short =  new IIOMetadataNode(TIFF_SHORT_TAG);
    	node_ru_short.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, "3"); // cm
    	node_ru_short.setAttribute(TIFF_FIELD_DESCRIPTION_ATTRIBUTE, "Centimeter"); // cm
    	node_ru_shorts.appendChild(node_ru_short);
    	node_resolution_unit.appendChild(node_ru_shorts);
    	ifd.appendChild(node_resolution_unit);
    	
	    IIOMetadataNode node_x_resolution = new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_x_resolution.setAttribute("number", "282");
	    node_x_resolution.setAttribute("name", "XResolution");
    	IIOMetadataNode node_x_rationals = new IIOMetadataNode(TIFF_RATIONALS_TAG);
    	IIOMetadataNode node_x_rational =  new IIOMetadataNode(TIFF_RATIONAL_TAG);
    	node_x_rational.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, fppcm+"/"+denom);
    	node_x_rationals.appendChild(node_x_rational);
    	node_x_resolution.appendChild(node_x_rationals);
    	ifd.appendChild(node_x_resolution);
    	
	    IIOMetadataNode node_y_resolution = new IIOMetadataNode(TIFF_FIELD_TAG);
	    node_y_resolution.setAttribute("number", "283");
	    node_y_resolution.setAttribute("name", "YResolution");
    	IIOMetadataNode node_y_rationals = new IIOMetadataNode(TIFF_RATIONALS_TAG);
    	IIOMetadataNode node_y_rational =  new IIOMetadataNode(TIFF_RATIONAL_TAG);
    	node_y_rational.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, fppcm+"/"+denom);
    	node_y_rationals.appendChild(node_y_rational);
    	node_y_resolution.appendChild(node_y_rationals);
    	ifd.appendChild(node_y_resolution);
	}
	
/*
 * 
    	IIOMetadataNode node_rationals = new IIOMetadataNode(TIFF_RATIONALS_TAG);
	    IIOMetadataNode node_hrs =       new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_hrs.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, dt.getHour()+"/1");
	    IIOMetadataNode node_mins =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_mins.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, dt.getMinute()+"/1");
	    IIOMetadataNode node_secs =      new IIOMetadataNode(TIFF_RATIONAL_TAG);
	    node_secs.setAttribute(TIFF_FIELD_VALUE_ATTRIBUTE, fsec+"/"+denom);
	    node_rationals.appendChild(node_hrs);
	    node_rationals.appendChild(node_mins);
	    node_rationals.appendChild(node_secs);
 * 
 * The number of pixels per ResolutionUnit in the ImageWidth direction.
            <TIFFField number='282' name='XResolution'>
                <TIFFRationals>
                    <TIFFRational value='1/1'/>
                </TIFFRationals>
            </TIFFField>
            <TIFFField number='283' name='YResolution'>
                <TIFFRationals>
                    <TIFFRational value='1/1'/>
                </TIFFRationals>
            </TIFFField>
            <TIFFField number='296' name='ResolutionUnit'>
                <TIFFShorts>
                    <TIFFShort value='1' description='None'/>
                </TIFFShorts>
            </TIFFField>

	public static String TIFF_SHORTS_TAG =                  "TIFFShorts";
	public static String TIFF_SHORT_TAG =                   "TIFFShort";

 */
	
	/*
	 * 
	FileImageOutputStream outputStream = new FileImageOutputStream(outputFile);