Package org.geomajas.internal.service

Source Code of org.geomajas.internal.service.FilterServiceImpl

/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/

package org.geomajas.internal.service;

import java.util.Date;
import java.util.HashSet;

import org.geomajas.geometry.Crs;
import org.geomajas.global.ExceptionCode;
import org.geomajas.global.GeomajasException;
import org.geomajas.internal.filter.NonLenientFilterFactoryImpl;
import org.geomajas.internal.layer.feature.FeatureModelRegistry;
import org.geomajas.layer.feature.FeatureModel;
import org.geomajas.service.FilterService;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.Hints;
import org.geotools.filter.identity.FeatureIdImpl;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.identity.Identifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;

/**
* <p>
* Utility class for creating ECQL filters. Make sure all arguments are correct before you use these
* functions. This class uses the OpenGIS FilterFactory2 interface, which extends the OGC FilterFactory specification
* with JTS geometry support.
* </p>
*
* @author Pieter De Graef
*/
@Component
public final class FilterServiceImpl implements FilterService {

  private final Logger log = LoggerFactory.getLogger(FilterServiceImpl.class);

  private static final FilterFactory2 FF;
  static {
    // use hint to get at the correct filter factory
    // TODO: find better way to make this configurable ?
    Hints hints = new Hints();
    hints.put(Hints.FILTER_FACTORY, NonLenientFilterFactoryImpl.class);
    FF = CommonFactoryFinder.getFilterFactory2(hints);
  }

  /**
   * Create a filter which passes all features from the FeatureInfo within featureTypeInfo with attribute values from
   * attribute 'name' between values 'lower and 'upper'.<BR>
   * This type of filter is only allowed on numeric object types (short, integer, long, float, double)
   *
   * @param name
   *            The name of the attribute from featureTypeInfo.
   * @param lower
   *            A literal. A string that can be converted to a double.
   * @param upper
   *            A literal. A string that can be converted to a double.
   * @return filter
   */
  public Filter createBetweenFilter(String name, String lower, String upper) {
    PropertyName attrib = FF.property(name);
    Expression d1 = FF.literal(lower);
    Expression d2 = FF.literal(upper);
    return FF.between(attrib, d1, d2);
  }

  /**
   * Filter based on the LIKE comparator as in sql. arg2 is a string to compare the values of arg1 with. Make sure
   * arg1 refers to a String-attribute. The expression understands wildcards like *(= zero or more unknown characters)
   * and ?(exactly 1 unknown character).
   *
   * @param name
   *            The name of the attribute from featureTypeInfo.
   * @param pattern
   *            Expression to compare with.
   * @return filter
   */
  public Filter createLikeFilter(String name, String pattern) {

    PropertyName attrib = FF.property(name);
    return FF.like(attrib, pattern, "*", "?", "\\");
  }

  /**
   * Create a filter that passes all features where the values of attribute 'name' compared to literal 'value' using
   * comparator 'comparator' returns true.
   *
   * @param name
   *            The name of the attribute from featureTypeInfo. This type of filter is only allowed on numeric object
   *            types (short, integer, long, float, double)
   * @param comparator
   *            Depending on the type of literal, a range of operators is supported.
   *            <ul>
   *            <li>For numeric object types (short, integer, long, float, double) all of the following operators are
   *            supported: "<", "<=", ">", ">=", "==", "<>".</li>
   *            <li>For boolean and string: "==" and "<>"</li>
   *            <li>Dates have their own method...</li>
   *            </ul>
   * @param value
   *            A literal. The actual value (as string).
   * @return filter
   */
  public Filter createCompareFilter(String name, String comparator, String value) {
    PropertyName attrib = FF.property(name);
    Expression val = FF.literal(value);
    if ("<".equals(comparator)) {
      return FF.less(attrib, val);
    } else if (">".equals(comparator)) {
      return FF.greater(attrib, val);
    } else if (">=".equals(comparator)) {
      return FF.greaterOrEqual(attrib, val);
    } else if ("<=".equals(comparator)) {
      return FF.lessOrEqual(attrib, val);
    } else if ("=".equals(comparator)) {
      return FF.equals(attrib, val);
    } else if ("==".equals(comparator)) {
      return FF.equals(attrib, val);
    } else if ("<>".equals(comparator)) {
      return FF.notEqual(attrib, val, false);
    } else {
      throw new IllegalArgumentException("Could not interprete compare filter. Argument (" + value
          + ") not known.");
    }
  }

