0001: package it.geosolutions.utils.imagemosaic;
0002:
0003: import it.geosolutions.utils.progress.ExceptionEvent;
0004: import it.geosolutions.utils.progress.ProcessingEvent;
0005: import it.geosolutions.utils.progress.ProcessingEventListener;
0006: import it.geosolutions.utils.progress.ProgressManager;
0007:
0008: import java.awt.Rectangle;
0009: import java.awt.geom.Rectangle2D;
0010: import java.awt.image.ColorModel;
0011: import java.awt.image.ComponentColorModel;
0012: import java.awt.image.IndexColorModel;
0013: import java.awt.image.SampleModel;
0014: import java.io.BufferedOutputStream;
0015: import java.io.File;
0016: import java.io.FileFilter;
0017: import java.io.FileNotFoundException;
0018: import java.io.FileOutputStream;
0019: import java.io.IOException;
0020: import java.net.MalformedURLException;
0021: import java.util.ArrayList;
0022: import java.util.Arrays;
0023: import java.util.HashSet;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.Properties;
0027: import java.util.Set;
0028: import java.util.logging.FileHandler;
0029: import java.util.logging.Level;
0030: import java.util.logging.Logger;
0031:
0032: import javax.imageio.ImageIO;
0033: import javax.imageio.ImageReader;
0034: import javax.imageio.ImageTypeSpecifier;
0035: import javax.imageio.stream.ImageInputStream;
0036:
0037: import org.apache.commons.cli2.option.DefaultOption;
0038: import org.apache.commons.cli2.option.GroupImpl;
0039: import org.apache.commons.cli2.util.HelpFormatter;
0040: import org.apache.commons.io.filefilter.DirectoryFileFilter;
0041: import org.apache.commons.io.filefilter.WildcardFilter;
0042: import org.geotools.coverage.grid.GridGeometry2D;
0043: import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
0044: import org.geotools.coverage.grid.io.AbstractGridFormat;
0045: import org.geotools.coverage.grid.io.GridFormatFinder;
0046: import org.geotools.data.DataSourceException;
0047: import org.geotools.data.DefaultTransaction;
0048: import org.geotools.data.FeatureWriter;
0049: import org.geotools.data.Transaction;
0050: import org.geotools.data.shapefile.ShapefileDataStore;
0051: import org.geotools.feature.Feature;
0052: import org.geotools.feature.FeatureType;
0053: import org.geotools.feature.FeatureTypeBuilder;
0054: import org.geotools.feature.IllegalAttributeException;
0055: import org.geotools.feature.SchemaException;
0056: import org.geotools.feature.type.GeometricAttributeType;
0057: import org.geotools.feature.type.TextualAttributeType;
0058: import org.geotools.geometry.GeneralEnvelope;
0059: import org.geotools.geometry.jts.ReferencedEnvelope;
0060: import org.geotools.referencing.CRS;
0061: import org.geotools.resources.CRSUtilities;
0062: import org.opengis.geometry.Envelope;
0063: import org.opengis.referencing.FactoryException;
0064: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0065: import org.opengis.referencing.operation.MathTransform;
0066: import org.opengis.referencing.operation.TransformException;
0067:
0068: import com.vividsolutions.jts.geom.GeometryFactory;
0069: import com.vividsolutions.jts.geom.Polygon;
0070: import com.vividsolutions.jts.geom.PrecisionModel;
0071:
0072: /**
0073: * This classis in charge for creating the index for a mosaic of images that we
0074: * want to tie together as a sigle bg coverage.
0075: *
0076: * <p>
0077: * To get instructions on how to run the toll just run it without any arguments
0078: * and nice and clean help will be printed to the command line.
0079: *
0080: *
0081: * <p>
0082: * It is worth to point out that this tool comes as a command line tool but it
0083: * has been built with in mind a GUI. It has the capapbility to register
0084: * {@link ProcessingEventListener} object that receive notifications about what
0085: * is going on. Moreover it delegates all the computations to an external
0086: * thread, hence we can stop the tool in the middle of processig with no so many
0087: * concerns (hopefully :-) ).
0088: * <p>
0089: *
0090: * <p>
0091: *
0092: * @author Simone Giannecchini
0093: * @author Alessio Fabiani
0094: * @author Blaz Repnik
0095: * @version 0.3
0096: *
0097: */
0098: public class MosaicIndexBuilder extends ProgressManager implements
0099: Runnable, ProcessingEventListener {
0100:
0101: /**
0102: * Number of resolution levels for the coverages.
0103: */
0104: private int numberOfLevels;
0105:
0106: /**
0107: * Resolutions levels.
0108: */
0109: private double[][] resolutionLevels;
0110:
0111: /** Number of files to process. */
0112: private int numFiles;
0113:
0114: /** Default Logger * */
0115: private final static Logger LOG = org.geotools.util.logging.Logging
0116: .getLogger("it.geosolutions.utils.imagemosaic");
0117:
0118: /** Program Version */
0119: private final static String versionNumber = "0.3";
0120:
0121: private static final double EPS = 1E-2;
0122:
0123: private final DefaultOption locationOpt;
0124:
0125: private String locationPath;
0126:
0127: private final DefaultOption wildcardOpt;
0128:
0129: private String wildcardString = "*.*";
0130:
0131: private DefaultOption nameOpt;
0132:
0133: /**
0134: * Index file name. Default is index.
0135: */
0136: private String indexName = "index";
0137:
0138: /**
0139: * This field will tell the plugin if it must do a conversion of color from
0140: * the original index color model to an RGB color model. This happens f the
0141: * original images uses different color maps between each other making for
0142: * us impossible to reuse it for the mosaic.
0143: */
0144: private boolean mustConvertToRGB = false;
0145:
0146: private ColorModel actualCM = null;
0147:
0148: private ColorModel defaultCM = null;
0149:
0150: private SampleModel defaultSM = null;
0151:
0152: private SampleModel actualSM = null;
0153:
0154: private GeneralEnvelope globEnvelope = null;
0155:
0156: private GeneralEnvelope envelope = null;
0157:
0158: private byte[][] defaultPalette = null;
0159:
0160: private CoordinateReferenceSystem defaultCRS = null;
0161:
0162: private CoordinateReferenceSystem actualCRS = null;
0163:
0164: /**
0165: * Recurses the directory tree and returns valid files.
0166: */
0167: private void recurse(List allFiles, String locationPath) {
0168: File dir = new File(locationPath);
0169: FileFilter fileFilter = new WildcardFilter(wildcardString);
0170: File[] files = dir.listFiles(fileFilter);
0171: File[] dirs = dir
0172: .listFiles((FileFilter) DirectoryFileFilter.INSTANCE);
0173: for (int i = 0; i < files.length; i++) {
0174: allFiles.add(files[i]);
0175: }
0176: for (int i = 0; i < dirs.length; i++) {
0177: recurse(allFiles, locationPath + '/' + dirs[i].getName());
0178: }
0179: }
0180:
0181: /**
0182: * Main thread for the mosaic index builder.
0183: */
0184: public void run() {
0185:
0186: // /////////////////////////////////////////////////////////////////////
0187: //
0188: // CREATING INDEX FILE
0189: //
0190: // /////////////////////////////////////////////////////////////////////
0191:
0192: // /////////////////////////////////////////////////////////////////////
0193: //
0194: // Create a file handler that write log record to a file called
0195: // my.log
0196: //
0197: // /////////////////////////////////////////////////////////////////////
0198: FileHandler handler;
0199: try {
0200: boolean append = true;
0201: handler = new FileHandler(new StringBuffer(locationPath)
0202: .append("/error.txt").toString(), append);
0203: handler.setLevel(Level.SEVERE);
0204: // Add to the desired logger
0205: LOG.addHandler(handler);
0206: } catch (SecurityException el) {
0207: fireException(el);
0208: return;
0209: } catch (IOException el) {
0210: fireException(el);
0211: return;
0212: }
0213:
0214: // /////////////////////////////////////////////////////////////////////
0215: //
0216: // Create a set of file names that have to be skipped since these are
0217: // our metadata files
0218: //
0219: // /////////////////////////////////////////////////////////////////////
0220: final Set skipFiles = new HashSet(Arrays.asList(new String[] {
0221: indexName + ".shp", indexName + ".dbf",
0222: indexName + ".shx", indexName + ".prj", "error.txt",
0223: "error.txt.lck", indexName + ".properties" }));
0224:
0225: // /////////////////////////////////////////////////////////////////////
0226: //
0227: // Creating temp vars
0228: //
0229: // /////////////////////////////////////////////////////////////////////
0230: ShapefileDataStore index = null;
0231: Transaction t = new DefaultTransaction();
0232: // declaring a preciosion model to adhere the java double type
0233: // precision
0234: PrecisionModel precMod = new PrecisionModel(
0235: PrecisionModel.FLOATING);
0236: GeometryFactory geomFactory = new GeometryFactory(precMod);
0237: try {
0238: index = new ShapefileDataStore(new File(locationPath + "/"
0239: + indexName + ".shp").toURL());
0240: } catch (MalformedURLException ex) {
0241: if (LOG.isLoggable(Level.SEVERE))
0242: LOG.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
0243: fireException(ex);
0244: return;
0245: }
0246:
0247: // /** Fixed local variables * */
0248: // AbstractGridCoverage2DReader reader;
0249: // ImageInputStream inStream;
0250: // ImageTypeSpecifier its;
0251: // Iterator it;
0252: // ImageReader r;
0253: // double[] res;
0254: // boolean skipFeature = false;
0255: // double resX = 0, resY = 0;
0256: // boolean doneSomething = false;
0257:
0258: // final File dir = new File(locationPath);
0259: final List files = new ArrayList(25);
0260: recurse(files, locationPath);
0261:
0262: // /////////////////////////////////////////////////////////////////////
0263: //
0264: // Cycling over the features
0265: //
0266: // /////////////////////////////////////////////////////////////////////
0267: numFiles = files.size();
0268: String validFileName = null;
0269: final Iterator filesIt = files.iterator();
0270: FeatureWriter fw = null;
0271: boolean doneSomething = false;
0272: for (int i = 0; i < numFiles; i++) {
0273: final File fileBeingProcessed = ((File) filesIt.next());
0274: StringBuffer message;
0275: // //
0276: //
0277: // Anyone has asked us to stop?
0278: //
0279: // //
0280: if (getStopThread()) {
0281: message = new StringBuffer(
0282: "Stopping requested at file ").append(i)
0283: .append(" of ").append(numFiles).append(
0284: " files");
0285: if (LOG.isLoggable(Level.FINE)) {
0286: LOG.fine(message.toString());
0287: }
0288: fireEvent(message.toString(), ((i * 100.0) / numFiles));
0289: return;
0290: } // replacing chars on input path
0291: try {
0292: validFileName = fileBeingProcessed.getCanonicalPath();
0293: } catch (IOException e1) {
0294: fireException(e1);
0295: return;
0296: }
0297: validFileName = validFileName.replace('\\', '/');
0298: validFileName = validFileName.substring(locationPath
0299: .length() + 1, fileBeingProcessed.getAbsolutePath()
0300: .length());
0301: if (skipFiles.contains(validFileName))
0302: continue;
0303: message = new StringBuffer("Now indexing file ")
0304: .append(validFileName);
0305:
0306: if (LOG.isLoggable(Level.FINE)) {
0307: LOG.fine(message.toString());
0308: }
0309: fireEvent(message.toString(), ((i * 100.0) / numFiles));
0310: try {
0311: // ////////////////////////////////////////////////////////
0312: //
0313: //
0314: // STEP 1
0315: // Getting an ImageIO reader for this coverage.
0316: //
0317: //
0318: // ////////////////////////////////////////////////////////
0319: final ImageInputStream inStream = ImageIO
0320: .createImageInputStream(fileBeingProcessed);
0321: inStream.mark();
0322: final Iterator it = ImageIO.getImageReaders(inStream);
0323:
0324: ImageReader r = null;
0325: if (it.hasNext()) {
0326: r = (ImageReader) it.next();
0327: r.setInput(inStream);
0328: } else {
0329: //release resources
0330: try {
0331: inStream.close();
0332: } catch (Exception e) {
0333: // ignore exception
0334: }
0335: try {
0336: r.dispose();
0337: } catch (Exception e) {
0338: // ignore exception
0339: }
0340:
0341: //send a message
0342: message = new StringBuffer("Skipped file ").append(
0343: files.get(i)).append(
0344: ":No ImageIO readeres avalaible.");
0345: if (LOG.isLoggable(Level.INFO))
0346: LOG.info(message.toString());
0347: fireEvent(message.toString(),
0348: ((i * 99.0) / numFiles));
0349: continue;
0350: }
0351:
0352: // ////////////////////////////////////////////////////////
0353: //
0354: //
0355: // STEP 2
0356: // Getting a coverage reader for this coverage. Right now we can
0357: // index
0358: // Geotiff or WorldImage
0359: //
0360: //
0361: // ////////////////////////////////////////////////////////
0362: if (LOG.isLoggable(Level.FINE))
0363: LOG.fine(new StringBuffer("Getting a reader")
0364: .toString());
0365: final AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder
0366: .findFormat(files.get(i));
0367: if (format == null) {
0368: //release resources
0369: try {
0370: inStream.close();
0371: } catch (Exception e) {
0372: // ignore exception
0373: }
0374: try {
0375: r.dispose();
0376: } catch (Exception e) {
0377: // ignore exception
0378: }
0379:
0380: message = new StringBuffer("Skipped file ").append(
0381: files.get(i)).append(
0382: ": File format is not supported.");
0383: if (LOG.isLoggable(Level.INFO))
0384: LOG.info(message.toString());
0385: fireEvent(message.toString(),
0386: ((i * 99.0) / numFiles));
0387: continue;
0388: }
0389: final AbstractGridCoverage2DReader reader = (AbstractGridCoverage2DReader) format
0390: .getReader(files.get(i));
0391: envelope = (GeneralEnvelope) reader
0392: .getOriginalEnvelope();
0393: actualCRS = reader.getCrs();
0394:
0395: // /////////////////////////////////////////////////////////////////////
0396: //
0397: // STEP 3
0398: // Get the type specifier for this image and the check that the
0399: // image has the correct sample model and color model.
0400: // If this is the first cycle of the loop we initialize
0401: // eveything.
0402: //
0403: // /////////////////////////////////////////////////////////////////////
0404: final ImageTypeSpecifier its = ((ImageTypeSpecifier) r
0405: .getImageTypes(0).next());
0406: boolean skipFeature = false;
0407: if (globEnvelope == null) {
0408: // /////////////////////////////////////////////////////////////////////
0409: //
0410: // at the first step we initialize everything that we will
0411: // reuse afterwards starting with color models, sample
0412: // models, crs, etc....
0413: //
0414: // /////////////////////////////////////////////////////////////////////
0415: defaultCM = its.getColorModel();
0416: if (defaultCM instanceof IndexColorModel) {
0417: IndexColorModel icm = (IndexColorModel) defaultCM;
0418: int numBands = defaultCM
0419: .getNumColorComponents();
0420: defaultPalette = new byte[3][icm.getMapSize()];
0421: icm.getReds(defaultPalette[0]);
0422: icm.getGreens(defaultPalette[0]);
0423: icm.getBlues(defaultPalette[0]);
0424: if (numBands == 4)
0425: icm.getAlphas(defaultPalette[0]);
0426:
0427: }
0428: defaultSM = its.getSampleModel();
0429: defaultCRS = actualCRS;
0430: globEnvelope = new GeneralEnvelope(envelope);
0431: // /////////////////////////////////////////////////////////////////////
0432: //
0433: // getting information about resolution
0434: //
0435: // /////////////////////////////////////////////////////////////////////
0436:
0437: // //
0438: //
0439: // get the dimension of the hr image and build the model
0440: // as well as
0441: // computing the resolution
0442: // //
0443: // resetting reader and recreating stream, turnarounf for a
0444: // strange imageio bug
0445: r.reset();
0446: inStream.reset();
0447: r.setInput(inStream);
0448: numberOfLevels = r.getNumImages(true);
0449: resolutionLevels = new double[2][numberOfLevels];
0450: double[] res = getResolution(
0451: envelope,
0452: new Rectangle(r.getWidth(0), r.getHeight(0)),
0453: defaultCRS);
0454: resolutionLevels[0][0] = res[0];
0455: resolutionLevels[1][0] = res[1];
0456:
0457: // resolutions levels
0458: if (numberOfLevels > 1) {
0459:
0460: for (int k = 0; k < numberOfLevels; k++) {
0461: res = getResolution(envelope,
0462: new Rectangle(r.getWidth(k), r
0463: .getHeight(k)), defaultCRS);
0464: resolutionLevels[0][k] = res[0];
0465: resolutionLevels[1][k] = res[1];
0466: }
0467: }
0468:
0469: // /////////////////////////////////////////////////////////////////////
0470: //
0471: // creating the schema
0472: //
0473: // /////////////////////////////////////////////////////////////////////
0474: final GeometricAttributeType refGeom = new GeometricAttributeType(
0475: "the_geom", Polygon.class, true, null,
0476: defaultCRS, null);
0477: final TextualAttributeType locationAttribute = new TextualAttributeType(
0478: "location", true, 1, 1, "none", null);
0479: final FeatureTypeBuilder builder = FeatureTypeBuilder
0480: .newInstance("index");
0481: builder.setDefaultGeometry(refGeom);
0482: builder.addType(locationAttribute);
0483:
0484: final FeatureType ftType;
0485: try {
0486: ftType = builder.getFeatureType();
0487: } catch (SchemaException e) {
0488: if (LOG.isLoggable(Level.SEVERE))
0489: LOG.log(Level.SEVERE, e
0490: .getLocalizedMessage(), e);
0491: fireEvent(e.getLocalizedMessage(), 0);
0492: return;
0493: }
0494: // create the schema for the new shape file
0495: index.createSchema(ftType);
0496:
0497: // get a feature writer
0498: fw = index.getFeatureWriter(
0499: index.getTypeNames()[0], t);
0500: } else {
0501: // ////////////////////////////////////////////////////////
0502: //
0503: // comparing ColorModel
0504: // comparing SampeModel
0505: // comparing CRSs
0506: // ////////////////////////////////////////////////////////
0507: globEnvelope.add(envelope);
0508: actualCM = its.getColorModel();
0509: actualSM = its.getSampleModel();
0510: skipFeature = (i > 0 ? !(CRS.equalsIgnoreMetadata(
0511: defaultCRS, actualCRS)) : false);
0512: if (skipFeature)
0513: LOG.warning(new StringBuffer("Skipping image ")
0514: .append(files.get(i)).append(
0515: " because CRSs do not match.")
0516: .toString());
0517: skipFeature = checkColorModels(defaultCM,
0518: defaultPalette, actualCM);
0519: if (skipFeature)
0520: LOG
0521: .warning(new StringBuffer(
0522: "Skipping image ")
0523: .append(files.get(i))
0524: .append(
0525: " because color models do not match.")
0526: .toString());
0527: // defaultCM.getNumComponents()==actualCM.getNumComponents()&&
0528: // defaultCM.getClass().equals(actualCM.getClass())
0529: // && defaultSM.getNumBands() == actualSM
0530: // .getNumBands()
0531: // && defaultSM.getDataType() == actualSM
0532: // .getDataType() &&
0533: //
0534: // if (skipFeature)
0535: // LOG
0536: // .warning(new StringBuffer("Skipping image ")
0537: // .append(files.get(i))
0538: // .append(
0539: // " because cm or sm does not match.")
0540: // .toString());
0541: // res = getResolution(envelope, new
0542: // Rectangle(r.getWidth(0),
0543: // r.getHeight(0)), defaultCRS);
0544: // if (Math.abs((resX - res[0]) / resX) > EPS
0545: // || Math.abs(resY - res[1]) > EPS) {
0546: // LOG.warning(new StringBuffer("Skipping image ").append(
0547: // files.get(i)).append(
0548: // " because resolutions does not match.")
0549: // .toString());
0550: // skipFeature = true;
0551: // }
0552: }
0553:
0554: // ////////////////////////////////////////////////////////
0555: //
0556: // STEP 4
0557: //
0558: // create and store features
0559: //
0560: // ////////////////////////////////////////////////////////
0561: if (!skipFeature) {
0562:
0563: final Feature feature = fw.next();
0564: feature.setAttribute(0, geomFactory
0565: .toGeometry(new ReferencedEnvelope(
0566: (Envelope) envelope)));
0567:
0568: feature.setAttribute(1, validFileName);
0569: fw.write();
0570:
0571: message = new StringBuffer("Done with file ")
0572: .append(files.get(i));
0573: if (LOG.isLoggable(Level.FINE)) {
0574: LOG.fine(message.toString());
0575: }
0576: message.append('\n');
0577: fireEvent(message.toString(),
0578: (((i + 1) * 99.0) / numFiles));
0579: doneSomething = true;
0580: } else
0581: skipFeature = false;
0582:
0583: // ////////////////////////////////////////////////////////
0584: //
0585: // STEP 5
0586: //
0587: // release resources
0588: //
0589: // ////////////////////////////////////////////////////////
0590: try {
0591: inStream.close();
0592: } catch (Exception e) {
0593: // ignore exception
0594: }
0595: try {
0596: r.dispose();
0597: } catch (Exception e) {
0598: // ignore exception
0599: }
0600: //release resources
0601: reader.dispose();
0602:
0603: } catch (IOException e) {
0604: fireException(e);
0605: break;
0606: } catch (ArrayIndexOutOfBoundsException e) {
0607: fireException(e);
0608: break;
0609: } catch (IllegalAttributeException e) {
0610: fireException(e);
0611: break;
0612: }
0613:
0614: }
0615: try {
0616: if (fw != null)
0617: fw.close();
0618: t.commit();
0619: t.close();
0620: } catch (IOException e) {
0621: LOG.log(Level.SEVERE, e.getLocalizedMessage(), e);
0622: }
0623: createPropertiesFiles(globEnvelope, doneSomething);
0624:
0625: }
0626:
0627: /**
0628: * @param globEnvelope
0629: * @param doneSomething
0630: */
0631: private void createPropertiesFiles(GeneralEnvelope globEnvelope,
0632: boolean doneSomething) {
0633: StringBuffer message;
0634: if (numFiles > 0 && doneSomething) {
0635: // /////////////////////////////////////////////////////////////////////
0636: //
0637: // FINAL STEP
0638: //
0639: // CREATING GENERAL INFO FILE
0640: //
0641: // /////////////////////////////////////////////////////////////////////
0642: message = new StringBuffer(
0643: "Creating final properties file ");
0644: if (LOG.isLoggable(Level.FINE)) {
0645: LOG.fine(message.toString());
0646: }
0647: fireEvent(message.toString(), 99.9);
0648:
0649: // envelope
0650: final Properties properties = new Properties();
0651: properties.setProperty("NumFiles", Integer
0652: .toString(numFiles));
0653: properties
0654: .setProperty("Envelope2D", new StringBuffer(Double
0655: .toString(globEnvelope.getMinimum(0)))
0656: .append(",").append(
0657: Double.toString(globEnvelope
0658: .getMinimum(1)))
0659: .append(" ").append(
0660: Double.toString(globEnvelope
0661: .getMaximum(0)))
0662: .append(",").append(
0663: Double.toString(globEnvelope
0664: .getMaximum(1))).toString());
0665: properties.setProperty("LevelsNum", Integer
0666: .toString(numberOfLevels));
0667: final StringBuffer levels = new StringBuffer();
0668: for (int k = 0; k < numberOfLevels; k++) {
0669: levels
0670: .append(Double.toString(resolutionLevels[0][k]))
0671: .append(",")
0672: .append(Double.toString(resolutionLevels[1][k]));
0673: if (k < numberOfLevels - 1)
0674: levels.append(" ");
0675: }
0676: properties.setProperty("Levels", levels.toString());
0677: properties.setProperty("Name", indexName);
0678: properties.setProperty("ExpandToRGB", Boolean
0679: .toString(mustConvertToRGB));
0680: try {
0681: properties.store(new BufferedOutputStream(
0682: new FileOutputStream(locationPath + "/"
0683: + indexName + ".properties")), "");
0684: } catch (FileNotFoundException e) {
0685: if (LOG.isLoggable(Level.SEVERE))
0686: LOG.log(Level.SEVERE, e.getLocalizedMessage(), e);
0687: fireEvent(e.getLocalizedMessage(), 0);
0688: } catch (IOException e) {
0689: if (LOG.isLoggable(Level.SEVERE))
0690: LOG.log(Level.SEVERE, e.getLocalizedMessage(), e);
0691: fireEvent(e.getLocalizedMessage(), 0);
0692: }
0693:
0694: // processing information
0695: message = new StringBuffer("Done!!!");
0696: if (LOG.isLoggable(Level.FINE)) {
0697: LOG.fine(message.toString());
0698: }
0699: fireEvent(message.toString(), 100);
0700: } else {
0701: // processing information
0702: message = new StringBuffer("No file to process!!!");
0703: if (LOG.isLoggable(Level.FINE)) {
0704: LOG.fine(message.toString());
0705: }
0706: fireEvent(message.toString(), 100);
0707: }
0708: }
0709:
0710: /**
0711: * This method checks the {@link ColorModel} of the current image with the
0712: * one of the first image in order to check if they are compatible or not in
0713: * order to perform a mosaic operation.
0714: *
0715: * <p>
0716: * It is worth to point out that we also check if, in case we have two index
0717: * color model image, we also try to suggest whether or not we should do a
0718: * color expansion.
0719: *
0720: * @param defaultCM
0721: * @param defaultPalette
0722: * @param actualCM
0723: * @return a boolean asking to skip this feature.
0724: */
0725: private boolean checkColorModels(ColorModel defaultCM,
0726: byte[][] defaultPalette, ColorModel actualCM) {
0727:
0728: // /////////////////////////////////////////////////////////////////////
0729: //
0730: //
0731: // ComponentColorModel
0732: //
0733: //
0734: // /////////////////////////////////////////////////////////////////////
0735: if (defaultCM instanceof ComponentColorModel
0736: && actualCM instanceof ComponentColorModel) {
0737: final ComponentColorModel defCCM = (ComponentColorModel) defaultCM, actualCCM = (ComponentColorModel) actualCM;
0738: return !(defCCM.getNumColorComponents() == actualCCM
0739: .getNumColorComponents()
0740: && defCCM.hasAlpha() == actualCCM.hasAlpha()
0741: && defCCM.getColorSpace().equals(
0742: actualCCM.getColorSpace())
0743: && defCCM.getTransparency() == actualCCM
0744: .getTransparency() && defCCM
0745: .getTransferType() == actualCCM.getTransferType());
0746: }
0747: // /////////////////////////////////////////////////////////////////////
0748: //
0749: //
0750: // IndexColorModel
0751: //
0752: //
0753: // /////////////////////////////////////////////////////////////////////
0754: if (defaultCM instanceof IndexColorModel
0755: && actualCM instanceof IndexColorModel) {
0756: final IndexColorModel defICM = (IndexColorModel) defaultCM, actualICM = (IndexColorModel) actualCM;
0757: if (defICM.getNumColorComponents() != actualICM
0758: .getNumColorComponents()
0759: || defICM.hasAlpha() != actualICM.hasAlpha()
0760: || !defICM.getColorSpace().equals(
0761: actualICM.getColorSpace())
0762: || defICM.getTransferType() != actualICM
0763: .getTransferType())
0764: return true;
0765: // ///
0766: //
0767: // Suggesting expansion in the simplest case
0768: //
0769: // ///
0770: if (defICM.getMapSize() != actualICM.getMapSize()
0771: || defICM.getTransparency() != actualICM
0772: .getTransparency()
0773: || defICM.getTransferType() != actualICM
0774: .getTransferType()
0775: || defICM.getTransparentPixel() != actualICM
0776: .getTransparentPixel()) {
0777: mustConvertToRGB = true;
0778: return false;
0779: }
0780: // //
0781: //
0782: // Now checking palettes to see if we need to do a color convert
0783: //
0784: // //
0785: // get the palette for this color model
0786: int numBands = actualICM.getNumColorComponents();
0787: byte[][] actualPalette = new byte[3][actualICM.getMapSize()];
0788: actualICM.getReds(actualPalette[0]);
0789: actualICM.getGreens(actualPalette[0]);
0790: actualICM.getBlues(actualPalette[0]);
0791: if (numBands == 4)
0792: actualICM.getAlphas(defaultPalette[0]);
0793: // compare them
0794: for (int i = 0; i < defICM.getMapSize(); i++)
0795: for (int j = 0; j < numBands; j++)
0796: if (actualPalette[j][i] != defaultPalette[j][i]) {
0797: mustConvertToRGB = true;
0798: break;
0799: }
0800: return false;
0801:
0802: }
0803: // //
0804: //
0805: // if we get here this means that the two color models where completely
0806: // different, hence skip this feature.
0807: //
0808: // //
0809: return true;
0810: }
0811:
0812: /**
0813: * Default constructor
0814: */
0815: public MosaicIndexBuilder() {
0816: // /////////////////////////////////////////////////////////////////////
0817: // Options for the command line
0818: // /////////////////////////////////////////////////////////////////////
0819: helpOpt = optionBuilder.withShortName("h").withShortName("?")
0820: .withLongName("helpOpt").withDescription(
0821: "print this message.").create();
0822: versionOpt = optionBuilder.withShortName("v").withLongName(
0823: "versionOpt").withDescription("print the versionOpt.")
0824: .create();
0825: locationOpt = optionBuilder.withShortName("s").withLongName(
0826: "source_directory").withArgument(
0827: arguments.withName("source").withMinimum(1)
0828: .withMaximum(1).create()).withDescription(
0829: "path where files are located").withRequired(true)
0830: .create();
0831: wildcardOpt = optionBuilder.withShortName("w").withLongName(
0832: "wildcardOpt").withArgument(
0833: arguments.withName("wildcardOpt").withMinimum(0)
0834: .withMaximum(1).create()).withDescription(
0835: "wildcardOpt to use for selecting files").withRequired(
0836: false).create();
0837:
0838: nameOpt = optionBuilder.withShortName("name").withLongName(
0839: "index_name").withArgument(
0840: arguments.withName("name").withMinimum(0)
0841: .withMaximum(1).create()).withDescription(
0842: "name for the index file").withRequired(false).create();
0843:
0844: priorityOpt = optionBuilder.withShortName("p").withLongName(
0845: "thread_priority").withArgument(
0846: arguments.withName("priority").withMinimum(0)
0847: .withMaximum(1).create()).withDescription(
0848: "priority for the underlying thread").withRequired(
0849: false).create();
0850:
0851: cmdOpts.add(helpOpt);
0852: cmdOpts.add(versionOpt);
0853: cmdOpts.add(locationOpt);
0854: cmdOpts.add(wildcardOpt);
0855: cmdOpts.add(nameOpt);
0856: cmdOpts.add(priorityOpt);
0857:
0858: optionsGroup = new GroupImpl(cmdOpts, "Options",
0859: "All the options", 1, 10);
0860:
0861: // /////////////////////////////////////////////////////////////////////
0862: //
0863: // Help Formatter
0864: //
0865: // /////////////////////////////////////////////////////////////////////
0866: final HelpFormatter cmdHlp = new HelpFormatter("| ", " ",
0867: " |", 75);
0868: cmdHlp.setShellCommand("MosaicIndexBuilder");
0869: cmdHlp.setHeader("Help");
0870: cmdHlp
0871: .setFooter(new StringBuffer(
0872: "MosaicIndexBuilder - GeoSolutions S.a.s (C) 2006 - v ")
0873: .append(MosaicIndexBuilder.versionNumber)
0874: .toString());
0875: cmdHlp
0876: .setDivider("|-------------------------------------------------------------------------|");
0877:
0878: cmdParser.setGroup(optionsGroup);
0879: cmdParser.setHelpOption(helpOpt);
0880: cmdParser.setHelpFormatter(cmdHlp);
0881: }
0882:
0883: /**
0884: * Entry point for the index builder.
0885: *
0886: * @param args
0887: */
0888: public static void main(String[] args) {
0889:
0890: final MosaicIndexBuilder mosaicIndexBuilder = new MosaicIndexBuilder();
0891: mosaicIndexBuilder
0892: .addProcessingEventListener(mosaicIndexBuilder);
0893: if (mosaicIndexBuilder.parseArgs(args)) {
0894: final Thread t = new Thread(mosaicIndexBuilder,
0895: "MosaicIndexBuilder");
0896: t.setPriority(mosaicIndexBuilder.priority);
0897: t.start();
0898: try {
0899: t.join();
0900: } catch (InterruptedException e) {
0901: LOG.log(Level.SEVERE, e.getLocalizedMessage(), e);
0902: }
0903:
0904: } else
0905: LOG.fine("Exiting...");
0906:
0907: }
0908:
0909: private boolean parseArgs(String[] args) {
0910: cmdLine = cmdParser.parseAndHelp(args);
0911: if (cmdLine != null && cmdLine.hasOption(versionOpt)) {
0912: System.out
0913: .print(new StringBuffer(
0914: "MosaicIndexBuilder - GeoSolutions S.a.s (C) 2006 - v")
0915: .append(MosaicIndexBuilder.versionNumber)
0916: .toString());
0917: System.exit(1);
0918:
0919: } else if (cmdLine != null) {
0920: // ////////////////////////////////////////////////////////////////
0921: //
0922: // parsing command line parameters and setting up
0923: // Mosaic Index Builder options
0924: //
0925: // ////////////////////////////////////////////////////////////////
0926: locationPath = (String) cmdLine.getValue(locationOpt);
0927: final File inDir = new File(locationPath);
0928: if (!inDir.isDirectory()) {
0929: LOG
0930: .severe("Provided input dir does not exist or is not a dir!");
0931: return false;
0932: }
0933: try {
0934: locationPath = inDir.getCanonicalPath();
0935: locationPath = locationPath.replace('\\', '/');
0936: } catch (IOException e) {
0937: LOG.log(Level.SEVERE, e.getLocalizedMessage(), e);
0938: return false;
0939: }
0940: // wildcard
0941: if (cmdLine.hasOption(wildcardOpt))
0942: wildcardString = (String) cmdLine.getValue(wildcardOpt);
0943:
0944: // index name
0945: if (cmdLine.hasOption(nameOpt))
0946: indexName = (String) cmdLine.getValue(nameOpt);
0947:
0948: // //
0949: //
0950: // Thread priority
0951: //
0952: // //
0953: // index name
0954: if (cmdLine.hasOption(priorityOpt))
0955: priority = Integer.parseInt((String) cmdLine
0956: .getValue(priorityOpt));
0957: return true;
0958:
0959: }
0960: return false;
0961:
0962: }
0963:
0964: /**
0965: * This method is responsible for computing the resolutions in for the
0966: * provided grid geometry in the provided crs.
0967: *
0968: * <P>
0969: * It is worth to note that the returned resolution array is of length of 2
0970: * and it always is lon, lat for the moment.<br>
0971: * It might be worth to remove the axes reordering code when we are
0972: * confident enough with the code to handle the north-up crs.
0973: * <p>
0974: * TODO use orthodromic distance?
0975: *
0976: * @param envelope
0977: * the GeneralEnvelope
0978: * @param dim
0979: * @param crs
0980: * @throws DataSourceException
0981: */
0982: protected final double[] getResolution(GeneralEnvelope envelope,
0983: Rectangle2D dim, CoordinateReferenceSystem crs)
0984: throws DataSourceException {
0985: double[] requestedRes = null;
0986: try {
0987: if (dim != null && envelope != null) {
0988: // do we need to transform the originalEnvelope?
0989: final CoordinateReferenceSystem crs2D = CRSUtilities
0990: .getCRS2D(envelope
0991: .getCoordinateReferenceSystem());
0992:
0993: if (crs != null
0994: && !CRS.equalsIgnoreMetadata(crs, crs2D)) {
0995: final MathTransform tr = CRS.findMathTransform(
0996: crs2D, crs);
0997: if (!tr.isIdentity())
0998: envelope = CRS.transform(tr, envelope);
0999: }
1000: requestedRes = new double[2];
1001: requestedRes[0] = envelope.getLength(0)
1002: / dim.getWidth();
1003: requestedRes[1] = envelope.getLength(1)
1004: / dim.getHeight();
1005: }
1006: return requestedRes;
1007: } catch (TransformException e) {
1008: throw new DataSourceException(
1009: "Unable to get the resolution", e);
1010: } catch (FactoryException e) {
1011: throw new DataSourceException(
1012: "Unable to get the resolution", e);
1013: }
1014: }
1015:
1016: /**
1017: * This method is repsonbile for sending the process progress events to the
1018: * logger.
1019: *
1020: * <p>
1021: * It should be used to do normal logging when running this tools as command
1022: * line tools but it should be disable when putting the tool behind a GUI.
1023: * In such a case the GUI should register itself as a
1024: * {@link ProcessingEventListener} and consume the processing events.
1025: *
1026: * @param event
1027: * is a {@link ProcessingEvent} that informs the receiver on the
1028: * precetnage of the progress as well as on what is happening.
1029: */
1030: public void getNotification(ProcessingEvent event) {
1031: LOG.info(new StringBuffer("Progress is at ").append(
1032: event.getPercentage()).append("\n").append(
1033: "attached message is: ").append(event.getMessage())
1034: .toString());
1035:
1036: }
1037:
1038: public void exceptionOccurred(ExceptionEvent event) {
1039: LOG.log(Level.SEVERE, "An error occurred during processing",
1040: event.getException());
1041: }
1042:
1043: /**
1044: * @param locationPath
1045: * the locationPath to set
1046: */
1047: public final void setLocationPath(String locationPath) {
1048: this .locationPath = locationPath;
1049: final File inDir = new File(locationPath);
1050: if (!inDir.isDirectory()) {
1051: LOG
1052: .severe("Provided input dir does not exist or is not a dir!");
1053: throw new IllegalArgumentException(
1054: "Provided input dir does not exist or is not a dir!");
1055: }
1056: try {
1057: locationPath = inDir.getCanonicalPath();
1058: locationPath = locationPath.replace('\\', '/');
1059: } catch (IOException e) {
1060: LOG.log(Level.SEVERE, e.getLocalizedMessage(), e);
1061: final IllegalArgumentException ex = new IllegalArgumentException();
1062: ex.initCause(e);
1063: throw ex;
1064: }
1065: }
1066:
1067: /**
1068: * @param wildcardString
1069: * the wildcardString to set
1070: */
1071: public final void setWildcardString(String wildcardString) {
1072: this .wildcardString = wildcardString;
1073: }
1074:
1075: public String getIndexName() {
1076: return indexName;
1077: }
1078:
1079: public void setIndexName(String indexName) {
1080: this .indexName = indexName;
1081: }
1082:
1083: public double getResolutionX() {
1084: return this .resolutionLevels[0][0];
1085: }
1086:
1087: public double getResolutionY() {
1088: return this .resolutionLevels[1][0];
1089: }
1090: }
|