
org.h2gis.drivers.kml.KMLWriterDriver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of h2drivers Show documentation
Show all versions of h2drivers Show documentation
Add H2 read/write support for file formats such as ESRI shape file
/**
* H2GIS is a library that brings spatial support to the H2 Database Engine
* .
*
* H2GIS is distributed under GPL 3 license. It is produced by CNRS
* .
*
* H2GIS is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* H2GIS. If not, see .
*
* For more information, please consult:
* or contact directly: info_at_h2gis.org
*/
package org.h2gis.drivers.kml;
import com.vividsolutions.jts.geom.Geometry;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.h2gis.drivers.utility.FileUtil;
import org.h2gis.h2spatialapi.ProgressVisitor;
import org.h2gis.utilities.JDBCUtilities;
import org.h2gis.utilities.SFSUtilities;
import org.h2gis.utilities.TableLocation;
/**
*
* @author Erwan Bocher
*/
public class KMLWriterDriver {
private final String tableName;
private final File fileName;
private final Connection connection;
private HashMap kmlFields;
private int columnCount = -1;
public KMLWriterDriver(Connection connection, String tableName, File fileName) {
this.connection = connection;
this.tableName = tableName;
this.fileName = fileName;
}
/**
* Write spatial table to kml or kmz file format.
*
* @param progress
* @throws SQLException
*/
public void write(ProgressVisitor progress) throws SQLException {
if (FileUtil.isExtensionWellFormated(fileName, "kml")) {
if (!fileName.exists()) {
writeKML(progress);
} else {
throw new SQLException("The file " + fileName.getPath() + " already exists.");
}
} else if (FileUtil.isExtensionWellFormated(fileName, "kmz")) {
if (!fileName.exists()) {
String name = fileName.getName();
int pos = name.lastIndexOf(".");
writeKMZ(progress, name.substring(0, pos) + ".kml");
} else {
throw new SQLException("The file " + fileName.getPath() + " already exists.");
}
} else {
throw new SQLException("Please use the extensions .kml or kmz.");
}
}
/**
* Write the spatial table to a KML format
*
* @param progress
* @throws SQLException
*/
private void writeKML(ProgressVisitor progress) throws SQLException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileName);
writeKMLDocument(progress, fos);
} catch (FileNotFoundException ex) {
throw new SQLException(ex);
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException ex) {
throw new SQLException(ex);
}
}
}
/**
* Write the spatial table to a KMZ format
*
* @param progress
* @param fileNameWithExtension
* @throws SQLException
*/
private void writeKMZ(ProgressVisitor progress, String fileNameWithExtension) throws SQLException {
ZipOutputStream zos = null;
try {
zos = new ZipOutputStream(new FileOutputStream(fileName));
// Create a zip entry for the main KML file
zos.putNextEntry(new ZipEntry(fileNameWithExtension));
writeKMLDocument(progress, zos);
} catch (FileNotFoundException ex) {
throw new SQLException(ex);
} catch (IOException ex) {
throw new SQLException(ex);
} finally {
try {
if (zos != null) {
zos.closeEntry();
zos.finish();
}
} catch (IOException ex) {
throw new SQLException(ex);
}
}
}
/**
* Write the KML document Note the document stores only the first geometry
* column in the placeMark element. The other geomtry columns are ignored.
*
* @param progress
* @param outputStream
* @throws SQLException
*/
private void writeKMLDocument(ProgressVisitor progress, OutputStream outputStream) throws SQLException {
// Read Geometry Index and type
List spatialFieldNames = SFSUtilities.getGeometryFields(connection, TableLocation.parse(tableName, JDBCUtilities.isH2DataBase(connection.getMetaData())));
if (spatialFieldNames.isEmpty()) {
throw new SQLException(String.format("The table %s does not contain a geometry field", tableName));
}
try {
final XMLOutputFactory streamWriterFactory = XMLOutputFactory.newFactory();
streamWriterFactory.setProperty("escapeCharacters", false);
XMLStreamWriter xmlOut = streamWriterFactory.createXMLStreamWriter(
new BufferedOutputStream(outputStream), "UTF-8");
xmlOut.writeStartDocument("UTF-8", "1.0");
xmlOut.writeStartElement("kml");
xmlOut.writeDefaultNamespace("http://www.opengis.net/kml/2.2");
xmlOut.writeNamespace("atom", "http://www.w3.org/2005/Atom");
xmlOut.writeNamespace("kml", "http://www.opengis.net/kml/2.2");
xmlOut.writeNamespace("gx", "http://www.google.com/kml/ext/2.2");
xmlOut.writeNamespace("xal", "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0");
xmlOut.writeStartElement("Document");
// Read table content
Statement st = connection.createStatement();
try {
ResultSet rs = st.executeQuery(String.format("select * from `%s`", tableName));
try {
int recordCount = JDBCUtilities.getRowCount(connection, tableName);
ProgressVisitor copyProgress = progress.subProcess(recordCount);
ResultSetMetaData resultSetMetaData = rs.getMetaData();
int geoFieldIndex = JDBCUtilities.getFieldIndex(resultSetMetaData, spatialFieldNames.get(0));
writeSchema(xmlOut, resultSetMetaData);
xmlOut.writeStartElement("Folder");
xmlOut.writeStartElement("name");
xmlOut.writeCharacters(tableName);
xmlOut.writeEndElement();//Name
while (rs.next()) {
writePlacemark(xmlOut, rs, geoFieldIndex, spatialFieldNames.get(0));
copyProgress.endStep();
}
} finally {
rs.close();
}
} finally {
st.close();
}
xmlOut.writeEndElement();//Folder
xmlOut.writeEndElement();//KML
xmlOut.writeEndDocument();//DOC
xmlOut.close();
} catch (XMLStreamException ex) {
throw new SQLException(ex);
}
}
/**
* Specifies a custom KML schema that is used to add custom data to KML
* Features. The "id" attribute is required and must be unique within the
* KML file.
* is always a child of .
*
* Syntax :
*
*
*
* ...
*
*
*
* @param xmlOut
* @param tableName
*/
private void writeSchema(XMLStreamWriter xmlOut, ResultSetMetaData metaData) throws XMLStreamException, SQLException {
columnCount = metaData.getColumnCount();
//The schema is writing only if there is more than one column
if (columnCount > 1) {
xmlOut.writeStartElement("Schema");
xmlOut.writeAttribute("name", tableName);
xmlOut.writeAttribute("id", tableName);
//Write column metadata
kmlFields = new HashMap();
for (int fieldId = 1; fieldId <= metaData.getColumnCount(); fieldId++) {
final String fieldTypeName = metaData.getColumnTypeName(fieldId);
if (!fieldTypeName.equalsIgnoreCase("geometry")) {
String fieldName = metaData.getColumnName(fieldId);
writeSimpleField(xmlOut, fieldName, getKMLType(metaData.getColumnType(fieldId), fieldTypeName));
kmlFields.put(fieldId, fieldName);
}
}
xmlOut.writeEndElement();//Write schema
}
}
/**
* The declaration of the custom field, which must specify both the type and
* the name of this field. If either the type or the name is omitted, the
* field is ignored. The type can be one of the following : string, int,
* uint, short, ushort, float, double, bool.
*
* Syntax :
*
*
*
* @param xmlOut
* @param columnName
* @param columnType
* @throws XMLStreamException
*/
private void writeSimpleField(XMLStreamWriter xmlOut, String columnName, String columnType) throws XMLStreamException {
xmlOut.writeStartElement("SimpleField");
xmlOut.writeAttribute("name", columnName);
xmlOut.writeAttribute("type", columnType);
xmlOut.writeEndElement();//Write schema
}
/**
* A Placemark is a Feature with associated Geometry.
*
* Syntax :
*
*
*
* ...
* 1
* 0
* ...
*
* ...
* ...
* ...
* ...
* ...
* ...
* ...
* ...
* ...
* ...
* ...
* ...
*
*
* ...
*
*
* @param xmlOut
*/
public void writePlacemark(XMLStreamWriter xmlOut, ResultSet rs, int geoFieldIndex, String spatialFieldName) throws XMLStreamException, SQLException {
xmlOut.writeStartElement("Placemark");
if (columnCount > 1) {
writeExtendedData(xmlOut, rs);
}
StringBuilder sb = new StringBuilder();
Geometry geom = (Geometry) rs.getObject(geoFieldIndex);
int inputSRID = geom.getSRID();
if (inputSRID == 0) {
throw new SQLException("A coordinate reference system must be set to save the KML file");
} else if (inputSRID != 4326) {
throw new SQLException("The kml format supports only the WGS84 projection. \n"
+ "Please use ST_Transform(" + spatialFieldName + "," + inputSRID + ")");
}
KMLGeometry.toKMLGeometry(geom, ExtrudeMode.NONE, AltitudeMode.NONE, sb);
//Write geometry
xmlOut.writeCharacters(sb.toString());
xmlOut.writeEndElement();//Write Placemark
}
/**
* The ExtendedData element offers three techniques for adding custom data
* to a KML Feature (NetworkLink, Placemark, GroundOverlay, PhotoOverlay,
* ScreenOverlay, Document, Folder). These techniques are
*
* Adding untyped data/value pairs using the element (basic)
* Declaring new typed fields using the element and then instancing
* them using the element (advanced) Referring to XML elements
* defined in other namespaces by referencing the external namespace within
* the KML file (basic)
*
* These techniques can be combined within a single KML file or Feature for
* different pieces of data.
*
* Syntax :
*
*
*
* ...
* ...
*
*
* ...
*
* ...
*
*
* @param xmlOut
*/
public void writeExtendedData(XMLStreamWriter xmlOut, ResultSet rs) throws XMLStreamException, SQLException {
xmlOut.writeStartElement("ExtendedData");
xmlOut.writeStartElement("SchemaData");
xmlOut.writeAttribute("schemaUrl", "#" + tableName);
for (Map.Entry entry : kmlFields.entrySet()) {
Integer fieldIndex = entry.getKey();
String fieldName = entry.getValue();
writeSimpleData(xmlOut, fieldName, rs.getString(fieldIndex));
}
xmlOut.writeEndElement();//Write SchemaData
xmlOut.writeEndElement();//Write ExtendedData
}
/**
*
* @param xmlOut
*/
public void writeSimpleData(XMLStreamWriter xmlOut, String columnName, String value) throws XMLStreamException {
xmlOut.writeStartElement("SimpleData");
xmlOut.writeAttribute("name", columnName);
xmlOut.writeCharacters(value);
xmlOut.writeEndElement();//Write ExtendedData
}
/**
* Return the kml type representation from SQL data type
*
* @param sqlTypeId
* @param sqlTypeName
* @return
* @throws SQLException
*/
private static String getKMLType(int sqlTypeId, String sqlTypeName) throws SQLException {
switch (sqlTypeId) {
case Types.BOOLEAN:
return "bool";
case Types.DOUBLE:
return "double";
case Types.FLOAT:
return "float";
case Types.INTEGER:
case Types.BIGINT:
return "int";
case Types.SMALLINT:
return "short";
case Types.DATE:
case Types.VARCHAR:
case Types.NCHAR:
case Types.CHAR:
return "string";
default:
throw new SQLException("Field type not supported by KML : " + sqlTypeName);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy