1 /* Copyright (c) 2002-2012 The University of the West Indies 2 * 3 * Contact: robert.lancashire@uwimona.edu.jm 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 package jspecview.export; 21 22 import java.io.IOException; 23 24 import javajs.util.DF; 25 import javajs.util.OC; 26 import javajs.util.Lst; 27 import javajs.util.PT; 28 29 import jspecview.api.JSVExporter; 30 import jspecview.common.Coordinate; 31 import jspecview.common.ExportType; 32 import jspecview.common.Spectrum; 33 import jspecview.common.JSViewer; 34 import jspecview.common.PanelData; 35 import jspecview.source.JDXReader; 36 import jspecview.source.JDXDataObject; 37 38 /** 39 * class <code>JDXExporter</code> contains methods for exporting a 40 * JCAMP-DX Spectrum in one of the compression formats DIF, FIX, PAC, SQZ or 41 * as x, y values. 42 * @author Debbie-Ann Facey 43 * @author Khari A. Bryan 44 * @author Craig A.D. Walters 45 * @author Prof Robert J. Lancashire 46 */ 47 48 public class JDXExporter implements JSVExporter { 49 50 public static final String newLine = System.getProperty("line.separator"); 51 private OC out; 52 private ExportType type; 53 private Spectrum spectrum; 54 private JSViewer vwr; 55 JDXExporter()56 public JDXExporter() { 57 58 } 59 /** 60 * The factor divisor used in compressing spectral data in one of DIF, SQZ, 61 * PAC and FIX formats 62 */ 63 private static final double FACTOR_DIVISOR = 1000000; 64 65 /** 66 * Exports spectrum in one of several formats 67 * @param type 68 * @param out 69 * @param startIndex 70 * @param endIndex 71 * @param spectrum the spectrum 72 * @return data if path is null 73 * @throws IOException 74 */ 75 @Override exportTheSpectrum(JSViewer viewer, ExportType type, OC out, Spectrum spectrum, int startIndex, int endIndex, PanelData pd, boolean asBase64)76 public String exportTheSpectrum(JSViewer viewer, ExportType type, OC out, Spectrum spectrum, int startIndex, int endIndex, PanelData pd, boolean asBase64) throws IOException{ 77 this.out = out; 78 this.type = type; 79 this.spectrum = spectrum; 80 this.vwr = viewer; 81 toStringAux(startIndex, endIndex); 82 out.closeChannel(); 83 return "OK " + out.getByteCount() + " bytes"; 84 } 85 86 /** 87 * Auxiliary function for the toString functions 88 * 89 * @param startIndex 90 * the start Coordinate Index 91 * @param endIndex 92 * the end Coordinate Index 93 */ toStringAux(int startIndex, int endIndex)94 private void toStringAux(int startIndex, int endIndex) { 95 96 Coordinate[] newXYCoords = spectrum.getXYCoords(); 97 String tabDataSet = "", tmpDataClass = "XYDATA"; 98 99 if (spectrum.isHZtoPPM()) { 100 // convert back to Hz. 101 Coordinate[] xyCoords = newXYCoords; 102 newXYCoords = new Coordinate[xyCoords.length]; 103 for (int i = 0; i < xyCoords.length; i++) 104 newXYCoords[i] = xyCoords[i].copy(); 105 Coordinate.applyScale(newXYCoords, spectrum.getObservedFreq(), 1); 106 } 107 108 double xCompFactor = spectrum.getXFactor(); 109 boolean isIntegerX = areIntegers(newXYCoords, startIndex, endIndex, 1.0, true); 110 if (!isIntegerX && !areIntegers(newXYCoords, startIndex, endIndex, xCompFactor, true)) 111 xCompFactor = 1; 112 113 double minY = Coordinate.getMinY(newXYCoords, startIndex, endIndex); 114 double maxY = Coordinate.getMaxY(newXYCoords, startIndex, endIndex); 115 double yCompFactor = spectrum.getYFactor(); 116 117 switch (type) { 118 case XY: 119 yCompFactor = 1; 120 tmpDataClass = (spectrum.isContinuous() ? "XYDATA" : "XYPOINTS"); 121 break; 122 case PAC: 123 yCompFactor = 1; 124 break; 125 default: 126 boolean isIntegerY = areIntegers(newXYCoords, startIndex, endIndex, 1.0, false); 127 if (!isIntegerY && !areIntegers(newXYCoords, startIndex, endIndex, yCompFactor, false)) { 128 yCompFactor = (maxY - minY) / FACTOR_DIVISOR; 129 } 130 break; 131 } 132 int step = 1; 133 if (spectrum.isExportXAxisLeftToRight() != (spectrum.getFirstX() < spectrum.getLastX())) { 134 int t = startIndex; 135 startIndex = endIndex; 136 endIndex = t; 137 step = -1; 138 } 139 switch (type) { 140 case DIF: 141 case DIFDUP: 142 tabDataSet = JDXCompressor.compressDIF(newXYCoords, startIndex, endIndex, step, 143 xCompFactor, yCompFactor, type == ExportType.DIFDUP); 144 break; 145 case FIX: 146 tabDataSet = JDXCompressor.compressFIX(newXYCoords, startIndex, endIndex, step, 147 xCompFactor, yCompFactor); 148 break; 149 case PAC: 150 tabDataSet = JDXCompressor.compressPAC(newXYCoords, startIndex, endIndex, step, 151 xCompFactor, yCompFactor); 152 break; 153 case SQZ: 154 tabDataSet = JDXCompressor.compressSQZ(newXYCoords, startIndex, endIndex, step, 155 xCompFactor, yCompFactor); 156 break; 157 case XY: 158 tabDataSet = JDXCompressor.getXYList(newXYCoords, startIndex, endIndex, step); 159 break; 160 default: 161 break; 162 } 163 164 String varList = JDXReader.getVarList(tmpDataClass); 165 getHeaderString(tmpDataClass, minY, maxY, 166 xCompFactor, yCompFactor, startIndex, endIndex); 167 out.append("##" + tmpDataClass + "= " + varList + newLine); 168 out.append(tabDataSet); 169 out.append("##END="); 170 } 171 172 /** 173 * Returns the String for the header of the spectrum 174 * @param tmpDataClass 175 * the dataclass 176 * @param minY 177 * @param maxY 178 * @param tmpXFactor 179 * the x factor 180 * @param tmpYFactor 181 * the y factor 182 * @param startIndex 183 * the index of the starting coordinate 184 * @param endIndex 185 * the index of the ending coordinate 186 */ getHeaderString(String tmpDataClass, double minY, double maxY, double tmpXFactor, double tmpYFactor, int startIndex, int endIndex)187 private void getHeaderString(String tmpDataClass, 188 double minY, double maxY, 189 double tmpXFactor, double tmpYFactor, 190 int startIndex, int endIndex) { 191 192 //final String CORE_STR = "TITLE,ORIGIN,OWNER,DATE,TIME,DATATYPE,JCAMPDX"; 193 194 // start of header 195 out.append("##TITLE= ").append(spectrum.getTitle()).append( 196 newLine); 197 out.append("##JCAMP-DX= 5.01").append(newLine); /*+ getJcampdx()*/ 198 out.append("##DATA TYPE= ").append(spectrum.getDataType()).append( 199 newLine); 200 out.append("##DATA CLASS= ").append(tmpDataClass).append( 201 newLine); 202 out.append("##ORIGIN= ").append(spectrum.getOrigin()).append( 203 newLine); 204 out.append("##OWNER= ").append(spectrum.getOwner()).append( 205 newLine); 206 String d = spectrum.getDate(); 207 String longdate = ""; 208 String currentTime = vwr.apiPlatform.getDateFormat(null); 209 if (spectrum.getLongDate().equals("") || d.length() != 8) { 210 longdate = currentTime + " $$ export date from JSpecView"; 211 } else if (d.length() == 8) { // give a 50 year window; Y2K compliant 212 longdate = (d.charAt(0) < '5' ? "20" : "19") + d + " " + spectrum.getTime(); 213 } else { 214 longdate = spectrum.getLongDate(); 215 } 216 out.append("##LONGDATE= ").append(longdate).append(newLine); 217 218 // optional header 219 Lst<String[]> headerTable = spectrum.getHeaderTable(); 220 for (int i = 0; i < headerTable.size(); i++) { 221 String[] entry = headerTable.get(i); 222 String label = entry[0]; 223 String dataSet = entry[1]; 224 String nl = (dataSet.startsWith("<") && dataSet.contains("</") ? newLine 225 : ""); 226 out.append(label).append("= ").append(nl).append(dataSet).append( 227 newLine); 228 } 229 double observedFreq = spectrum.getObservedFreq(); 230 if (!spectrum.is1D()) 231 out.append("##NUM DIM= ").append("" + spectrum.getNumDim()).append( 232 newLine); 233 if (observedFreq != JDXDataObject.ERROR) 234 out.append("##.OBSERVE FREQUENCY= ").append("" + observedFreq).append( 235 newLine); 236 String nuc = spectrum.getObservedNucleus(); 237 if (!"".equals(nuc)) 238 out.append("##.OBSERVE NUCLEUS= ").append(nuc).append( 239 newLine); 240 //now need to put pathlength here 241 242 // last part of header 243 244 //boolean toHz = (observedFreq != JDXSpectrum.ERROR && !spec.getDataType() 245 // .toUpperCase().contains("FID")); 246 out.append("##XUNITS= ").append(spectrum.isHZtoPPM() ? "HZ" : spectrum.getXUnits()).append( 247 newLine); 248 out.append("##YUNITS= ").append(spectrum.getYUnits()).append( 249 newLine); 250 out.append("##XFACTOR= ").append(fixExponentInt(tmpXFactor)) 251 .append(newLine); 252 out.append("##YFACTOR= ").append(fixExponentInt(tmpYFactor)) 253 .append(newLine); 254 double f = (spectrum.isHZtoPPM() ? observedFreq : 1); 255 Coordinate[] xyCoords = spectrum.getXYCoords(); 256 out.append("##FIRSTX= ").append( 257 fixExponentInt(xyCoords[startIndex].getXVal() * f)).append( 258 newLine); 259 out.append("##FIRSTY= ").append( 260 fixExponentInt(xyCoords[startIndex].getYVal())).append( 261 newLine); 262 out.append("##LASTX= ").append( 263 fixExponentInt(xyCoords[endIndex].getXVal() * f)).append( 264 newLine); 265 out.append("##NPOINTS= ").append("" + (Math.abs(endIndex - startIndex) + 1)) 266 .append(newLine); 267 out.append("##MINY= ").append(fixExponentInt(minY)).append( 268 newLine); 269 out.append("##MAXY= ").append(fixExponentInt(maxY)).append( 270 newLine); 271 } 272 areIntegers(Coordinate[] xyCoords, int startIndex, int endIndex, double factor, boolean isX)273 private static boolean areIntegers(Coordinate[] xyCoords, int startIndex, 274 int endIndex, double factor, boolean isX) { 275 for (int i = startIndex; i <= endIndex; i++) { 276 double x = (isX ? xyCoords[i].getXVal() : xyCoords[i].getYVal()) / factor; 277 if (isAlmostInteger(x)) 278 return false; 279 } 280 return true; 281 } 282 isAlmostInteger(double x)283 private static boolean isAlmostInteger(double x) { 284 return (x != 0 && Math.abs(x - Math.floor(x)) / x > 1e-8); 285 } 286 fixExponentInt(double x)287 private static String fixExponentInt(double x) { 288 return (x == Math.floor(x) ? String.valueOf((int) x) : PT.rep(fixExponent(x), "E+00", "")); 289 } 290 291 /** 292 * JCAMP-DX requires 1.5E[+|-]nn or 1.5E[+|-]nnn only 293 * not Java's 1.5E3 or 1.5E-2 294 * 295 * @param x 296 * @return exponent fixed 297 */ fixExponent(double x)298 private static String fixExponent(double x) { 299 String s = DF.formatDecimalDbl(x, -7); // "0.000000" 300 int pt = s.indexOf("E"); 301 if (pt < 0) { 302 return s; 303 } 304 // 4.3E+3 305 // 4.3E-3 306 if (s.length() == pt + 3) 307 s = s.substring(0, pt + 2) + "0" + s.substring(pt + 2); 308 return s; 309 } 310 311 312 } 313