/* * eXist Open Source Native XML Database * Copyright (C) 2007-09 The eXist Project * http://exist-db.org * * This program 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 * of the License, or (at your option) any later version. * * This program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ * * @author Pierrick Brihaye <pierrick.brihaye@free.fr> * @author ljo */ package org.exist.xquery.modules.spatial; import org.apache.log4j.Logger; import org.exist.dom.NodeProxy; import org.exist.dom.QName; import org.exist.indexing.spatial.AbstractGMLJDBCIndex; import org.exist.indexing.spatial.AbstractGMLJDBCIndexWorker; import org.exist.indexing.spatial.SpatialIndexException; import org.exist.xquery.BasicFunction; import org.exist.xquery.Cardinality; import org.exist.xquery.Constants; import org.exist.xquery.FunctionSignature; import org.exist.xquery.IndexUseReporter; import org.exist.xquery.XPathException; import org.exist.xquery.XQueryContext; import org.exist.xquery.value.Base64Binary; import org.exist.xquery.value.BooleanValue; import org.exist.xquery.value.DoubleValue; import org.exist.xquery.value.FunctionReturnSequenceType; import org.exist.xquery.value.FunctionParameterSequenceType; import org.exist.xquery.value.NodeValue; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.SequenceType; import org.exist.xquery.value.StringValue; import org.exist.xquery.value.Type; import org.w3c.dom.Element; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.WKBWriter; import com.vividsolutions.jts.io.WKTWriter; public class FunGeometricProperties extends BasicFunction implements IndexUseReporter { protected static final Logger logger = Logger.getLogger(FunGeometricProperties.class); boolean hasUsedIndex = false; protected WKTWriter wktWriter = new WKTWriter(); protected WKBWriter wkbWriter = new WKBWriter(); public final static FunctionSignature[] signatures = { new FunctionSignature( new QName("getWKT", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the WKT representation of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.STRING, Cardinality.ZERO_OR_ONE, "the WKT representation of geometry $geometry") ), new FunctionSignature( new QName("getWKB", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the WKB representation of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.BASE64_BINARY, Cardinality.ZERO_OR_ONE, "the WKB representation of geometry $geometry") ), new FunctionSignature( new QName("getMinX", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the minimal X of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the minimal X of geometry $geometry") ), new FunctionSignature( new QName("getMaxX", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the maximal X of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the maxmal X of geometry $geometry") ), new FunctionSignature( new QName("getMinY", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the minimal Y of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the minimal Y of geometry $geometry") ), new FunctionSignature( new QName("getMaxY", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the maximal Y of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the maximal Y of geometry $geometry") ), new FunctionSignature( new QName("getCentroidX", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the X of centroid of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the X of centroid of geometry $geometry") ), new FunctionSignature( new QName("getCentroidY", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the Y of centroid of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the Y of centroid of geometry $geometry") ), new FunctionSignature( new QName("getArea", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the area of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the area of geometry $geometry") ), new FunctionSignature( new QName("getEPSG4326WKT", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the WKT representation of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.STRING, Cardinality.ZERO_OR_ONE, "the WKT representation of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326WKB", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the WKB representation of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.BASE64_BINARY, Cardinality.ZERO_OR_ONE, "the WKB representation of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326MinX", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the minimal X of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the minimal X of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326MaxX", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the maximal X of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the maximal X of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326MinY", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the minimal Y of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the minimal Y of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326MaxY", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the maximal Y of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the maximal Y of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326CentroidX", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the X of centroid of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the X of centroid of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326CentroidY", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the Y of centroid of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the Y of centroid of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getEPSG4326Area", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the area of geometry $geometry in the EPSG:4326 SRS", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.DOUBLE, Cardinality.ZERO_OR_ONE, "the area of geometry $geometry in the EPSG:4326 SRS") ), new FunctionSignature( new QName("getSRS", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the spatial reference system of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.STRING, Cardinality.ZERO_OR_ONE, " the spatial reference system of geometry $geometry") ), new FunctionSignature( new QName("getGeometryType", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns the type of geometry $geometry", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER }, new FunctionReturnSequenceType(Type.STRING, Cardinality.ZERO_OR_ONE, "the type of geometry $geometry") ), new FunctionSignature( new QName("isClosed", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns true() if geometry $geometry is closed, otherwise false()", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER, }, new FunctionReturnSequenceType(Type.BOOLEAN, Cardinality.ZERO_OR_ONE, "true() if geometry $geometry is closed, otherwise false()") ), new FunctionSignature( new QName("isSimple", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns true() if geometry $geometry is simple, otherwise false()", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER, }, new FunctionReturnSequenceType(Type.BOOLEAN, Cardinality.ZERO_OR_ONE, "true() if geometry $geometry is simple, otherwise false()") ), new FunctionSignature( new QName("isValid", SpatialModule.NAMESPACE_URI, SpatialModule.PREFIX), "Returns true() if geometry $geometry is valid, otherwise false()", new SequenceType[]{ FunSpatialSearch.GEOMETRY_PARAMETER, }, new FunctionReturnSequenceType(Type.BOOLEAN, Cardinality.ZERO_OR_ONE, "true() if geometry $geometry is valid, otherwise false()") ) }; public FunGeometricProperties(XQueryContext context, FunctionSignature signature) { super(context, signature); } public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException { Sequence result = null; Sequence nodes = args[0]; if (nodes.isEmpty()) { result = Sequence.EMPTY_SEQUENCE; } else { try { Geometry geometry = null; String sourceCRS = null; AbstractGMLJDBCIndexWorker indexWorker = (AbstractGMLJDBCIndexWorker) context.getBroker().getIndexController().getWorkerByIndexId(AbstractGMLJDBCIndex.ID); if (indexWorker == null) { logger.error("Unable to find a spatial index worker"); throw new XPathException("Unable to find a spatial index worker"); } String propertyName = null; if (isCalledAs("getWKT")) { propertyName = "WKT"; } else if (isCalledAs("getWKB")) { propertyName = "WKB"; } else if (isCalledAs("getMinX")) { propertyName = "MINX"; } else if (isCalledAs("getMaxX")) { propertyName = "MAXX"; } else if (isCalledAs("getMinY")) { propertyName = "MINY"; } else if (isCalledAs("getMaxY")) { propertyName = "MAXY"; } else if (isCalledAs("getCentroidX")) { propertyName = "CENTROID_X"; } else if (isCalledAs("getCentroidY")) { propertyName = "CENTROID_Y"; } else if (isCalledAs("getArea")) { propertyName = "AREA"; } else if (isCalledAs("getEPSG4326WKT")) { propertyName = "EPSG4326_WKT"; } else if (isCalledAs("getEPSG4326WKB")) { propertyName = "EPSG4326_WKB"; } else if (isCalledAs("getEPSG4326MinX")) { propertyName = "EPSG4326_MINX"; } else if (isCalledAs("getEPSG4326MaxX")) { propertyName = "EPSG4326_MAXX"; } else if (isCalledAs("getEPSG4326MinY")) { propertyName = "EPSG4326_MINY"; } else if (isCalledAs("getEPSG4326MaxY")) { propertyName = "EPSG4326_MAXY"; } else if (isCalledAs("getEPSG4326CentroidX")) { propertyName = "EPSG4326_CENTROID_X"; } else if (isCalledAs("getEPSG4326CentroidY")) { propertyName = "EPSG4326_CENTROID_Y"; } else if (isCalledAs("getEPSG4326Area")) { propertyName = "EPSG4326_AREA"; } else if (isCalledAs("getSRS")) { propertyName = "SRS_NAME"; } else if (isCalledAs("getGeometryType")) { propertyName = "GEOMETRY_TYPE"; } else if (isCalledAs("isClosed")) { propertyName = "IS_CLOSED"; } else if (isCalledAs("isSimple")) { propertyName = "IS_SIMPLE"; } else if (isCalledAs("isValid")) { propertyName = "IS_VALID"; } else { logger.error("Unknown spatial property: " + mySignature.getName().getLocalName()); throw new XPathException("Unknown spatial property: " + mySignature.getName().getLocalName()); } NodeValue geometryNode = (NodeValue) nodes.itemAt(0); if (geometryNode.getImplementationType() == NodeValue.PERSISTENT_NODE) { //The node should be indexed : get its property result = indexWorker.getGeometricPropertyForNode(context.getBroker(), (NodeProxy)geometryNode, propertyName); hasUsedIndex = true; } else { //builds the geometry if (geometry == null) { sourceCRS = ((Element)geometryNode.getNode()).getAttribute("srsName").trim(); geometry = indexWorker.streamNodeToGeometry(context, geometryNode); } if (geometry == null) { logger.error("Unable to get a geometry from the node"); throw new XPathException("Unable to get a geometry from the node"); } //Transform the geometry to EPSG:4326 if relevant if (propertyName.indexOf("EPSG4326") != Constants.STRING_NOT_FOUND) { geometry = indexWorker.transformGeometry(geometry, sourceCRS, "EPSG:4326"); if (isCalledAs("getEPSG4326WKT")) { result = new StringValue(wktWriter.write(geometry)); } else if (isCalledAs("getEPSG4326WKB")) { result = new Base64Binary(wkbWriter.write(geometry)); } else if (isCalledAs("getEPSG4326MinX")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMinX()); } else if (isCalledAs("getEPSG4326MaxX")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMaxX()); } else if (isCalledAs("getEPSG4326MinY")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMinY()); } else if (isCalledAs("getEPSG4326MaxY")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMaxY()); } else if (isCalledAs("getEPSG4326CentroidX")) { result = new DoubleValue(geometry.getCentroid().getX()); } else if (isCalledAs("getEPSG4326CentroidY")) { result = new DoubleValue(geometry.getCentroid().getY()); } else if (isCalledAs("getEPSG4326Area")) { result = new DoubleValue(geometry.getArea()); } } else if (isCalledAs("getWKT")) { result = new StringValue(wktWriter.write(geometry)); } else if (isCalledAs("getWKB")) { result = new Base64Binary(wkbWriter.write(geometry)); } else if (isCalledAs("getMinX")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMinX()); } else if (isCalledAs("getMaxX")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMaxX()); } else if (isCalledAs("getMinY")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMinY()); } else if (isCalledAs("getMaxY")) { result = new DoubleValue(geometry.getEnvelopeInternal().getMaxY()); } else if (isCalledAs("getCentroidX")) { result = new DoubleValue(geometry.getCentroid().getX()); } else if (isCalledAs("getCentroidY")) { result = new DoubleValue(geometry.getCentroid().getY()); } else if (isCalledAs("getArea")) { result = new DoubleValue(geometry.getArea()); } else if (isCalledAs("getSRS")) { result = new StringValue(((Element)geometryNode).getAttribute("srsName")); } else if (isCalledAs("getGeometryType")) { result = new StringValue(geometry.getGeometryType()); } else if (isCalledAs("isClosed")) { result = new BooleanValue(!geometry.isEmpty()); } else if (isCalledAs("isSimple")) { result = new BooleanValue(geometry.isSimple()); } else if (isCalledAs("isValid")) { result = new BooleanValue(geometry.isValid()); } else { logger.error("Unknown spatial property: " + mySignature.getName().getLocalName()); throw new XPathException("Unknown spatial property: " + mySignature.getName().getLocalName()); } } } catch (SpatialIndexException e) { logger.error(e.getMessage()); throw new XPathException(e); } } return result; } public boolean hasUsedIndex() { return hasUsedIndex; } }