Package org.geotools.feature

Source Code of org.geotools.feature.FeatureTypes

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*    This library is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*    Lesser General Public License for more details.
*/
package org.geotools.feature;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.geotools.factory.FactoryRegistryException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeImpl;
import org.geotools.filter.LengthFunction;
import org.geotools.geometry.jts.JTS;
import org.geotools.util.Utilities;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.feature.type.PropertyType;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.Filter;
import org.opengis.filter.PropertyIsGreaterThan;
import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
import org.opengis.filter.PropertyIsLessThan;
import org.opengis.filter.PropertyIsLessThanOrEqualTo;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;

import com.vividsolutions.jts.geom.Geometry;

/**
* Utility methods for working against the FeatureType interface.
* <p>
* Many methods from DataUtilities should be refractored here.
* </p>
* <p>
* Responsibilities:
* <ul>
* <li>Schema construction from String spec
* <li>Schema Force CRS
* </ul>
*
* @author Jody Garnett, Refractions Research
* @since 2.1.M3
*
*
* @source $URL$
*/
public class FeatureTypes {

  /** the default namespace for feature types */
  //public static final URI = GMLSchema.NAMESPACE;
  public static final URI DEFAULT_NAMESPACE;
  static {
    URI uri;
    try {
      uri = new URI( "http://www.opengis.net/gml" );
    }
    catch (URISyntaxException e) {
      uri = null//will never happen
    }
    DEFAULT_NAMESPACE = uri;
  }

  /** abstract base type for all feature types */
    public final static SimpleFeatureType ABSTRACT_FEATURE_TYPE;
    static {
        SimpleFeatureType featureType = null;
        try {
            featureType = FeatureTypes.newFeatureType(null, "Feature",new URI("http://www.opengis.net/gml"), true);
        }
        catch(Exception e ) {
            //shold not happen
        }
        ABSTRACT_FEATURE_TYPE = featureType;
    }

  /** default feature collection name */
  final public static NameImpl DEFAULT_TYPENAME =
    new NameImpl( "AbstractFeatureCollectionType", DEFAULT_NAMESPACE.toString() );

  /** represent an unbounded field length */
    final public static int ANY_LENGTH = -1;

    /** An feature type with no attributes */
    public static final SimpleFeatureType EMPTY = new SimpleFeatureTypeImpl(
        new NameImpl( "Empty" ), Collections.EMPTY_LIST, null, false, Collections.EMPTY_LIST, null, null
    );

    /**
     * This is a 'suitable replacement for extracting the expected field length of an attribute
     * absed on its "facets" (ie Filter describing type restrictions);
     * <p>
     * This code is copied from the ShapefileDataStore where it was written (probably by dzwiers).
     * Cholmes is providing documentation.
     * </p>
     *
     * @param type the AttributeType
     *
     * @return an int indicating the max length of field in characters, or ANY_LENGTH
     */
    public static int getFieldLength( PropertyDescriptor descriptor ) {
        PropertyType type = descriptor.getType();
        Integer length = null;
        while( type != null ){
            // TODO: We should really go through all the restrictions and find
            // the minimum of all the length restrictions; for now we assume an
            // override behavior.
            for ( Filter f : type.getRestrictions()) {
                Integer filterLength = null;
                try {
                    if(f == null) {
                        continue;
                    }
                    if (f instanceof PropertyIsLessThan) {
                        BinaryComparisonOperator cf =  (BinaryComparisonOperator) f;
                        if (cf.getExpression1() instanceof LengthFunction) {
                            filterLength = cf.getExpression2().evaluate(null, Integer.class) - 1;
                        }
                    } else if (f instanceof PropertyIsLessThanOrEqualTo) {
                        BinaryComparisonOperator cf =  (BinaryComparisonOperator) f;
                        if (cf.getExpression1() instanceof LengthFunction) {
                            filterLength = cf.getExpression2().evaluate(null, Integer.class);
                        }
                    } else if(f instanceof PropertyIsGreaterThan) {
                        BinaryComparisonOperator cf =  (BinaryComparisonOperator) f;
                        if (cf.getExpression2() instanceof LengthFunction) {
                            filterLength = cf.getExpression1().evaluate(null, Integer.class) - 1;
                        }
                    } else if (f instanceof PropertyIsGreaterThanOrEqualTo) {
                        BinaryComparisonOperator cf =  (BinaryComparisonOperator) f;
                        if (cf.getExpression2() instanceof LengthFunction) {
                            filterLength = cf.getExpression1().evaluate(null, Integer.class);
                        }
                    }
                } catch (NullPointerException e) {
                    // was not an integer eh? Continue, worst case we'll return ANY_LENGTH
                }
               
                if(filterLength != null) {
                    if(length == null || filterLength < length) {
                        length = filterLength;
                    }
                }
            }
            type = type.getSuper();
        }
       
        return length != null ? length : ANY_LENGTH;
    }

    /**
     * Forces the specified CRS on all geometry attributes
     * @param schema the original schema
     * @param crs the forced crs
     * @return
     * @throws SchemaException
     */
    public static SimpleFeatureType transform( SimpleFeatureType schema, CoordinateReferenceSystem crs )
        throws SchemaException {
        return transform(schema, crs, false);
    }

    /**
     * Forces the specified CRS on geometry attributes (all or some, depends on the parameters).
     * @param schema the original schema
     * @param crs the forced crs
     * @param forceOnlyMissing if true, will force the specified crs only on the attributes that
     *        do miss one
     * @return
     * @throws SchemaException
     */
    public static SimpleFeatureType transform( SimpleFeatureType schema, CoordinateReferenceSystem crs, boolean forceOnlyMissing)
            throws SchemaException {
        SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
        tb.setName(schema.getTypeName());
        tb.setNamespaceURI( schema.getName().getNamespaceURI() );
        tb.setAbstract(schema.isAbstract());

        GeometryDescriptor defaultGeometryType = null;
        for( int i = 0; i < schema.getAttributeCount(); i++ ) {
            AttributeDescriptor attributeType = schema.getDescriptor(i);
            if (attributeType instanceof GeometryDescriptor) {
                GeometryDescriptor geometryType = (GeometryDescriptor) attributeType;
                AttributeDescriptor forced;

                tb.descriptor( geometryType );
                if ( !forceOnlyMissing || geometryType.getCoordinateReferenceSystem() == null ) {
                    tb.crs( crs );
                }

                tb.add( geometryType.getLocalName(), geometryType.getType().getBinding() );
            } else {
                tb.add(attributeType);
            }
        }
        if (schema.getGeometryDescriptor() != null) {
            tb.setDefaultGeometry(schema.getGeometryDescriptor().getLocalName());
        }

        tb.setSuperType((SimpleFeatureType) schema.getSuper());

        return tb.buildFeatureType();
    }

    /**
     * Applies transform to all geometry attribute.
     *
     * @param feature Feature to be transformed
     * @param schema Schema for target transformation - transform( schema, crs )
     * @param transform MathTransform used to transform coordinates - reproject( crs, crs )
     * @return transformed Feature of type schema
     * @throws TransformException
     * @throws MismatchedDimensionException
     * @throws IllegalAttributeException
     */
    public static SimpleFeature transform( SimpleFeature feature, SimpleFeatureType schema, MathTransform transform )
            throws MismatchedDimensionException, TransformException, IllegalAttributeException {
        feature = SimpleFeatureBuilder.copy(feature);

        GeometryDescriptor geomType = schema.getGeometryDescriptor();
        Geometry geom = (Geometry) feature.getAttribute(geomType.getLocalName());

        geom = JTS.transform(geom, transform);

        feature.setAttribute(geomType.getLocalName(), geom);

        return feature;
    }

    /**
     * The most specific way to create a new FeatureType.
     *
     * @param types The AttributeTypes to create the FeatureType with.
     * @param name The typeName of the FeatureType. Required, may not be null.
     * @param ns The namespace of the FeatureType. Optional, may be null.
     * @param isAbstract True if this created type should be abstract.
     * @param superTypes A Collection of types the FeatureType will inherit from. Currently, all
     *        types inherit from feature in the opengis namespace.
     * @return A new FeatureType created from the given arguments.
     * @throws FactoryRegistryException If there are problems creating a factory.
     * @throws SchemaException If the AttributeTypes provided are invalid in some way.
     */
    public static SimpleFeatureType newFeatureType( AttributeDescriptor[] types, String name, URI ns,
            boolean isAbstract, SimpleFeatureType[] superTypes ) throws FactoryRegistryException,
            SchemaException {
        return newFeatureType(types, name, ns, isAbstract, superTypes, null);
    }

