/* * 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.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.v321.AbstractGeometryType; import org.geotoolkit.gml.xml.v321.DirectPositionType; import org.geotoolkit.gml.xml.v321.EnvelopeType; import org.geotoolkit.ogc.xml.v200.*; import org.apache.sis.referencing.CRS; 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; /** * Transform OGC jaxb xml in GT classes. * * @author Johann Sorel (Geomatys) * @author Guilhem Legal (Geomatys) * @module */ public class OGC200toGTTransformer { protected final FilterFactory2 filterFactory; private final Map<String, String> namespaceMapping; public OGC200toGTTransformer(final FilterFactory2 factory) { this.filterFactory = factory; this.namespaceMapping = null; } public OGC200toGTTransformer(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.v200.FilterType ft) throws FactoryException{ if(ft == null) {return null;} if(ft.getComparisonOps() != null){ final JAXBElement<? extends org.geotoolkit.ogc.xml.v200.ComparisonOpsType> jax = ft.getComparisonOps(); return visitComparisonOp(jax); }else if(ft.getLogicOps() != null){ final JAXBElement<? extends org.geotoolkit.ogc.xml.v200.LogicOpsType> jax = ft.getLogicOps(); return visitLogicOp(jax); }else if(ft.getSpatialOps() != null){ final JAXBElement<? extends org.geotoolkit.ogc.xml.v200.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.v200.SpatialOpsType> jax) throws NoSuchAuthorityCodeException, FactoryException { final org.geotoolkit.ogc.xml.v200.SpatialOpsType ops = jax.getValue(); final String OpName = jax.getName().getLocalPart(); if (ops instanceof org.geotoolkit.ogc.xml.v200.BinarySpatialOpType) { final org.geotoolkit.ogc.xml.v200.BinarySpatialOpType binary = (org.geotoolkit.ogc.xml.v200.BinarySpatialOpType) ops; Object geom = binary.getAny(); if (geom instanceof JAXBElement) { geom = ((JAXBElement)geom).getValue(); } final Expression left = visitPropertyName(binary.getValueReference()); final Expression right; if (geom instanceof EnvelopeType){ try { right = visitEnv((EnvelopeType)geom); } catch (FactoryException ex) { throw new IllegalArgumentException("SRS name is unknowned : " + ex.getLocalizedMessage(),ex); } } else if (geom instanceof AbstractGeometryType){ right = visit((AbstractGeometryType)geom); } else { throw new IllegalArgumentException("Unexpected geometry type:" + 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.v200.DistanceBufferType) { final org.geotoolkit.ogc.xml.v200.DistanceBufferType dstOp = (org.geotoolkit.ogc.xml.v200.DistanceBufferType) ops; final org.geotoolkit.ogc.xml.v200.MeasureType dt = dstOp.getDistanceType(); if (!(dstOp.getAny() instanceof AbstractGeometryType)) { throw new IllegalArgumentException("geometry type is unexpected:" + dstOp.getAny()); } final AbstractGeometryType geom = (AbstractGeometryType) dstOp.getAny(); final String 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.getUom(); 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.v200.BBOXType) { final org.geotoolkit.ogc.xml.v200.BBOXType binary = (org.geotoolkit.ogc.xml.v200.BBOXType) ops; if (!(binary.getAny() instanceof EnvelopeType)) { throw new IllegalArgumentException("geometry type is unexpected:" + binary.getAny()); } final EnvelopeType box = (EnvelopeType) binary.getAny(); 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 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.v200.LogicOpsType> jax) throws NoSuchAuthorityCodeException, FactoryException { final org.geotoolkit.ogc.xml.v200.LogicOpsType ops = jax.getValue(); final String OpName = jax.getName().getLocalPart(); if (ops instanceof org.geotoolkit.ogc.xml.v200.UnaryLogicOpType) { final org.geotoolkit.ogc.xml.v200.UnaryLogicOpType unary = (org.geotoolkit.ogc.xml.v200.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.v200.BinaryLogicOpType) { final org.geotoolkit.ogc.xml.v200.BinaryLogicOpType binary = (org.geotoolkit.ogc.xml.v200.BinaryLogicOpType) ops; if (OGCJAXBStatics.FILTER_LOGIC_AND.equalsIgnoreCase(OpName)) { final List<Filter> filters = new ArrayList<Filter>(); for (final JAXBElement<?> ele : binary.getComparisonOpsOrSpatialOpsOrTemporalOps()) { if (ele.getValue() instanceof ComparisonOpsType) { filters.add(visitComparisonOp((JAXBElement<? extends ComparisonOpsType>)ele)); } else if (ele.getValue() instanceof LogicOpsType) { filters.add(visitLogicOp((JAXBElement<? extends LogicOpsType>)ele)); } else if (ele.getValue() instanceof SpatialOpsType) { filters.add(visitSpatialOp((JAXBElement<? extends SpatialOpsType>)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 ele : binary.getComparisonOpsOrSpatialOpsOrTemporalOps()) { if (ele.getValue() instanceof ComparisonOpsType) { filters.add(visitComparisonOp((JAXBElement<? extends ComparisonOpsType>)ele)); } else if (ele.getValue() instanceof LogicOpsType) { filters.add(visitLogicOp((JAXBElement<? extends LogicOpsType>)ele)); } else if (ele.getValue() instanceof SpatialOpsType) { filters.add(visitSpatialOp((JAXBElement<? extends SpatialOpsType>)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.v200.ComparisonOpsType> jax){ final org.geotoolkit.ogc.xml.v200.ComparisonOpsType ops = jax.getValue(); final String OpName = jax.getName().getLocalPart(); if (ops instanceof org.geotoolkit.ogc.xml.v200.BinaryComparisonOpType) { final org.geotoolkit.ogc.xml.v200.BinaryComparisonOpType binary = (org.geotoolkit.ogc.xml.v200.BinaryComparisonOpType) ops; if (binary.getExpression().size() < 2) { throw new IllegalArgumentException("comparison operator must have two expression (valueReference and literal)"); } final Expression left = visitExpression(binary.getExpression().get(0)); final Expression right = visitExpression(binary.getExpression().get(1)); Boolean match = binary.isMatchingCase(); 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.v200.PropertyIsLikeType) { final org.geotoolkit.ogc.xml.v200.PropertyIsLikeType property = (org.geotoolkit.ogc.xml.v200.PropertyIsLikeType) ops; final Expression expr = visitPropertyName(property.getPropertyName()); final String pattern = visitExpression(property.getLiteral()).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.v200.PropertyIsBetweenType) { final org.geotoolkit.ogc.xml.v200.PropertyIsBetweenType property = (org.geotoolkit.ogc.xml.v200.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.v200.PropertyIsNullType) { final org.geotoolkit.ogc.xml.v200.PropertyIsNullType property = (org.geotoolkit.ogc.xml.v200.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.v200.AbstractIdType> id : lst){ final AbstractIdType idd = id.getValue(); if(idd instanceof ResourceIdType){ ids.add( filterFactory.featureId(((ResourceIdType)idd).getRid())); } } 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 AbstractGeometryType ele) throws NoSuchAuthorityCodeException, FactoryException{ return filterFactory.literal(GeometrytoJTS.toJTS(ele)); } public Expression visitEnv(final EnvelopeType entry) throws FactoryException{ final String srs = entry.getSrsName(); final DirectPositionType lower = entry.getLowerCorner(); final 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 PropertyName pnt){ if (pnt != null) { return visitPropertyName(pnt.getPropertyName()); } 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); boolean isAtt = prefix.startsWith("@"); if(isAtt) prefix = prefix.substring(1); String namespace = namespaceMapping.get(prefix); if (namespace == null) { throw new IllegalArgumentException("Prefix " + prefix + " is nor bounded."); } else { sb.append('{').append(namespace).append('}'); if(isAtt) sb.append('@'); sb.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 PropertyName){ return visitPropertyName((PropertyName) obj); } else if(obj instanceof String){ return visitPropertyName((String) 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.getValue()); } /** * Transform a literalType in Expression. */ public Expression visitExpression(final LiteralType type){ final List<Object> content = type.getContent(); for(Object obj : content){ if(obj != null && !obj.toString().trim().isEmpty()){ //try to convert it to a number try{ obj = Double.valueOf(obj.toString().trim()); }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); } }