// // PrairieReader.java // /* LOCI Bio-Formats package for reading and converting biological file formats. Copyright (C) 2005-@year@ Melissa Linkert, Curtis Rueden, Chris Allan, Eric Kjellman and Brian Loranger. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.formats.in; import java.io.*; import java.text.*; import java.util.*; import loci.formats.*; /** * PrairieReader is the file format reader for * Prairie Technologies' TIFF variant. * * <dl><dt><b>Source code:</b></dt> * <dd><a href="https://skyking.microscopy.wisc.edu/trac/java/browser/trunk/loci/formats/in/PrairieReader.java">Trac</a>, * <a href="https://skyking.microscopy.wisc.edu/svn/java/trunk/loci/formats/in/PrairieReader.java">SVN</a></dd></dl> */ public class PrairieReader extends FormatReader { // -- Constants -- // Private tags present in Prairie TIFF files // IMPORTANT NOTE: these are the same as Metamorph's private tags - therefore, // it is likely that Prairie TIFF files will be incorrectly // identified unless the XML or CFG file is specified private static final int PRAIRIE_TAG_1 = 33628; private static final int PRAIRIE_TAG_2 = 33629; private static final int PRAIRIE_TAG_3 = 33630; // -- Fields -- /** List of files in the current dataset */ private String[] files; /** Helper reader for opening images */ private TiffReader tiff; /** Names of the associated XML files */ private String xmlFile, cfgFile; private boolean readXML = false, readCFG = false; // -- Constructor -- /** Constructs a new Prairie TIFF reader. */ public PrairieReader() { super("Prairie (TIFF)", new String[] {"tif", "tiff", "cfg", "xml"}); } // -- IFormatReader API methods -- /* @see loci.formats.IFormatReader#isThisType(byte[]) */ public boolean isThisType(byte[] block) { // adapted from MetamorphReader.isThisType(byte[]) if (block.length < 3) return false; if (block.length < 8) { return true; // we have no way of verifying further } String s = new String(block); if (s.indexOf("xml") != -1 && s.indexOf("PV") != -1) return true; boolean little = (block[0] == 0x49 && block[1] == 0x49); int ifdlocation = DataTools.bytesToInt(block, 4, little); if (ifdlocation < 0) return false; else if (ifdlocation + 1 > block.length) return true; else { int ifdnumber = DataTools.bytesToInt(block, ifdlocation, 2, little); for (int i=0; i<ifdnumber; i++) { if (ifdlocation + 3 + (i*12) > block.length) { return false; } else { int ifdtag = DataTools.bytesToInt(block, ifdlocation + 2 + (i*12), 2, little); if (ifdtag == PRAIRIE_TAG_1 || ifdtag == PRAIRIE_TAG_2 || ifdtag == PRAIRIE_TAG_3) { return true; } } } return false; } } /* @see loci.formats.IFormatReader#fileGroupOption(String) */ public int fileGroupOption(String id) throws FormatException, IOException { id = id.toLowerCase(); return (id.endsWith(".cfg") || id.endsWith(".xml")) ? FormatTools.MUST_GROUP : FormatTools.CAN_GROUP; } /* @see loci.formats.IFormatReader#getUsedFiles() */ public String[] getUsedFiles() { FormatTools.assertId(currentId, true, 1); String[] s = new String[files.length + 2]; System.arraycopy(files, 0, s, 0, files.length); s[files.length] = xmlFile; s[files.length + 1] = cfgFile; return s; } /* @see loci.formats.IFormatReader#openBytes(int, byte[]) */ public byte[] openBytes(int no, byte[] buf) throws FormatException, IOException { FormatTools.assertId(currentId, true, 1); FormatTools.checkPlaneNumber(this, no); tiff.setId(files[no]); return tiff.openBytes(0, buf); } /* @see loci.formats.IFormatReader#close(boolean) */ public void close(boolean fileOnly) throws IOException { if (fileOnly && tiff != null) tiff.close(fileOnly); else if (!fileOnly) close(); } // -- IFormatHandler API methods -- /* @see loci.formats.IFormatHandler#isThisType(String, boolean) */ public boolean isThisType(String name, boolean open) { if (!super.isThisType(name, open)) return false; // check extension if (!isGroupFiles()) return false; // check if there is an XML file in the same directory Location f = new Location(name); f = f.getAbsoluteFile(); Location parent = f.getParentFile(); String[] listing = parent.list(); int xmlCount = 0; for (int i=0; i<listing.length; i++) { if (listing[i].toLowerCase().endsWith(".xml")) { try { RandomAccessStream s = new RandomAccessStream( parent.getAbsolutePath() + File.separator + listing[i]); if (s.readString(512).indexOf("PV") != -1) xmlCount++; } catch (IOException e) { } } } if (xmlCount == 0) { listing = (String[]) Location.getIdMap().keySet().toArray(new String[0]); for (int i=0; i<listing.length; i++) { if (listing[i].toLowerCase().endsWith(".xml")) { try { RandomAccessStream s = new RandomAccessStream(listing[i]); if (s.readString(512).indexOf("PV") != -1) xmlCount++; } catch (IOException e) { } } } } boolean xml = xmlCount > 0; // just checking the filename isn't enough to differentiate between // Prairie and regular TIFF; open the file and check more thoroughly return open ? checkBytes(name, 524304) && xml : xml; } /* @see loci.formats.IFormatHandler#close() */ public void close() throws IOException { files = null; if (tiff != null) tiff.close(); tiff = null; currentId = null; readXML = false; readCFG = false; } // -- Internal FormatReader API methods -- /* @see loci.formats.IFormatReader#initFile(String) */ protected void initFile(String id) throws FormatException, IOException { if (debug) debug("PrairieReader.initFile(" + id + ")"); if (metadata == null) metadata = new Hashtable(); if (core == null) core = new CoreMetadata(1); if (id.endsWith("xml") || id.endsWith("cfg")) { // we have been given the XML file that lists TIFF files (best case) status("Parsing XML"); if (id.endsWith("xml")) { super.initFile(id); tiff = new TiffReader(); xmlFile = id; readXML = true; } else if (id.endsWith("cfg")) { cfgFile = id; readCFG = true; } RandomAccessStream is = new RandomAccessStream(id); byte[] b = new byte[(int) is.length()]; is.read(b); is.close(); String s = new String(b); Vector elements = new Vector(); while (s.length() > 0) { int ndx = s.indexOf("<"); int val1 = s.indexOf(">", ndx); if (val1 != -1 && val1 > ndx) { String sub = s.substring(ndx + 1, val1); s = s.substring(val1 + 1); elements.add(sub); } } int zt = 0; boolean isZ = false; Vector f = new Vector(); int fileIndex = 1; if (id.endsWith(".xml")) core.imageCount[0] = 0; String pastPrefix = ""; for (int i=1; i<elements.size(); i++) { String el = (String) elements.get(i); if (el.indexOf(" ") != -1) { boolean closed = el.endsWith("/"); String prefix = el.substring(0, el.indexOf(" ")); if (prefix.equals("File")) core.imageCount[0]++; if (prefix.equals("Frame")) { zt++; fileIndex = 1; } if (!prefix.equals("Key") && !prefix.equals("Frame")) { el = el.substring(el.indexOf(" ") + 1); while (el.indexOf("=") != -1) { int eq = el.indexOf("="); String key = el.substring(0, eq); String value = el.substring(eq + 2, el.indexOf("\"", eq + 2)); if (prefix.equals("File")) { addMeta(pastPrefix + " " + prefix + " " + fileIndex + " " + key, value); if (key.equals("filename")) fileIndex++; } else { addMeta(pastPrefix + " " + prefix + " " + key, value); if (pastPrefix.equals("PVScan") && prefix.equals("Sequence") && key.equals("type")) { isZ = value.equals("ZSeries"); } } el = el.substring(el.indexOf("\"", eq + 2) + 1).trim(); if (prefix.equals("File") && key.equals("filename")) { File current = new File(id).getAbsoluteFile(); String dir = ""; if (current.exists()) { dir = current.getPath(); dir = dir.substring(0, dir.lastIndexOf(File.separator) + 1); } f.add(dir + value); } } } else if (prefix.equals("Key")) { int keyIndex = el.indexOf("key") + 5; int valueIndex = el.indexOf("value") + 7; String key = el.substring(keyIndex, el.indexOf("\"", keyIndex)); String value = el.substring(valueIndex, el.indexOf("\"", valueIndex)); addMeta(key, value); if (key.equals("pixelsPerLine")) { core.sizeX[0] = Integer.parseInt(value); } else if (key.equals("linesPerFrame")) { core.sizeY[0] = Integer.parseInt(value); } } if (!closed) { pastPrefix = prefix; if (prefix.equals("Frame")) { int index = el.indexOf("index") + 7; String idx = el.substring(index, el.indexOf("\"", index)); pastPrefix += " " + idx; } } } } if (id.endsWith("xml")) { files = new String[f.size()]; f.copyInto(files); tiff.setId(files[0]); status("Populating metadata"); if (zt == 0) zt = 1; core.sizeZ[0] = isZ ? zt : 1; core.sizeT[0] = isZ ? 1 : zt; core.sizeC[0] = core.imageCount[0] / (core.sizeZ[0] * core.sizeT[0]); core.currentOrder[0] = "XYC" + (isZ ? "ZT" : "TZ"); core.pixelType[0] = FormatTools.UINT16; core.rgb[0] = false; core.interleaved[0] = false; core.littleEndian[0] = tiff.isLittleEndian(); core.indexed[0] = tiff.isIndexed(); core.falseColor[0] = false; String px = (String) getMeta("micronsPerPixel_XAxis"); String py = (String) getMeta("micronsPerPixel_YAxis"); float pixSizeX = px == null ? 0f : Float.parseFloat(px); float pixSizeY = py == null ? 0f : Float.parseFloat(py); MetadataStore store = getMetadataStore(); FormatTools.populatePixels(store, this); store.setDimensions(new Float(pixSizeX), new Float(pixSizeY), null, null, null, null); for (int i=0; i<core.sizeC[0]; i++) { String gain = (String) getMeta("pmtGain_" + i); String offset = (String) getMeta("pmtOffset_" + i); store.setLogicalChannel(i, null, null, null, null, null, null, null, null, offset == null ? null : new Float(offset), gain == null ? null : new Float(gain), null, null, null, null, null, null, null, null, null, null, null, null, null, null); } String date = (String) getMeta(" PVScan date"); if (date != null) { SimpleDateFormat parse = new SimpleDateFormat("MM/dd/yyyy h:mm:ss a"); Date d = parse.parse(date, new ParsePosition(0)); SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); date = fmt.format(d); } store.setImage(currentId, date, null, null); String laserPower = (String) getMeta("laserPower_0"); store.setLaser(null, null, null, null, null, null, laserPower == null ? null : new Float(laserPower), null, null, null, null); /* String zoom = (String) getMeta("opticalZoom"); if (zoom != null) { store.setDisplayOptions(new Float(zoom), new Boolean(core.sizeC[0] > 1), new Boolean(core.sizeC[0] > 1), new Boolean(core.sizeC[0] > 2), Boolean.FALSE, null, null, null, null, null, null, null, null, null, null, null); } */ } if (!readXML || !readCFG) { File file = new File(id).getAbsoluteFile(); File parent = file.getParentFile(); String[] listing = file.exists() ? parent.list() : (String[]) Location.getIdMap().keySet().toArray(new String[0]); for (int i=0; i<listing.length; i++) { String path = listing[i].toLowerCase(); if ((!readXML && path.endsWith(".xml")) || (readXML && path.endsWith(".cfg"))) { String dir = ""; if (file.exists()) { dir = parent.getPath(); if (!dir.endsWith(File.separator)) dir += File.separator; } initFile(dir + listing[i]); } } } } else { // we have been given a TIFF file - reinitialize with the proper XML file status("Finding XML file"); Location f = new Location(id); f = f.getAbsoluteFile(); Location parent = f.getParentFile(); String[] listing = parent.list(); for (int i=0; i<listing.length; i++) { String path = listing[i].toLowerCase(); if (path.endsWith(".xml") || path.endsWith(".cfg")) { initFile(new Location(path).getAbsolutePath()); } } } if (currentId == null) currentId = id; } }