/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jgrasstools.gears.utils.features;
import java.util.ArrayList;
import java.util.List;
import org.jgrasstools.gears.utils.geometry.EGeometryType;
import org.jgrasstools.gears.utils.geometry.GeometryUtilities;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.prep.PreparedGeometry;
import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory;
/**
* A wrapper for features that helps out with lots of stuff.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
public class FeatureMate {
private final SimpleFeature feature;
private Geometry geometry;
private PreparedGeometry preparedGeometry;
private Envelope envelope;
/**
* Constructor.
*
* @param feature the feature to wrap.
*/
public FeatureMate( SimpleFeature feature ) {
this.feature = feature;
}
public SimpleFeature getFeature() {
return feature;
}
/**
* Apply a buffer to the geometry and use that as new {@link Geometry}.
*
* @param buffer the buffer to apply.
*/
public void useBuffer( double buffer ) {
Geometry tmpGeometry = getGeometry();
geometry = tmpGeometry.buffer(buffer);
}
/**
* Getter for the geometry.
*
* @return the geometry of the feature.
*/
public Geometry getGeometry() {
if (geometry == null)
geometry = (Geometry) feature.getDefaultGeometry();
return geometry;
}
/**
* Getter for the {@link Envelope}.
*
* @return the envelope.
*/
public Envelope getEnvelope() {
if (envelope == null)
envelope = getGeometry().getEnvelopeInternal();
return envelope;
}
/**
* Getter for the list of attribute names.
*
* @return the list of attribute names.
*/
public List<String> getAttributesNames() {
SimpleFeatureType featureType = feature.getFeatureType();
List<AttributeDescriptor> attributeDescriptors = featureType.getAttributeDescriptors();
List<String> attributeNames = new ArrayList<String>();
for( AttributeDescriptor attributeDescriptor : attributeDescriptors ) {
String name = attributeDescriptor.getLocalName();
attributeNames.add(name);
}
return attributeNames;
}
/**
* Gets an attribute from the feature table, adapting to the supplied class.
*
* @param attrName the attribute name to pick.
* @param adaptee the class to adapt to.
* @return the adapted value if possible.
*/
@SuppressWarnings("unchecked")
public <T> T getAttribute( String attrName, Class<T> adaptee ) {
if (attrName == null) {
return null;
}
if (adaptee == null) {
adaptee = (Class<T>) String.class;
}
Object attribute = feature.getAttribute(attrName);
if (attribute == null) {
return null;
}
if (attribute instanceof Number) {
Number num = (Number) attribute;
if (adaptee.isAssignableFrom(Double.class)) {
return adaptee.cast(num.doubleValue());
} else if (adaptee.isAssignableFrom(Float.class)) {
return adaptee.cast(num.floatValue());
} else if (adaptee.isAssignableFrom(Integer.class)) {
return adaptee.cast(num.intValue());
} else if (adaptee.isAssignableFrom(Long.class)) {
return adaptee.cast(num.longValue());
} else if (adaptee.isAssignableFrom(String.class)) {
return adaptee.cast(num.toString());
} else {
throw new IllegalArgumentException();
}
} else if (attribute instanceof String) {
if (adaptee.isAssignableFrom(Double.class)) {
try {
Double parsed = Double.parseDouble((String) attribute);
return adaptee.cast(parsed);
} catch (Exception e) {
return null;
}
} else if (adaptee.isAssignableFrom(Float.class)) {
try {
Float parsed = Float.parseFloat((String) attribute);
return adaptee.cast(parsed);
} catch (Exception e) {
return null;
}
} else if (adaptee.isAssignableFrom(Integer.class)) {
try {
Integer parsed = Integer.parseInt((String) attribute);
return adaptee.cast(parsed);
} catch (Exception e) {
return null;
}
} else if (adaptee.isAssignableFrom(String.class)) {
return adaptee.cast(attribute);
} else {
throw new IllegalArgumentException();
}
} else if (attribute instanceof Geometry) {
return null;
} else {
throw new IllegalArgumentException("Can't adapt attribute of type: " + attribute.getClass().getCanonicalName());
}
}
/**
* Check for intersection.
*
* @param geometry the geometry to check against.
* @param usePrepared use prepared geometry.
* @return true if the geometries intersect.
*/
public boolean intersects( Geometry geometry, boolean usePrepared ) {
if (!getEnvelope().intersects(geometry.getEnvelopeInternal())) {
return false;
}
if (usePrepared) {
if (preparedGeometry == null) {
preparedGeometry = PreparedGeometryFactory.prepare(getGeometry());
}
return preparedGeometry.intersects(geometry);
} else {
return getGeometry().intersects(geometry);
}
}
/**
* Check for cover.
*
* @param geometry the geometry to check against.
* @param usePrepared use prepared geometry.
* @return true if the current geometries covers the supplied one.
*/
public boolean covers( Geometry geometry, boolean usePrepared ) {
if (!getEnvelope().covers(geometry.getEnvelopeInternal())) {
return false;
}
if (usePrepared) {
if (preparedGeometry == null) {
preparedGeometry = PreparedGeometryFactory.prepare(getGeometry());
}
return preparedGeometry.covers(geometry);
} else {
return getGeometry().covers(geometry);
}
}
/**
* Proxy for the intersection method.
*
* @param geometry the geometry to intersect.
* @return the intersection geometry.
*/
public Geometry intersection( Geometry geometry ) {
return getGeometry().intersection(geometry);
}
/**
* Tries to convert the internal geometry to a {@link LineString}.
*
* <p>This works only for Polygon and Lines features.
* <p>From this moment on the internal geometry (as got by the {@link #getGeometry()})
* will be the line type.
* <p>To get the original geometry one can simply call {@link #resetGeometry()}.
*
* @throws IllegalArgumentException in the case the geometry is a point.
*/
public void convertToLine() throws IllegalArgumentException {
EGeometryType geometryType = EGeometryType.forGeometry(getGeometry());
switch( geometryType ) {
case MULTIPOLYGON:
case POLYGON:
// convert to line
Coordinate[] tmpCoords = geometry.getCoordinates();
geometry = GeometryUtilities.gf().createLineString(tmpCoords);
// reset prepared geometry
preparedGeometry = null;
break;
case LINE:
case MULTILINE:
// do nothing, is already line
break;
default:
throw new IllegalArgumentException("Points not supported");
}
}
/**
* Tries to convert the internal geometry to a {@link com.vividsolutions.jts.geom.Point}.
*
* <p>From this moment on the internal geometry (as got by the {@link #getGeometry()})
* will be the point type.
* <p>To get the original geometry one can simply call {@link #resetGeometry()}.
*/
public void convertToPoint() {
EGeometryType geometryType = EGeometryType.forGeometry(getGeometry());
switch( geometryType ) {
case MULTIPOLYGON:
case POLYGON:
case LINE:
case MULTILINE:
// convert to line
Coordinate[] tmpCoords = geometry.getCoordinates();
geometry = GeometryUtilities.gf().createMultiPoint(tmpCoords);
// reset prepared geometry
preparedGeometry = null;
break;
default:
break;
}
}
/**
* Resets the geometry, so that at the next call of {@link #getGeometry()} the original geometry is reread.
*/
public void resetGeometry() {
geometry = null;
preparedGeometry = null;
}
public String toString() {
List<String> attributesNames = getAttributesNames();
StringBuilder sb = new StringBuilder();
sb.append("FeatureMate: [\n");
sb.append("\t").append("the_geom").append(" = ").append(getGeometry()).append("\n");
for( String name : attributesNames ) {
if (name.equals("the_geom")) {
continue;
}
String attribute = getAttribute(name, String.class);
sb.append("\t").append(name).append(" = ").append(attribute).append("\n");
}
sb.append("]\n");
return sb.toString();
}
}