/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.query.function; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.sql.SQLException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.teiid.core.types.BlobType; import org.teiid.core.types.ClobImpl; import org.teiid.core.types.ClobType; import org.teiid.core.types.ClobType.Type; import org.teiid.core.types.GeometryType; import org.teiid.designer.annotation.Since; import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version; import org.teiid.query.util.CommandContext; import org.teiid.runtime.client.Messages; import org.teiid.runtime.client.TeiidClientException; import org.wololo.geojson.GeoJSON; import org.wololo.jts2geojson.GeoJSONReader; import org.wololo.jts2geojson.GeoJSONWriter; import org.xml.sax.Attributes; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.io.InputStreamInStream; import com.vividsolutions.jts.io.WKBReader; import com.vividsolutions.jts.io.WKBWriter; import com.vividsolutions.jts.io.WKTReader; import com.vividsolutions.jts.io.gml2.GMLHandler; import com.vividsolutions.jts.io.gml2.GMLWriter; /** * Utility methods for geometry * TODO: determine if we should use buffermanager to minimize memory footprint * TODO: Split into GeometryFilterUtils and GeometryConvertUtils. - Tom */ @Since(Version.TEIID_8_10) public class GeometryUtils { private static final int SRID_4326 = 4326; public static ClobType geometryToClob(GeometryType geometry, boolean withSrid) throws Exception { Geometry jtsGeometry = getGeometry(geometry); int srid = jtsGeometry.getSRID(); StringBuilder geomText = new StringBuilder(); if (withSrid && srid != GeometryType.UNKNOWN_SRID) { geomText.append("SRID=").append(jtsGeometry.getSRID()).append(";"); //$NON-NLS-1$ //$NON-NLS-2$ } geomText.append(jtsGeometry.toText()); return new ClobType(new ClobImpl(geomText.toString())); } public static GeometryType geometryFromClob(ClobType wkt) throws Exception { return geometryFromClob(wkt, GeometryType.UNKNOWN_SRID, false); } public static GeometryType geometryFromClob(ClobType wkt, int srid, boolean allowEwkt) throws Exception { Reader r = null; try { WKTReader reader = new WKTReader(); r = wkt.getCharacterStream(); Geometry jtsGeometry = reader.read(r); if (!allowEwkt && (jtsGeometry.getSRID() != GeometryType.UNKNOWN_SRID || (jtsGeometry.getCoordinate() != null && !Double.isNaN(jtsGeometry.getCoordinate().z)))) { //don't allow ewkt that requires a specific function throw new TeiidClientException(Messages.gs(Messages.TEIID.TEIID31160, "EWKT")); } return getGeometryType(jtsGeometry, srid); } catch (Exception e) { throw new TeiidClientException(e); } finally { if (r != null) { try { r.close(); } catch (IOException e) { } } } } public static ClobType geometryToGeoJson(GeometryType geometry) throws Exception { Geometry jtsGeometry = getGeometry(geometry); GeoJSONWriter writer = new GeoJSONWriter(); try { GeoJSON geoJson = writer.write(jtsGeometry); ClobType result = new ClobType(new ClobImpl(geoJson.toString())); result.setType(Type.JSON); return result; } catch (Exception e) { throw new Exception(e); } } public static GeometryType geometryFromGeoJson(ClobType json) throws Exception { return geometryFromGeoJson(json, GeometryType.UNKNOWN_SRID); } public static GeometryType geometryFromGeoJson(ClobType json, int srid) throws Exception { try { GeoJSONReader reader = new GeoJSONReader(); String jsonText = ClobType.getString(json); Geometry jtsGeometry = reader.read(jsonText); return getGeometryType(jtsGeometry, srid); } catch (SQLException e) { throw new Exception(e); } catch (IOException e) { throw new Exception(e); } } public static ClobType geometryToGml(CommandContext ctx, GeometryType geometry, boolean withGmlPrefix) throws Exception { Geometry jtsGeometry = getGeometry(geometry); GMLWriter writer = new GMLWriter(); if (ctx.getTeiidVersion().isGreaterThanOrEqualTo(Version.TEIID_8_11)) { if (!withGmlPrefix) { if (geometry.getSrid() != SRID_4326) { if (geometry.getSrid() == GeometryType.UNKNOWN_SRID) { throw new TeiidClientException(Messages.gs(Messages.TEIID.TEIID31161)); } jtsGeometry = GeometryTransformUtils.transform(ctx, jtsGeometry, SRID_4326); } writer.setPrefix(null); } else if (geometry.getSrid() != GeometryType.UNKNOWN_SRID) { //TODO: should include the srsName //writer.setSrsName(String.valueOf(geometry.getSrid())); } } String gmlText = writer.write(jtsGeometry); return new ClobType(new ClobImpl(gmlText)); } public static GeometryType geometryFromGml(ClobType gml, Integer srid) throws Exception { try { return geometryFromGml(gml.getCharacterStream(), srid); } catch (SQLException e) { throw new Exception(e); } } /** * Custom SAX handler extending GMLHandler to handle parsing SRIDs. * * The default JTS logic only handles srsName=int or srsName=uri/int * whereas other systems commonly use srsName=name:int */ private static class GmlSridHandler extends GMLHandler { private int srid = GeometryType.UNKNOWN_SRID; public GmlSridHandler(GeometryFactory gf, ErrorHandler delegate) { super(gf, delegate); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { String srsName = attributes.getValue("srsName"); //$NON-NLS-1$ if (srsName != null) { String[] srsParts = srsName.split(":"); //$NON-NLS-1$ try { if (srsParts.length == 2) { srid = Integer.parseInt(srsParts[1]); } } catch (NumberFormatException e) { // ignore } } super.startElement(uri, localName, qName, attributes); } public int getSrid() { return srid; } } public static GeometryType geometryFromGml(Reader reader, Integer srid) throws Exception { GeometryFactory gf = new GeometryFactory(); Geometry jtsGeometry = null; try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(false); factory.setValidating(false); SAXParser parser = factory.newSAXParser(); GmlSridHandler handler = new GmlSridHandler(gf, null); parser.parse(new InputSource(reader), handler); jtsGeometry = handler.getGeometry(); if (srid == null) { if (jtsGeometry.getSRID() == GeometryType.UNKNOWN_SRID) { srid = handler.getSrid(); } else { srid = jtsGeometry.getSRID(); } } } catch (IOException e) { throw new Exception(e); } catch (SAXException e) { throw new Exception(e); } catch (ParserConfigurationException e) { throw new Exception(e); } finally { if (reader != null) { try { reader.close(); } catch (Exception e) { // Nothing } } } return getGeometryType(jtsGeometry, srid); } public static GeometryType geometryFromBlob(BlobType wkb) throws Exception { return geometryFromBlob(wkb, GeometryType.UNKNOWN_SRID); } //TODO: should allow an option to assume well formed public static GeometryType geometryFromBlob(BlobType wkb, int srid) throws Exception { //return as geometry GeometryType gt = new GeometryType(wkb.getReference(), srid); //validate getGeometry(gt); return gt; } public static Boolean intersects(GeometryType geom1, GeometryType geom2) throws Exception { Geometry g1 = getGeometry(geom1); Geometry g2 = getGeometry(geom2); return g1.intersects(g2); } public static Boolean contains(GeometryType geom1, GeometryType geom2) throws Exception { Geometry g1 = getGeometry(geom1); Geometry g2 = getGeometry(geom2); return g1.contains(g2); } public static Boolean disjoint(GeometryType geom1, GeometryType geom2) throws Exception { Geometry g1 = getGeometry(geom1); Geometry g2 = getGeometry(geom2); return g1.disjoint(g2); } public static Boolean crosses(GeometryType geom1, GeometryType geom2) throws Exception { Geometry g1 = getGeometry(geom1); Geometry g2 = getGeometry(geom2); return g1.crosses(g2); } public static Double distance(GeometryType geom1, GeometryType geom2) throws Exception { Geometry g1 = getGeometry(geom1); Geometry g2 = getGeometry(geom2); return g1.distance(g2); } public static Boolean touches(GeometryType geom1, GeometryType geom2) throws Exception { Geometry g1 = getGeometry(geom1); Geometry g2 = getGeometry(geom2); return g1.touches(g2); } public static Boolean overlaps(GeometryType geom1, GeometryType geom2) throws Exception { Geometry g1 = getGeometry(geom1); Geometry g2 = getGeometry(geom2); return g1.overlaps(g2); } public static GeometryType getGeometryType(Geometry jtsGeom) { return getGeometryType(jtsGeom, jtsGeom.getSRID()); } public static GeometryType getGeometryType(Geometry jtsGeom, int srid) { WKBWriter writer = new WKBWriter(); byte[] bytes = writer.write(jtsGeom); return new GeometryType(bytes, srid); } public static Geometry getGeometry(GeometryType geom) throws Exception { try { return getGeometry(geom.getBinaryStream(), geom.getSrid(), false); } catch (SQLException e) { throw new Exception(e); } } public static Geometry getGeometry(InputStream is1, Integer srid, boolean allowEwkb) throws Exception { try { WKBReader reader = new WKBReader(); Geometry jtsGeom = reader.read(new InputStreamInStream(is1)); if (!allowEwkb && (jtsGeom.getSRID() != GeometryType.UNKNOWN_SRID || (jtsGeom.getCoordinate() != null && !Double.isNaN(jtsGeom.getCoordinate().z)))) { //don't allow ewkb - that needs an explicit function throw new TeiidClientException(Messages.gs(Messages.TEIID.TEIID31160, "EWKB")); } if (srid != null) { jtsGeom.setSRID(srid); } return jtsGeom; } catch (Exception e) { throw new TeiidClientException(e); } finally { if (is1 != null) { try { is1.close(); } catch (IOException e) { } } } } public static Boolean equals(GeometryType geom1, GeometryType geom2) throws Exception { return getGeometry(geom1).equalsTopo(getGeometry(geom2)); } public static GeometryType geometryFromEwkb(InputStream is, Integer srid) throws Exception { Geometry geom = getGeometry(is, srid, true); return getGeometryType(geom); } }