    /**
     * The most specific way to create a new FeatureType.
     *
     * @param types The AttributeTypes to create the FeatureType with.
     * @param name The typeName of the FeatureType. Required, may not be null.
     * @param ns The namespace of the FeatureType. Optional, may be null.
     * @param isAbstract True if this created type should be abstract.
     * @param superTypes A Collection of types the FeatureType will inherit from. Currently, all
     *        types inherit from feature in the opengis namespace.
     * @return A new FeatureType created from the given arguments.
     * @throws FactoryRegistryException If there are problems creating a factory.
     * @throws SchemaException If the AttributeTypes provided are invalid in some way.
     */
    public static SimpleFeatureType newFeatureType( AttributeDescriptor[] types, String name, URI ns,
            boolean isAbstract, SimpleFeatureType[] superTypes, AttributeDescriptor defaultGeometry )
            throws FactoryRegistryException, SchemaException {
        SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();

        tb.setName(name);
        tb.setNamespaceURI(ns);
        tb.setAbstract(isAbstract);
        if(types != null) {
            tb.addAll(types);
        }

        if ( defaultGeometry != null ) {
            //make sure that the default geometry was one of the types specified
            boolean add = true;
            for ( int i = 0; i < types.length; i++ ) {
                if (types[i] == defaultGeometry) {
                    add = false;
                    break;
                }
            }
            if ( add ) {
                tb.add(defaultGeometry);
            }
            tb.setDefaultGeometry(defaultGeometry.getLocalName());
        }
        if ( superTypes != null && superTypes.length > 0) {
            if ( superTypes.length > 1 ) {
                throw new SchemaException("Can only specify a single super type");
            }
            tb.setSuperType(superTypes[0]);

        }
        else {
            //use the default super type
            tb.setSuperType(ABSTRACT_FEATURE_TYPE);
        }
        return (SimpleFeatureType) tb.buildFeatureType();
    }

    /**
     * The most specific way to create a new FeatureType.
     *
     * @param types The AttributeTypes to create the FeatureType with.
     * @param name The typeName of the FeatureType. Required, may not be null.
     * @param ns The namespace of the FeatureType. Optional, may be null.
     * @param isAbstract True if this created type should be abstract.
     * @param superTypes A Collection of types the FeatureType will inherit from. Currently, all
     *        types inherit from feature in the opengis namespace.
     * @return A new FeatureType created from the given arguments.
     * @throws FactoryRegistryException If there are problems creating a factory.
     * @throws SchemaException If the AttributeTypes provided are invalid in some way.
     */
    public static SimpleFeatureType newFeatureType( AttributeDescriptor[] types, String name, URI ns,
            boolean isAbstract, SimpleFeatureType[] superTypes, GeometryDescriptor   defaultGeometry )
            throws FactoryRegistryException, SchemaException {
        return newFeatureType(types,name,ns,isAbstract,superTypes,(AttributeDescriptor)defaultGeometry);
    }

    /**
     * Create a new FeatureType with the given AttributeTypes. A short cut for calling
     * <code>newFeatureType(types,name,ns,isAbstract,null)</code>.
     *
     * @param types The AttributeTypes to create the FeatureType with.
     * @param name The typeName of the FeatureType. Required, may not be null.
     * @param ns The namespace of the FeatureType. Optional, may be null.
     * @param isAbstract True if this created type should be abstract.
     * @return A new FeatureType created from the given arguments.
     * @throws FactoryRegistryException If there are problems creating a factory.
     * @throws SchemaException If the AttributeTypes provided are invalid in some way.
     */
    public static SimpleFeatureType newFeatureType( AttributeDescriptor[] types, String name, URI ns,
            boolean isAbstract ) throws FactoryRegistryException, SchemaException {
        return newFeatureType(types, name, ns, isAbstract, null);
    }

