/**
* H2GIS is a library that brings spatial support to the H2 Database Engine
* <http://www.h2database.com>. H2GIS is developed by CNRS
* <http://www.cnrs.fr/>.
*
* 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 <http://www.gnu.org/licenses/>.
*
*
* For more information, please consult: <http://www.h2gis.org/>
* or contact directly: info_at_h2gis.org
*/
package org.h2gis.functions.io.kml;
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.Point;
import com.vividsolutions.jts.geom.Polygon;
import java.sql.SQLException;
/**
* Tools to convert JTS geometry to KML representation
*
* @author Erwan Bocher
*/
public class KMLGeometry {
private KMLGeometry() {
}
/**
* Convert JTS geometry to a kml geometry representation.
*
* @param geom
* @param sb
* @throws SQLException
*/
public static void toKMLGeometry(Geometry geom, StringBuilder sb) throws SQLException {
toKMLGeometry(geom, ExtrudeMode.NONE, AltitudeMode.NONE, sb);
}
/**
* Convert JTS geometry to a kml geometry representation.
*
* @param geometry
* @param extrude
* @param altitudeModeEnum
* @param sb
*/
public static void toKMLGeometry(Geometry geometry, ExtrudeMode extrude, int altitudeModeEnum, StringBuilder sb) throws SQLException {
if (geometry instanceof Point) {
toKMLPoint((Point) geometry, extrude, altitudeModeEnum, sb);
} else if (geometry instanceof LineString) {
toKMLLineString((LineString) geometry, extrude, altitudeModeEnum, sb);
} else if (geometry instanceof Polygon) {
toKMLPolygon((Polygon) geometry, extrude, altitudeModeEnum, sb);
} else if (geometry instanceof GeometryCollection) {
toKMLMultiGeometry((GeometryCollection) geometry, extrude, altitudeModeEnum, sb);
} else {
throw new SQLException("This geometry type is not supported : " + geometry.toString());
}
}
/**
* A geographic location defined by longitude, latitude, and (optional)
* altitude.
*
* Syntax :
*
* <Point id="ID">
* <!-- specific to Point -->
* <extrude>0</extrude> <!-- boolean -->
* <altitudeMode>clampToGround</altitudeMode>
* <!-- kml:altitudeModeEnum: clampToGround, relativeToGround, or absolute
* -->
* <!-- or, substitute gx:altitudeMode: clampToSeaFloor, relativeToSeaFloor
* -->
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </Point>
*
* Supported syntax :
* <Point>
* <extrude>0</extrude>
* <altitudeMode>clampToGround</altitudeMode>
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </Point>
*
* @param point
* @param extrude
* @param altitudeModeEnum
*/
public static void toKMLPoint(Point point, ExtrudeMode extrude, int altitudeModeEnum, StringBuilder sb) {
sb.append("<Point>");
appendExtrude(extrude, sb);
appendAltitudeMode(altitudeModeEnum, sb);
sb.append("<coordinates>");
Coordinate coord = point.getCoordinate();
sb.append(coord.x).append(",").append(coord.y);
if (!Double.isNaN(coord.z)) {
sb.append(",").append(coord.z);
}
sb.append("</coordinates>").append("</Point>");
}
/**
* Defines a connected set of line segments.
*
* Syntax :
*
* <LineString id="ID">
* <!-- specific to LineString -->
* <gx:altitudeOffset>0</gx:altitudeOffset> <!-- double -->
* <extrude>0</extrude> <!-- boolean -->
* <tessellate>0</tessellate> <!-- boolean -->
* <altitudeMode>clampToGround</altitudeMode>
* <!-- kml:altitudeModeEnum: clampToGround, relativeToGround, or absolute
* -->
* <!-- or, substitute gx:altitudeMode: clampToSeaFloor, relativeToSeaFloor
* -->
* <gx:drawOrder>0</gx:drawOrder> <!-- integer -->
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </LineString>
*
* Supported syntax :
*
* <LineString>
* <extrude>0</extrude>
* <altitudeMode>clampToGround</altitudeMode>
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </LineString>
*
* @param lineString
*/
public static void toKMLLineString(LineString lineString, ExtrudeMode extrude, int altitudeModeEnum, StringBuilder sb) {
sb.append("<LineString>");
appendExtrude(extrude, sb);
appendAltitudeMode(altitudeModeEnum, sb);
appendKMLCoordinates(lineString.getCoordinates(), sb);
sb.append("</LineString>");
}
/**
* Defines a closed line string, typically the outer boundary of a Polygon.
*
* Syntax :
*
* <LinearRing id="ID">
* <!-- specific to LinearRing -->
* <gx:altitudeOffset>0</gx:altitudeOffset> <!-- double -->
* <extrude>0</extrude> <!-- boolean -->
* <tessellate>0</tessellate> <!-- boolean -->
* <altitudeMode>clampToGround</altitudeMode>
* <!-- kml:altitudeModeEnum: clampToGround, relativeToGround, or absolute
* -->
* <!-- or, substitute gx:altitudeMode: clampToSeaFloor, relativeToSeaFloor
* -->
* <coordinates>...</coordinates> <!-- lon,lat[,alt] tuples -->
* </LinearRing>
*
* Supported syntax :
*
* <LinearRing>
* <extrude>0</extrude>
* <altitudeMode>clampToGround</altitudeMode>
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </LinearRing>
*
* @param lineString
*/
public static void toKMLLinearRing(LineString lineString, ExtrudeMode extrude, int altitudeModeEnum, StringBuilder sb) {
sb.append("<LinearRing>");
appendExtrude(extrude, sb);
appendAltitudeMode(altitudeModeEnum, sb);
appendKMLCoordinates(lineString.getCoordinates(), sb);
sb.append("</LinearRing>");
}
/**
* A Polygon is defined by an outer boundary and 0 or more inner boundaries.
* The boundaries, in turn, are defined by LinearRings.
*
* Syntax :
*
* <Polygon id="ID">
* <!-- specific to Polygon -->
* <extrude>0</extrude> <!-- boolean -->
* <tessellate>0</tessellate> <!-- boolean -->
* <altitudeMode>clampToGround</altitudeMode>
* <!-- kml:altitudeModeEnum: clampToGround, relativeToGround, or absolute
* -->
* <!-- or, substitute gx:altitudeMode: clampToSeaFloor, relativeToSeaFloor
* -->
* <outerBoundaryIs>
* <LinearRing>
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </LinearRing>
* </outerBoundaryIs>
* <innerBoundaryIs>
* <LinearRing>
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </LinearRing>
* </innerBoundaryIs>
* </Polygon>
*
* Supported syntax :
*
* <Polygon>
* <extrude>0</extrude>
* <altitudeMode>clampToGround</altitudeMode>
* <outerBoundaryIs>
* <LinearRing>
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </LinearRing>
* </outerBoundaryIs>
* <innerBoundaryIs>
* <LinearRing>
* <coordinates>...</coordinates> <!-- lon,lat[,alt] -->
* </LinearRing>
* </innerBoundaryIs>
* </Polygon>
*
* @param polygon
*/
public static void toKMLPolygon(Polygon polygon, ExtrudeMode extrude, int altitudeModeEnum, StringBuilder sb) {
sb.append("<Polygon>");
appendExtrude(extrude, sb);
appendAltitudeMode(altitudeModeEnum, sb);
sb.append("<outerBoundaryIs>");
toKMLLinearRing(polygon.getExteriorRing(), extrude, altitudeModeEnum, sb);
sb.append("</outerBoundaryIs>");
for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
sb.append("<innerBoundaryIs>");
toKMLLinearRing(polygon.getInteriorRingN(i), extrude, altitudeModeEnum, sb);
sb.append("</innerBoundaryIs>");
}
sb.append("</Polygon>");
}
/**
*
*
* A container for zero or more geometry primitives associated with the same
* feature.
*
* <MultiGeometry id="ID">
* <!-- specific to MultiGeometry -->
* <!-- 0 or more Geometry elements -->
* </MultiGeometry>
*
* @param gc
*/
public static void toKMLMultiGeometry(GeometryCollection gc, ExtrudeMode extrude, int altitudeModeEnum, StringBuilder sb) {
sb.append("<MultiGeometry>");
for (int i = 0; i < gc.getNumGeometries(); i++) {
Geometry geom = gc.getGeometryN(i);
if (geom instanceof Point) {
toKMLPoint((Point) geom, extrude, altitudeModeEnum, sb);
} else if (geom instanceof LineString) {
toKMLLineString((LineString) geom, extrude, altitudeModeEnum, sb);
} else if (geom instanceof Polygon) {
toKMLPolygon((Polygon) geom, extrude, altitudeModeEnum, sb);
}
}
sb.append("</MultiGeometry>");
}
/**
* Build a string represention to kml coordinates
*
* Syntax :
*
* <coordinates>...</coordinates> <!-- lon,lat[,alt] tuples -->
*
* @param coords
*/
public static void appendKMLCoordinates(Coordinate[] coords, StringBuilder sb) {
sb.append("<coordinates>");
for (int i = 0; i < coords.length; i++) {
Coordinate coord = coords[i];
sb.append(coord.x).append(",").append(coord.y);
if (!Double.isNaN(coord.z)) {
sb.append(",").append(coord.z);
}
if (i < coords.length - 1) {
sb.append(" ");
}
}
sb.append("</coordinates>");
}
/**
* Append the extrude value
*
* Syntax :
*
* <extrude>0</extrude>
*
* @param extrude
* @param sb
*/
private static void appendExtrude(ExtrudeMode extrude, StringBuilder sb) {
if (extrude.equals(ExtrudeMode.TRUE)) {
sb.append("<extrude>").append(1).append("</extrude>");
} else if (extrude.equals(ExtrudeMode.FALSE)) {
sb.append("<extrude>").append(0).append("</extrude>");
}
}
/**
* Append the altitudeMode
*
* Syntax :
*
* <altitudeMode>clampToGround</altitudeMode>
*
* @param altitudeModeEnum
* @param sb
*/
private static void appendAltitudeMode(int altitudeModeEnum, StringBuilder sb) {
AltitudeMode.append(altitudeModeEnum, sb);
}
}