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.geom;
034:
035: import java.util.Iterator;
036: import java.util.List;
037:
038: import com.vividsolutions.jts.geom.*;
039:
040: /**
041: * A heuristic used by Microscope to expand a single segment,
042: * while maintaining its orientation.
043: */
044: public class SingleSegmentExpander {
045: private Coordinate[] adjPt = new Coordinate[2];
046:
047: public SingleSegmentExpander() {
048: }
049:
050: public static Envelope getInsetEnvelope(Envelope env,
051: double insetPct) {
052: double insetX = (env.getWidth() * insetPct) / 2;
053: double insetY = (env.getWidth() * insetPct) / 2;
054: double inset = insetX;
055:
056: if (insetY < inset) {
057: inset = insetY;
058: }
059:
060: return new Envelope(env.getMinX() + inset, env.getMaxX()
061: - inset, env.getMinY() + inset, env.getMaxY() - inset);
062: }
063:
064: public boolean isApplicable(List segList, List ptList) {
065: if (segList.size() < 1) {
066: return false;
067: }
068:
069: LineSegment seg = (LineSegment) segList.get(0);
070:
071: return allSegsEqual(seg, segList) && allPtsInSeg(seg, ptList);
072: }
073:
074: private boolean allSegsEqual(LineSegment seg, List segList) {
075: for (Iterator i = segList.iterator(); i.hasNext();) {
076: LineSegment seg2 = (LineSegment) i.next();
077:
078: if (!seg.equalsTopo(seg2)) {
079: return false;
080: }
081: }
082:
083: return true;
084: }
085:
086: private boolean allPtsInSeg(LineSegment seg, List ptList) {
087: for (Iterator i = ptList.iterator(); i.hasNext();) {
088: Coordinate pt = (Coordinate) i.next();
089:
090: if (seg.p0.equals(pt)) {
091: return true;
092: }
093:
094: if (seg.p1.equals(pt)) {
095: return true;
096: }
097: }
098:
099: return false;
100: }
101:
102: public Coordinate[] expandSegment(LineSegment seg, Envelope env) {
103: Envelope insetEnv = getInsetEnvelope(env, 0.2);
104: double dx = seg.p1.x - seg.p0.x;
105: double dy = seg.p1.y - seg.p0.y;
106:
107: if (Math.abs(dx) <= 1.0E-6) {
108: double y0 = insetEnv.getMinY();
109: double y1 = insetEnv.getMaxY();
110:
111: if (seg.p0.y < seg.p1.y) {
112: y0 = insetEnv.getMaxY();
113: y1 = insetEnv.getMinY();
114: }
115:
116: adjPt[0] = new Coordinate(seg.p0.x, y0);
117: adjPt[1] = new Coordinate(seg.p0.x, y1);
118:
119: return adjPt;
120: }
121:
122: if (Math.abs(dy) <= 1.0E-6) {
123: double x0 = insetEnv.getMinX();
124: double x1 = insetEnv.getMaxX();
125:
126: if (seg.p0.x < seg.p1.x) {
127: x0 = insetEnv.getMaxX();
128: x1 = insetEnv.getMinX();
129: }
130:
131: adjPt[0] = new Coordinate(x0, seg.p0.y);
132: adjPt[1] = new Coordinate(x1, seg.p0.y);
133:
134: return adjPt;
135: }
136:
137: // compute intersection of ray with inset Envelope
138: adjPt[0] = rayEnvIntersection(seg.p0, seg.p1, insetEnv);
139: adjPt[1] = rayEnvIntersection(seg.p1, seg.p0, insetEnv);
140:
141: return adjPt;
142: }
143:
144: /**
145: * Computes the intersection of the ray p0-p1 with one of the edges
146: * of the envelope
147: */
148: private Coordinate rayEnvIntersection(Coordinate p0, Coordinate p1,
149: Envelope env) {
150: Coordinate x0 = segIntX(p0, p1, env.getMinX(), env.getMinY(),
151: env.getMaxY());
152:
153: if (x0 != null) {
154: return x0;
155: }
156:
157: Coordinate x1 = segIntX(p0, p1, env.getMaxX(), env.getMinY(),
158: env.getMaxY());
159:
160: if (x1 != null) {
161: return x1;
162: }
163:
164: Coordinate y0 = segIntY(p0, p1, env.getMinY(), env.getMinX(),
165: env.getMaxX());
166:
167: if (y0 != null) {
168: return y0;
169: }
170:
171: Coordinate y1 = segIntY(p0, p1, env.getMaxY(), env.getMinX(),
172: env.getMaxX());
173:
174: if (y1 != null) {
175: return y1;
176: }
177:
178: // should never reach here - if we do, need a tolerance in segInt routines
179: return null;
180: }
181:
182: /**
183: * Computes the dot product of the vectors p-p0 and p-p1.
184: * If the dot product is negative the vectors contain an obtuse angle,
185: * if positive they contain an acute angle. If the angle is acute,
186: * the vectors can be considered to be "in the same direction".
187: */
188: private double dotProduct(Coordinate p, Coordinate p0, Coordinate p1) {
189: double dx0 = p0.x - p.x;
190: double dy0 = p0.y - p.y;
191: double dx1 = p1.x - p.x;
192: double dy1 = p1.y - p.y;
193:
194: return (dx0 * dx1) + (dy0 * dy1);
195: }
196:
197: private Coordinate segIntX(Coordinate p0, Coordinate p1, double x,
198: double miny, double maxy) {
199: double m = (p1.y - p0.y) / (p1.x - p0.x);
200: double y2 = (m * (x - p0.x)) + p0.y;
201:
202: if ((y2 > miny) && (y2 < maxy)) {
203: Coordinate intPt = new Coordinate(x, y2);
204:
205: if (dotProduct(p0, p1, intPt) < 0.0) {
206: return intPt;
207: }
208: }
209:
210: return null;
211: }
212:
213: private Coordinate segIntY(Coordinate p0, Coordinate p1, double y,
214: double minx, double maxx) {
215: double m = (p1.x - p0.x) / (p1.y - p0.y);
216: double x2 = (m * (y - p0.y)) + p0.x;
217:
218: if ((x2 > minx) && (x2 < maxx)) {
219: Coordinate intPt = new Coordinate(x2, y);
220:
221: if (dotProduct(p0, p1, intPt) < 0.0) {
222: return intPt;
223: }
224: }
225:
226: return null;
227: }
228: }
|