org.h2gis.functions.spatial.volume.GeometryExtrude Maven / Gradle / Ivy
The newest version!
/**
* H2GIS is a library that brings spatial support to the H2 Database Engine
* . H2GIS is developed by CNRS
* .
*
* This code is part of the H2GIS project. H2GIS 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;
* version 3.0 of the License.
*
* H2GIS 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 .
*
*
* For more information, please consult:
* or contact directly: info_at_h2gis.org
*/
package org.h2gis.functions.spatial.volume;
import com.vividsolutions.jts.algorithm.CGAlgorithms;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.CoordinateSequenceFilter;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import java.util.ArrayList;
import org.h2gis.functions.spatial.edit.ST_UpdateZ.UpdateZCoordinateSequenceFilter;
/**
* This class is used to extrude a polygon or a linestring to a set of walls,
* roof, or floor using a height.
*
* @author Erwan Bocher
*/
public class GeometryExtrude {
private GeometryExtrude() {
}
/**
* Extrude the polygon as a collection of geometries
* The output geometryCollection contains the floor, the walls and the roof.
* @param polygon
* @param height
* @return
*/
public static GeometryCollection extrudePolygonAsGeometry(Polygon polygon, double height){
GeometryFactory factory = polygon.getFactory();
Geometry[] geometries = new Geometry[3];
//Extract floor
//We process the exterior ring
final LineString shell = getClockWise(polygon.getExteriorRing());
ArrayList walls = new ArrayList();
for (int i = 1; i < shell.getNumPoints(); i++) {
walls.add(extrudeEdge(shell.getCoordinateN(i - 1), shell.getCoordinateN(i), height, factory));
}
final int nbOfHoles = polygon.getNumInteriorRing();
final LinearRing[] holes = new LinearRing[nbOfHoles];
for (int i = 0; i < nbOfHoles; i++) {
final LineString hole = getCounterClockWise(polygon.getInteriorRingN(i));
for (int j = 1; j < hole.getNumPoints(); j++) {
walls.add(extrudeEdge(hole.getCoordinateN(j - 1),
hole.getCoordinateN(j), height, factory));
}
holes[i] = factory.createLinearRing(hole.getCoordinateSequence());
}
geometries[0]= factory.createPolygon(factory.createLinearRing(shell.getCoordinateSequence()), holes);
geometries[1]= factory.createMultiPolygon(walls.toArray(new Polygon[walls.size()]));
geometries[2]= extractRoof(polygon, height);
return polygon.getFactory().createGeometryCollection(geometries);
}
/**
* Extrude the linestring as a collection of geometries
* The output geometryCollection contains the floor, the walls and the roof.
* @param lineString
* @param height
* @return
*/
public static GeometryCollection extrudeLineStringAsGeometry(LineString lineString, double height){
Geometry[] geometries = new Geometry[3];
GeometryFactory factory = lineString.getFactory();
//Extract the walls
Coordinate[] coords = lineString.getCoordinates();
Polygon[] walls = new Polygon[coords.length - 1];
for (int i = 0; i < coords.length - 1; i++) {
walls[i] = extrudeEdge(coords[i], coords[i + 1], height, factory);
}
lineString.apply(new TranslateCoordinateSequenceFilter(0));
geometries[0]= lineString;
geometries[1]= factory.createMultiPolygon(walls);
geometries[2]= extractRoof(lineString, height);
return factory.createGeometryCollection(geometries);
}
/**
* Extract the linestring "roof".
*
* @param lineString
* @param height
* @return
*/
public static Geometry extractRoof(LineString lineString, double height) {
LineString result = (LineString) lineString.clone();
result.apply(new TranslateCoordinateSequenceFilter(height));
return result;
}
/**
* Extract the walls from a polygon
* @param polygon
* @param height
* @return
*/
public static MultiPolygon extractWalls(Polygon polygon, double height){
GeometryFactory factory = polygon.getFactory();
//We process the exterior ring
final LineString shell = getClockWise(polygon.getExteriorRing());
ArrayList walls = new ArrayList();
for (int i = 1; i < shell.getNumPoints(); i++) {
walls.add(extrudeEdge(shell.getCoordinateN(i - 1), shell.getCoordinateN(i), height, factory));
}
// We create the walls for all holes
int nbOfHoles = polygon.getNumInteriorRing();
for (int i = 0; i < nbOfHoles; i++) {
final LineString hole = getCounterClockWise(polygon.getInteriorRingN(i));
for (int j = 1; j < hole.getNumPoints(); j++) {
walls.add(extrudeEdge(hole.getCoordinateN(j - 1),
hole.getCoordinateN(j), height, factory));
}
}
return polygon.getFactory().createMultiPolygon(walls.toArray(new Polygon[walls.size()]));
}
/**
* Extract the roof of a polygon
*
* @param polygon
* @param height
* @return
*/
public static Polygon extractRoof(Polygon polygon, double height) {
GeometryFactory factory = polygon.getFactory();
Polygon roofP = (Polygon) polygon.clone();
roofP.apply(new TranslateCoordinateSequenceFilter(height));
final LinearRing shell = factory.createLinearRing(getCounterClockWise(roofP.getExteriorRing()).getCoordinates());
final int nbOfHoles = roofP.getNumInteriorRing();
final LinearRing[] holes = new LinearRing[nbOfHoles];
for (int i = 0; i < nbOfHoles; i++) {
holes[i] = factory.createLinearRing(getClockWise(
roofP.getInteriorRingN(i)).getCoordinates());
}
return factory.createPolygon(shell, holes);
}
/**
* Extrude the LineString as a set of walls.
* @param lineString
* @param height
* @return
*/
public static MultiPolygon extractWalls(LineString lineString, double height) {
GeometryFactory factory = lineString.getFactory();
//Extract the walls
Coordinate[] coords = lineString.getCoordinates();
Polygon[] walls = new Polygon[coords.length - 1];
for (int i = 0; i < coords.length - 1; i++) {
walls[i] = extrudeEdge(coords[i], coords[i + 1], height, factory);
}
return lineString.getFactory().createMultiPolygon(walls);
}
/**
* Reverse the LineString to be oriented clockwise.
* All NaN z values are replaced by a zero value.
* @param lineString
* @return
*/
private static LineString getClockWise(final LineString lineString) {
final Coordinate c0 = lineString.getCoordinateN(0);
final Coordinate c1 = lineString.getCoordinateN(1);
final Coordinate c2 = lineString.getCoordinateN(2);
lineString.apply(new UpdateZCoordinateSequenceFilter(0, 3));
if (CGAlgorithms.computeOrientation(c0, c1, c2) == CGAlgorithms.CLOCKWISE) {
return lineString;
} else {
return (LineString) lineString.reverse();
}
}
/**
* Reverse the LineString to be oriented counter-clockwise.
* @param lineString
* @return
*/
private static LineString getCounterClockWise(final LineString lineString) {
final Coordinate c0 = lineString.getCoordinateN(0);
final Coordinate c1 = lineString.getCoordinateN(1);
final Coordinate c2 = lineString.getCoordinateN(2);
lineString.apply(new UpdateZCoordinateSequenceFilter(0, 3));
if (CGAlgorithms.computeOrientation(c0, c1, c2) == CGAlgorithms.COUNTERCLOCKWISE) {
return lineString;
} else {
return (LineString) lineString.reverse();
}
}
/**
* Reverse the polygon to be oriented clockwise
* @param polygon
* @return
*/
private static Polygon extractFloor(final Polygon polygon) {
GeometryFactory factory = polygon.getFactory();
final LinearRing shell = factory.createLinearRing(getClockWise(
polygon.getExteriorRing()).getCoordinates());
final int nbOfHoles = polygon.getNumInteriorRing();
final LinearRing[] holes = new LinearRing[nbOfHoles];
for (int i = 0; i < nbOfHoles; i++) {
holes[i] = factory.createLinearRing(getCounterClockWise(
polygon.getInteriorRingN(i)).getCoordinates());
}
return factory.createPolygon(shell, holes);
}
/**
* Create a polygon corresponding to the wall.
*
*
* @param beginPoint
* @param endPoint
* @param height
* @return
*/
private static Polygon extrudeEdge(final Coordinate beginPoint,
Coordinate endPoint, final double height, GeometryFactory factory) {
beginPoint.z = Double.isNaN(beginPoint.z) ? 0 : beginPoint.z;
endPoint.z = Double.isNaN(endPoint.z) ? 0 : endPoint.z;
return factory.createPolygon(new Coordinate[]{
beginPoint,
new Coordinate(beginPoint.x, beginPoint.y, beginPoint.z
+ height),
new Coordinate(endPoint.x, endPoint.y, endPoint.z
+ height), endPoint, beginPoint});
}
/**
* Translate a geometry to a specific z value added to each vertexes.
*
*/
public static class TranslateCoordinateSequenceFilter implements CoordinateSequenceFilter {
private boolean done = false;
private final double z;
public TranslateCoordinateSequenceFilter(double z) {
this.z = z;
}
@Override
public boolean isGeometryChanged() {
return true;
}
@Override
public boolean isDone() {
return done;
}
@Override
public void filter(CoordinateSequence seq, int i) {
Coordinate coord = seq.getCoordinate(i);
double currentZ = coord.z;
if (!Double.isNaN(currentZ)) {
seq.setOrdinate(i, 2, currentZ + z);
}
else{
seq.setOrdinate(i, 2, z);
}
if (i == seq.size()) {
done = true;
}
}
}
}