All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotools.feature.simple.SimpleFeatureImpl Maven / Gradle / Ivy

Go to download

The main module contains the GeoTools public interfaces that are used by other GeoTools modules (and GeoTools applications). Where possible we make use industry standard terms as provided by OGC and ISO standards. The formal GeoTools public api consists of gt-metadata, jts and the gt-main module. The main module contains the default implementations that are available provided to other GeoTools modules using our factory system. Factories are obtained from an appropriate FactoryFinder, giving applications a chance configure the factory used using the Factory Hints facilities. FilterFactory ff = CommonFactoryFinder.getFilterFactory(); Expression expr = ff.add( expression1, expression2 ); If you find yourself using implementation specific classes chances are you doing it wrong: Expression expr = new AddImpl( expression1, expressiom2 );

The newest version!
/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2002-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.simple;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.geotools.feature.GeometryAttributeImpl;
import org.geotools.feature.type.AttributeDescriptorImpl;
import org.geotools.feature.type.Types;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.geometry.jts.coordinatesequence.CoordinateSequences;
import org.geotools.util.Converters;
import org.geotools.util.SuppressFBWarnings;
import org.geotools.util.Utilities;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.GeometryAttribute;
import org.opengis.feature.IllegalAttributeException;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.feature.type.Name;
import org.opengis.filter.identity.FeatureId;
import org.opengis.filter.identity.Identifier;
import org.opengis.geometry.BoundingBox;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
 * An implementation of {@link SimpleFeature} geared towards speed and backed by an Object[].
 *
 * @author Justin
 * @author Andrea Aime
 */
public class SimpleFeatureImpl implements SimpleFeature {

    protected FeatureId id;
    protected SimpleFeatureType featureType;
    /** The actual values held by this feature */
    protected Object[] values;
    /** The attribute name -> position index */
    protected Map index;
    /** The set of user data attached to the feature (lazily created) */
    protected Map userData;
    /** The set of user data attached to each attribute (lazily created) */
    protected Map[] attributeUserData;

    /** Whether this feature is self validating or not */
    protected boolean validating;

    /** Builds a new feature based on the provided values and feature type */
    public SimpleFeatureImpl(List values, SimpleFeatureType featureType, FeatureId id) {
        this(values.toArray(), featureType, id, false, index(featureType));
    }

