/** * Copyright (c) Codice Foundation * <p> * This 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 3 of the * License, or any later version. * <p> * 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. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package ddf.catalog.filter.proxy.builder; import java.util.Arrays; import java.util.Date; import java.util.Locale; import org.geotools.filter.FilterFactoryImpl; import org.geotools.geometry.GeometryBuilder; import org.geotools.geometry.jts.spatialschema.geometry.primitive.PrimitiveFactoryImpl; import org.geotools.geometry.text.WKTParser; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.styling.UomOgcMapping; import org.geotools.temporal.object.DefaultInstant; import org.geotools.temporal.object.DefaultPeriod; import org.geotools.temporal.object.DefaultPeriodDuration; import org.geotools.temporal.object.DefaultPosition; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.expression.Expression; import org.opengis.geometry.Geometry; import org.opengis.temporal.Instant; import org.opengis.temporal.Period; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import ddf.catalog.data.Metacard; import ddf.catalog.impl.filter.FuzzyFunction; import ddf.catalog.impl.filter.JTSGeometryWrapper; /** * * @author michael.menousek@lmco.com * */ class GeotoolsBuilder { // Use the OGC SE standard mapping for meters so that units assigned here // will // match units assigned elsewhere throughout DDF private static final String METERS = UomOgcMapping.METRE.name(); private static final Logger LOGGER = LoggerFactory.getLogger(GeotoolsBuilder.class); private static WKTReader reader = new WKTReader(); private static WKTParser parser = new WKTParser(new GeometryBuilder(DefaultGeographicCRS.WGS84)); private FilterFactory factory = new FilterFactoryImpl(); private String attribute; private Operator operator; private Object value; private Object secondaryValue; GeotoolsBuilder() { this(null, null); } protected GeotoolsBuilder(GeotoolsBuilder builder) { this(builder.getAttribute(), builder.getOperator()); } private GeotoolsBuilder(String attribute, Operator operator) { this.attribute = attribute; this.operator = operator; parser.setFactory(new PrimitiveFactoryImpl(DefaultGeographicCRS.WGS84)); } // // /** // * @param expression the expression to set // */ // void setExpression(Expression expression) { // this.expression = expression; // } protected Filter build() { LOGGER.debug("BUILDING attribute = {}, operator = {}, value = {}, secondaryValue = {}", attribute, operator, value, secondaryValue); Filter filter = null; String wkt = null; Date date = null; double distance = 0; switch (operator) { case AFTER: date = getValue(Date.class); if (date != null) { filter = factory.after(factory.property(attribute), factory.literal(new DefaultInstant(new DefaultPosition(date)))); } break; case BEFORE: date = getValue(Date.class); if (date != null) { filter = factory.before(factory.property(attribute), factory.literal(new DefaultInstant(new DefaultPosition(date)))); } break; case BETWEEN: filter = factory.between(factory.property(attribute), factory.literal(value), factory.literal(secondaryValue)); break; case DURING: Date start = getValue(Date.class); Date end = getSecondaryValue(Date.class); if (start != null && end != null) { DefaultPosition defaultPosition = new DefaultPosition(start); Instant startInstant = new DefaultInstant(defaultPosition); Instant endInstant = new DefaultInstant(new DefaultPosition(end)); Period period = new DefaultPeriod(startInstant, endInstant); filter = factory.during(factory.property(attribute), factory.literal(period)); } break; case DURING_RELATIVE: Long longValue = getValue(Long.class); if (null != value) { filter = factory.during(factory.property(attribute), factory.literal(new DefaultPeriodDuration(longValue))); } break; case EQ: filter = factory.equals(factory.property(attribute), factory.literal(value)); break; case GT: filter = factory.greater(factory.property(attribute), factory.literal(value)); break; case GTE: filter = factory.greaterOrEqual(factory.property(attribute), factory.literal(value)); break; case LT: filter = factory.less(factory.property(attribute), factory.literal(value)); break; case LTE: filter = factory.lessOrEqual(factory.property(attribute), factory.literal(value)); break; case NEQ: filter = factory.notEqual(factory.property(attribute), factory.literal(value)); break; case NULL: filter = factory.isNull(factory.property(attribute)); break; case TOVERLAPS: filter = factory.toverlaps(factory.property(attribute), factory.literal(value)); break; case BEYOND: wkt = getValue(String.class); distance = getSecondaryValue(Double.class); if (wkt != null && wkt.length() > 0) { filter = factory.beyond(attribute, toGeometry(wkt), distance, METERS); } break; case CONTAINS: wkt = getValue(String.class); if (wkt != null && wkt.length() > 0) { filter = factory.contains(attribute, toGeometry(wkt)); } break; case DWITHIN: wkt = getValue(String.class); distance = getSecondaryValue(Double.class); if (wkt != null && wkt.length() > 0) { filter = factory.dwithin(attribute, toGeometry(wkt), distance, METERS); } break; case INTERSECTS: wkt = getValue(String.class); if (wkt != null && wkt.length() > 0) { filter = factory.intersects(attribute, toGeometry(wkt)); } break; case WITHIN: wkt = getValue(String.class); if (wkt != null && wkt.length() > 0) { filter = factory.within(attribute, toGeometry(wkt)); } break; case LIKE: filter = factory.like(factory.property(attribute), getValue(String.class), "*", "%", "'", getSecondaryValue(Boolean.class)); break; case FUZZY: Expression expression = factory.property(attribute); filter = factory.like(new FuzzyFunction(Arrays.asList(expression), factory.literal(Metacard.ANY_TEXT)), getValue(String.class), "*", "%", "'", getSecondaryValue(Boolean.class)); break; default: // return null } if (filter == null) { throw new IllegalArgumentException( "Illegal argument for operation [" + operator.name() + "]"); } return filter; } protected Filter build(Object arg) { value = arg; return build(); } protected Filter build(Object arg0, Object arg1) { value = arg0; secondaryValue = arg1; return build(); } private <T> T convert(Class<T> clazz, Object inputValue) { T convertedValue = null; if (inputValue != null && inputValue.getClass() .isAssignableFrom(clazz)) { convertedValue = clazz.cast(inputValue); } return convertedValue; } /** * @return the attribute */ String getAttribute() { return attribute; } /** * @param attribute * the attribute to set */ protected void setAttribute(String attribute) { this.attribute = attribute; } // public Expression(String attribute, Operator operator) { // this.attribute = attribute; // this.operator = operator; // } // // public Expression() { // attribute = null; // operator = null; // value = null; // secondaryValue = null; // } /** * @return the factory */ FilterFactory getFactory() { return factory; } /** * @param factory * the factory to set */ void setFactory(FilterFactory factory) { this.factory = factory; } protected Operator getOperator() { return operator; } /** * @param operator * the operator to set */ protected void setOperator(Operator operator) { LOGGER.debug("setting operator to {}", operator); this.operator = operator; } <T> T getSecondaryValue(Class<T> clazz) { return convert(clazz, secondaryValue); } /** * @return the value */ <T> T getValue(Class<T> clazz) { return convert(clazz, value); } protected void setSecondaryValue(Object arg1) { this.secondaryValue = arg1; } /** * @param value * the value to set */ protected void setValue(Object value) { this.value = value; } public Geometry toGeometry(String wkt) { Geometry geometry = null; try { if (wkt.toLowerCase(Locale.US) .startsWith("multi") || wkt.toLowerCase(Locale.US) .trim() .indexOf("geometrycollection") != -1) { // WKTParser does not currently support MultiPolygon, // MultiLineString, or MultiPoint com.vividsolutions.jts.geom.Geometry geo = reader.read(wkt); geometry = new JTSGeometryWrapper(geo); } else { geometry = parser.parse(wkt); } } catch (ParseException | java.text.ParseException e) { LOGGER.debug("Unable to compute geometry for WKT = {}", wkt, e); } return geometry; } }