/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2008 - 2009, Geomatys * * 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. */ package org.geotoolkit.ogc.xml; import java.awt.Color; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import org.geotoolkit.util.NamesExt; import org.apache.sis.geometry.GeneralEnvelope; import org.geotoolkit.gml.GeometrytoJTS; import org.geotoolkit.gml.xml.v311.AbstractGeometryType; import org.geotoolkit.gml.xml.v311.DirectPositionType; import org.geotoolkit.gml.xml.v311.EnvelopeType; import org.geotoolkit.ogc.xml.v110.AbstractIdType; import org.geotoolkit.ogc.xml.v110.BinaryOperatorType; import org.geotoolkit.ogc.xml.v110.ComparisonOpsType; import org.geotoolkit.ogc.xml.v110.FeatureIdType; import org.geotoolkit.ogc.xml.v110.FunctionType; import org.geotoolkit.ogc.xml.v110.GmlObjectIdType; import org.geotoolkit.ogc.xml.v110.LiteralType; import org.geotoolkit.ogc.xml.v110.LogicOpsType; import org.geotoolkit.ogc.xml.v110.PropertyNameType; import org.geotoolkit.ogc.xml.v110.SortByType; import org.geotoolkit.ogc.xml.v110.SortPropertyType; import org.geotoolkit.ogc.xml.v110.SpatialOpsType; import org.apache.sis.referencing.CRS; import org.apache.sis.util.ObjectConverters; import org.opengis.util.GenericName; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.filter.MatchAction; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.PropertyName; import org.opengis.filter.identity.Identifier; import org.opengis.filter.sort.SortBy; import org.opengis.util.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.apache.sis.util.UnconvertibleObjectException; import org.apache.sis.util.logging.Logging; /** * Transform OGC jaxb xml in GT classes. * * @author Johann Sorel (Geomatys) * @module */ public class OGC110toGTTransformer { protected final FilterFactory2 filterFactory; private final Map<String, String> namespaceMapping; public OGC110toGTTransformer(final FilterFactory2 factory) { this.filterFactory = factory; this.namespaceMapping = null; } public OGC110toGTTransformer(final FilterFactory2 factory, final Map<String, String> namespaceMapping) { this.filterFactory = factory; this.namespaceMapping = namespaceMapping; } /** * Transform a SLD filter v1.1 in GT filter. */ public Filter visitFilter(final org.geotoolkit.ogc.xml.v110.FilterType ft) throws FactoryException{ if (ft == null) {return null;} if(ft.getComparisonOps() != null){ final JAXBElement<? extends org.geotoolkit.ogc.xml.v110.ComparisonOpsType> jax = ft.getComparisonOps(); return visitComparisonOp(jax); }else if(ft.getLogicOps() != null){ final JAXBElement<? extends org.geotoolkit.ogc.xml.v110.LogicOpsType> jax = ft.getLogicOps(); return visitLogicOp(jax); }else if(ft.getSpatialOps() != null){ final JAXBElement<? extends org.geotoolkit.ogc.xml.v110.SpatialOpsType> jax = ft.getSpatialOps(); return visitSpatialOp(jax); }else if(ft.getId() != null && !ft.getId().isEmpty()){ return visitIds(ft.getId()); }else{ //this case should not happen but if so, we consider it's an ALL features filter return Filter.INCLUDE; } } /** * Transform a SLD spatial Filter v1.1 in GT filter. */ public Filter visitSpatialOp(final JAXBElement<? extends org.geotoolkit.ogc.xml.v110.SpatialOpsType> jax) throws NoSuchAuthorityCodeException, FactoryException { final org.geotoolkit.ogc.xml.v110.SpatialOpsType ops = jax.getValue(); final String OpName = jax.getName().getLocalPart(); if (ops instanceof org.geotoolkit.ogc.xml.v110.BinarySpatialOpType) { final org.geotoolkit.ogc.xml.v110.BinarySpatialOpType binary = (org.geotoolkit.ogc.xml.v110.BinarySpatialOpType) ops; final JAXBElement<? extends AbstractGeometryType> geom = binary.getAbstractGeometry(); final JAXBElement<EnvelopeType> env = binary.getEnvelope(); final org.geotoolkit.ogc.xml.v110.PropertyNameType pnt = binary.getPropertyName().getValue(); final Expression left = visitPropertyName(pnt); final Expression right; if(env != null && env.getValue() != null){ try { right = visitEnv(env); } catch (FactoryException ex) { throw new IllegalArgumentException("SRS name is unknowned : " + ex.getLocalizedMessage(),ex); } }else{ right = visit(geom); } if (OGCJAXBStatics.FILTER_SPATIAL_CONTAINS.equalsIgnoreCase(OpName)) { return filterFactory.contains(left,right); } else if (OGCJAXBStatics.FILTER_SPATIAL_CROSSES.equalsIgnoreCase(OpName)) { return filterFactory.crosses(left,right); } else if (OGCJAXBStatics.FILTER_SPATIAL_DISJOINT.equalsIgnoreCase(OpName)) { return filterFactory.disjoint(left,right); } else if (OGCJAXBStatics.FILTER_SPATIAL_EQUALS.equalsIgnoreCase(OpName)) { return filterFactory.equal(left,right); } else if (OGCJAXBStatics.FILTER_SPATIAL_INTERSECTS.equalsIgnoreCase(OpName)) { return filterFactory.intersects(left,right); } else if (OGCJAXBStatics.FILTER_SPATIAL_OVERLAPS.equalsIgnoreCase(OpName)) { return filterFactory.overlaps(left,right); } else if (OGCJAXBStatics.FILTER_SPATIAL_TOUCHES.equalsIgnoreCase(OpName)) { return filterFactory.touches(left,right); } else if (OGCJAXBStatics.FILTER_SPATIAL_WITHIN.equalsIgnoreCase(OpName)) { return filterFactory.within(left,right); } throw new IllegalArgumentException("Illegal filter element" + OpName + " : " + ops); } else if (ops instanceof org.geotoolkit.ogc.xml.v110.DistanceBufferType) { final org.geotoolkit.ogc.xml.v110.DistanceBufferType dstOp = (org.geotoolkit.ogc.xml.v110.DistanceBufferType) ops; final org.geotoolkit.ogc.xml.v110.DistanceType dt = dstOp.getDistanceType(); final JAXBElement<? extends AbstractGeometryType> geom = dstOp.getAbstractGeometry(); final org.geotoolkit.ogc.xml.v110.PropertyNameType pnt = dstOp.getPropertyName(); final Expression geom1 = visitPropertyName(pnt); final Expression geom2 = visit(geom); //TODO marche pas ? ou est la distance ? Double.valueOf(dt.getContent()); final double distance = 0; final String units = dt.getUnits(); if (OGCJAXBStatics.FILTER_SPATIAL_DWITHIN.equalsIgnoreCase(OpName)) { return filterFactory.dwithin(geom1, geom2, distance, units); } else if (OGCJAXBStatics.FILTER_SPATIAL_BEYOND.equalsIgnoreCase(OpName)) { return filterFactory.beyond(geom1, geom2, distance, units); } throw new IllegalArgumentException("Illegal filter element" + OpName + " : " + ops); } else if (ops instanceof org.geotoolkit.ogc.xml.v110.BBOXType) { final org.geotoolkit.ogc.xml.v110.BBOXType binary = (org.geotoolkit.ogc.xml.v110.BBOXType) ops; final EnvelopeType box = binary.getEnvelope(); final String pnt = binary.getPropertyName(); final Expression geom; if (pnt != null) { geom = visitPropertyName(pnt); } else { geom = null; } final double minx = box.getLowerCorner().getOrdinate(0); final double maxx = box.getUpperCorner().getOrdinate(0); final double miny = box.getLowerCorner().getOrdinate(1); final double maxy = box.getUpperCorner().getOrdinate(1); // final double minx = box.getPos().get(0).getOrdinate(0); // final double maxx = box.getPos().get(0).getOrdinate(1); // final double miny = box.getPos().get(1).getOrdinate(0); // final double maxy = box.getPos().get(1).getOrdinate(1); final String srs = box.getSrsName(); if (OGCJAXBStatics.FILTER_SPATIAL_BBOX.equalsIgnoreCase(OpName)) { return filterFactory.bbox(geom, minx, miny, maxx, maxy, srs); } throw new IllegalArgumentException("Illegal filter element" + OpName + " : " + ops); } throw new IllegalArgumentException("Unknowed filter element" + jax); } /** * Transform a SLD logic Filter v1.1 in GT filter. */ public Filter visitLogicOp(final JAXBElement<? extends org.geotoolkit.ogc.xml.v110.LogicOpsType> jax) throws NoSuchAuthorityCodeException, FactoryException { final org.geotoolkit.ogc.xml.v110.LogicOpsType ops = jax.getValue(); final String OpName = jax.getName().getLocalPart(); if (ops instanceof org.geotoolkit.ogc.xml.v110.UnaryLogicOpType) { final org.geotoolkit.ogc.xml.v110.UnaryLogicOpType unary = (org.geotoolkit.ogc.xml.v110.UnaryLogicOpType) ops; if (OGCJAXBStatics.FILTER_LOGIC_NOT.equalsIgnoreCase(OpName)) { Filter filter = null; if(unary.getComparisonOps() != null) {filter = visitComparisonOp(unary.getComparisonOps());} else if(unary.getLogicOps() != null) {filter = visitLogicOp(unary.getLogicOps());} else if(unary.getSpatialOps() != null) {filter = visitSpatialOp(unary.getSpatialOps());} if(filter == null){ throw new IllegalArgumentException("Invalide filter element" + unary); } return filterFactory.not(filter); } } else if (ops instanceof org.geotoolkit.ogc.xml.v110.BinaryLogicOpType) { final org.geotoolkit.ogc.xml.v110.BinaryLogicOpType binary = (org.geotoolkit.ogc.xml.v110.BinaryLogicOpType) ops; if (OGCJAXBStatics.FILTER_LOGIC_AND.equalsIgnoreCase(OpName)) { final List<Filter> filters = new ArrayList<Filter>(); for (final JAXBElement<? extends ComparisonOpsType> ele : binary.getComparisonOps()) { if (ele.getValue() instanceof ComparisonOpsType) { filters.add(visitComparisonOp(ele)); } } for (final JAXBElement<? extends LogicOpsType> ele : binary.getLogicOps()) { if (ele.getValue() instanceof LogicOpsType) { filters.add(visitLogicOp(ele)); } } for (final JAXBElement<? extends SpatialOpsType> ele : binary.getSpatialOps()) { if (ele.getValue() instanceof SpatialOpsType) { filters.add(visitSpatialOp(ele)); } } if(filters.isEmpty()){ return Filter.INCLUDE; }else if(filters.size() == 1){ return filters.get(0); }else{ return filterFactory.and(filters); } } else if (OGCJAXBStatics.FILTER_LOGIC_OR.equalsIgnoreCase(OpName)) { final List<Filter> filters = new ArrayList<Filter>(); for (final JAXBElement<? extends ComparisonOpsType> ele : binary.getComparisonOps()) { if (ele.getValue() instanceof ComparisonOpsType) { filters.add(visitComparisonOp(ele)); } } for (final JAXBElement<? extends LogicOpsType> ele : binary.getLogicOps()) { if (ele.getValue() instanceof LogicOpsType) { filters.add(visitLogicOp(ele)); } } for (final JAXBElement<? extends SpatialOpsType> ele : binary.getSpatialOps()) { if (ele.getValue() instanceof SpatialOpsType) { filters.add(visitSpatialOp(ele)); } } if(filters.isEmpty()){ return Filter.INCLUDE; }else if(filters.size() == 1){ return filters.get(0); }else{ return filterFactory.or(filters); } } } throw new IllegalArgumentException("Unknowed filter element" + jax); } /** * Transform a SLD comparison Filter v1.1 in GT filter. */ public Filter visitComparisonOp(final JAXBElement<? extends org.geotoolkit.ogc.xml.v110.ComparisonOpsType> jax){ final org.geotoolkit.ogc.xml.v110.ComparisonOpsType ops = jax.getValue(); final String OpName = jax.getName().getLocalPart(); if (ops instanceof org.geotoolkit.ogc.xml.v110.BinaryComparisonOpType) { final org.geotoolkit.ogc.xml.v110.BinaryComparisonOpType binary = (org.geotoolkit.ogc.xml.v110.BinaryComparisonOpType) ops; final Expression left = visitExpression(binary.getExpression().get(0)); final Expression right = visitExpression(binary.getExpression().get(1)); Boolean match = binary.getMatchCase(); if(match == null) {match = Boolean.TRUE;} final MatchAction action = binary.getMatchAction(); if (OGCJAXBStatics.FILTER_COMPARISON_ISEQUAL.equalsIgnoreCase(OpName)) { return filterFactory.equal(left,right,match, action); } else if (OGCJAXBStatics.FILTER_COMPARISON_ISNOTEQUAL.equalsIgnoreCase(OpName)) { return filterFactory.notEqual(left, right, match, action); } else if (OGCJAXBStatics.FILTER_COMPARISON_ISLESS.equalsIgnoreCase(OpName)) { return filterFactory.less(left, right, match, action); } else if (OGCJAXBStatics.FILTER_COMPARISON_ISGREATER.equalsIgnoreCase(OpName)) { return filterFactory.greater(left, right, match, action); } else if (OGCJAXBStatics.FILTER_COMPARISON_ISLESSOREQUAL.equalsIgnoreCase(OpName)) { return filterFactory.lessOrEqual(left, right, match, action); } else if (OGCJAXBStatics.FILTER_COMPARISON_ISGREATEROREQUAL.equalsIgnoreCase(OpName)) { return filterFactory.greaterOrEqual(left, right, match, action); } throw new IllegalArgumentException("Illegal filter element" + OpName + " : " + ops); } else if (ops instanceof org.geotoolkit.ogc.xml.v110.PropertyIsLikeType) { final org.geotoolkit.ogc.xml.v110.PropertyIsLikeType property = (org.geotoolkit.ogc.xml.v110.PropertyIsLikeType) ops; final Expression expr = visitPropertyName(property.getPropertyName()); final String pattern = visitExpression(property.getLiteralType()).toString(); final String wild = property.getWildCard(); final String single = property.getSingleChar(); final String escape = property.getEscapeChar(); if (OGCJAXBStatics.FILTER_COMPARISON_ISLIKE.equalsIgnoreCase(OpName)) { return filterFactory.like(expr, pattern, wild, single, escape); } throw new IllegalArgumentException("Illegal filter element" + OpName + " : " + ops); } else if (ops instanceof org.geotoolkit.ogc.xml.v110.PropertyIsBetweenType) { final org.geotoolkit.ogc.xml.v110.PropertyIsBetweenType property = (org.geotoolkit.ogc.xml.v110.PropertyIsBetweenType) ops; final Expression lower = visitExpression( property.getLowerBoundary().getExpression() ); final Expression upper = visitExpression( property.getUpperBoundary().getExpression() ); final Expression expr = visitExpression( property.getExpressionType() ); if (OGCJAXBStatics.FILTER_COMPARISON_ISBETWEEN.equalsIgnoreCase(OpName)) { return filterFactory.between(expr, lower, upper); } throw new IllegalArgumentException("Illegal filter element" + OpName + " : " + ops); } else if (ops instanceof org.geotoolkit.ogc.xml.v110.PropertyIsNullType) { final org.geotoolkit.ogc.xml.v110.PropertyIsNullType property = (org.geotoolkit.ogc.xml.v110.PropertyIsNullType) ops; final Expression expr = visitPropertyName(property.getPropertyName()); if (OGCJAXBStatics.FILTER_COMPARISON_ISNULL.equalsIgnoreCase(OpName)) { return filterFactory.isNull(expr); } throw new IllegalArgumentException("Illegal filter element" + OpName + " : " + ops); } throw new IllegalArgumentException("Unknowed filter element" + jax); } /** * Transform a SLD IDS Filter v1.1 in GT filter. */ public Filter visitIds(final List<JAXBElement<? extends AbstractIdType>> lst){ final Set<Identifier> ids = new HashSet<Identifier>(); for(final JAXBElement<? extends org.geotoolkit.ogc.xml.v110.AbstractIdType> id : lst){ final AbstractIdType idd = id.getValue(); if(idd instanceof FeatureIdType){ ids.add( filterFactory.featureId(( (FeatureIdType)idd).getFid()) ); }else if(idd instanceof GmlObjectIdType){ ids.add( filterFactory.featureId(( (GmlObjectIdType)idd).getID()) ); } } return filterFactory.id(ids); } public List<SortBy> visitSortBy(final SortByType type){ final List<SortBy> sorts = new ArrayList<SortBy>(); for(final SortPropertyType spt : type.getSortProperty()){ final PropertyName pn = visitPropertyName(spt.getPropertyName()); sorts.add(filterFactory.sort(pn.getPropertyName(), spt.getSortOrder())); } return sorts; } public Expression visit(final JAXBElement<? extends AbstractGeometryType> ele) throws NoSuchAuthorityCodeException, FactoryException{ return filterFactory.literal(GeometrytoJTS.toJTS(ele.getValue())); } public Expression visitEnv(final JAXBElement<EnvelopeType> env) throws FactoryException{ final EnvelopeType entry = env.getValue(); String srs = entry.getSrsName(); DirectPositionType lower = entry.getLowerCorner(); DirectPositionType upper = entry.getUpperCorner(); GeneralEnvelope genv = new GeneralEnvelope(CRS.forCode(srs)); genv.setRange(0, lower.getOrdinate(0), upper.getOrdinate(0)); genv.setRange(1, lower.getOrdinate(1), upper.getOrdinate(1)); return filterFactory.literal(genv); } public PropertyName visitPropertyName(final PropertyNameType pnt){ if (pnt != null) { return visitPropertyName(pnt.getContent()); } return null; } public PropertyName visitPropertyName(final String pnt){ String brutPname = pnt; if (brutPname.indexOf(':') == -1) { return filterFactory.property(brutPname); } String[] pnames = brutPname.split("/"); StringBuilder sb = new StringBuilder(); int i = 0; for (String pname : pnames) { if (pnames.length > 1 && i != 0) { sb.append("/"); } int pos = pname.indexOf(':'); if (pos != -1 && namespaceMapping != null) { String prefix = pname.substring(0, pos); String namespace = namespaceMapping.get(prefix); if (namespace == null) { throw new IllegalArgumentException("Prefix " + prefix + " is nor bounded."); } else { sb.append('{').append(namespace).append('}').append(pname.substring(pos +1)); } } else { sb.append(pname); } i++; } return filterFactory.property(sb.toString()); } /** * Transform a JaxBelement in Expression. */ public Expression visitExpression(final JAXBElement<?> jax){ // JAXBElementFunctionType> ---NS // JAXBElementExpressionType> ---k // JAXBElementLiteralType> ---k // JAXBElementBinaryOperatorType> ---k // JAXBElementBinaryOperatorType> ---k // JAXBElementBinaryOperatorType> ---k // JAXBElementPropertyNameType> ---k // JAXBElementBinaryOperatorType> ---k final String expName = jax.getName().getLocalPart(); final Object obj = jax.getValue(); if(obj instanceof LiteralType){ return visitExpression( (LiteralType)obj ); }else if(obj instanceof BinaryOperatorType){ final BinaryOperatorType bot = (BinaryOperatorType) obj; final Expression left = visitExpression(bot.getExpression().get(0)); final Expression right = visitExpression(bot.getExpression().get(1)); if(OGCJAXBStatics.EXPRESSION_ADD.equalsIgnoreCase(expName)){ return filterFactory.add(left, right); }else if(OGCJAXBStatics.EXPRESSION_DIV.equalsIgnoreCase(expName)){ return filterFactory.divide(left, right); }else if(OGCJAXBStatics.EXPRESSION_MUL.equalsIgnoreCase(expName)){ return filterFactory.multiply(left, right); }else if(OGCJAXBStatics.EXPRESSION_SUB.equalsIgnoreCase(expName)){ return filterFactory.subtract(left, right); } throw new IllegalArgumentException("Unknowed expression element : Name > " + expName +" JAXB > " + jax + " OBJECT >" + obj); }else if(obj instanceof PropertyNameType){ return visitPropertyName((PropertyNameType) obj); }else if(obj instanceof FunctionType){ final FunctionType ft = (FunctionType) obj; final Expression[] exps = new Expression[ft.getExpression().size()]; int i=0; for(final JAXBElement<?> ele : ft.getExpression()){ exps[i] = visitExpression(ele); i++; } return filterFactory.function(ft.getName(), exps); } throw new IllegalArgumentException("Unknowed expression element" + jax); } /** * Transform a literalType in Expression. */ public Expression visitExpression(final LiteralType type){ final List<Object> content = type.getContent(); for(Object obj : content){ if(obj != null){ final String str = obj.toString().trim(); if(!str.isEmpty()){ if(str.charAt(0) == '#'){ //try to convert it to a color try { Color c = ObjectConverters.convert(str, Color.class); if(c!=null) obj = c; } catch (UnconvertibleObjectException e) { Logging.recoverableException(null, OGC110toGTTransformer.class, "visitExpression", e); // TODO - do we really want to ignore? } }else{ //try to convert it to a number try{ obj = Double.valueOf(str); }catch(NumberFormatException ex){ } } return filterFactory.literal(obj); } } } return filterFactory.literal(""); } /** * Change a QName in Name. */ public GenericName visitQName(final QName qname){ if(qname == null) {return null;} return NamesExt.create(qname); } }