001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032:
033: package com.vividsolutions.jump.qa.diff;
034:
035: import com.vividsolutions.jts.geom.*;
036: import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier;
037:
038: /**
039: * Matches geometries based on whether each Geometry is contained in the
040: * other's buffer. This is equivalent to each geometry being entirely
041: * within the distance tolerance of the other.
042: */
043: public class BufferGeometryMatcher implements DiffGeometryMatcher {
044: /**
045: * Computes whether two geometries match under
046: * this similarity test.
047: * This is not the most efficient way of
048: * executing this predicate for multiple geometries.
049: *
050: * @param g1 a Geometry
051: * @param g2 a Geometry
052: * @return true if the geometries match under this comparison operation
053: */
054: public static boolean isMatch(Geometry g1, Geometry g2,
055: double tolerance) {
056: BufferGeometryMatcher matcher = new BufferGeometryMatcher(
057: tolerance);
058: matcher.setQueryGeometry(g1);
059: return matcher.isMatch(g2);
060: }
061:
062: // the percentage of the buffer distance to use as the error tolerance for perturbation
063: public static final double ERROR_TOLERANCE = .1;
064:
065: /**
066: * if true the buffers of the boundary will be matched as well as the buffers of the
067: * geometries themselves. Matching the boundaries as well is
068: * a more accurate matching algorithm.
069: */
070: private static final boolean checkBoundary = false;
071:
072: public static double maxOrthogonalDistance(Envelope env1,
073: Envelope env2) {
074: double deltaMinX = Math.abs(env1.getMinX() - env2.getMinX());
075: double maxDist = deltaMinX;
076: double deltaMaxX = Math.abs(env1.getMaxX() - env2.getMaxX());
077: if (deltaMaxX > maxDist)
078: maxDist = deltaMaxX;
079: double deltaMinY = Math.abs(env1.getMinY() - env2.getMinY());
080: if (deltaMinY > maxDist)
081: maxDist = deltaMinY;
082: double deltaMaxY = Math.abs(env1.getMaxY() - env2.getMaxY());
083: if (deltaMaxY > maxDist)
084: maxDist = deltaMaxY;
085:
086: return maxDist;
087: }
088:
089: private double tolerance;
090: private Geometry queryGeom;
091: private Geometry queryBuffer;
092: private Geometry queryBoundary = null;
093: private Geometry queryBoundaryBuffer = null;
094:
095: public BufferGeometryMatcher(double tolerance) {
096: this .tolerance = tolerance;
097: }
098:
099: public void setQueryGeometry(Geometry geom) {
100: queryGeom = geom;
101: queryBuffer = checkedBuffer(geom);
102:
103: if (checkBoundary) {
104: if (queryGeom.getDimension() == 2) {
105: queryBoundary = queryGeom.getBoundary();
106: queryBoundaryBuffer = getBoundaryBuffer(queryGeom);
107: }
108: }
109: }
110:
111: public Geometry getQueryGeometry() {
112: return queryBuffer;
113: }
114:
115: public boolean isMatch(Geometry geom) {
116: if (geom.getClass() != queryGeom.getClass())
117: return false;
118: if (!isEnvelopeMatch(geom))
119: return false;
120:
121: boolean buffersMatch = isBufferMatch(geom);
122: if (!buffersMatch)
123: return false;
124:
125: if (!checkBoundary)
126: return true;
127: else {
128: // for non-area geometries this is all we need to check
129: if (queryGeom.getDimension() < 2)
130: return true;
131:
132: // for area geometries, check that the linework matches as well
133: return isBoundaryBufferMatch(geom);
134: }
135: }
136:
137: private boolean isBufferMatch(Geometry geom) {
138: Geometry buf = checkedBuffer(geom);
139:
140: boolean queryContains = queryBuffer.contains(geom);
141: boolean queryIsContained = buf.contains(queryGeom);
142: return queryContains && queryIsContained;
143: }
144:
145: private boolean isBoundaryBufferMatch(Geometry geom) {
146: Geometry boundary = geom.getBoundary();
147: Geometry bndBuf = getBoundaryBuffer(geom);
148:
149: boolean queryContains = queryBoundaryBuffer.contains(boundary);
150: boolean queryIsContained = bndBuf.contains(queryBoundary);
151: return queryContains && queryIsContained;
152: }
153:
154: private Geometry checkedBuffer(Geometry geom) {
155: Geometry buf = null;
156: try {
157: buf = geom.buffer(tolerance);
158: //buf = bufferDP.buffer(geom);
159: } catch (RuntimeException ex) {
160: // hack to get around buffer robustness problems
161: System.out.println("Buffer error!");
162: System.out.println(geom);
163: buf = geom;
164: }
165: return buf;
166: }
167:
168: private Geometry getBoundaryBuffer(Geometry geom) {
169: double scaleFactor = 1 / (tolerance * ERROR_TOLERANCE);
170: Geometry boundary = DouglasPeuckerSimplifier.simplify(geom
171: .getBoundary(), scaleFactor);
172: Geometry buf = checkedBuffer(boundary);
173: if (buf.isEmpty()) {
174: System.out.println("Empty boundary buffer found");
175: System.out.println(geom);
176: System.out.println(boundary);
177: }
178: return buf;
179: }
180:
181: private boolean isEnvelopeMatch(Geometry geom) {
182: // first check envelopes - if they are too far apart the geometries do not match
183: double envDist = maxOrthogonalDistance(queryGeom
184: .getEnvelopeInternal(), geom.getEnvelopeInternal());
185: return envDist <= tolerance;
186: }
187: }
|