001: /*
002: * The JTS Topology Suite is a collection of Java classes that
003: * implement the fundamental operations required to validate a given
004: * geo-spatial data set to a known topological specification.
005: *
006: * Copyright (C) 2001 Vivid Solutions
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public
019: * License along with this library; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021: *
022: * For more information, contact:
023: *
024: * Vivid Solutions
025: * Suite #1A
026: * 2328 Government Street
027: * Victoria BC V8T 5G5
028: * Canada
029: *
030: * (250)385-6040
031: * www.vividsolutions.com
032: */
033: package com.vividsolutions.jts.algorithm;
034:
035: import com.vividsolutions.jts.geom.*;
036:
037: /**
038: * Computes a point in the interior of an area geometry.
039: *
040: * <h2>Algorithm</h2>
041: * <ul>
042: * <li>Find the intersections between the geometry
043: * and the horizontal bisector of the area's envelope
044: * <li>Pick the midpoint of the largest intersection (the intersections
045: * will be lines and points)
046: * </ul>
047: *
048: * <b>
049: * Note: If a fixed precision model is used,
050: * in some cases this method may return a point
051: * which does not lie in the interior.
052: * </b>
053: *
054: * @version 1.7
055: */
056: public class InteriorPointArea {
057:
058: private static double avg(double a, double b) {
059: return (a + b) / 2.0;
060: }
061:
062: private GeometryFactory factory;
063: private Coordinate interiorPoint = null;
064: private double maxWidth = 0.0;
065:
066: public InteriorPointArea(Geometry g) {
067: factory = g.getFactory();
068: add(g);
069: }
070:
071: public Coordinate getInteriorPoint() {
072: return interiorPoint;
073: }
074:
075: /**
076: * Tests the interior vertices (if any)
077: * defined by a linear Geometry for the best inside point.
078: * If a Geometry is not of dimension 1 it is not tested.
079: * @param geom the geometry to add
080: */
081: private void add(Geometry geom) {
082: if (geom instanceof Polygon) {
083: addPolygon(geom);
084: } else if (geom instanceof GeometryCollection) {
085: GeometryCollection gc = (GeometryCollection) geom;
086: for (int i = 0; i < gc.getNumGeometries(); i++) {
087: add(gc.getGeometryN(i));
088: }
089: }
090: }
091:
092: /**
093: * Finds a reasonable point at which to label a Geometry.
094: * @param geometry the geometry to analyze
095: * @return the midpoint of the largest intersection between the geometry and
096: * a line halfway down its envelope
097: */
098: public void addPolygon(Geometry geometry) {
099: LineString bisector = horizontalBisector(geometry);
100:
101: Geometry intersections = bisector.intersection(geometry);
102: Geometry widestIntersection = widestGeometry(intersections);
103:
104: double width = widestIntersection.getEnvelopeInternal()
105: .getWidth();
106: if (interiorPoint == null || width > maxWidth) {
107: interiorPoint = centre(widestIntersection
108: .getEnvelopeInternal());
109: maxWidth = width;
110: }
111: }
112:
113: //@return if geometry is a collection, the widest sub-geometry; otherwise,
114: //the geometry itself
115: protected Geometry widestGeometry(Geometry geometry) {
116: if (!(geometry instanceof GeometryCollection)) {
117: return geometry;
118: }
119: return widestGeometry((GeometryCollection) geometry);
120: }
121:
122: private Geometry widestGeometry(GeometryCollection gc) {
123: if (gc.isEmpty()) {
124: return gc;
125: }
126:
127: Geometry widestGeometry = gc.getGeometryN(0);
128: for (int i = 1; i < gc.getNumGeometries(); i++) { //Start at 1
129: if (gc.getGeometryN(i).getEnvelopeInternal().getWidth() > widestGeometry
130: .getEnvelopeInternal().getWidth()) {
131: widestGeometry = gc.getGeometryN(i);
132: }
133: }
134: return widestGeometry;
135: }
136:
137: protected LineString horizontalBisector(Geometry geometry) {
138: Envelope envelope = geometry.getEnvelopeInternal();
139:
140: // Assert: for areas, minx <> maxx
141: double avgY = avg(envelope.getMinY(), envelope.getMaxY());
142: return factory.createLineString(new Coordinate[] {
143: new Coordinate(envelope.getMinX(), avgY),
144: new Coordinate(envelope.getMaxX(), avgY) });
145: }
146:
147: /**
148: * Returns the centre point of the envelope.
149: * @param envelope the envelope to analyze
150: * @return the centre of the envelope
151: */
152: public Coordinate centre(Envelope envelope) {
153: return new Coordinate(avg(envelope.getMinX(), envelope
154: .getMaxX()),
155: avg(envelope.getMinY(), envelope.getMaxY()));
156: }
157:
158: }
|