Package org.geoserver.catalog

Source Code of org.geoserver.catalog.CatalogBuilder

/* Copyright (c) 2001 - 2008 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.catalog;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.measure.unit.Unit;

import org.geoserver.catalog.impl.ModificationProxy;
import org.geoserver.catalog.impl.ResourceInfoImpl;
import org.geoserver.catalog.impl.StoreInfoImpl;
import org.geoserver.catalog.impl.StyleInfoImpl;
import org.geoserver.data.util.CoverageStoreUtils;
import org.geoserver.data.util.CoverageUtils;
import org.geoserver.ows.util.ClassProperties;
import org.geoserver.ows.util.OwsUtils;
import org.geotools.coverage.Category;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.data.FeatureSource;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.metadata.Identifier;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;

import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

/**
* Builder class which provides convenience methods for interacting with the catalog.
* <p>
* Warning: this class is stateful, and is not meant to be accessed by multiple threads
* and should not be an member variable of another class.
* </p>
* @author Justin Deoliveira, OpenGEO
*
*/
public class CatalogBuilder {
   
    static final Logger LOGGER = Logging.getLogger(CatalogBuilder.class);

    /**
     * the catalog
     */
    Catalog catalog;
   
    /**
     * the current workspace
     */
    WorkspaceInfo workspace;
    /**
     * the current store
     */
    StoreInfo store;
   
    public CatalogBuilder( Catalog catalog ) {
        this.catalog = catalog;
    }
   
    /**
     * Sets the workspace to be used when creating store objects.
     */
    public void setWorkspace(WorkspaceInfo workspace) {
        this.workspace = workspace;
    }

    /**
     * Sets the store to be used when creating resource objects.
     */
    public void setStore( StoreInfo store ) {
        this.store = store;
    }
   
    /**
     * Updates a workspace with the properties of another.
     *
     * @param original The workspace being updated.
     * @param update The workspace containing the new values.
     */
    public void updateWorkspace( WorkspaceInfo original, WorkspaceInfo update ) {
        update(original,update,WorkspaceInfo.class);
    }
   

    /**
     * Updates a namespace with the properties of another.
     *
     * @param original The namespace being updated.
     * @param update The namespace containing the new values.
     */
    public void updateNamespace( NamespaceInfo original, NamespaceInfo update ) {
        update(original,update,NamespaceInfo.class);
    }
   
    /**
     * Updates a datastore with the properties of another.
     *
     * @param original The datastore being updated.
     * @param update The datastore containing the new values.
     */
    public void updateDataStore( DataStoreInfo original, DataStoreInfo update ) {
        update( original, update, DataStoreInfo.class );
    }
   
    /**
     * Updates a coveragestore with the properties of another.
     *
     * @param original The coveragestore being updated.
     * @param update The coveragestore containing the new values.
     */
    public void updateCoverageStore( CoverageStoreInfo original, CoverageStoreInfo update ) {
        update( original, update, CoverageStoreInfo.class );
    }
   
    /**
     * Updates a feature type with the properties of another.
     *
     * @param original The feature type being updated.
     * @param update The feature type containing the new values.
     */
    public void updateFeatureType( FeatureTypeInfo original, FeatureTypeInfo update ) {
        update( original, update, FeatureTypeInfo.class );
    }
   
    /**
     * Updates a coverage with the properties of another.
     *
     * @param original The coverage being updated.
     * @param update The coverage containing the new values.
     */
    public void updateCoverage( CoverageInfo original, CoverageInfo update ) {
        update( original, update, CoverageInfo.class );
    }
   
    /**
     * Updates a layer with the properties of another.
     *
     * @param original The layer being updated.
     * @param update The layer containing the new values.
     */
    public void updateLayer( LayerInfo original, LayerInfo update ) {
        update( original, update, LayerInfo.class );
    }
   
    /**
     * Updates a layer group with the properties of another.
     *
     * @param original The layer group being updated.
     * @param update The layer group containing the new values.
     */
    public void updateLayerGroup( LayerGroupInfo original, LayerGroupInfo update ) {
        update( original, update, LayerGroupInfo.class );
    }
   
    /**
     * Updates a style with the properties of another.
     *
     * @param original The style being updated.
     * @param update The style containing the new values.
     */
    public void updateStyle( StyleInfo original, StyleInfo update ) {
        update( original, update, StyleInfo.class );
    }
   
