/*******************************************************************************
* Copyright 2012 Geoscience Australia
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package au.gov.ga.earthsci.layer;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.avlist.AVListImpl;
import gov.nasa.worldwind.cache.FileStore;
import gov.nasa.worldwind.data.BasicDataRasterReaderFactory;
import gov.nasa.worldwind.data.DataRasterReader;
import gov.nasa.worldwind.data.DataRasterReaderFactory;
import gov.nasa.worldwind.data.DataStoreProducer;
import gov.nasa.worldwind.data.TiledElevationProducer;
import gov.nasa.worldwind.data.TiledImageProducer;
import gov.nasa.worldwind.data.WWDotNetLayerSetConverter;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwindx.applications.worldwindow.features.DataImportUtil;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Helper class used to load (non layer definition) files as layers. Examples of
* files that could be loaded are a GeoTIFF, a GOCAD geometry file, or a KML
* file.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
public class FileLoader
{
/**
* Listener interface for {@link FileLoader} events.
*/
public interface FileLoadListener
{
void loaded(Element element, URL sourceUrl);
void error(Exception e);
void cancelled();
}
/**
* Attempt to load the given file. Runs the load operation asynchronously on
* a separate thread.
*
* @param file
* File to load
* @param listener
* Listener for load events
* @param shell
* Parent shell to show any loading progress bars as children of
* @param fileStore
* File store to save a local tileset to (for georeferenced
* images such as GeoTIFFs)
*/
public static void loadFile(final File file, final FileLoadListener listener, final Shell parent,
final FileStore fileStore)
{
Thread thread = new Thread(new Runnable()
{
@Override
public void run()
{
Document dataConfig = null;
try
{
//first attempt loading the file from the WorldWindInstalled cache
dataConfig = loadCachedData(file, fileStore);
if (dataConfig == null)
{
// Import the file into a form usable by World Wind components.
dataConfig = importDataFromFile(parent, file, fileStore);
}
if (dataConfig == null)
{
listener.cancelled();
}
else
{
URL sourceUrl = file.toURI().toURL();
listener.loaded(dataConfig.getDocumentElement(), sourceUrl);
}
}
catch (Exception e)
{
listener.error(e);
e.printStackTrace();
}
}
});
thread.setDaemon(true);
thread.setName("File loader"); //$NON-NLS-1$
thread.start();
}
protected static Document loadCachedData(File file, FileStore fileStore)
{
//TODO
return null;
}
//FROM ImportingImagesAndElevationsDemo.java, modified for SWT/JFace
protected static Document importDataFromFile(Shell parent, final File file, final FileStore fileStore)
throws Exception
{
// Create a DataStoreProducer which is capable of processing the file.
final DataStoreProducer producer = createDataStoreProducerFromFile(file);
if (producer == null)
{
throw new IllegalArgumentException("Unrecognized file type");
}
final AtomicInteger progress = new AtomicInteger(0);
PropertyChangeListener progressListener = new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
{
if (evt.getPropertyName().equals(AVKey.PROGRESS))
{
progress.set((int) (100 * (Double) evt.getNewValue()));
}
}
};
producer.addPropertyChangeListener(progressListener);
final AtomicBoolean close = new AtomicBoolean(false);
final ProgressMonitorDialog dialog = new ProgressMonitorDialog(parent);
Display.getDefault().asyncExec(new Runnable()
{
@Override
public void run()
{
try
{
dialog.run(true, true, new IRunnableWithProgress()
{
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException,
InterruptedException
{
monitor.beginTask("Importing " + file.getName(), 100);
while (true)
{
if (close.get())
{
break;
}
if (monitor.isCanceled())
{
producer.stopProduction();
break;
}
int monitorProgress = 0;
int currentProgress = progress.get();
int diff = currentProgress - monitorProgress;
if (diff > 0)
{
monitor.worked(diff);
}
Thread.sleep(100);
}
}
});
}
catch (InvocationTargetException e)
{
}
catch (InterruptedException e)
{
producer.stopProduction();
}
}
});
Document doc = null;
try
{
// Import the file into the specified FileStore.
doc = createDataStoreFromFile(file, fileStore, producer);
// The user clicked the ProgressMonitor's "Cancel" button. Revert any change made during production, and
// discard the returned DataConfiguration reference.
if (dialog.getReturnCode() == Dialog.CANCEL)
{
doc = null;
producer.removeProductionState();
}
}
finally
{
// Remove the progress event listener from the DataStoreProducer. stop the progress timer, and signify to the
// ProgressMonitor that we're done.
close.set(true);
producer.removePropertyChangeListener(progressListener);
}
return doc;
}
protected static Document createDataStoreFromFile(File file, FileStore fileStore, DataStoreProducer producer)
throws Exception
{
File importLocation = DataImportUtil.getDefaultImportLocation(fileStore);
if (importLocation == null)
{
String message = Logging.getMessage("generic.NoDefaultImportLocation"); //$NON-NLS-1$
Logging.logger().severe(message);
return null;
}
// Create the production parameters. These parameters instruct the DataStoreProducer where to import the cached
// data, and what name to put in the data configuration document.
AVList params = new AVListImpl();
params.setValue(AVKey.DATASET_NAME, file.getName());
params.setValue(AVKey.DATA_CACHE_NAME, file.getName());
params.setValue(AVKey.FILE_STORE_LOCATION, importLocation.getAbsolutePath());
producer.setStoreParameters(params);
// Use the specified file as the the production data source.
producer.offerDataSource(file, params);
try
{
// Convert the file to a form usable by World Wind components, according to the specified DataStoreProducer.
// This throws an exception if production fails for any reason.
producer.startProduction();
}
catch (Exception e)
{
// Exception attempting to convert the file. Revert any change made during production.
producer.removeProductionState();
throw e;
}
// Return the DataConfiguration from the production results. Since production sucessfully completed, the
// DataStoreProducer should contain a DataConfiguration in the production results. We test the production
// results anyway.
Iterable<?> results = producer.getProductionResults();
if (results != null && results.iterator() != null && results.iterator().hasNext())
{
Object o = results.iterator().next();
if (o != null && o instanceof Document)
{
return (Document) o;
}
}
return null;
}
//**************************************************************//
//******************** Utility Methods ***********************//
//**************************************************************//
protected static DataStoreProducer createDataStoreProducerFromFile(File file)
{
if (file == null)
{
return null;
}
DataStoreProducer producer = null;
DataRasterReaderFactory readerFactory = new BasicDataRasterReaderFactory();
AVListImpl params = new AVListImpl();
DataRasterReader reader = readerFactory.findReaderFor(file, params);
if (reader != null && reader.isElevationsRaster(file, params))
{
producer = new TiledElevationProducer();
}
else if (reader != null && reader.isImageryRaster(file, params))
{
producer = new TiledImageProducer();
}
else if (DataImportUtil.isWWDotNetLayerSet(file))
{
producer = new WWDotNetLayerSetConverter();
}
return producer;
}
}