/* * Geotools2 - OpenSource mapping toolkit * http://geotools.org * (C) 2002, Geotools Project Managment Committee (PMC) * * 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; * version 2.1 of the License. * * 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. * */ package org.geotools.filter; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import org.geotools.data.oracle.sdo.SDO; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.CoordinateSequenceFactory; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * Encodes Geometry filters into valid oracle SDO statements. * *
* At this stage it only supports the GEOMETRY_BBOX types. *
* *
* Encoded filters get written to the protected Writer called out
*
* 2D geometries is assumed. If higher dimensional geometries are used the * query will be encoded as a 2D geometry. *
* * @param line The line to encode. * @param srid DOCUMENT ME! * * @return An SDO SQL geometry object construction statement */ private static String toSDOGeom(LineString line, int srid) { if (SDO.D(line) > 2) { LOGGER.warning("" + SDO.D(line) + " dimensioned geometry provided." + " This encoder only supports 2D geometries. The query will be constructed as" + " a 2D query."); } StringBuffer buffer = new StringBuffer("MDSYS.SDO_GEOMETRY("); buffer.append(SDO.D(line)); buffer.append("002,"); if (srid > 0) { LOGGER.fine("Using layer SRID: " + srid); buffer.append(srid); } else { LOGGER.fine("Using NULL SRID: "); buffer.append("NULL"); } buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),"); buffer.append("MDSYS.SDO_ORDINATE_ARRAY("); Coordinate[] coordinates = line.getCoordinates(); for (int i = 0; i < coordinates.length; i++) { buffer.append(coordinates[i].x); buffer.append(","); buffer.append(coordinates[i].y); if (i != (coordinates.length - 1)) { buffer.append(","); } } buffer.append("))"); return buffer.toString(); } /** * Converts a Point Geometry in an SDO SQL geometry construction statement. * ** 2D geometries is assumed. If higher dimensional geometries are used the * query will be encoded as a 2D geometry. *
* * @param point The point to encode. * @param srid DOCUMENT ME! * * @return An SDO SQL geometry object construction statement */ private static String toSDOGeom(Point point, int srid) { if (SDO.D(point) > 2) { LOGGER.warning("" + SDO.D(point) + " dimensioned geometry provided." + " This encoder only supports 2D geometries. The query will be constructed as" + " a 2D query."); } StringBuffer buffer = new StringBuffer("MDSYS.SDO_GEOMETRY("); buffer.append(SDO.D(point)); buffer.append("001,"); if (srid > 0) { LOGGER.fine("Using layer SRID: " + srid); buffer.append(srid); } else { LOGGER.fine("Using NULL SRID: "); buffer.append("NULL"); } buffer.append(",MDSYS.SDO_POINT_TYPE("); buffer.append(point.getX()); buffer.append(","); buffer.append(point.getY()); buffer.append(",NULL),NULL,NULL)"); return buffer.toString(); } /** * Converts a Polygon Geometry in an SDO SQL geometry construction * statement. * ** 2D geometries is assumed. If higher dimensional geometries are used the * query will be encoded as a 2D geometry. *
* * @param polygon The polygon to encode. * @param srid DOCUMENT ME! * * @return An SDO SQL geometry object construction statement */ private static String toSDOGeom(Polygon polygon, int srid) { StringBuffer buffer = new StringBuffer(); if (SDO.D(polygon) > 2) { LOGGER.warning("" + SDO.D(polygon) + " dimensioned geometry provided." + " This encoder only supports 2D geometries. The query will be constructed as" + " a 2D query."); } if (polygon.getExteriorRing() != null) { buffer.append("MDSYS.SDO_GEOMETRY("); buffer.append(SDO.D(polygon)); buffer.append("003,"); if (srid > 0) { LOGGER.fine("Using layer SRID: " + srid); buffer.append(srid); } else { LOGGER.fine("Using NULL SRID: "); buffer.append("NULL"); } buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),"); buffer.append("MDSYS.SDO_ORDINATE_ARRAY("); CoordinateSequenceFactory fact = polygon.getFactory().getCoordinateSequenceFactory(); CoordinateSequence exterior = polygon.getExteriorRing().getCoordinateSequence(); CoordinateSequence coordSeq = SDO.counterClockWise(fact, exterior); for (int i = 0, size = coordSeq.size(); i < size; i++) { Coordinate cur = coordSeq.getCoordinate(i); buffer.append(cur.x); buffer.append(","); buffer.append(cur.y); if (i != (size - 1)) { buffer.append(","); } } /* This could be expensive if coordSeq implementation is not an an array. Leaving in for now as I can't test, and this is more likely to work right. Coordinate[] coordinates = coordSeq.toCoordinateArray(); for (int i = 0; i < coordinates.length; i++) { buffer.append(coordinates[i].x); buffer.append(","); buffer.append(coordinates[i].y); if (i != (coordinates.length - 1)) { buffer.append(","); } }*/ buffer.append("))"); } else { LOGGER.warning("No Exterior ring on polygon. " + "This encode only supports Polygons with exterior rings."); } if (polygon.getNumInteriorRing() > 0) { LOGGER.warning("Polygon contains Interior Rings. " + "These rings will not be included in the query."); } return buffer.toString(); } /** * Handles Geometry Filter encoding. Currently only supports the encoding * of GEOMETRY_BBOX filters. If a GEOMETRY_BBOX filter is encounter it * will be converted into an SDO_RELATE() function. If another filter is * found, nothing will happen. * * @param geomFilter The geometry filter to encode. * * @see org.geotools.filter.FilterVisitor#visit(org.geotools.filter.GeometryFilter) */ public void visit(GeometryFilter geomFilter) { LOGGER.finer("Visiting a Geometry filter"); try { short filterType = geomFilter.getFilterType(); if ((filterType == AbstractFilter.GEOMETRY_DWITHIN) || (filterType == AbstractFilter.GEOMETRY_BEYOND)){ doSdoDistance((GeometryDistanceFilter)geomFilter); //} else if (filterType == AbstractFilter.GEOMETRY_INTERSECTS //|| filterType == AbstractFilter.GEOMETRY_BBOX) { //doNotDisjointFilter(geomFilter); } else if (SDO_RELATE_MASK_MAP.get(new Short(geomFilter.getFilterType())) != null) { doSdoRelate(geomFilter); } else { LOGGER.warning("Unknown filter type: " + geomFilter.getFilterType()); } } catch (IOException e) { LOGGER.warning("IO Error exporting geometry filter"); } } /** * Writes the SQL for the Like Filter. Assumes the current java * implemented wildcards for the Like Filter: . for multi and .? for * single. And replaces them with the SQL % and _, respectively. Currently * does nothing, and should not be called, not included in the * capabilities. * * @param filter the Like Filter to be visited. * * @task TODO: LikeFilter doesn't work right...revisit this when it does. * Need to think through the escape char, so it works right when * Java uses one, and escapes correctly with an '_'. */ public void visit(LikeFilter filter) { try { String pattern = filter.getPattern(); String multi = "\\Q"+filter.getWildcardMulti()+"\\E"; pattern = pattern.replaceAll( multi, SQL_WILD_MULTI); String single = "\\Q"+filter.getWildcardSingle()+"\\E"; pattern = pattern.replaceAll( single, SQL_WILD_SINGLE); //pattern = pattern.replace('\\', ''); //get rid of java escapes. out.write("UPPER("); ((Expression) filter.getValue()).accept(this); out.write(") LIKE "); out.write("UPPER('" + pattern + "')"); String esc = filter.getEscape(); if (pattern.indexOf(esc) != -1) { //if it uses the escape char out.write(" ESCAPE " + "'" + esc + "'"); //this needs testing } //TODO figure out when to add ESCAPE clause, probably just for the // '_' char. } catch (java.io.IOException ioe) { LOGGER.warning("Unable to export filter" + ioe); } } /** * Converts a literal expression into a valid SDO object. Only handles * Literal Geometries, all other literals are passed up to the parent. * * @param literal The Literal expression to encode. * * @see org.geotools.filter.FilterVisitor#visit(org.geotools.filter.LiteralExpression) */ public void visit(LiteralExpression literal) { if (literal.getType() == DefaultExpression.LITERAL_GEOMETRY) { Geometry geometry = (Geometry) literal.getLiteral(); try { int srid = -1; Integer sridO = (Integer) srids.get(currentGeomColumnName); if (sridO == null) { // try for default sridO = (Integer) srids.get(null); } if (sridO != null) { srid = sridO.intValue(); } out.write(toSDOGeom(geometry, srid)); } catch (IOException e) { LOGGER.warning("IO Error exporting Literal Geometry"); } } else { // can't do it, send it off to the parent super.visit(literal); } } /** * DOCUMENT ME! * * @param filter * * @see org.geotools.filter.SQLEncoder#visit(org.geotools.filter.FidFilter) */ public void visit(FidFilter filter) { if (fidColumn != null) { String[] fids = filter.getFids(); LOGGER.finer("Exporting FID=" + Arrays.asList(fids)); for (int i = 0; i < fids.length; i++) { try { out.write(fidColumn); out.write(" = '"); int pos; if ((pos = fids[i].indexOf('.')) != -1) { out.write(fids[i].substring(pos + 1)); } else { out.write(fids[i]); } out.write("'"); if (i < (fids.length - 1)) { out.write(" OR "); } } catch (IOException e) { LOGGER.warning("IO Error exporting FID Filter."); } } } else { super.visit(filter); } } /* * (non-Javadoc) * * @see org.geotools.filter.SQLEncoder#visit(org.geotools.filter.AttributeExpression) */ public void visit(AttributeExpression ae) throws RuntimeException { super.visit(ae); if (inGeomFilter) { currentGeomColumnName = ae.getAttributePath(); } } }