/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.geom;
import java.util.*;
import com.vividsolutions.jts.geom.*;
/**
* A MicroscopePointAdjuster takes some line segments and an envelope, and
* adjusts the points of the line segments within the envelope
* so that small differences are visible.
* Points will not be moved outside the envelope.
*/
public class MicroscopePointAdjuster {
private static final Coordinate origin = new Coordinate(0.0, 0.0, 0.0);
private List segList;
private Envelope env;
private double minSep;
private Map adjPtMap = new TreeMap();
public MicroscopePointAdjuster(List segList, Envelope env, double minSep) {
this.segList = segList;
this.env = env;
this.minSep = minSep;
}
public Map getAdjustedPointMap() {
computeAdjustments();
return adjPtMap;
}
private void computeAdjustments() {
// find all points in Envelope
List ptsInEnv = findPointsInEnv(env);
List segsInEnv = findSegmentsInEnv(env);
SingleSegmentExpander ssex = new SingleSegmentExpander();
if (ssex.isApplicable(segsInEnv, ptsInEnv)) {
LineSegment seg = (LineSegment) segsInEnv.get(0);
Coordinate[] adjPt = ssex.expandSegment(seg, env);
adjPtMap.put(new Coordinate(seg.p0), adjPt[0]);
adjPtMap.put(new Coordinate(seg.p1), adjPt[1]);
} else {
computeAdjustedPtMap(ptsInEnv);
}
}
/**
* Return a list of adjusted Segments.
* Probably for testing only.
*/
public List adjustSegments() {
// find all points in Envelope
List ptsInEnv = findPointsInEnv(env);
computeAdjustedPtMap(ptsInEnv);
return adjustSegs();
}
private List findPointsInEnv(Envelope env) {
List ptsInEnv = new ArrayList();
for (Iterator i = segList.iterator(); i.hasNext();) {
LineSegment seg = (LineSegment) i.next();
if (env.contains(seg.p0)) {
ptsInEnv.add(seg.p0);
}
if (env.contains(seg.p1)) {
ptsInEnv.add(seg.p1);
}
}
return ptsInEnv;
}
private List findSegmentsInEnv(Envelope env) {
List segsInEnv = new ArrayList();
for (Iterator i = segList.iterator(); i.hasNext();) {
LineSegment seg = (LineSegment) i.next();
if (env.contains(seg.p0) && env.contains(seg.p1)) {
segsInEnv.add(seg);
}
}
return segsInEnv;
}
private void computeAdjustedPtMap(List ptsInEnv) {
for (Iterator i = ptsInEnv.iterator(); i.hasNext();) {
Coordinate pt = (Coordinate) i.next();
Coordinate adjPt = computeAdjustment(pt);
if (!adjPt.equals(pt)) {
// copy key to ensure we don't have aliased modifications
adjPtMap.put(new Coordinate(pt), adjPt);
}
}
}
private List adjustSegs() {
List adjSegList = new ArrayList();
for (Iterator i = segList.iterator(); i.hasNext();) {
LineSegment seg = (LineSegment) i.next();
LineSegment adjSeg = new LineSegment();
adjSeg.p0 = adjustPt(seg.p0);
adjSeg.p1 = adjustPt(seg.p1);
adjSegList.add(adjSeg);
}
return adjSegList;
}
private Coordinate adjustPt(Coordinate p) {
Coordinate adjMapPt = (Coordinate) adjPtMap.get(p);
if (adjMapPt != null) {
return new Coordinate(adjMapPt);
}
return new Coordinate(p);
}
private Coordinate computeAdjustment(Coordinate p) {
Coordinate adjVec = new Coordinate();
for (Iterator i = segList.iterator(); i.hasNext();) {
LineSegment seg = (LineSegment) i.next();
double dist = seg.distance(p);
// if too close, compute an adjustment weight vector
if (dist < minSep) {
Coordinate adjWeightVec = adjustmentWeightVector(p, seg);
adjVec.x += adjWeightVec.x;
adjVec.y += adjWeightVec.y;
}
}
Coordinate adjPt = new Coordinate(p);
adjPt.x += adjVec.x;
adjPt.y += adjVec.y;
return adjPt;
}
private Coordinate adjustmentWeightVector(Coordinate p, LineSegment seg) {
if (p.equals(seg.p0)) {
return adjWeightEndPoint(p, seg.p1);
}
if (p.equals(seg.p1)) {
return adjWeightEndPoint(p, seg.p0);
}
return adjWeightSegmentProximity(p, seg);
}
private Coordinate adjWeightEndPoint(Coordinate p, Coordinate p2) {
Coordinate adjWeightVec = new Coordinate();
adjWeightVec.x = p.x - p2.x;
adjWeightVec.y = p.y - p2.y;
double len = adjWeightVec.distance(origin);
if (len > minSep) {
return origin;
}
double scale = minSep / len;
adjWeightVec.x *= scale;
adjWeightVec.y *= scale;
return adjWeightVec;
}
private Coordinate adjWeightSegmentProximity(Coordinate p, LineSegment seg) {
Coordinate proj = seg.project(p);
Coordinate adjWeightVec = new Coordinate();
adjWeightVec.x = p.x - proj.x;
adjWeightVec.y = p.y - proj.y;
double len = adjWeightVec.distance(origin);
/**
* have to do something smarter if length is really small.
* Probably test for which side of line point is and move it perp to line
* a fixed amount.
*/
double scale = minSep / len;
adjWeightVec.x *= scale;
adjWeightVec.y *= scale;
return adjWeightVec;
}
}