    /**
     * Update method which uses reflection to grab property values from one
     * object and set them on another.
     * <p>
     * Null values from the <tt>update</tt> object are ignored.
     * </p>
     */
    <T> void update( T original, T update, Class<T> clazz ) {
        ClassProperties properties = OwsUtils.getClassProperties( clazz );
        for ( String p : properties.properties() ) {
            Method getter = properties.getter( p, null );
            if ( getter == null ) {
                continue; // should not really happen
            }
           
            Class type = getter.getReturnType();
            Method setter = properties.setter( p, type );
           
            //do a check for read only before calling the getter to avoid an uneccesary call
            if ( setter == null &&
                    !(Collection.class.isAssignableFrom( type ) || Map.class.isAssignableFrom( type ))) {
                //read only
                continue;
            }
           
            try {
                Object newValue = getter.invoke( update, null );
                if( newValue == null ) {
                    continue;
                    //TODO: make this a flag whether to overwrite with null values
                }
                if ( setter == null ){
                    if ( Collection.class.isAssignableFrom( type ) ) {
                        updateCollectionProperty( original, (Collection) newValue, getter );
                    }
                    else if ( Map.class.isAssignableFrom( type ) ) {
                        updateMapProperty( original, (Map) newValue, getter );
                    }
                    continue;
                }

                setter.invoke( original, newValue );
            }
            catch( Exception e ) {
                throw new RuntimeException( e );
            }
        }
    }

    /**
     * Helper method for updating a collection based property.
     */
    void updateCollectionProperty( Object object, Collection newValue, Method getter ) throws Exception {
        Collection oldValue = (Collection) getter.invoke( object, null );
        oldValue.clear();
        oldValue.addAll( newValue );
    }

    /**
     * Helper method for updating a map based property.
     */

    void updateMapProperty( Object object, Map newValue, Method getter ) throws Exception {
        Map oldValue = (Map) getter.invoke(object, null);
        oldValue.clear();
        oldValue.putAll( newValue );
    }

    /**
     * Builds a new data store.
     */
    public DataStoreInfo buildDataStore( String name ) {
        DataStoreInfo info = catalog.getFactory().createDataStore();
        buildStore(info,name);
           
        return info;
    }
   
    /**
     * Builds a new coverage store.
     */
    public CoverageStoreInfo buildCoverageStore( String name ) {
        CoverageStoreInfo info = catalog.getFactory().createCoverageStore();
        buildStore(info,name);
           
        return info;
    }
   
    /**
     * Builds a store.
     * <p>
     * The workspace of the resulting store is {@link #workspace} if set, else the
     * default workspace from the catalog.
     * </p>
     */
    void buildStore( StoreInfo info, String name ) {

        info.setName( name );
        info.setEnabled( true );
       
        //set workspace, falling back on default if none specified
        if ( workspace != null ) {
            info.setWorkspace( workspace );
        }
        else {
            info.setWorkspace( catalog.getDefaultWorkspace() );
        }
    }
   
   
    /**
     * Builds a {@link FeatureTypeInfo} from the current datastore and the specified type name
     * <p>
     * The resulting object is not added to the catalog, it must be done by the calling code
     * after the fact.
     * </p>
     */
    public FeatureTypeInfo buildFeatureType( Name typeName ) throws Exception {
        if ( store == null || !( store instanceof DataStoreInfo ) ) {
            throw new IllegalStateException( "Data store not set.");
        }
       
        DataStoreInfo dstore = (DataStoreInfo) store;
        return buildFeatureType(dstore.getDataStore(null).getFeatureSource(typeName));
    }
   
    /**
     * Builds a feature type from a geotools feature source. The resulting {@link FeatureTypeInfo}
     * will still miss the bounds and might miss the SRS. Use {@link #lookupSRS(FeatureTypeInfo, true)} and
     * {@link #setupBounds(FeatureTypeInfo)} if you want to force them in (and spend time accordingly)
     * <p>
     * The resulting object is not added to the catalog, it must be done by the calling code
     * after the fact.
     * </p>
     */
    public FeatureTypeInfo buildFeatureType( FeatureSource featureSource )  {
        if ( store == null || !( store instanceof DataStoreInfo ) ) {
            throw new IllegalStateException( "Data store not set.");
        }
       
        FeatureType featureType = featureSource.getSchema();
       
        FeatureTypeInfo ftinfo = catalog.getFactory().createFeatureType();
        ftinfo.setStore( store );
        ftinfo.setEnabled(true);
       
        //naming
        ftinfo.setNativeName( featureType.getName().getLocalPart() );
        ftinfo.setName( featureType.getName().getLocalPart() );
       
        WorkspaceInfo workspace = store.getWorkspace();
        NamespaceInfo namespace = catalog.getNamespaceByPrefix( workspace.getName() );
        if ( namespace == null ) {
            namespace = catalog.getDefaultNamespace();
        }
       
        ftinfo.setNamespace( namespace );
       
        CoordinateReferenceSystem crs = featureType.getCoordinateReferenceSystem();
        if (crs == null && featureType.getGeometryDescriptor() != null) {
            crs = featureType.getGeometryDescriptor().getCoordinateReferenceSystem();
        }
        ftinfo.setNativeCRS(crs);
       
        // srs look and set (by default we just use fast lookup)
        try {
            lookupSRS(ftinfo, false);
        } catch (Exception e) {
            LOGGER.log(Level.WARNING, "SRS lookup failed", e);
        }
        setupProjectionPolicy(ftinfo);
       
        // quick metadata
        ftinfo.setTitle(featureType.getName().getLocalPart());
       
        return ftinfo;
    }
   