    /**
     * Fast construction of a new feature.
     *
     * 

The object takes ownership of the provided value array, do not modify after calling the * constructor */ public SimpleFeatureImpl( Object[] values, SimpleFeatureType featureType, FeatureId id, boolean validating) { this(values, featureType, id, validating, index(featureType)); } /** * Fast construction of a new feature. * *

The object takes ownership of the provided value array, do not modify after calling the * constructor * * @param index - attribute name to value index mapping */ public SimpleFeatureImpl( Object[] values, SimpleFeatureType featureType, FeatureId id, boolean validating, Map index) { this.id = id; this.featureType = featureType; this.values = values; this.validating = validating; this.index = index; // if we're self validating, do validation right now if (validating) validate(); } /** * Generate (or lookup) an "index" mapping attribute to index for the provided FeatureType. * *

This method will use the following: * *

    *
  • SimpleFeatureTypeImpl.index; or *
  • Check getUserData().get("indexLookup"); *
  • or call {@link SimpleFeatureTypeImpl#buildIndex(SimpleFeatureType)} to generate the * required index *
* * @return mapping between attribute name to attribute index */ @SuppressWarnings("unchecked") private static Map index(SimpleFeatureType featureType) { // in the most common case reuse the map cached in the feature type if (featureType instanceof SimpleFeatureTypeImpl) { return ((SimpleFeatureTypeImpl) featureType).index; } else { synchronized (featureType) { // if we're not lucky, rebuild the index completely... Object cache = featureType.getUserData().get("indexLookup"); if (cache instanceof Map) { return (Map) cache; } else { Map generatedIndex = SimpleFeatureTypeImpl.buildIndex(featureType); featureType.getUserData().put("indexLookup", generatedIndex); return generatedIndex; } } } } public FeatureId getIdentifier() { return id; } public String getID() { return id.getID(); } public int getNumberOfAttributes() { return values.length; } public Object getAttribute(int index) throws IndexOutOfBoundsException { return values[index]; } public Object getAttribute(String name) { Integer idx = index.get(name); if (idx != null) return getAttribute(idx); else return null; } public Object getAttribute(Name name) { return getAttribute(name.getLocalPart()); } public int getAttributeCount() { return values.length; } public List getAttributes() { return new ArrayList(Arrays.asList(values)); } public Object getDefaultGeometry() { // should be specified in the index as the default key (null) Integer idx = index.get(null); Object defaultGeometry = idx != null ? getAttribute(idx) : null; // not found? do we have a default geometry at all? if (defaultGeometry == null) { GeometryDescriptor geometryDescriptor = featureType.getGeometryDescriptor(); if (geometryDescriptor != null) { Integer defaultGeomIndex = index.get(geometryDescriptor.getName().getLocalPart()); defaultGeometry = getAttribute(defaultGeomIndex.intValue()); } } return defaultGeometry; } public SimpleFeatureType getFeatureType() { return featureType; } public SimpleFeatureType getType() { return featureType; } public void setAttribute(int index, Object value) throws IndexOutOfBoundsException { // first do conversion Object converted = Converters.convert( value, getFeatureType().getDescriptor(index).getType().getBinding()); // if necessary, validation too if (validating) Types.validate(featureType.getDescriptor(index), converted); // finally set the value into the feature values[index] = converted; } public void setAttribute(String name, Object value) { final Integer idx = index.get(name); if (idx == null) throw new IllegalAttributeException("Unknown attribute " + name); setAttribute(idx.intValue(), value); } public void setAttribute(Name name, Object value) { setAttribute(name.getLocalPart(), value); } public void setAttributes(List values) { for (int i = 0; i < this.values.length; i++) { this.values[i] = values.get(i); } } public void setAttributes(Object[] values) { setAttributes(Arrays.asList(values)); } public void setDefaultGeometry(Object geometry) { Integer geometryIndex = index.get(null); if (geometryIndex != null) { setAttribute(geometryIndex, geometry); } } public BoundingBox getBounds() { // TODO: cache this value CoordinateReferenceSystem crs = featureType.getCoordinateReferenceSystem(); Envelope bounds = ReferencedEnvelope.create(crs); for (Object o : values) { if (o instanceof Geometry) { Geometry g = (Geometry) o; // TODO: check userData for crs... and ensure its of the same // crs as the feature type if (bounds.isNull()) { bounds.init(JTS.bounds(g, crs)); } else { bounds.expandToInclude(JTS.bounds(g, crs)); } } } return (BoundingBox) bounds; } public GeometryAttribute getDefaultGeometryProperty() { GeometryDescriptor geometryDescriptor = featureType.getGeometryDescriptor(); GeometryAttribute geometryAttribute = null; if (geometryDescriptor != null) { Object defaultGeometry = getDefaultGeometry(); geometryAttribute = new GeometryAttributeImpl(defaultGeometry, geometryDescriptor, null); } return geometryAttribute; } public void setDefaultGeometryProperty(GeometryAttribute geometryAttribute) { if (geometryAttribute != null) setDefaultGeometry(geometryAttribute.getValue()); else setDefaultGeometry(null); } public Collection getProperties() { return new AttributeList(); } public Collection getProperties(Name name) { return getProperties(name.getLocalPart()); } public Collection getProperties(String name) { final Integer idx = index.get(name); if (idx != null) { // cast temporarily to a plain collection to avoid type problems with generics Collection c = Collections.singleton(new Attribute(idx)); return c; } else { return Collections.emptyList(); } } public Property getProperty(Name name) { return getProperty(name.getLocalPart()); } public Property getProperty(String name) { final Integer idx = index.get(name); if (idx == null) { return null; } else { int index = idx.intValue(); AttributeDescriptor descriptor = featureType.getDescriptor(index); if (descriptor instanceof GeometryDescriptor) { return new GeometryAttributeImpl( values[index], (GeometryDescriptor) descriptor, null); } else { return new Attribute(index); } } } public Collection getValue() { return getProperties(); } public void setValue(Collection values) { int i = 0; for (Property p : values) { this.values[i++] = p.getValue(); } } public void setValue(Object newValue) { setValue((Collection) newValue); } /** @see org.opengis.feature.Attribute#getDescriptor() */ public AttributeDescriptor getDescriptor() { return new AttributeDescriptorImpl( featureType, featureType.getName(), 0, Integer.MAX_VALUE, true, null); } /** * @return same name than this feature's {@link SimpleFeatureType} * @see org.opengis.feature.Property#getName() */ public Name getName() { return featureType.getName(); } public boolean isNillable() { return true; } public Map getUserData() { if (userData == null) userData = new HashMap(); return userData; } @Override public boolean hasUserData() { return userData != null && !userData.isEmpty(); } /** * returns a unique code for this feature * * @return A unique int */ public int hashCode() { return id.hashCode() * featureType.hashCode(); } /** * override of equals. Returns if the passed in object is equal to this. * * @param obj the Object to test for equality. * @return true if the object is equal, false otherwise. */ @SuppressFBWarnings("NP_NULL_ON_SOME_PATH") public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof SimpleFeatureImpl)) { return false; } SimpleFeatureImpl feat = (SimpleFeatureImpl) obj; // this check shouldn't exist, by contract, // all features should have an ID. if (id == null) { if (feat.getIdentifier() != null) { return false; } } if (!id.equals(feat.getIdentifier())) { return false; } if (!feat.getFeatureType().equals(featureType)) { return false; } for (int i = 0, ii = values.length; i < ii; i++) { Object otherAtt = feat.getAttribute(i); if (values[i] == null) { if (otherAtt != null) { return false; } } else { if (values[i] instanceof Geometry) { if (!(otherAtt instanceof Geometry)) { return false; } else if (!CoordinateSequences.equalsND( (Geometry) values[i], (Geometry) otherAtt)) { return false; } } else if (!values[i].equals(otherAtt)) { return false; } } } return true; } public void validate() { for (int i = 0; i < values.length; i++) { AttributeDescriptor descriptor = getType().getDescriptor(i); Types.validate(descriptor, values[i]); } } /** Live collection backed directly on the value array */ class AttributeList extends AbstractList { public Property get(int index) { AttributeDescriptor descriptor = featureType.getDescriptor(index); if (descriptor instanceof GeometryDescriptor) { return new SimpleGeometryAttribute(index); } return new Attribute(index); } public Attribute set(int index, Property element) { values[index] = element.getValue(); return null; } public int size() { return values.length; } } public String toString() { StringBuffer sb = new StringBuffer("SimpleFeatureImpl:"); sb.append(getType().getName().getLocalPart()); sb.append("="); sb.append(getValue()); return sb.toString(); } /** Attribute that delegates directly to the value array */ class Attribute implements org.opengis.feature.Attribute { int index; Attribute(int index) { this.index = index; } public Identifier getIdentifier() { return null; } public AttributeDescriptor getDescriptor() { return featureType.getDescriptor(index); } public AttributeType getType() { return featureType.getType(index); } public Name getName() { return getDescriptor().getName(); } public Map getUserData() { // lazily create the user data holder if (attributeUserData == null) attributeUserData = new HashMap[values.length]; // lazily create the attribute user data if (attributeUserData[index] == null) attributeUserData[index] = new HashMap(); return attributeUserData[index]; } public Object getValue() { return values[index]; } public boolean isNillable() { return getDescriptor().isNillable(); } public void setValue(Object newValue) { values[index] = newValue; } /** * Override of hashCode; uses descriptor name to agree with AttributeImpl * * @return hashCode for this object. */ public int hashCode() { return 37 * getDescriptor().hashCode() + (37 * (getValue() == null ? 0 : getValue().hashCode())); } /** * Override of equals. * * @param obj the object to be tested for equality. * @return whether other is equal to this attribute Type. */ public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Attribute)) { return false; } Attribute other = (Attribute) obj; if (!Utilities.equals(getDescriptor(), other.getDescriptor())) { return false; } if (!Utilities.deepEquals(getValue(), other.getValue())) { return false; } return Utilities.equals(getIdentifier(), other.getIdentifier()); } public void validate() { Types.validate(getDescriptor(), values[index]); } public String toString() { StringBuffer sb = new StringBuffer("SimpleFeatureImpl.Attribute: "); sb.append(getDescriptor().getName().getLocalPart()); if (!getDescriptor() .getName() .getLocalPart() .equals(getDescriptor().getType().getName().getLocalPart()) || id != null) { sb.append("<"); sb.append(getDescriptor().getType().getName().getLocalPart()); if (id != null) { sb.append(" id="); sb.append(id); } sb.append(">"); } sb.append("="); sb.append(values[index]); return sb.toString(); } } class SimpleGeometryAttribute extends Attribute implements GeometryAttribute { SimpleGeometryAttribute(int index) { super(index); } @Override public GeometryType getType() { return (GeometryType) super.getType(); } @Override public GeometryDescriptor getDescriptor() { return (GeometryDescriptor) super.getDescriptor(); } @Override public BoundingBox getBounds() { ReferencedEnvelope bounds = new ReferencedEnvelope(featureType.getCoordinateReferenceSystem()); Object value = getAttribute(index); if (value instanceof Geometry) { bounds.init(((Geometry) value).getEnvelopeInternal()); } return bounds; } @Override public void setBounds(BoundingBox bounds) { // do nothing, this property is strictly derived. Shall throw unsupported operation // exception? } @Override public int hashCode() { return 17 * super.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof SimpleGeometryAttribute)) { return false; } return super.equals(obj); } } }