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.*;
036:
037: import com.vividsolutions.jts.geom.*;
038:
039: /**
040: * A MicroscopePointAdjuster takes some line segments and an envelope, and
041: * adjusts the points of the line segments within the envelope
042: * so that small differences are visible.
043: * Points will not be moved outside the envelope.
044: */
045: public class MicroscopePointAdjuster {
046: private static final Coordinate origin = new Coordinate(0.0, 0.0,
047: 0.0);
048: private List segList;
049: private Envelope env;
050: private double minSep;
051: private Map adjPtMap = new TreeMap();
052:
053: public MicroscopePointAdjuster(List segList, Envelope env,
054: double minSep) {
055: this .segList = segList;
056: this .env = env;
057: this .minSep = minSep;
058: }
059:
060: public Map getAdjustedPointMap() {
061: computeAdjustments();
062:
063: return adjPtMap;
064: }
065:
066: private void computeAdjustments() {
067: // find all points in Envelope
068: List ptsInEnv = findPointsInEnv(env);
069: List segsInEnv = findSegmentsInEnv(env);
070:
071: SingleSegmentExpander ssex = new SingleSegmentExpander();
072:
073: if (ssex.isApplicable(segsInEnv, ptsInEnv)) {
074: LineSegment seg = (LineSegment) segsInEnv.get(0);
075: Coordinate[] adjPt = ssex.expandSegment(seg, env);
076: adjPtMap.put(new Coordinate(seg.p0), adjPt[0]);
077: adjPtMap.put(new Coordinate(seg.p1), adjPt[1]);
078: } else {
079: computeAdjustedPtMap(ptsInEnv);
080: }
081: }
082:
083: /**
084: * Return a list of adjusted Segments.
085: * Probably for testing only.
086: */
087: public List adjustSegments() {
088: // find all points in Envelope
089: List ptsInEnv = findPointsInEnv(env);
090: computeAdjustedPtMap(ptsInEnv);
091:
092: return adjustSegs();
093: }
094:
095: private List findPointsInEnv(Envelope env) {
096: List ptsInEnv = new ArrayList();
097:
098: for (Iterator i = segList.iterator(); i.hasNext();) {
099: LineSegment seg = (LineSegment) i.next();
100:
101: if (env.contains(seg.p0)) {
102: ptsInEnv.add(seg.p0);
103: }
104:
105: if (env.contains(seg.p1)) {
106: ptsInEnv.add(seg.p1);
107: }
108: }
109:
110: return ptsInEnv;
111: }
112:
113: private List findSegmentsInEnv(Envelope env) {
114: List segsInEnv = new ArrayList();
115:
116: for (Iterator i = segList.iterator(); i.hasNext();) {
117: LineSegment seg = (LineSegment) i.next();
118:
119: if (env.contains(seg.p0) && env.contains(seg.p1)) {
120: segsInEnv.add(seg);
121: }
122: }
123:
124: return segsInEnv;
125: }
126:
127: private void computeAdjustedPtMap(List ptsInEnv) {
128: for (Iterator i = ptsInEnv.iterator(); i.hasNext();) {
129: Coordinate pt = (Coordinate) i.next();
130: Coordinate adjPt = computeAdjustment(pt);
131:
132: if (!adjPt.equals(pt)) {
133: // copy key to ensure we don't have aliased modifications
134: adjPtMap.put(new Coordinate(pt), adjPt);
135: }
136: }
137: }
138:
139: private List adjustSegs() {
140: List adjSegList = new ArrayList();
141:
142: for (Iterator i = segList.iterator(); i.hasNext();) {
143: LineSegment seg = (LineSegment) i.next();
144:
145: LineSegment adjSeg = new LineSegment();
146: adjSeg.p0 = adjustPt(seg.p0);
147: adjSeg.p1 = adjustPt(seg.p1);
148: adjSegList.add(adjSeg);
149: }
150:
151: return adjSegList;
152: }
153:
154: private Coordinate adjustPt(Coordinate p) {
155: Coordinate adjMapPt = (Coordinate) adjPtMap.get(p);
156:
157: if (adjMapPt != null) {
158: return new Coordinate(adjMapPt);
159: }
160:
161: return new Coordinate(p);
162: }
163:
164: private Coordinate computeAdjustment(Coordinate p) {
165: Coordinate adjVec = new Coordinate();
166:
167: for (Iterator i = segList.iterator(); i.hasNext();) {
168: LineSegment seg = (LineSegment) i.next();
169: double dist = seg.distance(p);
170:
171: // if too close, compute an adjustment weight vector
172: if (dist < minSep) {
173: Coordinate adjWeightVec = adjustmentWeightVector(p, seg);
174: adjVec.x += adjWeightVec.x;
175: adjVec.y += adjWeightVec.y;
176: }
177: }
178:
179: Coordinate adjPt = new Coordinate(p);
180: adjPt.x += adjVec.x;
181: adjPt.y += adjVec.y;
182:
183: return adjPt;
184: }
185:
186: private Coordinate adjustmentWeightVector(Coordinate p,
187: LineSegment seg) {
188: if (p.equals(seg.p0)) {
189: return adjWeightEndPoint(p, seg.p1);
190: }
191:
192: if (p.equals(seg.p1)) {
193: return adjWeightEndPoint(p, seg.p0);
194: }
195:
196: return adjWeightSegmentProximity(p, seg);
197: }
198:
199: private Coordinate adjWeightEndPoint(Coordinate p, Coordinate p2) {
200: Coordinate adjWeightVec = new Coordinate();
201: adjWeightVec.x = p.x - p2.x;
202: adjWeightVec.y = p.y - p2.y;
203:
204: double len = adjWeightVec.distance(origin);
205:
206: if (len > minSep) {
207: return origin;
208: }
209:
210: double scale = minSep / len;
211: adjWeightVec.x *= scale;
212: adjWeightVec.y *= scale;
213:
214: return adjWeightVec;
215: }
216:
217: private Coordinate adjWeightSegmentProximity(Coordinate p,
218: LineSegment seg) {
219: Coordinate proj = seg.project(p);
220: Coordinate adjWeightVec = new Coordinate();
221: adjWeightVec.x = p.x - proj.x;
222: adjWeightVec.y = p.y - proj.y;
223:
224: double len = adjWeightVec.distance(origin);
225:
226: /**
227: * have to do something smarter if length is really small.
228: * Probably test for which side of line point is and move it perp to line
229: * a fixed amount.
230: */
231: double scale = minSep / len;
232: adjWeightVec.x *= scale;
233: adjWeightVec.y *= scale;
234:
235: return adjWeightVec;
236: }
237: }
|