    /**
     * Sets the projection policy for a resource based on the following rules:
     * <ul>
     *   <li>If getSRS() returns a non null value it is set to {@Link ProjectionPolicy#FORCE_DECLARED}
     *   <li>If getSRS() returns a null value it is set to {@link ProjectionPolicy#NONE}
     * </ul>
     *
     * TODO: make this method smarter, and compare the native crs to figure out if prejection
     * actually needs to be done, and sync it up with setting proj policy on coverage layers.
     */
    public void setupProjectionPolicy(ResourceInfo rinfo) {
         if ( rinfo.getSRS() != null ) {
             rinfo.setProjectionPolicy(ProjectionPolicy.FORCE_DECLARED);
         }
         else {
             rinfo.setProjectionPolicy(ProjectionPolicy.NONE);
         }
    }

   
    /**
     * Given a {@link ResourceInfo} this method:
     * <ul>
     *   <li>computes, if missing, the native bounds (warning, this might be very expensive,
     *       cases in which this case take minutes are not uncommon if the data set is made
     *       of million of features)</li>
     *   <li>updates, if possible, the geographic bounds accordingly by
     *       re-projecting the native bounds into WGS84</li>
     * @param ftinfo
     * @throws IOException if computing the native bounds fails or if a transformation error occurs
     *         during the geographic bounds computation
     */
    public void setupBounds(ResourceInfo rinfo) throws IOException  {
        // setup the native bbox if needed
        if(rinfo.getNativeBoundingBox() == null) {
            ReferencedEnvelope bounds = getNativeBounds(rinfo);
            rinfo.setNativeBoundingBox(bounds);
        }
       
        // setup the geographic bbox if missing and we have enough info
        rinfo.setLatLonBoundingBox(getLatLonBounds(rinfo.getNativeBoundingBox(), rinfo.getCRS()));
    }

    /**
     * Computes the geographic bounds of a {@link ResourceInfo} by reprojecting the
     * available native bounds
     * @param rinfo
     * @return the geographic bounds, or null if the native bounds are not available
     * @throws IOException
     */
    public ReferencedEnvelope getLatLonBounds(ReferencedEnvelope nativeBounds, CoordinateReferenceSystem declaredCRS) throws IOException {
        if(nativeBounds != null && declaredCRS != null) {
            // make sure we use the declared CRS, not the native one, the may differ
            if ( !CRS.equalsIgnoreMetadata( DefaultGeographicCRS.WGS84, declaredCRS) ) {
                //transform
                try {
                    ReferencedEnvelope bounds = new ReferencedEnvelope(nativeBounds, declaredCRS);
                    return bounds.transform( DefaultGeographicCRS.WGS84, true );
                } catch( Exception e ) {
                    throw (IOException) new IOException("transform error").initCause( e );
                }
            } else {
                return new ReferencedEnvelope(nativeBounds, DefaultGeographicCRS.WGS84);
            }
        }
        return null;
    }

    /**
     * Computes the native bounds of a {@link ResourceInfo} taking into account the nature
     * of the data and the reprojection policy in act
     * @param rinfo
     * @return the native bounds, or null if the could not be computed
     * @throws IOException
     */
    public ReferencedEnvelope getNativeBounds(ResourceInfo rinfo) throws IOException {
        ReferencedEnvelope bounds = null;
        if(rinfo instanceof FeatureTypeInfo) {
            FeatureTypeInfo ftinfo = (FeatureTypeInfo) rinfo;
           
            // bounds
            bounds = ftinfo.getFeatureSource(null, null).getBounds();
           
            // fix the native bounds if necessary, some datastores do
            // not build a proper referenced envelope
            CoordinateReferenceSystem crs = ftinfo.getNativeCRS();
            if(bounds != null && bounds.getCoordinateReferenceSystem() == null && crs != null) {
                bounds = new ReferencedEnvelope(bounds, crs);
            }
           
            if(bounds != null) {
                // expansion factor if the bounds are empty or one dimensional
                double expandBy = 1; // 1 meter
                if(bounds.getCoordinateReferenceSystem() instanceof GeographicCRS) {
                    expandBy = 0.0001;
                }
                if(bounds.getWidth() == 0 || bounds.getHeight() == 0) {
                    bounds.expandBy(expandBy);
                }
            }
           
        } else if(rinfo instanceof CoverageInfo) {
            // the coverage bounds computation path is a bit more linear, the
            // readers always return the bounds and in the proper CRS (afaik)
            CoverageInfo cinfo = (CoverageInfo) rinfo;
            AbstractGridCoverage2DReader reader = (AbstractGridCoverage2DReader) cinfo.getGridCoverageReader(null, null);
            bounds = new ReferencedEnvelope(reader.getOriginalEnvelope());
        }
       
        // apply the bounds, taking into account the reprojection policy if need be
        if (rinfo.getProjectionPolicy() == ProjectionPolicy.REPROJECT_TO_DECLARED && bounds != null) {
            try {
                bounds = bounds.transform(rinfo.getCRS(), true);
                GridGeometry grid = ((CoverageInfo) rinfo).getGrid();
                ((CoverageInfo) rinfo).setGrid(new GridGeometry2D(grid.getGridRange(),grid.getGridToCRS(), rinfo.getCRS()));
            } catch(Exception e) {
                throw (IOException) new IOException("transform error").initCause(e);
            }
        }
       
        return bounds;
    }
   
   
    /**
     * Looks up and sets the SRS based on the feature type info native
     * {@link CoordinateReferenceSystem}
     * @param ftinfo
     * @param extensive if true an extenstive lookup will be performed (more accurate, but
     *        might take various seconds)
     * @throws IOException
     */
    public void lookupSRS(FeatureTypeInfo ftinfo, boolean extensive) throws IOException {
        CoordinateReferenceSystem crs = ftinfo.getNativeCRS();
        if ( crs == null ) {
            crs = ftinfo.getFeatureType().getCoordinateReferenceSystem();
        }
        if ( crs != null ) {
            try {
                Integer code = CRS.lookupEpsgCode(crs, extensive);
                if(code != null)
                    ftinfo.setSRS("EPSG:" + code);
            } catch (FactoryException e) {
                throw (IOException) new IOException().initCause( e );
            }
        }
    }
   
    /**
     * Initializes a feature type object setting any info that has not been set.
     */
    public void initFeatureType(FeatureTypeInfo featureType) throws Exception {
        if ( featureType.getCatalog() == null ) {
            featureType.setCatalog( catalog );
        }
        if ( featureType.getNativeName() == null && featureType.getName() != null ) {
            featureType.setNativeName( featureType.getName() );
        }
        if ( featureType.getNativeName() != null && featureType.getName() == null ) {
            featureType.setName( featureType.getNativeName() );
        }
        // setup the srs if missing
        if ( featureType.getSRS() == null ) {
            lookupSRS(featureType, true);
        }
        if (featureType.getProjectionPolicy() == null) {
            setupProjectionPolicy(featureType);
        }
       
        // deal with bounding boxes as possible
        CoordinateReferenceSystem crs = featureType.getCRS();
        if(featureType.getLatLonBoundingBox() == null && featureType.getNativeBoundingBox() == null) {
            // both missing, we compute them
            setupBounds(featureType);
        } else if(featureType.getLatLonBoundingBox() == null) {
            // native available but geographic to be computed
            setupBounds(featureType);
        } else if(featureType.getNativeBoundingBox() == null && crs != null) {
            // we know the geographic and we can reproject back to native
            ReferencedEnvelope boundsLatLon = featureType.getLatLonBoundingBox();
            featureType.setNativeBoundingBox(boundsLatLon.transform(crs, true));
        }
    }
   
    /**
     * Builds the default coverage contained in the current store
     * @return
     * @throws Exception
     */
    public CoverageInfo buildCoverage() throws Exception {
        if ( store == null || !( store instanceof CoverageStoreInfo ) ) {
            throw new IllegalStateException( "Coverage store not set.");
        }
       
        CoverageStoreInfo csinfo = (CoverageStoreInfo) store;
        AbstractGridCoverage2DReader reader = (AbstractGridCoverage2DReader) catalog.getResourcePool().getGridCoverageReader(csinfo, null);
       
        if(reader == null)
            throw new Exception ("Unable to acquire a reader for this coverage with format: " + csinfo.getFormat().getName());
       
        return buildCoverage(reader);
    }
   
