/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; 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.jts.algorithm;
import com.vividsolutions.jts.geom.*;
/**
* Computes a point in the interior of an area geometry.
*
* <h2>Algorithm</h2>
* <ul>
* <li>Find the intersections between the geometry
* and the horizontal bisector of the area's envelope
* <li>Pick the midpoint of the largest intersection (the intersections
* will be lines and points)
* </ul>
*
* <h3>KNOWN BUGS</h3>
* <ul>
* <li>If a fixed precision model is used,
* in some cases this method may return a point
* which does not lie in the interior.
* </ul>
*
* @version 1.7
*/
public class InteriorPointArea {
private static double avg(double a, double b)
{
return (a + b) / 2.0;
}
private GeometryFactory factory;
private Coordinate interiorPoint = null;
private double maxWidth = 0.0;
public InteriorPointArea(Geometry g)
{
factory = g.getFactory();
add(g);
}
public Coordinate getInteriorPoint()
{
return interiorPoint;
}
/**
* Tests the interior vertices (if any)
* defined by an areal Geometry for the best inside point.
* If a component Geometry is not of dimension 2 it is not tested.
*
* @param geom the geometry to add
*/
private void add(Geometry geom)
{
if (geom instanceof Polygon) {
addPolygon(geom);
}
else if (geom instanceof GeometryCollection) {
GeometryCollection gc = (GeometryCollection) geom;
for (int i = 0; i < gc.getNumGeometries(); i++) {
add(gc.getGeometryN(i));
}
}
}
/**
* Finds a reasonable point at which to label a Geometry.
* @param geometry the geometry to analyze
*/
public void addPolygon(Geometry geometry) {
LineString bisector = horizontalBisector(geometry);
Geometry intersections = bisector.intersection(geometry);
Geometry widestIntersection = widestGeometry(intersections);
double width = widestIntersection.getEnvelopeInternal().getWidth();
if (interiorPoint == null || width > maxWidth) {
interiorPoint = centre(widestIntersection.getEnvelopeInternal());
maxWidth = width;
}
}
//@return if geometry is a collection, the widest sub-geometry; otherwise,
//the geometry itself
protected Geometry widestGeometry(Geometry geometry) {
if (!(geometry instanceof GeometryCollection)) {
return geometry;
}
return widestGeometry((GeometryCollection) geometry);
}
private Geometry widestGeometry(GeometryCollection gc) {
if (gc.isEmpty()) {
return gc;
}
Geometry widestGeometry = gc.getGeometryN(0);
for (int i = 1; i < gc.getNumGeometries(); i++) { //Start at 1
if (gc.getGeometryN(i).getEnvelopeInternal().getWidth() >
widestGeometry.getEnvelopeInternal().getWidth()) {
widestGeometry = gc.getGeometryN(i);
}
}
return widestGeometry;
}
protected LineString horizontalBisector(Geometry geometry) {
Envelope envelope = geometry.getEnvelopeInternal();
// Assert: for areas, minx <> maxx
double avgY = avg(envelope.getMinY(), envelope.getMaxY());
return factory.createLineString(new Coordinate[] {
new Coordinate(envelope.getMinX(), avgY),
new Coordinate(envelope.getMaxX(), avgY)
});
}
/**
* Returns the centre point of the envelope.
* @param envelope the envelope to analyze
* @return the centre of the envelope
*/
public Coordinate centre(Envelope envelope) {
return new Coordinate(avg(envelope.getMinX(),
envelope.getMaxX()),
avg(envelope.getMinY(), envelope.getMaxY()));
}
}