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

org.metacsp.multi.spatial.DE9IM.DE9IMRelation Maven / Gradle / Ivy

There is a newer version: 1.3.5
Show newest version
package org.metacsp.multi.spatial.DE9IM;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;

import org.metacsp.framework.BinaryConstraint;
import org.metacsp.framework.Constraint;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.IntersectionMatrix;

/**
 * This class represents spatial relations between geometric shapes (variables of type {@link GeometricShapeVariable}).
 * These shapes can be points, strings of segments, or polygons. The relations constitute a topological mode called
 * the Dimensionally Extended nine-Intersection Model (DE-9IM) (see (Clementini et al., 1993) and https://en.wikipedia.org/wiki/DE-9IM).
 * If the shapes are all polygons, then a Jointly Exclusive and Pairwise Disjoint (JEPD) subset of these relations is
 * equivalent to RCC8 (Cohn et al., 2007).
 * 
 * @author Federico Pecora
 *
 */
public class DE9IMRelation extends BinaryConstraint {
	
	private static final long serialVersionUID = 5511066094460985805L;

	/**
	 * The 10 meaningful relations in DE-9IM. The subset
	 * { {@code Contains}, {@code Within}, {@code Covers}, {@code CoveredBy}, {@code Disjoint}, {@code Overlaps}, {@code Touches}, {@code Equals} }
	 * is Jointly Exclusive and Pairwise Disjoint (JEPD) and equivalent to RCC8 (Cohn et al., 2007).
	 */
	public static enum Type {Contains, Within, Covers, CoveredBy, Intersects, Disjoint, Crosses, Overlaps, Touches, Equals};

	//Subset for polygons, equivalent to RCC8 (Cohn, 2007)
	private static HashSet RCC8Types = new HashSet(Arrays.asList("Contains", "Within", "Covers", "CoveredBy", "Disjoint", "Overlaps", "Touches", "Equals"));

	private Type[] types = null;

	/**
	 * Assess whether this relation is of a given {@link Type}.
	 * @param relationType The {@link Type} to compare against.
	 * @return true iff this relation is of the given {@link Type}.
	 */
	public boolean isRelation(DE9IMRelation.Type relationType) {
		for (Type t : types) if (t.equals(relationType)) return true;
		return false;
	}
	
	/**
	 * Get the DE-9IM relation(s) existing between two {@link GeometricShapeVariable}s.
	 * @param gv1 The first {@link GeometricShapeVariable} (the source of the directed edge).
	 * @param gv2 The second {@link GeometricShapeVariable} (the destination of the directed edge).
	 * @return The DE-9IM relation(s) existing between the two given {@link GeometricShapeVariable}s.
	 */
	public static Type[] getRelations(GeometricShapeVariable gv1, GeometricShapeVariable gv2) {
		return getRelations(gv1, gv2, false);		
	}
	
	/**
	 * Get the RCC8 relation(s) existing between two {@link GeometricShapeVariable}s.
	 * @param gv1 The first {@link GeometricShapeVariable} (the source of the directed edge).
	 * @param gv2 The second {@link GeometricShapeVariable} (the destination of the directed edge).
	 * @return The RCC8 relation(s) existing between the two given {@link GeometricShapeVariable}s.
	 */
	public static Type[] getRCC8Relations(GeometricShapeVariable gv1, GeometricShapeVariable gv2) {
		return getRelations(gv1, gv2, true);		
	}
	