    /**
     * Create a new FeatureType with the given AttributeTypes. A short cut for calling
     * <code>newFeatureType(types,name,ns,false,null)</code>.
     *
     * @param types The AttributeTypes to create the FeatureType with.
     * @param name The typeName of the FeatureType. Required, may not be null.
     * @param ns The namespace of the FeatureType. Optional, may be null.
     * @return A new FeatureType created from the given arguments.
     * @throws FactoryRegistryException If there are problems creating a factory.
     * @throws SchemaException If the AttributeTypes provided are invalid in some way.
     */
    public static SimpleFeatureType newFeatureType( AttributeDescriptor[] types, String name, URI ns )
            throws FactoryRegistryException, SchemaException {
        return newFeatureType(types, name, ns, false);
    }

    /**
     * Create a new FeatureType with the given AttributeTypes. A short cut for calling
     * <code>newFeatureType(types,name,null,false,null)</code>. Useful for test cases or
     * datasources which may not allow a namespace.
     *
     * @param types The AttributeTypes to create the FeatureType with.
     * @param name The typeName of the FeatureType. Required, may not be null.
     * @return A new FeatureType created from the given arguments.
     * @throws FactoryRegistryException If there are problems creating a factory.
     * @throws SchemaException If the AttributeTypes provided are invalid in some way.
     */
    public static SimpleFeatureType newFeatureType( AttributeDescriptor[] types, String name )
            throws FactoryRegistryException, SchemaException {
        return newFeatureType(types, name, DEFAULT_NAMESPACE, false);
    }

    /**
     * Walks up the type hierarchy of the feature returning all super types of the specified feature
     * type. The search terminates when a non-FeatureType or null is found. The original featureType
     * is not included as an ancestor, only its strict ancestors.
     */
    public static List<FeatureType> getAncestors(FeatureType featureType) {
        List<FeatureType> ancestors = new ArrayList<FeatureType>();
        while (featureType.getSuper() instanceof FeatureType) {
            FeatureType superType = (FeatureType) featureType.getSuper();
            ancestors.add(superType);
            featureType = superType;
        }
        return ancestors;
    }