    /**
     * Builds a coverage from a geotools grid coverage reader.
     */
    public CoverageInfo buildCoverage( AbstractGridCoverage2DReader reader ) throws Exception {
        if ( store == null || !( store instanceof CoverageStoreInfo ) ) {
            throw new IllegalStateException( "Coverage store not set.");
        }
       
        CoverageStoreInfo csinfo = (CoverageStoreInfo) store;
        CoverageInfo cinfo = catalog.getFactory().createCoverage();
       
        cinfo.setStore( csinfo );
        cinfo.setEnabled(true);
       
        WorkspaceInfo workspace = store.getWorkspace();
        NamespaceInfo namespace = catalog.getNamespaceByPrefix( workspace.getName() );
        if ( namespace == null ) {
            namespace = catalog.getDefaultNamespace();
        }
        cinfo.setNamespace(namespace);
       
        CoordinateReferenceSystem nativeCRS = reader.getCrs();
        cinfo.setNativeCRS(nativeCRS);
       
        // mind the default projection policy, Coverages do not have a flexible
        // handling as feature types, they do reproject if the native srs is set,
        // force if missing
        if ( nativeCRS != null && !nativeCRS.getIdentifiers().isEmpty()) {
            cinfo.setSRS( nativeCRS.getIdentifiers().toArray()[0].toString() );
            cinfo.setProjectionPolicy(ProjectionPolicy.REPROJECT_TO_DECLARED);
        }
        if(nativeCRS == null) {
            cinfo.setProjectionPolicy(ProjectionPolicy.FORCE_DECLARED);
        }
       
       
        GeneralEnvelope envelope = reader.getOriginalEnvelope();
        cinfo.setNativeBoundingBox( new ReferencedEnvelope( envelope ) );
        cinfo.setLatLonBoundingBox( new ReferencedEnvelope(CoverageStoreUtils.getWGS84LonLatEnvelope(envelope)) );
       
        GridEnvelope originalRange=reader.getOriginalGridRange();
        cinfo.setGrid(new GridGeometry2D(originalRange,reader.getOriginalGridToWorld(PixelInCell.CELL_CENTER),nativeCRS));

        ///////////////////////////////////////////////////////////////////////
        //
        // Now reading a fake small GridCoverage just to retrieve meta
                // information about bands:
        //
        // - calculating a new envelope which is 1/20 of the original one
        // - reading the GridCoverage subset
        //
        ///////////////////////////////////////////////////////////////////////
        Format format = csinfo.getFormat();
        final GridCoverage2D gc;

       
        final ParameterValueGroup readParams = format.getReadParameters();
        final Map parameters = CoverageUtils.getParametersKVP(readParams);
        final int minX=originalRange.getLow(0);
        final int minY=originalRange.getLow(1);
        final int width=originalRange.getSpan(0);
        final int height=originalRange.getSpan(1);
        final int maxX=minX+(width<=5?width:5);
        final int maxY=minY+(height<=5?height:5);
       
        //we have to be sure that we are working against a valid grid range.
        final GridEnvelope2D testRange= new GridEnvelope2D(minX,minY,maxX,maxY);
       
        //build the corresponding envelope
        final MathTransform gridToWorldCorner =  reader.getOriginalGridToWorld(PixelInCell.CELL_CORNER);
        final GeneralEnvelope testEnvelope =CRS.transform(gridToWorldCorner,new GeneralEnvelope(testRange.getBounds()));
        testEnvelope.setCoordinateReferenceSystem(nativeCRS);
       
        parameters.put(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString(),
            new GridGeometry2D(testRange, testEnvelope));

        //try to read this coverage
        gc = (GridCoverage2D) reader.read(CoverageUtils.getParameters(readParams, parameters,
                    true));
        if(gc==null){
            throw new Exception ("Unable to acquire test coverage for format:"+ format.getName());
        }
       
        cinfo.getDimensions().addAll( getCoverageDimensions(gc.getSampleDimensions()));
           
        //TODO:
        //dimentionNames = getDimensionNames(gc);
        /*
        StringBuilder cvName =null;
        int count = 0;
        while (true) {
            final StringBuilder key = new StringBuilder(gc.getName().toString());
            if (count > 0) {
                key.append("_").append(count);
            }

            Map coverages = dataConfig.getCoverages();
            Set cvKeySet = coverages.keySet();
            boolean key_exists = false;

            for (Iterator it = cvKeySet.iterator(); it.hasNext();) {
                String cvKey = ((String) it.next()).toLowerCase();
                if (cvKey.endsWith(key.toString().toLowerCase())) {
                    key_exists = true;
                }
            }

            if (!key_exists) {
                cvName = key;
                break;
            } else {
                count++;
            }
        }

        String name = cvName.toString();
        */
        String name = gc.getName().toString();
        cinfo.setName(name);
        cinfo.setTitle(name);
        cinfo.setDescription(new StringBuffer("Generated from ").append(format.getName()).toString() );
       
        //keywords
        cinfo.getKeywords().add("WCS");
        cinfo.getKeywords().add(format.getName());
        cinfo.getKeywords().add(name);
       
        //native format name
        cinfo.setNativeFormat(format.getName());
        cinfo.getMetadata().put( "dirName", new StringBuffer(store.getName()).append("_").append(name).toString());
       
        //request SRS's
        if ((gc.getCoordinateReferenceSystem2D().getIdentifiers() != null)
                && !gc.getCoordinateReferenceSystem2D().getIdentifiers().isEmpty()) {
            cinfo.getRequestSRS().add(((Identifier) gc.getCoordinateReferenceSystem2D().getIdentifiers()
                                            .toArray()[0]).toString());
        }
       
        //response SRS's
        if ((gc.getCoordinateReferenceSystem2D().getIdentifiers() != null)
                && !gc.getCoordinateReferenceSystem2D().getIdentifiers().isEmpty()) {
            cinfo.getResponseSRS().add(((Identifier) gc.getCoordinateReferenceSystem2D().getIdentifiers()
                                             .toArray()[0]).toString());
        }
       
        //supported formats
        final List formats = CoverageStoreUtils.listDataFormats();
        for (Iterator i = formats.iterator(); i.hasNext();) {
            final Format fTmp = (Format) i.next();
            final  String fName = fTmp.getName();

            if (fName.equalsIgnoreCase("WorldImage")) {
                // TODO check if coverage can encode Format
                cinfo.getSupportedFormats().add("GIF");
                cinfo.getSupportedFormats().add("PNG");
                cinfo.getSupportedFormats().add("JPEG");
                cinfo.getSupportedFormats().add("TIFF");
            } else if (fName.toLowerCase().startsWith("geotiff")) {
                // TODO check if coverage can encode Format
                cinfo.getSupportedFormats().add("GEOTIFF");
            } else {
                // TODO check if coverage can encode Format
                cinfo.getSupportedFormats().add(fName);
            }
        }

        //interpolation methods
        cinfo.setDefaultInterpolationMethod("nearest neighbor");
        cinfo.getInterpolationMethods().add("nearest neighbor");
        cinfo.getInterpolationMethods().add("bilinear");
        cinfo.getInterpolationMethods().add("bicubic");
       
        //read parameters
        cinfo.getParameters().putAll( CoverageUtils.getParametersKVP(format.getReadParameters()) );
       
        return cinfo;
    }

