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 com.vividsolutions.jts.geom.*;
036: import com.vividsolutions.jump.util.MathUtil;
037:
038: /**
039: * Find a reasonable point at which to label a Geometry.
040: * <p>
041: * Algorithm is:
042: * <ul>
043: * <li>Find the intersections between the geometry and a line halfway
044: * down the envelope
045: * <li>Pick the midpoint of the largest intersection (the intersections
046: * will be lines and points)
047: * </ul>
048: */
049: public class InteriorPointFinder {
050: private GeometryFactory factory = new GeometryFactory();
051:
052: //<<TODO:REFACTORING>> Move this class to JTS [Jon Aquino]
053: public InteriorPointFinder() {
054: }
055:
056: /**
057: * Finds a reasonable point at which to label a Geometry.
058: * @param geometry the geometry to analyze
059: * @return the midpoint of the largest intersection between the geometry and
060: * a line halfway down its envelope
061: */
062: public Coordinate findPoint(Geometry geometry) {
063: if (geometry.isEmpty()) {
064: //Can't use geometry#getPoint because it returns null [Jon Aquino]
065: return new Coordinate(0, 0);
066: }
067:
068: if (geometry.getDimension() == 0) {
069: //Points and multipoints [Jon Aquino]
070: return geometry.getCoordinate();
071: }
072:
073: if (geometry instanceof GeometryCollection) {
074: return findPoint(((GeometryCollection) geometry)
075: .getGeometryN(0));
076: }
077:
078: Geometry envelopeMiddle = envelopeMiddle(geometry);
079:
080: if (envelopeMiddle instanceof Point) {
081: return envelopeMiddle.getCoordinate();
082: }
083:
084: Geometry intersections = envelopeMiddle.intersection(geometry);
085: Geometry widestIntersection = widestGeometry(intersections);
086:
087: return centre(widestIntersection.getEnvelopeInternal());
088: }
089:
090: //@return if geometry is a collection, the widest sub-geometry; otherwise,
091: //the geometry itself
092: protected Geometry widestGeometry(Geometry geometry) {
093: if (!(geometry instanceof GeometryCollection)) {
094: return geometry;
095: }
096:
097: return widestGeometry((GeometryCollection) geometry);
098: }
099:
100: private Geometry widestGeometry(GeometryCollection gc) {
101: if (gc.isEmpty()) {
102: return gc;
103: }
104:
105: Geometry widestGeometry = gc.getGeometryN(0);
106:
107: for (int i = 1; i < gc.getNumGeometries(); i++) { //Start at 1
108:
109: if (gc.getGeometryN(i).getEnvelopeInternal().getWidth() > widestGeometry
110: .getEnvelopeInternal().getWidth()) {
111: widestGeometry = gc.getGeometryN(i);
112: }
113: }
114:
115: return widestGeometry;
116: }
117:
118: protected Geometry envelopeMiddle(Geometry geometry) {
119: Envelope envelope = geometry.getEnvelopeInternal();
120:
121: if (envelope.getWidth() == 0) {
122: return factory.createPoint(centre(envelope));
123: }
124:
125: return factory.createLineString(new Coordinate[] {
126: new Coordinate(envelope.getMinX(), MathUtil.avg(
127: envelope.getMinY(), envelope.getMaxY())),
128: new Coordinate(envelope.getMaxX(), MathUtil.avg(
129: envelope.getMinY(), envelope.getMaxY())) });
130: }
131:
132: /**
133: * Returns the centre-of-mass of the envelope.
134: * @param envelope the envelope to analyze
135: * @return the centre of the envelope
136: */
137: public Coordinate centre(Envelope envelope) {
138: //<<TODO:REFACTORING>> Move #avg from GUIUtilities to a core JCS util class [Jon Aquino]
139: return new Coordinate(MathUtil.avg(envelope.getMinX(), envelope
140: .getMaxX()), MathUtil.avg(envelope.getMinY(), envelope
141: .getMaxY()));
142: }
143: }
|