  /**
   * Create a filter that passes all features where the values of attribute 'name' compared to literal 'value' using
   * comparator 'comparator' returns true. This function compares object of the type Date.
   *
   * @param name
   *            The name of the attribute from featureTypeInfo. This type of filter is only allowed on dates.
   * @param comparator
   *            The sort of comparison, can be "<", "<=", ">", ">=", "==", "<>".
   * @param value
   *            A literal. A string that can be converted to a double.
   * @return filter
   */
  public Filter createCompareFilter(String name, String comparator, Date value) {
    PropertyName attrib = FF.property(name);
    Expression val = FF.literal(value);
    if ("<".equals(comparator)) {
      return FF.less(attrib, val);
    } else if (">".equals(comparator)) {
      return FF.greater(attrib, val);
    } else if (">=".equals(comparator)) {
      return FF.greaterOrEqual(attrib, val);
    } else if ("<=".equals(comparator)) {
      return FF.lessOrEqual(attrib, val);
    } else if ("=".equals(comparator)) {
      return FF.equals(attrib, val);
    } else if ("==".equals(comparator)) {
      return FF.equals(attrib, val);
    } else if ("<>".equals(comparator)) {
      return FF.notEqual(attrib, val, false);
    } else {
      throw new IllegalArgumentException("Could not interprete compare filter. Argument (" + value
          + ") not known.");
    }
  }
 
  public Filter createGeometryTypeFilter(String geomName, String geometryType) {
    return FF.equals(FF.literal(geometryType), FF.function("geometryType", FF.property(geomName)));
  }

  /**
   * Creates a logic filter. This is a combination of filters.
   *
   * @param filter1
   *            first filter to combine
   * @param logic
   *            The logic operator. Can be 'AND', 'OR', 'NOT'.
   * @param filter2
   *            second filter to combine In the case of "NOT", this parameter is not used.
   * @return filter
   */
  public Filter createLogicFilter(Filter filter1, String logic, Filter filter2) {
    if ("and".equalsIgnoreCase(logic)) {
      return FF.and(filter1, filter2);
    } else if ("or".equalsIgnoreCase(logic)) {
      return FF.or(filter1, filter2);
    } else if ("not".equalsIgnoreCase(logic)) {
      return FF.not(filter1);
    } else {
      throw new IllegalArgumentException("Could not interpret logic filter. Argument (" + logic + ") not known.");
    }
  }

  /**
   * Create a feature ID filter.
   *
   * @param featureIDs
   *            Array of identifier strings for the features that need looking up.
   * @return filter
   */
  public Filter createFidFilter(String[] featureIDs) {
    HashSet<Identifier> ids = new HashSet<Identifier>();
    for (String id : featureIDs) {
      ids.add(new FeatureIdImpl(id));
    }
    return FF.id(ids);
  }

  /**
   * Creates a filter with all the geometries that contain the parameterized geometry (including the geometry itself).
   *
   * @param geometry
   *            the (parameterized) geometry
   * @param geomName
   *            the name of the geometry field ("the_geom")
   * @return filter
   */
  public Filter createContainsFilter(Geometry geometry, String geomName) {
    Expression nameExpression = FF.property(geomName);
    Literal geomLiteral = FF.literal(geometry);
    return FF.contains(nameExpression, geomLiteral);
  }

  /**
   * Creates a filter with all the geometries which are completely within the given geometry (including the
   * geometry itself).
   *
   * @param geometry
   *            the (parameterized) geometry
   * @param geomName
   *            the name of the geometry field ("the_geom")
   * @return filter
   */
  public Filter createWithinFilter(Geometry geometry, String geomName) {
    Expression nameExpression = FF.property(geomName);
    Literal geomLiteral = FF.literal(geometry);
    return FF.within(nameExpression, geomLiteral);
  }

