/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.kml.utils;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.linearref.LengthIndexedLine;
/**
* A KML specific geometry centroid extractor
*/
public class KmlCentroidBuilder {
/**
* Returns the centroid of the geometry, handling a geometry collection.
* <p>
* In the case of a collection a multi point containing the centroid of each geometry in the
* collection is calculated. The first point in the multi point is returned as the controid.
* </p>
*/
public Coordinate geometryCentroid(Geometry g) {
if (g instanceof GeometryCollection) {
g = selectRepresentativeGeometry((GeometryCollection) g);
}
if (g == null) {
return null;
} else if (g instanceof Point) {
// simple case
return g.getCoordinate();
} else if (g instanceof LineString) {
// make sure the point we return is actually on the line
LineString line = (LineString) g;
LengthIndexedLine lil = new LengthIndexedLine(line);
return lil.extractPoint(line.getLength() / 2.0);
} else {
// return the actual centroid
return g.getCentroid().getCoordinate();
}
}
/**
* Selects a representative geometry from the collection (the one covering the biggest area)
*
* @param g
*
*/
private Geometry selectRepresentativeGeometry(GeometryCollection g) {
GeometryCollection gc = (GeometryCollection) g;
if (gc.isEmpty()) {
return null;
}
// check for case of single geometry or multipoint
Geometry first = gc.getGeometryN(0);
if (gc.getNumGeometries() == 1 || g instanceof MultiPoint) {
return first;
} else {
// get the geometry with the largest bbox
double maxAreaSoFar = first.getEnvelope().getArea();
Geometry geometryToReturn = first;
for (int t = 0; t < gc.getNumGeometries(); t++) {
Geometry curr = gc.getGeometryN(t);
double area = curr.getEnvelope().getArea();
if (area > maxAreaSoFar) {
maxAreaSoFar = area;
geometryToReturn = curr;
}
}
return geometryToReturn;
}
}
}