    List<CoverageDimensionInfo> getCoverageDimensions(GridSampleDimension[] sampleDimensions) {
   
        final int length = sampleDimensions.length;
        List<CoverageDimensionInfo> dims = new ArrayList<CoverageDimensionInfo>();
       
        for (int i = 0; i < length; i++) {
            CoverageDimensionInfo dim = catalog.getFactory().createCoverageDimension();
            dim.setName(sampleDimensions[i].getDescription().toString(Locale.getDefault()));

            StringBuffer label = new StringBuffer("GridSampleDimension".intern());
            final Unit uom = sampleDimensions[i].getUnits();

            if (uom != null) {
                label.append("(".intern());
                parseUOM(label, uom);
                label.append(")".intern());
            }

            label.append("[".intern());
            label.append(sampleDimensions[i].getMinimumValue());
            label.append(",".intern());
            label.append(sampleDimensions[i].getMaximumValue());
            label.append("]".intern());
           
            dim.setDescription(label.toString());
            dim.setRange(sampleDimensions[i].getRange());

            final List<Category> categories = sampleDimensions[i].getCategories();
            if(categories!=null) {
                for (Category cat:categories) {
   
                    if ((cat != null) && cat.getName().toString().equalsIgnoreCase("no data")) {
                        double min = cat.getRange().getMinimum();
                        double max = cat.getRange().getMaximum();
   
                        dim.getNullValues().add( min );
                        if ( min != max ) {
                            dim.getNullValues().add( max );
                        }
                    }
                }
            }
           
            dims.add(dim);
        }

        return dims;
    }
   