  /**
   * Creates a filter with all the geometries that have a non-empty intersection (overlap or touching) with the
   * given geometry (including the geometry itself).
   *
   * @param geometry
   *            the geometry
   * @param geomName
   *            the name of the geometry field ("the_geom")
   * @return filter
   */
  public Filter createIntersectsFilter(Geometry geometry, String geomName) {
    Expression nameExpression = FF.property(geomName);
    Literal geomLiteral = FF.literal(geometry);
    return FF.intersects(nameExpression, geomLiteral);
  }

  /**
   * Creates a filter with all the geometries that touch the given geometry.
   *
   * @param geometry
   *            the geometry
   * @param geomName
   *            the name of the geometry field ("the_geom")
   * @return filter
   */
  public Filter createTouchesFilter(Geometry geometry, String geomName) {
    Expression nameExpression = FF.property(geomName);
    Literal geomLiteral = FF.literal(geometry);
    return FF.touches(nameExpression, geomLiteral);
  }

  /**
   * Create a filter the evaluates all geometries within a certain bounding box.
   *
   * @param epsg
   *            The bounding box' coordinate system reference.
   * @param bbox
   *            The bounding box itself.
   * @param geomName
   *            The name of the geometry field ("the_geom")
   * @return filter
   */
  public Filter createBboxFilter(String epsg, Envelope bbox, String geomName) {
    return FF.bbox(geomName, bbox.getMinX(), bbox.getMinY(), bbox.getMaxX(), bbox.getMaxY(), epsg);
  }

  /**
   * Create a filter the evaluates all geometries within a certain bounding box.
   *
   * @param crs
   *            The bounding box' coordinate system reference.
   * @param bbox
   *            The bounding box itself.
   * @param geomName
   *            The name of the geometry field ("the_geom")
   * @return filter
   */
  public Filter createBboxFilter(Crs crs, Envelope bbox, String geomName) {
    return FF.bbox(FF.property(geomName), new ReferencedEnvelope(bbox, crs));
  }

  /**
   * Creates a filter with all the geometries that overlap the given geometry.
   *
   * @param geometry
   *            the geometry
   * @param geomName
   *            the name of the geometry field ("the_geom")
   * @return filter
   */
  public Filter createOverlapsFilter(Geometry geometry, String geomName) {
    Expression nameExpression = FF.property(geomName);
    Literal geomLiteral = FF.literal(geometry);
    return FF.overlaps(nameExpression, geomLiteral);
  }

  /**
   * Creates a filter that allows everything.
   *
   * @return filter
   */
  public Filter createTrueFilter() {
    return Filter.INCLUDE;
  }

  /**
   * Creates a filter that allows nothing.
   *
   * @return filter
   */
  public Filter createFalseFilter() {
    return Filter.EXCLUDE;
  }

  /**
   * Register a feature model.
   * <p/>
   * Only feature models which are registered can be used when filtering.
   *
   * @param featureModel feature model to register
   */
  public void registerFeatureModel(FeatureModel featureModel) {
    FeatureModelRegistry.getRegistry().register(featureModel);
  }

  /**
   * Combine two filters using the "AND" operator.
   *
   * @param left first filter to combine
   * @param right second filter to combine
   * @return new filter combining the parameters using "and"
   */
  public Filter createAndFilter(Filter left, Filter right) {
    return FF.and(left, right);
  }

  /** @inheritDoc */
  public Filter parseFilter(String filter) throws GeomajasException {
    try {
      return ECQL.toFilter(filter, FF);
    } catch (CQLException e) {
      // ECQL should be a superset of CQL, but there are apparently extra key words like "id"
      // fall back to CQL for backwards compatibility
      log.warn("Filter not parsable by ECQL, falling back to CQL", e);
      try {
        return CQL.toFilter(filter, FF);
      } catch (CQLException ce) {
        throw new GeomajasException(ce, ExceptionCode.FILTER_PARSE_PROBLEM, filter);
      }
    }
  }

}
TOP

Related Classes of org.geomajas.internal.service.FilterServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.