Package org.geoserver.wps.gs

Source Code of org.geoserver.wps.gs.ImportProcess

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.gs;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import javax.media.jai.Interpolation;

import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogBuilder;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ProjectionPolicy;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.wps.WPSException;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams;
import org.geotools.data.DataStore;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.gce.geotiff.GeoTiffFormat;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.gce.geotiff.GeoTiffWriteParams;
import org.geotools.gce.geotiff.GeoTiffWriter;
import org.geotools.process.ProcessException;
import org.geotools.process.factory.DescribeParameter;
import org.geotools.process.factory.DescribeProcess;
import org.geotools.process.factory.DescribeResult;
import org.geotools.process.gs.GSProcess;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.vfny.geoserver.util.WCSUtils;

/**
* Imports a feature collection into the GeoServer catalog
*
* @author Andrea Aime - OpenGeo
*
*/
@DescribeProcess(title = "Import to Catalog", description = "Imports a feature collection into the catalog")
public class ImportProcess implements GSProcess {

    static final Logger LOGGER = Logging.getLogger(ImportProcess.class);

    private final static GeoTiffWriteParams DEFAULT_WRITE_PARAMS;

    static {
        // setting the write parameters (we my want to make these configurable in the future
        DEFAULT_WRITE_PARAMS = new GeoTiffWriteParams();
        DEFAULT_WRITE_PARAMS.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT);
        DEFAULT_WRITE_PARAMS.setCompressionType("LZW");
        DEFAULT_WRITE_PARAMS.setCompressionQuality(0.75F);
        DEFAULT_WRITE_PARAMS.setTilingMode(GeoToolsWriteParams.MODE_EXPLICIT);
        DEFAULT_WRITE_PARAMS.setTiling(512, 512);
    }
   
    private Catalog catalog;

    public ImportProcess(Catalog catalog) {
        this.catalog = catalog;
    }

    @DescribeResult(name = "layerName", description = "Name of the new featuretype, with workspace")
    public String execute(
            @DescribeParameter(name = "features", min = 0, description = "Input feature collection") SimpleFeatureCollection features,
            @DescribeParameter(name = "coverage", min = 0, description = "Input raster") GridCoverage2D coverage,
            @DescribeParameter(name = "workspace", min = 0, description = "Target workspace (default is the system default)") String workspace,
            @DescribeParameter(name = "store", min = 0, description = "Target store (default is the workspace default)") String store,
            @DescribeParameter(name = "name", min = 0, description = "Name of the new featuretype/coverage (default is the name of the features in the collection)") String name,
            @DescribeParameter(name = "srs", min = 0, description = "Target coordinate reference system (default is based on source when possible)") CoordinateReferenceSystem srs,
            @DescribeParameter(name = "srsHandling", min = 0, description = "Desired SRS handling (default is FORCE_DECLARED, others are REPROJECT_TO_DECLARED or NONE)") ProjectionPolicy srsHandling,
            @DescribeParameter(name = "styleName", min = 0, description = "Name of the style to be associated with the layer (default is a standard geometry-specific style)") String styleName)
            throws ProcessException {

        // first off, decide what is the target store
        WorkspaceInfo ws;
        if (workspace != null) {
            ws = catalog.getWorkspaceByName(workspace);
            if (ws == null) {
                throw new ProcessException("Could not find workspace " + workspace);
            }
        } else {
            ws = catalog.getDefaultWorkspace();
            if (ws == null) {
                throw new ProcessException(
                        "The catalog is empty, could not find a default workspace");
            }
        }

      //create a builder to help build catalog objects
        CatalogBuilder cb = new CatalogBuilder(catalog);
        cb.setWorkspace( ws );

        // ok, find the target store
        StoreInfo storeInfo = null;
        boolean add = false;
        if (store != null) {
          if (features != null)
          {
            storeInfo = catalog.getDataStoreByName(ws.getName(), store);
          }
          else if (coverage != null)
          {
            storeInfo = catalog.getCoverageStoreByName(ws.getName(), store);
          }
            if (storeInfo == null) {
                throw new ProcessException("Could not find store " + store + " in workspace "
                        + workspace);
                // TODO: support store creation
            }
        } else if (features != null) {
            storeInfo = catalog.getDefaultDataStore(ws);
            if (storeInfo == null) {
                throw new ProcessException("Could not find a default store in workspace "
                        + ws.getName());
            }
        } else if (coverage != null) {
          //create a new coverage store
            LOGGER.info("Auto-configuring coverage store: " + (name != null ? name : coverage.getName().toString()));
           
            storeInfo = cb.buildCoverageStore((name != null ? name : coverage.getName().toString()));
            add = true;
            store = (name != null ? name : coverage.getName().toString());
           
            if (storeInfo == null) {
                throw new ProcessException("Could not find a default store in workspace " + ws.getName());
            }
        }

        // check the target style if any
        StyleInfo targetStyle = null;
        if (styleName != null) {
            targetStyle = catalog.getStyleByName(styleName);
            if (targetStyle == null) {
                throw new ProcessException("Could not find style " + styleName);
            }
        }

        if (features != null)
        {
          // check if the target layer and the target feature type are not
          // already there (this is a half-assed attempt as we don't have
          // an API telling us how the feature type name will be changed
          // by DataStore.createSchema(...), but better than fully importing
          // the data into the target store to find out we cannot create the layer...)
          String tentativeTargetName = null;
          if (name != null) {
            tentativeTargetName = ws.getName() + ":" + name;
          } else {
            tentativeTargetName = ws.getName() + ":" + features.getSchema().getTypeName();
          }
          if (catalog.getLayer(tentativeTargetName) != null) {
            throw new ProcessException("Target layer " + tentativeTargetName + " already exists");
          }

          // check the target crs
          String targetSRSCode = null;
          if (srs != null) {
            try {
              Integer code = CRS.lookupEpsgCode(srs, true);
              if (code == null) {
                throw new WPSException("Could not find a EPSG code for " + srs);
              }
              targetSRSCode = "EPSG:" + code;
            } catch (Exception e) {
              throw new ProcessException("Could not lookup the EPSG code for the provided srs", e);
            }
          } else {
            // check we can extract a code from the original data
            GeometryDescriptor gd = features.getSchema().getGeometryDescriptor();
            if (gd == null) {
              // data is geometryless, we need a fake SRS
              targetSRSCode = "EPSG:4326";
              srsHandling = ProjectionPolicy.FORCE_DECLARED;
            } else {
              CoordinateReferenceSystem nativeCrs = gd.getCoordinateReferenceSystem();
              if (nativeCrs == null) {
                throw new ProcessException("The original data has no native CRS, "
                    + "you need to specify the srs parameter");
              } else {
                try {
                  Integer code = CRS.lookupEpsgCode(nativeCrs, true);
                  if (code == null) {
                    throw new ProcessException("Could not find an EPSG code for data "
                        + "native spatial reference system: " + nativeCrs);
                  } else {
                    targetSRSCode = "EPSG:" + code;
                  }
                } catch (Exception e) {
                  throw new ProcessException("Failed to loookup an official EPSG code for "
                      + "the source data native " + "spatial reference system", e);
                }
              }
            }
          }

          // import the data into the target store
          SimpleFeatureType targetType;
          try {
            targetType = importDataIntoStore(features, name, (DataStoreInfo) storeInfo);
          } catch (IOException e) {
            throw new ProcessException("Failed to import data into the target store", e);
          }

          // now import the newly created layer into GeoServer
          try {
            cb.setStore(storeInfo);

            // build the typeInfo and set CRS if necessary
            FeatureTypeInfo typeInfo = cb.buildFeatureType(targetType.getName());
            if (targetSRSCode != null) {
              typeInfo.setSRS(targetSRSCode);
            }
            if (srsHandling != null) {
              typeInfo.setProjectionPolicy(srsHandling);
            }
            // compute the bounds
            cb.setupBounds(typeInfo);

            // build the layer and set a style
            LayerInfo layerInfo = cb.buildLayer(typeInfo);
            if (targetStyle != null) {
              layerInfo.setDefaultStyle(targetStyle);
            }

            catalog.add(typeInfo);
            catalog.add(layerInfo);

            return layerInfo.prefixedName();
          } catch (Exception e) {
            throw new ProcessException(
                "Failed to complete the import inside the GeoServer catalog", e);
          }
        }
        else if (coverage != null)
        {
          try {
            final File directory = catalog.getResourceLoader().findOrCreateDirectory("data", workspace, store);
            final File file = File.createTempFile(store, ".tif", directory);
                ((CoverageStoreInfo)storeInfo).setURL( file.toURL().toExternalForm() );
                ((CoverageStoreInfo)storeInfo).setType("GeoTIFF");
               
              // check the target crs
                CoordinateReferenceSystem cvCrs = coverage.getCoordinateReferenceSystem();
                String targetSRSCode = null;
              if (srs != null) {
                try {
                  Integer code = CRS.lookupEpsgCode(srs, true);
                  if (code == null) {
                    throw new WPSException("Could not find a EPSG code for " + srs);
                  }
                  targetSRSCode = "EPSG:" + code;
                } catch (Exception e) {
                  throw new ProcessException("Could not lookup the EPSG code for the provided srs", e);
                }
              } else {
                // check we can extract a code from the original data
                if (cvCrs == null) {
                  // data is geometryless, we need a fake SRS
                  targetSRSCode = "EPSG:4326";
                  srsHandling = ProjectionPolicy.FORCE_DECLARED;
                  srs = DefaultGeographicCRS.WGS84;
                } else {
                  CoordinateReferenceSystem nativeCrs = cvCrs;
                  if (nativeCrs == null) {
                    throw new ProcessException("The original data has no native CRS, "
                        + "you need to specify the srs parameter");
                  } else {
                    try {
                      Integer code = CRS.lookupEpsgCode(nativeCrs, true);
                      if (code == null) {
                        throw new ProcessException("Could not find an EPSG code for data "
                            + "native spatial reference system: " + nativeCrs);
                      } else {
                        targetSRSCode = "EPSG:" + code;
                        srs = CRS.decode(targetSRSCode, true);
                      }
                    } catch (Exception e) {
                      throw new ProcessException("Failed to loookup an official EPSG code for "
                          + "the source data native " + "spatial reference system", e);
                    }
                  }
                }
              }
             
              MathTransform tx = CRS.findMathTransform(cvCrs, srs);
             
              if (!tx.isIdentity() || !CRS.equalsIgnoreMetadata(cvCrs, srs))
              {
                coverage = WCSUtils.resample(coverage, cvCrs, srs, null, Interpolation.getInstance(Interpolation.INTERP_NEAREST));
              }
             
              GeoTiffWriter writer = new GeoTiffWriter(file);

                // setting the write parameters for this geotiff
                final ParameterValueGroup params = new GeoTiffFormat().getWriteParameters();
                params.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(
                        DEFAULT_WRITE_PARAMS);
                final GeneralParameterValue[] wps = (GeneralParameterValue[]) params.values().toArray(
                        new GeneralParameterValue[1]);
               
                try {
                    writer.write(coverage, wps);
                } finally {
                    try {
                        writer.dispose();
                    } catch (Exception e) {
                        // we tried, no need to fuss around this one
                    }
                }
               
                //add or update the datastore info
                if ( add ) {
                    catalog.add( (CoverageStoreInfo)storeInfo );
                }
                else {
                    catalog.save( (CoverageStoreInfo)storeInfo );
                }
               
                cb.setStore( (CoverageStoreInfo)storeInfo );
               
                GridCoverage2DReader reader = new GeoTiffReader(file);
                if ( reader == null ) {
                    throw new ProcessException( "Could not aquire reader for coverage." );
                }
               
                // coverage read params
                final Map customParameters = new HashMap();
                /*String useJAIImageReadParam = "USE_JAI_IMAGEREAD";
                if (useJAIImageReadParam != null) {
                  customParameters.put(AbstractGridFormat.USE_JAI_IMAGEREAD.getName().toString(), Boolean.valueOf(useJAIImageReadParam));
                }*/
               
                CoverageInfo cinfo = cb.buildCoverage( reader, customParameters );
               
                //check if the name of the coverage was specified
                if ( name != null ) {
                    cinfo.setName( name );
                }
               
                if ( !add ) {
                    //update the existing
                    CoverageInfo existing = catalog.getCoverageByCoverageStore((CoverageStoreInfo) storeInfo, name != null ? name : coverage.getName().toString() );
                    if ( existing == null ) {
                        //grab the first if there is only one
                        List<CoverageInfo> coverages = catalog.getCoveragesByCoverageStore( (CoverageStoreInfo) storeInfo );
                        if ( coverages.size() == 1 ) {
                            existing = coverages.get(0);
                        }
                        if ( coverages.size() == 0 ) {
                            //no coverages yet configured, change add flag and continue on
                            add = true;
                        }
                        else {
                            // multiple coverages, and one to configure not specified
                            throw new ProcessException( "Unable to determine coverage to configure.");
                        }
                    }
                   
                    if ( existing != null ) {
                        cb.updateCoverage(existing,cinfo);
                        catalog.save( existing );
                        cinfo = existing;
                    }
                }
               
                //do some post configuration, if srs is not known or unset, transform to 4326
                if ("UNKNOWN".equals(cinfo.getSRS())) {
                    //CoordinateReferenceSystem sourceCRS = cinfo.getBoundingBox().getCoordinateReferenceSystem();
                    //CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326", true);
                    //ReferencedEnvelope re = cinfo.getBoundingBox().transform(targetCRS, true);
                    cinfo.setSRS( "EPSG:4326" );
                    //cinfo.setCRS( targetCRS );
                    //cinfo.setBoundingBox( re );
                }

                //add/save
                if ( add ) {
                    catalog.add( cinfo );
                   
                    LayerInfo layerInfo = cb.buildLayer( cinfo );
                    if ( styleName != null && targetStyle != null ) {
                      layerInfo.setDefaultStyle( targetStyle );
                    }
                    //JD: commenting this out, these sorts of edits should be handled
                    // with a second PUT request on the created coverage
                    /*
                    String styleName = form.getFirstValue("style");
                    if ( styleName != null ) {
                        StyleInfo style = catalog.getStyleByName( styleName );
                        if ( style != null ) {
                            layerInfo.setDefaultStyle( style );
                            if ( !layerInfo.getStyles().contains( style ) ) {
                                layerInfo.getStyles().add( style );
                            }
                        }
                        else {
                            LOGGER.warning( "Client specified style '" + styleName + "'but no such style exists.");
                        }
                    }

                    String path = form.getFirstValue( "path");
                    if ( path != null ) {
                        layerInfo.setPath( path );
                    }
                    */

                    boolean valid = true;
                    try {
                        if (!catalog.validate(layerInfo, true).isValid()) {
                            valid = false;
                        }
                    } catch (Exception e) {
                        valid = false;
                    }

                    layerInfo.setEnabled(valid);
                    catalog.add(layerInfo);
                   
                    return layerInfo.prefixedName();
                }
                else {
                    catalog.save( cinfo );
                   
                    LayerInfo layerInfo = catalog.getLayerByName(cinfo.getName());
                    if ( styleName != null && targetStyle != null ) {
                      layerInfo.setDefaultStyle( targetStyle );
                    }
                   
                    return layerInfo.prefixedName();
                }
               
            } catch (MalformedURLException e) {
                throw new ProcessException( "URL Error", e );
            } catch (IOException e) {
                throw new ProcessException( "I/O Exception", e );
      } catch (Exception e) {
        e.printStackTrace();
        throw new ProcessException( "Exception", e );
      }
        }
       
        return null;
    }

    private SimpleFeatureType importDataIntoStore(SimpleFeatureCollection features, String name,
            DataStoreInfo storeInfo) throws IOException, ProcessException {
        SimpleFeatureType targetType;
        // grab the data store
        DataStore ds = (DataStore) storeInfo.getDataStore(null);

        // decide on the target ft name
        SimpleFeatureType sourceType = features.getSchema();
        if (name != null) {
            SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
            tb.init(sourceType);
            tb.setName(name);
            sourceType = tb.buildFeatureType();
        }

        // create the schema
        ds.createSchema(sourceType);

        // try to get the target feature type (might have slightly different
        // name and structure)
        targetType = ds.getSchema(sourceType.getTypeName());
        if (targetType == null) {
            // ouch, the name was changed... we can only guess now...
            // try with the typical Oracle mangling
            targetType = ds.getSchema(sourceType.getTypeName().toUpperCase());
        }

        if (targetType == null) {
            throw new WPSException(
                    "The target schema was created, but with a name "
                            + "that we cannot relate to the one we provided the data store. Cannot proceeed further");
        } else {
            // check the layer is not already there
            String newLayerName = storeInfo.getWorkspace().getName() + ":"
                    + targetType.getTypeName();
            LayerInfo layer = catalog.getLayerByName(newLayerName);
            // todo: we should not really reach here and know beforehand what the targetType
            // name is, but if we do we should at least get a way to drop it
            if (layer != null) {
                throw new ProcessException("Target layer " + newLayerName
                        + " already exists in the catalog");
            }
        }

        // try to establish a mapping with old and new attributes. This is again
        // just guesswork until we have a geotools api that will give us the
        // exact mapping to be performed
        Map<String, String> mapping = buildAttributeMapping(sourceType, targetType);

        // start a transaction and fill the target with the input features
        Transaction t = new DefaultTransaction();
        SimpleFeatureStore fstore = (SimpleFeatureStore) ds.getFeatureSource(targetType
                .getTypeName());
        fstore.setTransaction(t);
        SimpleFeatureIterator fi = features.features();
        SimpleFeatureBuilder fb = new SimpleFeatureBuilder(targetType);
        while (fi.hasNext()) {
            SimpleFeature source = fi.next();
            fb.reset();
            for (String sname : mapping.keySet()) {
                fb.set(mapping.get(sname), source.getAttribute(sname));
            }
            SimpleFeature target = fb.buildFeature(null);
            fstore.addFeatures(DataUtilities.collection(target));
        }
        t.commit();
        t.close();

        return targetType;
    }

    /**
     * Applies a set of heuristics to find which target attribute corresponds to a certain input
     * attribute
     *
     * @param sourceType
     * @param targetType
     * @return
     */
    Map<String, String> buildAttributeMapping(SimpleFeatureType sourceType,
            SimpleFeatureType targetType) {
        // look for the typical manglings. For example, if the target is a
        // shapefile store it will move the geometry and name it the_geom

        // collect the source names
        Set<String> sourceNames = new HashSet<String>();
        for (AttributeDescriptor sd : sourceType.getAttributeDescriptors()) {
            sourceNames.add(sd.getLocalName());
        }

        // first check if we have been kissed by sheer luck and the names are
        // the same
        Map<String, String> result = new HashMap<String, String>();
        for (String name : sourceNames) {
            if (targetType.getDescriptor(name) != null) {
                result.put(name, name);
            }
        }
        sourceNames.removeAll(result.keySet());

        // then check for simple case difference (Oracle case)
        for (String name : sourceNames) {
            for (AttributeDescriptor td : targetType.getAttributeDescriptors()) {
                if (td.getLocalName().equalsIgnoreCase(name)) {
                    result.put(name, td.getLocalName());
                    break;
                }
            }
        }
        sourceNames.removeAll(result.keySet());

        // then check attribute names being cut (another Oracle case)
        for (String name : sourceNames) {
            String loName = name.toLowerCase();
            for (AttributeDescriptor td : targetType.getAttributeDescriptors()) {
                String tdName = td.getLocalName().toLowerCase();
                if (loName.startsWith(tdName)) {
                    result.put(name, td.getLocalName());
                    break;
                }
            }
        }
        sourceNames.removeAll(result.keySet());

        // consider the shapefile geometry descriptor mangling
        if (targetType.getGeometryDescriptor() != null
                && "the_geom".equals(targetType.getGeometryDescriptor().getLocalName())
                && !"the_geom".equalsIgnoreCase(sourceType.getGeometryDescriptor().getLocalName())) {
            result.put(sourceType.getGeometryDescriptor().getLocalName(), "the_geom");
        }

        // and finally we return with as much as we can match
        if (!sourceNames.isEmpty()) {
            LOGGER.warning("Could not match the following attributes " + sourceNames
                    + " to the target feature type ones: " + targetType);
        }
        return result;
    }
}
TOP

Related Classes of org.geoserver.wps.gs.ImportProcess

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.