    void parseUOM(StringBuffer label, Unit uom) {
        String uomString = uom.toString();
        uomString = uomString.replaceAll("�", "^2");
        uomString = uomString.replaceAll("�", "^3");
        uomString = uomString.replaceAll("�", "A");
        uomString = uomString.replaceAll("�", "");
        label.append(uomString);
    }
   
    /**
     * Builds a layer for a feature type.
     * <p>
     * The resulting object is not added to the catalog, it must be done by the calling code
     * after the fact.
     * </p>
     */
    public LayerInfo buildLayer( FeatureTypeInfo featureType ) throws IOException {
        //also create a layer for the feautre type
        LayerInfo layer = buildLayer( (ResourceInfo) featureType );
       
        StyleInfo style = getDefaultStyle(featureType);
        layer.setDefaultStyle(style);
       
        return layer;
    }
   
    /**
     * Builds a layer for a coverage.
     * <p>
     * The resulting object is not added to the catalog, it must be done by the calling code
     * after the fact.
     * </p>
     */
    public LayerInfo buildLayer( CoverageInfo coverage ) throws IOException {
        LayerInfo layer = buildLayer((ResourceInfo)coverage);
       
        layer.setDefaultStyle(getDefaultStyle(coverage));
       
        return layer;
    }
   
    /**
     * Returns the default style for the specified resource, or null if the layer is vector
     * and geometryless
     * @param resource
     * @return
     * @throws IOException
     */
    public StyleInfo getDefaultStyle(ResourceInfo resource) throws IOException {
        // raster wise, only one style
        if(resource instanceof CoverageInfo)
            return catalog.getStyleByName(StyleInfo.DEFAULT_RASTER);
    
        // for vectors we depend on the the nature of the default geometry
        String styleName;
        FeatureTypeInfo featureType = (FeatureTypeInfo) resource;
        GeometryDescriptor gd = featureType.getFeatureType().getGeometryDescriptor();
        if(gd == null)
            return null;
           
        Class gtype = gd.getType().getBinding();
        if ( Point.class.isAssignableFrom(gtype) || MultiPoint.class.isAssignableFrom(gtype)) {
            styleName = StyleInfo.DEFAULT_POINT;
        }
        else if ( LineString.class.isAssignableFrom(gtype) || MultiLineString.class.isAssignableFrom(gtype)) {
            styleName = StyleInfo.DEFAULT_LINE;
        }
        else if ( Polygon.class.isAssignableFrom(gtype) || MultiPolygon.class.isAssignableFrom(gtype)) {
            styleName = StyleInfo.DEFAULT_POLYGON;
        } else {
            //fall back to point
            styleName = StyleInfo.DEFAULT_POINT;
        }
       
        return catalog.getStyleByName( styleName );
    }
   
    LayerInfo buildLayer( ResourceInfo resource ) {
        LayerInfo layer = catalog.getFactory().createLayer();
        layer.setResource( resource );
        layer.setName( resource.getName() );
        layer.setEnabled(true);
       
        // setup the layer type
        if ( layer.getResource() instanceof FeatureTypeInfo ) {
            layer.setType( LayerInfo.Type.VECTOR );
        } else if ( layer.getResource() instanceof CoverageInfo ) {
            layer.setType( LayerInfo.Type.RASTER );
        }
       
        return layer;
    }
   
    /**
     * Calculates the bounds of a layer group specifying a particular crs.
     */
    public void calculateLayerGroupBounds( LayerGroupInfo lg, CoordinateReferenceSystem crs )
        throws Exception {
       
        if ( lg.getLayers().isEmpty() ) {
            return;
        }
       
        LayerInfo l = lg.getLayers().get( 0 );
        ReferencedEnvelope bounds = transform( l.getResource().getLatLonBoundingBox(), crs );
       
        for ( int i = 1; i < lg.getLayers().size(); i++ ) {
            l = lg.getLayers().get( i );
            bounds.expandToInclude( transform( l.getResource().getLatLonBoundingBox(), crs ) );
        }
        lg.setBounds( bounds );
    }
   