	/**
	 * Check if the spatial relation between two {@link GeometricShapeVariable}s is of a given type.
	 * @param gv1 The source {@link GeometricShapeVariable}.
	 * @param gv2 The destination {@link GeometricShapeVariable}.
	 * @param t The type of relation to check.
	 * @return true iff the given variables are in the given relation.
	 */
	public static boolean isRelation(GeometricShapeVariable gv1, GeometricShapeVariable gv2, DE9IMRelation.Type t) {
		try {
			String methodName = t.name().substring(0, 1).toLowerCase() + t.name().substring(1);
			Method m = Geometry.class.getMethod(methodName, Geometry.class);
			Geometry g1 = ((GeometricShapeDomain)gv1.getDomain()).getGeometry();
			Geometry g2 = ((GeometricShapeDomain)gv2.getDomain()).getGeometry();
			return ((Boolean)m.invoke(g1, g2));
		}
		catch (NoSuchMethodException e) { e.printStackTrace(); }
		catch (SecurityException e) { e.printStackTrace(); }
		catch (IllegalAccessException e) { e.printStackTrace(); }
		catch (IllegalArgumentException e) { e.printStackTrace(); }
		catch (InvocationTargetException e) { e.printStackTrace(); }
		return false;
	}
	
	/**
	 * Find out whether a relation is a {@link DE9IMRelation} belongs to the RCC8 subset.
	 * @param t The relation to test
	 * @return true iff the given {@link DE9IMRelation} belongs to the RCC8 subset.
	 */
	public static boolean isRCC8Relation(DE9IMRelation.Type t) {
		return RCC8Types.contains(t.name());
	}

	private static Type[] getRelations(GeometricShapeVariable gv1, GeometricShapeVariable gv2, boolean rcc8Relations) {
		ArrayList ret = new ArrayList();
		Geometry g1 = ((GeometricShapeDomain)gv1.getDomain()).getGeometry();
		Geometry g2 = ((GeometricShapeDomain)gv2.getDomain()).getGeometry();
		
		for (Type t : Type.values()) {
			try {
				if (rcc8Relations && !RCC8Types.contains(t.name())) continue;
				String methodName = t.name().substring(0, 1).toLowerCase() + t.name().substring(1);
				Method m = Geometry.class.getMethod(methodName, Geometry.class);
				if ((Boolean)m.invoke(g1, g2)) {
					boolean skip = false;
					if (t.equals(Type.Covers) || t.equals(Type.CoveredBy)) {
						IntersectionMatrix mat = g1.relate(g2);
						if (mat.get(1,1) == -1) skip = true;
					}
					else if (t.equals(Type.Contains) || t.equals(Type.Within)) {
						IntersectionMatrix mat = g1.relate(g2);
						if (mat.get(1,1) == 1) skip = true;
					}
					if (!skip) ret.add(t);
				}
			}
			catch (NoSuchMethodException e) { e.printStackTrace(); }
			catch (SecurityException e) { e.printStackTrace(); }
			catch (IllegalAccessException e) { e.printStackTrace(); }
			catch (IllegalArgumentException e) { e.printStackTrace(); }
			catch (InvocationTargetException e) { e.printStackTrace(); }
		}
		if (rcc8Relations && ret.contains(Type.Equals)) return new Type[] {Type.Equals};
		return ret.toArray(new Type[ret.size()]);
	}
	
	//cannot make a DE9IM relation without at least one type
	protected DE9IMRelation() {}
	
	/**
	 * Create a new DE-9IM relation with given types.
	 * @param types The type(s) of the new relation.
	 */
	public DE9IMRelation(Type ... types) {
		this.types = types;
	}
	
	/**
	 * Get the types of this DE-9IM relation.
	 * @return The types of this DE-9IM relation.
	 */
	public Type[] getTypes() {
		return types;
	}
	
	@Override
	public String getEdgeLabel() {
		return Arrays.toString(types);
	}

	@Override
	public Object clone() {
		DE9IMRelation ret = new DE9IMRelation(types);
		ret.setFrom(this.getFrom());
		ret.setTo(this.getTo());
		return ret;
	}

	@Override
	public boolean isEquivalent(Constraint c) {
		if (!(c instanceof DE9IMRelation)) return false;
		DE9IMRelation r = (DE9IMRelation)c;
		HashSet thisTypes = new HashSet();
		for (Type t : this.getTypes()) thisTypes.add(t);
		HashSet thatTypes = new HashSet();
		for (Type t : r.getTypes()) thatTypes.add(t);
		return thisTypes.size() == thatTypes.size() && thisTypes.containsAll(thatTypes);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy