/*******************************************************************************
* Copyright 2013 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.model.core.raster;
import gov.nasa.worldwind.layers.Layer;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.gdal.gdal.Dataset;
import org.gdal.gdal.gdal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import au.gov.ga.earthsci.common.util.URIBuilder;
import au.gov.ga.earthsci.common.util.URIUtil;
import au.gov.ga.earthsci.core.intent.AbstractRetrieveIntentHandler;
import au.gov.ga.earthsci.core.retrieve.IRetrievalData;
import au.gov.ga.earthsci.core.retrieve.IRetrievalProperties;
import au.gov.ga.earthsci.core.retrieve.RetrievalProperties;
import au.gov.ga.earthsci.intent.AbstractIntentCallback;
import au.gov.ga.earthsci.intent.IIntentCallback;
import au.gov.ga.earthsci.intent.Intent;
import au.gov.ga.earthsci.intent.IntentManager;
import au.gov.ga.earthsci.model.IModel;
import au.gov.ga.earthsci.model.core.worldwind.BasicModelLayer;
import au.gov.ga.earthsci.model.core.worldwind.IModelLayer;
/**
* An intent handler that responds to intents that match GDAL-supported raster
* formats and generates an IModel
*
* @author James Navin (james.navin@ga.gov.au)
*/
public class GDALRasterModelIntentHandler extends AbstractRetrieveIntentHandler
{
private static final Logger logger = LoggerFactory.getLogger(GDALRasterModelIntentHandler.class);
public static String GDAL_RASTER_MODEL_URI_SCHEME = "gdalrastermodel"; //$NON-NLS-1$
public static String GDAL_RASTER_MODEL_URI_HOST = "model"; //$NON-NLS-1$
public static String SOURCE_URI_PARAM_KEY = "source"; //$NON-NLS-1$
public static String MODEL_PARAMS_EXTRAS_KEY = "gdalRasterModelParams"; //$NON-NLS-1$
public static String DATASET_EXTRAS_KEY = "dataset"; //$NON-NLS-1$
@Inject
private IEclipseContext eclipseContext;
@Override
public void handle(Intent intent, IIntentCallback callback)
{
if (isModelURI(intent.getURI()))
{
Map<String, String> queryParams = URIUtil.getParameterMap(intent.getURI());
URI source = null;
GDALRasterModelParameters modelParams = null;
try
{
source = new URI(queryParams.get(SOURCE_URI_PARAM_KEY));
modelParams = new GDALRasterModelParameters(queryParams);
}
catch (Exception e)
{
callback.error(e, intent);
return;
}
intent.setURI(source);
intent.putExtra(MODEL_PARAMS_EXTRAS_KEY, modelParams);
super.handle(intent, callback);
}
else
{
super.handle(intent, callback);
}
}
@Override
protected void handle(IRetrievalData data, URL url, Intent intent, IIntentCallback callback)
{
try
{
File source = data.getFile();
/* Create the model from the source file
*
* Implementation passes the original callback through for use in (possibly)
* asynchronous operations
*
* Processing chain is as follows:
*
* Open dataset -> Get model parameters -> Create model
*
*/
openDataset(source, intent, callback);
}
catch (Exception e)
{
callback.error(e, intent);
}
}
@Override
protected IRetrievalProperties getRetrievalProperties()
{
RetrievalProperties result = new RetrievalProperties();
result.setFileRequired(true);
return result;
}
private void openDataset(File source, Intent intent, IIntentCallback callback)
{
logger.debug("Creating model from dataset {}", source.getAbsoluteFile()); //$NON-NLS-1$
Dataset ds = gdal.Open(source.getAbsolutePath());
if (ds == null)
{
logger.debug("Unable to open dataset {}", source.getAbsoluteFile()); //$NON-NLS-1$
callback.error(new IllegalArgumentException(gdal.GetLastErrorMsg()), intent);
return;
}
obtainParameters(ds, intent, callback);
}
private void obtainParameters(final Dataset ds, final Intent intent, final IIntentCallback callback)
{
// If model params already exist, they were likely loaded from a URL
// If so, use them here and don't collect them from elsewhere
if (intent.getExtra(MODEL_PARAMS_EXTRAS_KEY) != null)
{
GDALRasterModelParameters params = (GDALRasterModelParameters) intent.getExtra(MODEL_PARAMS_EXTRAS_KEY);
createModel(ds, params, intent, callback);
return;
}
Intent paramsIntent = new Intent();
paramsIntent.setAction("collectRasterParameters"); //$NON-NLS-1$
paramsIntent.setRequiredReturnType(GDALRasterModelParameters.class);
paramsIntent.putExtra(DATASET_EXTRAS_KEY, ds);
IntentManager.getInstance().start(paramsIntent, new AbstractIntentCallback()
{
@Override
public void error(Exception e, Intent paramsIntent)
{
logger.debug("Error signaled during raster parameter collection"); //$NON-NLS-1$
callback.error(e, intent);
}
@Override
public void completed(Object result, Intent paramsIntent)
{
logger.debug("Raster parameters completed"); //$NON-NLS-1$
GDALRasterModelParameters parameters = (GDALRasterModelParameters) result;
if (parameters == null)
{
callback.completed(null, intent);
return;
}
createModel(ds, parameters, intent, callback);
}
@Override
public void aborted(Intent paramsIntent)
{
logger.debug("Raster parameter collection aborted"); //$NON-NLS-1$
callback.aborted(intent);
}
@Override
public void canceled(Intent paramsIntent)
{
logger.debug("Raster parameter collection canceled"); //$NON-NLS-1$
callback.canceled(intent);
}
}, eclipseContext);
}
private void createModel(final Dataset ds, GDALRasterModelParameters parameters, final Intent intent,
final IIntentCallback callback)
{
try
{
GDALRasterModel model = GDALRasterModelFactory.createModel(ds, parameters);
URI newURI = rewriteURI(intent.getURI(), model);
if (isModelIntent(intent))
{
callback.completed(model, intent);
}
else if (isLayerIntent(intent))
{
intent.setURI(newURI);
callback.completed(createModelLayer(model), intent);
}
}
catch (Exception e)
{
callback.error(e, intent);
}
}
/**
* Create a new {@link IModelLayer} that contains the provided model.
*
* @param m
* The model to wrap with a layer
*
* @return A new {@link IModelLayer} that contains the provided model.
*/
private IModelLayer createModelLayer(IModel m) throws Exception
{
return new BasicModelLayer(m.getName(), m);
}
private boolean isModelURI(URI source)
{
return source.getScheme().equalsIgnoreCase(GDAL_RASTER_MODEL_URI_SCHEME);
}
private boolean isModelIntent(Intent intent)
{
return intent.getExpectedReturnType().isAssignableFrom(IModel.class);
}
private boolean isLayerIntent(Intent intent)
{
return intent.getExpectedReturnType().isAssignableFrom(Layer.class);
}
/**
* Re-write the source URI as a GDAL Raster Model URI with all parameters
* encoded.
*
* @param sourceURI
* The source URI from which the model was originally created
* @param model
* The loaded model
*
* @return A model re-written using the
* {@link #GDAL_RASTER_MODEL_URI_SCHEME} scheme
*/
private URI rewriteURI(URI sourceURI, GDALRasterModel model) throws Exception
{
URIBuilder builder = new URIBuilder();
builder.setScheme(GDAL_RASTER_MODEL_URI_SCHEME);
builder.setHost(GDAL_RASTER_MODEL_URI_HOST);
builder.setParam(SOURCE_URI_PARAM_KEY, sourceURI.toString());
// Add configuration params
Map<String, String> modelParams = model.getParameters().asParameterMap();
for (Entry<String, String> paramEntry : modelParams.entrySet())
{
builder.setParam(paramEntry.getKey(), paramEntry.getValue());
}
return builder.build();
}
}