/** * Saves an image as a gif. Currently this uses ImageMagick's "convert" (Windows or Linux) because * it does the best job at color reduction (and is fast and is cross-platform). This will * overwrite an existing file. * * @param bi * @param fullGifName but without the .gif at the end * @throws Exception if trouble */ public static void saveAsGif(BufferedImage bi, String fullGifName) throws Exception { // POLICY: because this procedure may be used in more than one thread, // do work on unique temp files names using randomInt, then rename to proper file name. // If procedure fails half way through, there won't be a half-finished file. int randomInt = Math2.random(Integer.MAX_VALUE); // save as .bmp (note: doesn't support transparent pixels) long time = System.currentTimeMillis(); if (verbose) String2.log("SgtUtil.saveAsGif"); ImageIO.write(bi, "bmp", new File(fullGifName + randomInt + ".bmp")); if (verbose) String2.log(" make .bmp done. time=" + (System.currentTimeMillis() - time)); // "convert" to .gif SSR.dosOrCShell( "convert " + fullGifName + randomInt + ".bmp" + " " + fullGifName + randomInt + ".gif", 30); File2.delete(fullGifName + randomInt + ".bmp"); // try fancy color reduction algorithms // Image2.saveAsGif(Image2.reduceTo216Colors(bi), fullGifName + randomInt + ".gif"); // try dithering // Image2.saveAsGif216(bi, fullGifName + randomInt + ".gif", true); // last step: rename to final gif name File2.rename(fullGifName + randomInt + ".gif", fullGifName + ".gif"); if (verbose) String2.log( "SgtUtil.saveAsGif done. TOTAL TIME=" + (System.currentTimeMillis() - time) + "\n"); }
/** * This returns the maxBoldCharsPerLine based on charsPerLine. * * @param legendTextWidth in pixels * @param fontScale */ public static int maxCharsPerLine(int legendTextWidth, double fontScale) { // lessen the effect of small fonts (they stay wide to stay legible) if (fontScale < 1) fontScale = (1 + fontScale) / 2; int m = Math2.roundToInt(legendTextWidth / (SgtUtil.AVG_CHAR_WIDTH * fontScale)); // String2.log("\n***maxCharsPerLine=" + m + " legendWidth=" + legendTextWidth + " fontScale=" + // fontScale); return m; }
/** * Saves an image as a gif. Currently this uses ImageMagick's "convert" (Windows or Linux) because * it does the best job at color reduction (and is fast and is cross-platform). This will * overwrite an existing file. * * @param bi * @param transparent the color to be made transparent * @param fullGifName but without the .gif at the end * @throws Exception if trouble */ public static void saveAsTransparentGif(BufferedImage bi, Color transparent, String fullGifName) throws Exception { // POLICY: because this procedure may be used in more than one thread, // do work on unique temp files names using randomInt, then rename to proper file name. // If procedure fails half way through, there won't be a half-finished file. int randomInt = Math2.random(Integer.MAX_VALUE); // convert transparent color to be transparent long time = System.currentTimeMillis(); Image image = Image2.makeImageBackgroundTransparent(bi, transparent, 10000); // convert image back to bufferedImage bi = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics g = bi.getGraphics(); g.drawImage(image, 0, 0, bi.getWidth(), bi.getHeight(), null); image = null; // encourage garbage collection // save as png int random = Math2.random(Integer.MAX_VALUE); ImageIO.write(bi, "png", new File(fullGifName + randomInt + ".png")); // "convert" to .gif SSR.dosOrCShell( "convert " + fullGifName + randomInt + ".png" + " " + fullGifName + randomInt + ".gif", 30); File2.delete(fullGifName + randomInt + ".png"); // try fancy color reduction algorithms // Image2.saveAsGif(Image2.reduceTo216Colors(bi), fullGifName + randomInt + ".gif"); // try dithering // Image2.saveAsGif216(bi, fullGifName + randomInt + ".gif", true); // last step: rename to final gif name File2.rename(fullGifName + randomInt + ".gif", fullGifName + ".gif"); if (verbose) String2.log( "SgtUtil.saveAsTransparentGif TIME=" + (System.currentTimeMillis() - time) + "\n"); }
/** * The default palette (aka color bar) range ([0]=min, [1]=max). The values are also suitable for * the axis range on a graph. * * @param dataMin the raw minimum value of the data * @param dataMax the raw maximum value of the data * @return the default palette (aka color bar) range ([0]=min, [1]=max). */ public static double[] suggestPaletteRange(double dataMin, double dataMax) { double lowHigh[] = Math2.suggestLowHigh(dataMin, dataMax); // log axis? if (suggestPaletteScale(dataMin, dataMax) .equals("Log")) { // yes, use dataMin,dataMax, not lowHigh lowHigh[0] = Math2.suggestLowHigh(dataMin, 2 * dataMin)[0]; // trick to get nice suggested min>0 return lowHigh; } // axis is linear // suggest symmetric around 0 (symbolized by BlueWhiteRed)? if (suggestPalette(dataMin, dataMax) .equals("BlueWhiteRed")) { // yes, use dataMin,dataMax, not lowHigh double rangeMax = Math.max(-lowHigh[0], lowHigh[1]); lowHigh[0] = -rangeMax; lowHigh[1] = rangeMax; } // standard Rainbow Linear return lowHigh; }
/** * Saves an image as a png. This will overwrite an existing file. * * @param bi * @param transparent the color to be made transparent (or null if none) * @param fullPngName but without the .png at the end * @throws Exception if trouble */ public static void saveAsTransparentPng(BufferedImage bi, Color transparent, String fullPngName) throws Exception { // POLICY: because this procedure may be used in more than one thread, // do work on unique temp files names using randomInt, then rename to proper file name. // If procedure fails half way through, there won't be a half-finished file. int randomInt = Math2.random(Integer.MAX_VALUE); // create fileOutputStream BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fullPngName + randomInt + ".png")); // save the image saveAsTransparentPng(bi, transparent, bos); bos.close(); // last step: rename to final Png name File2.rename(fullPngName + randomInt + ".png", fullPngName + ".png"); }
for (int i = 0; i < n; i++) { int tRow = Math2.random(n); Test.ensureEqual(pt.readBinaryInt(3, tRow), tRow, ""); }
for (int i = 0; i < n; i++) { int tRow = Math2.random(n); Test.ensureEqual(pt.readDouble(2, tRow), tRow, ""); }
/** * This tests this class. * * @throws Throwable if trouble. */ public static void test() throws Throwable { String2.log("\nPersistentTable.test()"); verbose = true; reallyVerbose = true; int n; long time; // find longest FLOAT_LENGTH String s = ("" + Float.MIN_VALUE * -4f / 3f); int longest = s.length(); String longestS = s; for (int i = 0; i < 1000; i++) { s = "" + ((float) Math.random() / -1e10f); if (s.length() > longest) { longest = s.length(); longestS = s; } } String2.log("float longestS=" + longestS + " length=" + longest); Test.ensureTrue(longest <= 15, ""); // find longest DOUBLE_LENGTH s = ("" + Double.MIN_VALUE * -4.0 / 3.0); longest = s.length(); longestS = s; for (int i = 0; i < 1000; i++) { s = "" + (Math.random() / -1e150); if (s.length() > longest) { longest = s.length(); longestS = s; } } String2.log("double longestS=" + longestS + " length=" + longest); Test.ensureTrue(longest <= 24, ""); // make a new table String name = EDStatic.fullTestCacheDirectory + "testPersistentTable.txt"; File2.delete(name); int widths[] = { BOOLEAN_LENGTH, BYTE_LENGTH, BINARY_BYTE_LENGTH, BINARY_CHAR_LENGTH, SHORT_LENGTH, BINARY_SHORT_LENGTH, INT_LENGTH, BINARY_INT_LENGTH, LONG_LENGTH, BINARY_LONG_LENGTH, FLOAT_LENGTH, BINARY_FLOAT_LENGTH, DOUBLE_LENGTH, BINARY_DOUBLE_LENGTH, 20 }; PersistentTable pt = new PersistentTable(name, "rw", widths); Test.ensureEqual(pt.nRows(), 0, ""); Test.ensureEqual(pt.addRows(2), 2, ""); String testS = "Now is the time f\u0F22r all good countrymen to come ..."; pt.writeBoolean(0, 0, true); pt.writeBoolean(0, 1, false); pt.writeByte(1, 0, Byte.MIN_VALUE); pt.writeByte(1, 1, Byte.MAX_VALUE); pt.writeBinaryByte(2, 0, Byte.MIN_VALUE); pt.writeBinaryByte(2, 1, Byte.MAX_VALUE); pt.writeBinaryChar(3, 0, ' '); // hard because read will trim it to "" pt.writeBinaryChar(3, 1, '\u0F22'); pt.writeShort(4, 0, Short.MIN_VALUE); pt.writeShort(4, 1, Short.MAX_VALUE); pt.writeBinaryShort(5, 0, Short.MIN_VALUE); pt.writeBinaryShort(5, 1, Short.MAX_VALUE); pt.writeInt(6, 0, Integer.MIN_VALUE); pt.writeInt(6, 1, Integer.MAX_VALUE); pt.writeBinaryInt(7, 0, Integer.MIN_VALUE); pt.writeBinaryInt(7, 1, Integer.MAX_VALUE); pt.writeLong(8, 0, Long.MIN_VALUE); pt.writeLong(8, 1, Long.MAX_VALUE); pt.writeBinaryLong(9, 0, Long.MIN_VALUE); pt.writeBinaryLong(9, 1, Long.MAX_VALUE); pt.writeFloat(10, 0, -Float.MAX_VALUE); pt.writeFloat(10, 1, Float.NaN); pt.writeBinaryFloat(11, 0, -Float.MAX_VALUE); pt.writeBinaryFloat(11, 1, Float.NaN); pt.writeDouble(12, 0, -Double.MAX_VALUE); pt.writeDouble(12, 1, Double.NaN); pt.writeBinaryDouble(13, 0, -Double.MAX_VALUE); pt.writeBinaryDouble(13, 1, Double.NaN); pt.writeString(14, 0, ""); pt.writeString(14, 1, testS); Test.ensureEqual(pt.readBoolean(0, 0), true, ""); Test.ensureEqual(pt.readBoolean(0, 1), false, ""); Test.ensureEqual(pt.readByte(1, 0), Byte.MIN_VALUE, ""); Test.ensureEqual(pt.readByte(1, 1), Byte.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryByte(2, 0), Byte.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryByte(2, 1), Byte.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryChar(3, 0), ' ', ""); Test.ensureEqual(pt.readBinaryChar(3, 1), '\u0F22', ""); Test.ensureEqual(pt.readShort(4, 0), Short.MIN_VALUE, ""); Test.ensureEqual(pt.readShort(4, 1), Short.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryShort(5, 0), Short.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryShort(5, 1), Short.MAX_VALUE, ""); Test.ensureEqual(pt.readInt(6, 0), Integer.MIN_VALUE, ""); Test.ensureEqual(pt.readInt(6, 1), Integer.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryInt(7, 0), Integer.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryInt(7, 1), Integer.MAX_VALUE, ""); Test.ensureEqual(pt.readLong(8, 0), Long.MIN_VALUE, ""); Test.ensureEqual(pt.readLong(8, 1), Long.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryLong(9, 0), Long.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryLong(9, 1), Long.MAX_VALUE, ""); Test.ensureEqual(pt.readFloat(10, 0), -Float.MAX_VALUE, ""); Test.ensureEqual(pt.readFloat(10, 1), Float.NaN, ""); Test.ensureEqual(pt.readBinaryFloat(11, 0), -Float.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryFloat(11, 1), Float.NaN, ""); Test.ensureEqual(pt.readDouble(12, 0), -Double.MAX_VALUE, ""); Test.ensureEqual(pt.readDouble(12, 1), Double.NaN, ""); Test.ensureEqual(pt.readBinaryDouble(13, 0), -Double.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryDouble(13, 1), Double.NaN, ""); Test.ensureEqual(pt.readString(14, 0), "", ""); // only 18 char returned because one takes 3 bytes in UTF-8 Test.ensureEqual(pt.readString(14, 1), testS.substring(0, 18), ""); pt.close(); // reopen the file data still there? pt = new PersistentTable(name, "rw", widths); Test.ensureEqual(pt.nRows(), 2, ""); Test.ensureEqual(pt.readBoolean(0, 0), true, ""); Test.ensureEqual(pt.readBoolean(0, 1), false, ""); Test.ensureEqual(pt.readByte(1, 0), Byte.MIN_VALUE, ""); Test.ensureEqual(pt.readByte(1, 1), Byte.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryByte(2, 0), Byte.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryByte(2, 1), Byte.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryChar(3, 0), ' ', ""); Test.ensureEqual(pt.readBinaryChar(3, 1), '\u0F22', ""); Test.ensureEqual(pt.readShort(4, 0), Short.MIN_VALUE, ""); Test.ensureEqual(pt.readShort(4, 1), Short.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryShort(5, 0), Short.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryShort(5, 1), Short.MAX_VALUE, ""); Test.ensureEqual(pt.readInt(6, 0), Integer.MIN_VALUE, ""); Test.ensureEqual(pt.readInt(6, 1), Integer.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryInt(7, 0), Integer.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryInt(7, 1), Integer.MAX_VALUE, ""); Test.ensureEqual(pt.readLong(8, 0), Long.MIN_VALUE, ""); Test.ensureEqual(pt.readLong(8, 1), Long.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryLong(9, 0), Long.MIN_VALUE, ""); Test.ensureEqual(pt.readBinaryLong(9, 1), Long.MAX_VALUE, ""); Test.ensureEqual(pt.readFloat(10, 0), -Float.MAX_VALUE, ""); Test.ensureEqual(pt.readFloat(10, 1), Float.NaN, ""); Test.ensureEqual(pt.readBinaryFloat(11, 0), -Float.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryFloat(11, 1), Float.NaN, ""); Test.ensureEqual(pt.readDouble(12, 0), -Double.MAX_VALUE, ""); Test.ensureEqual(pt.readDouble(12, 1), Double.NaN, ""); Test.ensureEqual(pt.readBinaryDouble(13, 0), -Double.MAX_VALUE, ""); Test.ensureEqual(pt.readBinaryDouble(13, 1), Double.NaN, ""); Test.ensureEqual(pt.readString(14, 0), "", ""); // only 18 char returned because one takes 3 bytes in UTF-8 Test.ensureEqual(pt.readString(14, 1), testS.substring(0, 18), ""); pt.close(); String modes[] = {"rw", "rw", "rws", "rwd"}; n = 1000; for (int mode = 0; mode < modes.length; mode++) { File2.delete(name); pt = new PersistentTable( name, modes[mode], new int[] {80, BINARY_DOUBLE_LENGTH, DOUBLE_LENGTH, BINARY_INT_LENGTH, INT_LENGTH}); pt.addRows(n); if (mode == 1) String2.log("*** Note: 2nd rw test uses flush()"); // string speed test time = System.currentTimeMillis(); long modeTime = System.currentTimeMillis(); for (int i = 0; i < n; i++) pt.writeString(0, i, testS + i); if (mode == 1) pt.flush(); time = System.currentTimeMillis(); String2.log( "\n" + modes[mode] + " time to write " + n + " Strings=" + (System.currentTimeMillis() - time) + " (" + new int[] {0, 0, 0, 0}[mode] + "ms)"); // java 1.6 0,0,0,0 for (int i = 0; i < n; i++) { int tRow = Math2.random(n); Test.ensureEqual(pt.readString(0, tRow), testS + tRow, ""); } String2.log( modes[mode] + " time to read " + n + " Strings=" + (System.currentTimeMillis() - time) + " (" + new int[] {15, 16, 47, 15}[mode]
/** This reads a short from the file (or Short.MAX_VALUE if trouble). */ public short readShort(int col, int row) throws IOException { return Math2.narrowToShort(String2.parseInt(readString(col, row))); }
/** This reads a byte from the file (or Byte.MAX_VALUE if trouble). */ public byte readByte(int col, int row) throws IOException { return Math2.narrowToByte(String2.parseInt(readString(col, row))); }
/** * This creates the POST HTML form with the EmaAttributes. * * @param request is a request from a user * @param displayErrorMessage if false (for example, the first time the user sees the page), error * messages aren't displayed * @return the HTML code for the form. The line separator is the newline character. */ public String getHTMLForm(HttpServletRequest request, boolean displayErrorMessages) { long startTime = System.currentTimeMillis(); HttpSession session = request.getSession(); if (verbose) String2.log("\ngetHTMLForm session isNew=" + session.isNew() + " id=" + session.getId()); nRequestsInitiated++; int rowNumber = 0; StringBuilder sb = new StringBuilder(); sb.append(getStartOfHTMLForm()); displayErrorMessages = false; // always // create the rows of the table with the attributes // title sb.append( " <tr>\n" + " <td colspan=\"2\">" + title + "</td>\n" + " <td rowspan=\"6\"><img border=\"0\" src=\"" + regionsImage + "\"\n" + " width=\"228\" height=\"208\"\n" + " alt=\"" + regionsImageAlt + "\" title=\"" + regionsImageTitle + "\"\n" + " usemap=#regionCoordinates>\n" + " <br><center><small>" + regionsImageTitle + "</small></center></td>\n" + " </tr>\n"); // dataset String dataSetValue = dataSet.getValue(session); int whichDataSet = String2.indexOf(activeDataSetOptions, dataSetValue); if (whichDataSet < 0) { whichDataSet = 0; dataSetValue = activeDataSetOptions[0]; dataSet.setValue(session, dataSetValue); } dataSetRequests[String2.indexOf(dataSetOptions, dataSetValue)]++; if (verbose) String2.log("dataSetValue = " + dataSetValue); setBeginRow(beginRowArray[Math2.odd(rowNumber++) ? 1 : 0]); sb.append(dataSet.getTableEntry(dataSetValue, displayErrorMessages)); // timePeriod Object[] object = (Object[]) activeDataSetContents.get(whichDataSet); String[] activeTimePeriodOptions = (String[]) object[0]; String[] activeTimePeriodTitles = (String[]) object[1]; Vector activeTimePeriodContents = (Vector) object[2]; String dataSetDirectory = (String) object[3]; if (verbose) String2.log("dataSetDirectory = " + dataSetDirectory); timePeriod.setOptions(activeTimePeriodOptions); timePeriod.setTitles(activeTimePeriodTitles); String timePeriodValue = timePeriod.getValue(session); int whichTimePeriod = String2.indexOf(activeTimePeriodOptions, timePeriodValue); if (whichTimePeriod < 0) { whichTimePeriod = 0; timePeriodValue = activeTimePeriodOptions[0]; timePeriod.setValue(session, timePeriodValue); } timePeriodRequests[String2.indexOf(timePeriodOptions, timePeriodValue)]++; if (verbose) String2.log("timePeriodValue = " + timePeriodValue); setBeginRow(beginRowArray[Math2.odd(rowNumber++) ? 1 : 0]); sb.append(timePeriod.getTableEntry(timePeriodValue, displayErrorMessages)); // region object = (Object[]) activeTimePeriodContents.get(whichTimePeriod); String[] activeRegionOptions = (String[]) object[0]; String[] activeRegionTitles = (String[]) object[1]; String[] activeRegionCoordinates = (String[]) object[2]; Vector activeRegionContents = (Vector) object[3]; String timePeriodDirectory = (String) object[4]; region.setOptions(activeRegionOptions); region.setTitles(activeRegionTitles); String regionValue = region.getValue(session); int whichRegion = String2.indexOf(activeRegionOptions, regionValue); if (whichRegion < 0) { whichRegion = 0; regionValue = activeRegionOptions[0]; region.setValue(session, regionValue); } regionRequests[String2.indexOf(regionOptions, regionValue)]++; if (verbose) String2.log("regionValue = " + regionValue); setBeginRow(beginRowArray[Math2.odd(rowNumber++) ? 1 : 0]); sb.append(region.getTableEntry(regionValue, displayErrorMessages)); // define regionCoordinates for regionImage after activeRegionCoordinates known // do in reverse order, so small regions detected before large regions // (first match found is used) sb.append(" <map name=\"regionCoordinates\">\n"); for (int i = activeRegionCoordinates.length - 1; i >= 0; i--) // for (int i = 0; i < activeRegionCoordinates.length; i++) sb.append( " <area shape=\"rect\" coords=\"" + activeRegionCoordinates[i] + "\"\n" + " title=\"" + activeRegionTitles[i + 1] + "\"\n" + // +1 since 0 is main title " href=\"#\" " + // was href=\"javascript: "onClick=\"" + "document.forms[0].region[" + i + "].checked=true; document.forms[0].submit();\">\n"); sb.append(" </map>\n"); // formSubmitted sb.append(formSubmitted.getControl("true")); // date object = (Object[]) activeRegionContents.get(whichRegion); String[] gifs = (String[]) object[0]; String[] activeTimeOptions = (String[]) object[1]; int[] getBits = (int[]) object[2]; // if (verbose) // String2.log("activeTimeOptions = " + String2.toCSSVString(activeTimeOptions)); date.setOptions(activeTimeOptions); String timeValue = date.getValue(session); // find exact date match or one past (activeTimeOptions are sorted) // date will be off first time, and if user changes above settings and same date not available int whichDate = activeTimeOptions.length - 1; // last one if (timeValue != null) { for (int i = 0; i < activeTimeOptions.length; i++) { if (timeValue.compareTo(activeTimeOptions[i]) <= 0) { whichDate = i; break; } } } timeValue = activeTimeOptions[whichDate]; date.setValue(session, timeValue); if (verbose) String2.log("timeValue = " + timeValue); setBeginRow(beginRowArray[Math2.odd(rowNumber++) ? 1 : 0]); sb.append(date.getTableEntry(timeValue, displayErrorMessages)); // submitForm // may or may not be visible; so always a unique color (light red) setBeginRow("<tr bgcolor=\"#FFCCCC\">"); sb.append( " <noscript>\n" + submitForm.getTableEntry(submitForm.getValue(session), displayErrorMessages)); sb.append(" </noscript>\n"); // get String gifName = gifs[whichDate]; int bits = getBits[whichDate]; String currentFileDir = dataServer + dataSetDirectory + "/" + // for example, "QS" timePeriodDirectory + "/"; // for example, "1day" setBeginRow(beginRowArray[Math2.odd(rowNumber++) ? 1 : 0]); sb.append( " " + getBeginRow() + "\n" + " <td>" + classRB2.getString("get.label", "") + " </td>\n" + " <td>"); int nGetOptions = getOptions.length; for (int getI = 0; getI < nGetOptions; getI++) if ((bits & Math2.Two[getI]) != 0) sb.append( "<a href=\"" + currentFileDir + getDirectories[getI] + "/" + gifName + getExtensions[getI] + "\"\n title=\"" + getTitles[getI + 1] + "\">" + // +1: title 0 is main title getOptions[getI] + "</a>\n "); sb.append( // "<br><small>" + hereIs + "</small>\n" + " </td>\n" + " </tr>\n"); // image String currentGifName = currentFileDir + getDirectories[0] + "/" + gifName + getExtensions[0]; // "QN2005001_2005001_curl_westus.gif"; sb.append( // " <tr><td> </td></tr>\n" + //row 6 // " <tr><td colspan=\"2\">" + hereIs + "</td></tr>\n" + //row 7 " <tr>\n" + // standard " <td colspan=\"3\"><img border=\"0\" src=\"" + currentGifName + "\"\n" + // row 8 " width=\"650\" height=\"502\"\n" + " title=\"" + hereIs + " " + currentGifName + "\"\n" + " alt=\"" + hereIsAlt + " " + currentGifName + "\">\n" + " </td>\n" + " </tr>\n"); // update requestedFilesMap Integer I = (Integer) requestedFilesMap.get(gifName); requestedFilesMap.put(gifName, new Integer(I == null ? 1 : I.intValue() + 1)); // end of table, end of form sb.append(getEndOfHTMLForm(startTime, "")); nRequestsCompleted++; sb.append( "<p>DISCLAIMER OF ENDORSEMENT\n" + "<br>Any reference obtained from this server to a specific commercial product,\n" + "process, or service does not constitute or imply an endorsement by CoastWatch,\n" + "NOAA, or the United States Government of the product, process, or service, or \n" + "its producer or provider. The views and opinions expressed in any referenced \n" + "document do not necessarily state or reflect those of CoastWatch,\n" + "NOAA, or the United States Government.\n" + "\n" + "<p>DISCLAIMER FOR EXTERNAL LINKS\n" + "<br>The appearance of external links on this World Wide Web site does not\n" + "constitute endorsement by the\n" + "<a href=\"http://www.doc.gov\">Department of Commerce</a>/<a href=\"http://www.noaa.gov\">National\n" + "Oceanic and Atmospheric Administration</a> of external Web sites or the information,\n" + "products or services contained\n" + "therein. For other than authorized activities the Department of Commerce/NOAA does not\n" + "exercise any editorial control over the information you may find at these locations. These\n" + "links are provided consistent with the stated purpose of this Department of Commerce/NOAA\n" + "Web site.\n" + "\n" + "<p>DISCLAIMER OF LIABILITY\n" + "<br>Neither the data Contributors, CoastWatch, NOAA, nor the United States Government, \n" + "nor any of their employees or contractors, makes any warranty, express or implied, \n" + "including warranties of merchantability and fitness for a particular purpose, \n" + "or assumes any legal liability for the accuracy, completeness, or usefulness,\n" + "of any information at this site.\n" + "\n" + "<p><font size=\"-1\">Please email questions, comments, or\n" + "suggestions regarding this web page to\n" + "<A HREF=\"mailto:[email protected]\">[email protected]</A>.</font>"); return sb.toString(); }