    /**
     * Calculates the bounds of a layer group by aggregating the bounds of each layer.
     * TODO: move this method to a utility class, it should not be on a builder.
     */
    public void calculateLayerGroupBounds( LayerGroupInfo lg ) throws Exception {
        if ( lg.getLayers().isEmpty() ) {
            return;
        }
       
        LayerInfo l = lg.getLayers().get( 0 );
        ReferencedEnvelope bounds = l.getResource().boundingBox();
        boolean latlon = false;
        if ( bounds == null ) {
            bounds = l.getResource().getLatLonBoundingBox();
            latlon = true;
        }
       
        if ( bounds == null ) {
            throw new IllegalArgumentException( "Could not calculate bounds from layer with no bounds, " + l.getName());
        }
       
        for ( int i = 1; i < lg.getLayers().size(); i++ ) {
            l = lg.getLayers().get( i );
           
            ReferencedEnvelope re;
            if ( latlon ) {
                re = l.getResource().getLatLonBoundingBox();
            }
            else {
                re = l.getResource().boundingBox();
            }
           
            re = transform( re, bounds.getCoordinateReferenceSystem() );
            if ( re == null ) {
                throw new IllegalArgumentException( "Could not calculate bounds from layer with no bounds, " + l.getName());
            }
            bounds.expandToInclude( re );
        }
      
        lg.setBounds( bounds );
    }
   
    /**
     * Helper method for transforming an envelope.
     */
    ReferencedEnvelope transform( ReferencedEnvelope e, CoordinateReferenceSystem crs ) throws TransformException, FactoryException {
        if ( !CRS.equalsIgnoreMetadata( crs, e.getCoordinateReferenceSystem() ) ) {
            return e.transform( crs, true );
        }
        return e;
    }
   
    //
    //remove methods
    //
   
    /**
     * Removes a workspace from the catalog.
     * <p>
     * The <tt>recursive</tt> flag controls whether objects linked to the workspace such as stores
     * should also be deleted.
     * </p>
     */
    public void removeWorkspace( WorkspaceInfo workspace, boolean recursive ) {
        if ( recursive ) {
            workspace.accept(new CascadeDeleteVisitor(catalog));
        } else {
            catalog.remove( workspace );
        }
    }
   
    /**
     * Removes a store from the catalog.
     * <p>
     * The <tt>recursive</tt> flag controls whether objects linked to the store such as resources
     * should also be deleted.
     * </p>
     */
    public void removeStore( StoreInfo store, boolean recursive ) {
        if ( recursive ) {
            store.accept(new CascadeDeleteVisitor(catalog));
        } else {
            catalog.remove( store );
        }
    }
   
    /**
     * Removes a resource from the catalog.
     * <p>
     * The <tt>recursive</tt> flag controls whether objects linked to the resource such as layers
     * should also be deleted.
     * </p>
     */
    public void removeResource( ResourceInfo resource, boolean recursive ) {
        if ( recursive ) {
            resource.accept(new CascadeDeleteVisitor(catalog));
        } else {
            catalog.remove( resource );
        }
    }
   
    /**
     * Reattaches a serialized {@link StoreInfo} to the catalog
     */
    public void attach(StoreInfo storeInfo) {
        storeInfo = ModificationProxy.unwrap(storeInfo);
        ((StoreInfoImpl) storeInfo).setCatalog(catalog);
    }
   
    /**
     * Reattaches a serialized {@link ResourceInfo} to the catalog
     */
    public void attach(ResourceInfo resourceInfo) {
        resourceInfo = ModificationProxy.unwrap(resourceInfo);
        ((ResourceInfoImpl) resourceInfo).setCatalog(catalog);
    }
   
    /**
     * Reattaches a serialized {@link LayerInfo} to the catalog
     */
    public void attach(LayerInfo layerInfo) {
        attach(layerInfo.getResource());
    }
   
    /**
     * Reattaches a serialized {@link MapInfo} to the catalog
     */
    public void attach(MapInfo mapInfo) {
        // hmmm... mapInfo has a list of layers inside? Not names?
        for (LayerInfo layer : mapInfo.getLayers()) {
            attach(layer);
        }
    }
   
    /**
     * Reattaches a serialized {@link LayerGroupInfo} to the catalog
     */
    public void attach(LayerGroupInfo groupInfo) {
        for (LayerInfo layer : groupInfo.getLayers()) {
            attach(layer);
        }
        for (StyleInfo style : groupInfo.getStyles()) {
            if(style != null)
                attach(style);
        }
    }
   
    /**
     * Reattaches a serialized {@link StyleInfo} to the catalog
     */
    public void attach(StyleInfo styleInfo) {
        styleInfo = ModificationProxy.unwrap(styleInfo);
        ((StyleInfoImpl) styleInfo).setCatalog(catalog);
    }
   
    /**
     * Reattaches a serialized {@link NamespaceInfo} to the catalog
     */
    public void attach(NamespaceInfo nsInfo) {
        // nothing to do
    }
   
    /**
     * Reattaches a serialized {@link WorkspaceInfo} to the catalog
     */
    public void attach(WorkspaceInfo wsInfo) {
        // nothing to do
    }
}
TOP

Related Classes of org.geoserver.catalog.CatalogBuilder

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.