    /**
     * A query of the the types ancestor information.
     * <p>
     * This utility method may be used as common implementation for
     * <code>FeatureType.isDecendedFrom( namespace, typeName )</code>, however for specific uses,
     * such as GML, an implementor may be able to provide a more efficient implemenation based on
     * prior knolwege.
     * </p>
     * <p>
     * This is a proper check, if the provided FeatureType matches the given namespace and typename
     * it is <b>not </b> considered to be decended from itself.
     * </p>
     *
     * @param featureType
     *            typeName with parentage in question
     * @param namespace
     *            namespace to match against, or null for a "wildcard"
     * @param typeName
     *            typename to match against, or null for a "wildcard"
     * @return true if featureType is a decendent of the indicated namespace & typeName
     */
    public static boolean isDecendedFrom(FeatureType featureType, URI namespace, String typeName) {
        if (featureType == null)
            return false;
        List<FeatureType> ancestors = getAncestors(featureType);
        for (FeatureType superType : ancestors) {
            if (namespace == null) {
                // dont match on namespace
                if (Utilities.equals(superType.getName().getLocalPart(), typeName)) {
                    return true;
                }
            } else {
                if (Utilities.equals(superType.getName().getNamespaceURI(), namespace.toString())
                        && Utilities.equals(superType.getName().getLocalPart(), typeName)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean isDecendedFrom(FeatureType featureType, FeatureType isParentType) {
        try {
            return isDecendedFrom(featureType, new URI(isParentType.getName().getNamespaceURI()),
                    isParentType.getName().getLocalPart());
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    /** Exact equality based on typeNames, namespace, attributes and ancestors */
    public static boolean equals( SimpleFeatureType typeA, SimpleFeatureType typeB ) {
        return equals(typeA, typeB, false);
    }
   
    /** Exact equality based on typeNames, namespace, attributes and ancestors, including the user maps contents */
    public static boolean equalsExact( SimpleFeatureType typeA, SimpleFeatureType typeB ) {
        return equals(typeA, typeB, true);
    }
   
    /** Exact equality based on typeNames, namespace, attributes and ancestors */
    static boolean equals( SimpleFeatureType typeA, SimpleFeatureType typeB, boolean compareUserMaps) {
        if (typeA == typeB)
            return true;

        if (typeA == null || typeB == null) {
            return false;
        }
       
        if(compareUserMaps) {
            if(!equals(typeA.getUserData(), typeB.getUserData()))
                return false;
        }
       
        return equalsId(typeA, typeB)
                && equals(typeA.getAttributeDescriptors(), typeB.getAttributeDescriptors(), compareUserMaps) &&
                equalsAncestors( typeA, typeB );
    }
   
    static boolean equals( List attributesA, List attributesB, boolean compareUserMaps) {
        return equals(
            (AttributeDescriptor[]) attributesA.toArray(new AttributeDescriptor[attributesA.size()]),
            (AttributeDescriptor[]) attributesB.toArray(new AttributeDescriptor[attributesB.size()]),
            compareUserMaps);
    }

    public static boolean equals( List attributesA, List attributesB) {
        return equals(attributesA, attributesB, false);
    }
   
    public static boolean equalsExact( List attributesA, List attributesB) {
        return equals(attributesA, attributesB, true);
    }
   
    public static boolean equals( AttributeDescriptor attributesA[], AttributeDescriptor attributesB[] ) {
        return equals(attributesA, attributesB, false);
    }
   
    public static boolean equalsExact( AttributeDescriptor attributesA[], AttributeDescriptor attributesB[] ) {
        return equals(attributesA, attributesB, true);
    }

    static boolean equals( AttributeDescriptor attributesA[], AttributeDescriptor attributesB[], boolean compareUserMaps ) {
        if (attributesA.length != attributesB.length)
            return false;

        for( int i = 0, length = attributesA.length; i < length; i++ ) {
            if (!equals(attributesA[i], attributesB[i], compareUserMaps))
                return false;
        }
        return true;
    }
    /**
     * This method depends on the correct implementation of FeatureType equals
     * <p>
     * We may need to write an implementation that can detect cycles,
     * </p>
     *
     * @param typeA
     * @param typeB
     */
    public static boolean equalsAncestors( SimpleFeatureType typeA, SimpleFeatureType typeB ) {
        return ancestors( typeA ).equals( ancestors(typeB) );
    }

    public static Set ancestors( SimpleFeatureType featureType ) {
        if (featureType == null || getAncestors(featureType).isEmpty()) {
            return Collections.EMPTY_SET;
        }
        return new HashSet(getAncestors(featureType));
    }
   
    public static boolean equals( AttributeDescriptor a, AttributeDescriptor b) {
        return equals(a, b, false);
    }
   
    public static boolean equalsExact( AttributeDescriptor a, AttributeDescriptor b) {
        return equals(a, b, true);
    }

    static boolean equals( AttributeDescriptor a, AttributeDescriptor b, boolean compareUserMaps) {
        if(a == b)
            return true;
       
        if(a == null)
            return true;
       
        if(!a.equals(b))
            return false;
       
        if(compareUserMaps) {
            if(!equals(a.getUserData(), b.getUserData()))
                return false;
            if(!equals(a.getType().getUserData(), b.getType().getUserData()))
                return false;
        }
       
        return true;
           
    }
   
    /**
     * Tolerant map comparison. Two maps are considered to be equal if they express the
     * same content. So for example two null maps are equal, but also a null and an
     * empty one are
     */
    static boolean equals(Map a, Map b) {
        if(a == b)
            return true;
       
        // null == null handled above
        if(a == null || b == null)
            return false;
       
        if(a != null && a.size() == 0 && b == null)
            return true;
       
        if(b != null && b.size() == 0 && a == null)
            return true;
       
        return a.equals(b);
    }
   
    /** Quick check of namespace and typename */
    public static boolean equalsId( SimpleFeatureType typeA, SimpleFeatureType typeB ) {
        if (typeA == typeB)
            return true;

        if (typeA == null || typeB == null) {
            return false;
        }

        String typeNameA = typeA.getTypeName();
        String typeNameB = typeB.getTypeName();
        if (typeNameA == null && typeNameB != null)
            return false;
        else if (!typeNameA.equals(typeNameB))
            return false;

        String namespaceA = typeA.getName().getNamespaceURI();
        String namespaceB = typeB.getName().getNamespaceURI();
        if(namespaceA == null && namespaceB == null)
            return true;
       
        if (namespaceA == null && namespaceB != null)
            return false;
        else if (!namespaceA.equals(namespaceB))
            return false;

        return true;
    }

}
TOP

Related Classes of org.geotools.feature.FeatureTypes

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.