/**
* Copyright (c) Codice Foundation
*
* 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.
*
* 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 org.codice.ddf.spatial.ogc.wfs.v2_0_0.catalog.source;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.common.util.CollectionUtils;
import org.codice.ddf.spatial.ogc.wfs.catalog.common.FeatureAttributeDescriptor;
import org.codice.ddf.spatial.ogc.wfs.catalog.common.FeatureMetacardType;
import org.codice.ddf.spatial.ogc.wfs.catalog.mapper.MetacardMapper;
import org.codice.ddf.spatial.ogc.wfs.v2_0_0.catalog.common.Wfs20Constants;
import org.codice.ddf.spatial.ogc.wfs.v2_0_0.catalog.common.Wfs20Constants.COMPARISON_OPERATORS;
import org.codice.ddf.spatial.ogc.wfs.v2_0_0.catalog.common.Wfs20Constants.CONFORMANCE_CONSTRAINTS;
import org.codice.ddf.spatial.ogc.wfs.v2_0_0.catalog.common.Wfs20Constants.SPATIAL_OPERATORS;
import org.codice.ddf.spatial.ogc.wfs.v2_0_0.catalog.common.Wfs20Constants.TEMPORAL_OPERATORS;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.opengis.filter.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.io.WKTWriter;
import ddf.catalog.data.Metacard;
import ddf.catalog.filter.FilterDelegate;
import net.opengis.filter.v_2_0_0.AbstractIdType;
import net.opengis.filter.v_2_0_0.BBOXType;
import net.opengis.filter.v_2_0_0.BinaryComparisonOpType;
import net.opengis.filter.v_2_0_0.BinaryLogicOpType;
import net.opengis.filter.v_2_0_0.BinarySpatialOpType;
import net.opengis.filter.v_2_0_0.BinaryTemporalOpType;
import net.opengis.filter.v_2_0_0.ComparisonOperatorType;
import net.opengis.filter.v_2_0_0.ComparisonOperatorsType;
import net.opengis.filter.v_2_0_0.ComparisonOpsType;
import net.opengis.filter.v_2_0_0.ConformanceType;
import net.opengis.filter.v_2_0_0.DistanceBufferType;
import net.opengis.filter.v_2_0_0.FilterCapabilities;
import net.opengis.filter.v_2_0_0.FilterType;
import net.opengis.filter.v_2_0_0.GeometryOperandsType;
import net.opengis.filter.v_2_0_0.GeometryOperandsType.GeometryOperand;
import net.opengis.filter.v_2_0_0.LiteralType;
import net.opengis.filter.v_2_0_0.LowerBoundaryType;
import net.opengis.filter.v_2_0_0.MeasureType;
import net.opengis.filter.v_2_0_0.ObjectFactory;
import net.opengis.filter.v_2_0_0.PropertyIsBetweenType;
import net.opengis.filter.v_2_0_0.PropertyIsLikeType;
import net.opengis.filter.v_2_0_0.ResourceIdType;
import net.opengis.filter.v_2_0_0.ScalarCapabilitiesType;
import net.opengis.filter.v_2_0_0.SpatialCapabilitiesType;
import net.opengis.filter.v_2_0_0.SpatialOperatorType;
import net.opengis.filter.v_2_0_0.SpatialOperatorsType;
import net.opengis.filter.v_2_0_0.SpatialOpsType;
import net.opengis.filter.v_2_0_0.TemporalCapabilitiesType;
import net.opengis.filter.v_2_0_0.TemporalOperandsType;
import net.opengis.filter.v_2_0_0.TemporalOperandsType.TemporalOperand;
import net.opengis.filter.v_2_0_0.TemporalOperatorType;
import net.opengis.filter.v_2_0_0.TemporalOperatorsType;
import net.opengis.filter.v_2_0_0.UnaryLogicOpType;
import net.opengis.filter.v_2_0_0.UpperBoundaryType;
import net.opengis.gml.v_3_2_1.AbstractRingPropertyType;
import net.opengis.gml.v_3_2_1.CoordinatesType;
import net.opengis.gml.v_3_2_1.DirectPositionType;
import net.opengis.gml.v_3_2_1.EnvelopeType;
import net.opengis.gml.v_3_2_1.LineStringType;
import net.opengis.gml.v_3_2_1.LinearRingType;
import net.opengis.gml.v_3_2_1.PointType;
import net.opengis.gml.v_3_2_1.PolygonType;
import net.opengis.gml.v_3_2_1.TimeInstantType;
import net.opengis.gml.v_3_2_1.TimePeriodType;
import net.opengis.gml.v_3_2_1.TimePositionType;
import net.opengis.ows.v_1_1_0.AllowedValues;
import net.opengis.ows.v_1_1_0.DomainType;
import net.opengis.ows.v_1_1_0.ValueType;
/**
* The purpose of this class is to convert DDF OGC Filters into WFS compatible OGC Filters. This
* class will return an "Invalid"(null) filter if a translation could not be made. It will return an
* "Empty" filter, meaning no filters are set, only if it is a Content Type filter.
*/
public class WfsFilterDelegate extends FilterDelegate<FilterType> {
private static final Logger LOGGER = LoggerFactory.getLogger(WfsFilterDelegate.class);
private static final String MISSING_PARAMETERS_MSG = "Required parameters are missing";
private static final String PROPERTY_NOT_QUERYABLE = "'%s' is not a queryable property.";
// Regex to match coords in WKT
private static final Pattern COORD_PATTERN = Pattern
.compile("-?\\.?\\d+(\\.?\\d+)?\\s-?\\.?\\d+(\\.?\\d+)?");
private FeatureMetacardType featureMetacardType;
private ObjectFactory filterObjectFactory = new ObjectFactory();
private net.opengis.gml.v_3_2_1.ObjectFactory gml320ObjectFactory = new net.opengis.gml.v_3_2_1.ObjectFactory();
private boolean logicalOps;
private Set<COMPARISON_OPERATORS> comparisonOps;
private Map<SPATIAL_OPERATORS, SpatialOperatorType> spatialOps = new ConcurrentHashMap<SPATIAL_OPERATORS, SpatialOperatorType>(
new EnumMap<SPATIAL_OPERATORS, SpatialOperatorType>(SPATIAL_OPERATORS.class));
private List<QName> geometryOperands;
private List<QName> temporalOperands;
private Map<TEMPORAL_OPERATORS, TemporalOperatorType> temporalOps = new ConcurrentHashMap<TEMPORAL_OPERATORS, TemporalOperatorType>(
new EnumMap<TEMPORAL_OPERATORS, TemporalOperatorType>(TEMPORAL_OPERATORS.class));
private boolean isSortingSupported;
private Set<SortOrder> allowedSortOrders;
private String coordinateOrder;
private String srsName;
private boolean isEpsg4326 = false;
private MetacardMapper metacardToFeatureMapper;
public WfsFilterDelegate(FeatureMetacardType featureMetacardType,
FilterCapabilities filterCapabilities, String srsName,
MetacardMapper metacardToFeatureMapper, String coordinateOrder) {
if (featureMetacardType == null) {
throw new IllegalArgumentException("FeatureMetacardType can not be null");
}
this.featureMetacardType = featureMetacardType;
this.metacardToFeatureMapper = metacardToFeatureMapper;
this.srsName = srsName;
if (Wfs20Constants.EPSG_4326.equalsIgnoreCase(srsName) || Wfs20Constants.EPSG_4326_URN
.equalsIgnoreCase(srsName)) {
isEpsg4326 = true;
} else {
LOGGER.debug(
"Unable to convert geometry to {}. All geospatial queries for this featureType will be invalidated!",
srsName);
}
this.coordinateOrder = coordinateOrder;
this.allowedSortOrders = new HashSet<SortOrder>();
this.isSortingSupported = false;
updateAllowedOperations(filterCapabilities);
}
private final void updateAllowedOperations(FilterCapabilities filterCapabilities) {
comparisonOps = Collections.newSetFromMap(
new ConcurrentHashMap<COMPARISON_OPERATORS, Boolean>(
new EnumMap<COMPARISON_OPERATORS, Boolean>(COMPARISON_OPERATORS.class)));
geometryOperands = new ArrayList<QName>();
temporalOperands = new ArrayList<QName>();
if (filterCapabilities == null) {
LOGGER.error("WFS 2.0 Service doesn't support any filters");
return;
}
// CONFORMANCE
configureConformance(filterCapabilities.getConformance());
ScalarCapabilitiesType scalarCapabilities = filterCapabilities.getScalarCapabilities();
if (scalarCapabilities != null) {
// LOGICAL OPERATORS
if (scalarCapabilities.getLogicalOperators() != null) {
logicalOps = true;
}
// COMPARISON OPERATORS
ComparisonOperatorsType comparisonOperators = scalarCapabilities
.getComparisonOperators();
if (comparisonOperators != null) {
for (ComparisonOperatorType comp : comparisonOperators.getComparisonOperator()) {
if (null != comp) {
comparisonOps.add(COMPARISON_OPERATORS.valueOf(comp.getName()));
}
}
}
}
// SPATIAL OPERATORS
SpatialCapabilitiesType spatialCapabilities = filterCapabilities.getSpatialCapabilities();
if (spatialCapabilities != null) {
if (spatialCapabilities.getSpatialOperators() != null) {
setSpatialOps(spatialCapabilities.getSpatialOperators());
}
// GEOMETRY OPERANDS
GeometryOperandsType geometryOperandsType = spatialCapabilities.getGeometryOperands();
if (geometryOperandsType != null) {
for (GeometryOperandsType.GeometryOperand geoOperand : geometryOperandsType
.getGeometryOperand()) {
if (geoOperand.getName() != null) {
geometryOperands.add(geoOperand.getName());
}
}
LOGGER.debug("geometryOperands: {}", geometryOperands);
}
}
// TEMPORAL OPERATORS
TemporalCapabilitiesType temporalCapabilitiesType = filterCapabilities
.getTemporalCapabilities();
if (temporalCapabilitiesType != null) {
if (temporalCapabilitiesType.getTemporalOperators() != null) {
setTemporalOps(temporalCapabilitiesType.getTemporalOperators());
}
// TEMPORAL OPERANDS
TemporalOperandsType temporalOperandsType = temporalCapabilitiesType
.getTemporalOperands();
if (temporalOperandsType != null) {
for (TemporalOperandsType.TemporalOperand temporalOperand : temporalOperandsType
.getTemporalOperand()) {
if (temporalOperand.getName() != null) {
temporalOperands.add(temporalOperand.getName());
}
}
LOGGER.debug("temporalOperands: {}", temporalOperands);
}
}
}
private void configureConformance(ConformanceType conformance) {
if (conformance != null) {
List<DomainType> constraints = conformance.getConstraint();
if (!CollectionUtils.isEmpty(constraints)) {
for (DomainType constraint : constraints) {
if (CONFORMANCE_CONSTRAINTS.ImplementsSorting
.equals(CONFORMANCE_CONSTRAINTS.valueOf(constraint.getName()))) {
configureSorting(constraint);
}
}
}
}
}
private void configureSorting(DomainType constraint) {
if (constraint.getNoValues() != null && constraint.getDefaultValue() != null) {
if (StringUtils.equalsIgnoreCase(constraint.getDefaultValue().getValue(),
Boolean.TRUE.toString())) {
this.isSortingSupported = true;
} else if (StringUtils.equalsIgnoreCase(constraint.getDefaultValue().getValue(),
Boolean.FALSE.toString())) {
this.isSortingSupported = false;
}
}
if (constraint.getAllowedValues() != null) {
this.isSortingSupported = true;
AllowedValues allowedValues = constraint.getAllowedValues();
List<Object> values = allowedValues.getValueOrRange();
for (Object value : values) {
if (value instanceof ValueType) {
String sortOrder = ((ValueType) value).getValue();
// Could be ASC, ASCENDING, etc.
if (StringUtils.startsWithIgnoreCase(sortOrder, "A")) {
allowedSortOrders.add(SortOrder.ASCENDING);
} else if (StringUtils.startsWithIgnoreCase(sortOrder, "D")) {
allowedSortOrders.add(SortOrder.DESCENDING);
}
}
}
}
}
@Override
public FilterType and(List<FilterType> filtersToBeAnded) {
filtersToBeAnded = applyTemporalFallbacks(filtersToBeAnded);
return buildAndOrFilter(filtersToBeAnded,
filterObjectFactory.createAnd(new BinaryLogicOpType()));
}
@Override
public FilterType or(List<FilterType> filtersToBeOred) {
if (filtersToBeOred.contains(Collections.singleton(null))) {
throw new UnsupportedOperationException("Invalid filters found in list of filters.");
}
return buildAndOrFilter(filtersToBeOred,
filterObjectFactory.createOr(new BinaryLogicOpType()));
}
@Override
public FilterType not(FilterType filterToBeNoted) {
areLogicalOperationsSupported();
FilterType returnFilter = new FilterType();
if (filterToBeNoted == null) {
return null;
}
UnaryLogicOpType notType = new UnaryLogicOpType();
if (filterToBeNoted.isSetComparisonOps()) {
notType.setComparisonOps(filterToBeNoted.getComparisonOps());
} else if (filterToBeNoted.isSetLogicOps()) {
notType.setLogicOps(filterToBeNoted.getLogicOps());
} else if (filterToBeNoted.isSetSpatialOps()) {
notType.setSpatialOps(filterToBeNoted.getSpatialOps());
} else {
return returnFilter;
}
returnFilter.setLogicOps(filterObjectFactory.createNot(notType));
return returnFilter;
}
private Set<String> getFeatureIds(List<FilterType> filters) {
Set<String> ids = new HashSet<String>();
// This filter delegate requires that if one filter is a featureId
// filter, they
// must all be.
if (!CollectionUtils.isEmpty(filters)) {
boolean isFeatureIdFilter = filters.get(0) != null && filters.get(0).isSetId();
for (FilterType filterType : filters) {
if ((filterType != null && filterType.isSetId()) != isFeatureIdFilter) {
throw new UnsupportedOperationException(
"Query with mix of feature ID and non-feature ID queries not supported");
}
if (isFeatureIdFilter) {
List<JAXBElement<? extends AbstractIdType>> idFilterTypeList = filterType
.getId();
for (JAXBElement<? extends AbstractIdType> idFilter : idFilterTypeList) {
AbstractIdType absId = idFilter.getValue();
ResourceIdType resId = (ResourceIdType) absId;
ids.add(resId.getRid());
}
}
}
}
return ids;
}
private FilterType buildFeatureIdFilter(Set<String> ids) {
FilterType filterType = new FilterType();
for (String id : ids) {
List<JAXBElement<? extends AbstractIdType>> idFilterTypeList = filterType.getId();
ResourceIdType resId = new ResourceIdType();
resId.setRid(id);
idFilterTypeList.add(filterObjectFactory.createResourceId(resId));
}
return filterType;
}
private FilterType buildAndOrFilter(List<FilterType> filters,
JAXBElement<BinaryLogicOpType> andOrFilter) {
areLogicalOperationsSupported();
if (filters == null || filters.isEmpty()) {
return null;
}
removeEmptyFilters(filters);
// Check if these filters contain featureID(s)
Set<String> featureIds = getFeatureIds(filters);
if (!CollectionUtils.isEmpty(featureIds)) {
return buildFeatureIdFilter(featureIds);
}
// If we have 1 filter don't wrap it with AND/OR
if (filters.size() == 1) {
return filters.get(0);
}
for (FilterType filterType : filters) {
// Determine which filterType is set
if (filterType.isSetComparisonOps()) {
andOrFilter.getValue().getComparisonOpsOrSpatialOpsOrTemporalOps()
.add(filterType.getComparisonOps());
} else if (filterType.isSetLogicOps()) {
andOrFilter.getValue().getComparisonOpsOrSpatialOpsOrTemporalOps()
.add(filterType.getLogicOps());
} else if (filterType.isSetSpatialOps()) {
andOrFilter.getValue().getComparisonOpsOrSpatialOpsOrTemporalOps()
.add(filterType.getSpatialOps());
} else if (filterType.isSetTemporalOps()) {
andOrFilter.getValue().getComparisonOpsOrSpatialOpsOrTemporalOps()
.add(filterType.getTemporalOps());
}
}
FilterType returnFilter = new FilterType();
returnFilter.setLogicOps(andOrFilter);
return returnFilter;
}
protected List<FilterType> applyTemporalFallbacks(List<FilterType> filters) {
if (null == filters || filters.isEmpty()) {
return filters;
}
String startDate = "";
String endDate = "";
String property = "";
List<FilterType> newFilters = new ArrayList<>();
for (FilterType filterType : filters) {
if (null == filterType) {
continue;
}
if (filterType.isSetTemporalOps() && (!isTemporalOpSupported(TEMPORAL_OPERATORS.Before)
&& !isTemporalOpSupported(TEMPORAL_OPERATORS.After)) && !isDuringFilter(
filterType)) {
BinaryTemporalOpType binaryTemporalOpType = (BinaryTemporalOpType) filterType
.getTemporalOps().getValue();
property = binaryTemporalOpType.getValueReference();
JAXBElement xp = binaryTemporalOpType.getExpression();
TimeInstantType obj = (TimeInstantType) xp.getValue();
TimePositionType timePositionType = obj.getTimePosition();
List<String> value = timePositionType.getValue();
if (isAfterFilter(filterType)) {
startDate = value.get(0);
} else if (isBeforeFilter(filterType)) {
endDate = value.get(0);
}
} else {
newFilters.add(filterType);
}
}
if (isTemporalOpSupported(TEMPORAL_OPERATORS.During) && (StringUtils
.isNotEmpty(startDate))) {
if (StringUtils.isEmpty(endDate)) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date now = new Date();
endDate = dateFormat.format(now);
}
FilterType duringFilter = buildDuringFilterType(
featureMetacardType.getFeatureType().getLocalPart() + "." + property, startDate,
endDate);
newFilters.add(duringFilter);
}
return newFilters;
}
private void removeEmptyFilters(List<FilterType> filters) {
// Loop through the filters and remove any empty filters
List<FilterType> filtersToBeRemoved = new ArrayList<FilterType>(filters.size());
Boolean foundInvalidFilter = false;
for (FilterType filterType : filters) {
if (filterType == null) {
foundInvalidFilter = true;
} else if (!isFilterSet(filterType)) {
filtersToBeRemoved.add(filterType);
}
}
// If we found an invalid filter we want to return an invalid filter.
if (foundInvalidFilter) {
filters.clear();
filters.add(null);
} else {
filters.removeAll(filtersToBeRemoved);
filters.removeAll(Collections.singleton(null));
if (filters.isEmpty()) {
filters.add(new FilterType());
}
}
}
private Boolean isFilterSet(FilterType filter) {
return (filter.isSetComparisonOps() || filter.isSetLogicOps() || filter.isSetSpatialOps() ||
filter.isSetId() || filter.isSetTemporalOps());
}
@Override
public FilterType propertyIsEqualTo(String propertyName, String literal,
boolean isCaseSensitive) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsEqualTo(String propertyName, Date date) {
return buildPropertyIsFilterType(propertyName, convertDateToIso8601Format(date),
PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsEqualTo(String propertyName, int literal) {
return buildPropertyIsFilterType(propertyName, Integer.valueOf(literal),
PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsEqualTo(String propertyName, short literal) {
return buildPropertyIsFilterType(propertyName, Short.valueOf(literal),
PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsEqualTo(String propertyName, long literal) {
return buildPropertyIsFilterType(propertyName, Long.valueOf(literal),
PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsEqualTo(String propertyName, float literal) {
return buildPropertyIsFilterType(propertyName, Float.valueOf(literal),
PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsEqualTo(String propertyName, double literal) {
return buildPropertyIsFilterType(propertyName, Double.valueOf(literal),
PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsEqualTo(String propertyName, boolean literal) {
return buildPropertyIsFilterType(propertyName, Boolean.valueOf(literal),
PROPERTY_IS_OPS.PropertyIsEqualTo);
}
@Override
public FilterType propertyIsLike(String propertyName, String literal, boolean isCaseSensitive) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsLike);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, String literal,
boolean isCaseSensitive) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, Date literal) {
return buildPropertyIsFilterType(propertyName, convertDateToIso8601Format(literal),
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, int literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, short literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, long literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, float literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, double literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsNotEqualTo(String propertyName, boolean literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsNotEqualTo);
}
@Override
public FilterType propertyIsGreaterThan(String propertyName, String literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThan);
}
@Override
public FilterType propertyIsGreaterThan(String propertyName, Date literal) {
return buildPropertyIsFilterType(propertyName, convertDateToIso8601Format(literal),
PROPERTY_IS_OPS.PropertyIsGreaterThan);
}
@Override
public FilterType propertyIsGreaterThan(String propertyName, int literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThan);
}
@Override
public FilterType propertyIsGreaterThan(String propertyName, short literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThan);
}
@Override
public FilterType propertyIsGreaterThan(String propertyName, long literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThan);
}
@Override
public FilterType propertyIsGreaterThan(String propertyName, float literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThan);
}
@Override
public FilterType propertyIsGreaterThan(String propertyName, double literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThan);
}
@Override
public FilterType propertyIsGreaterThanOrEqualTo(String propertyName, String literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThanOrEqualTo);
}
@Override
public FilterType propertyIsGreaterThanOrEqualTo(String propertyName, Date literal) {
return buildPropertyIsFilterType(propertyName, convertDateToIso8601Format(literal),
PROPERTY_IS_OPS.PropertyIsGreaterThanOrEqualTo);
}
@Override
public FilterType propertyIsGreaterThanOrEqualTo(String propertyName, int literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThanOrEqualTo);
}
@Override
public FilterType propertyIsGreaterThanOrEqualTo(String propertyName, short literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThanOrEqualTo);
}
@Override
public FilterType propertyIsGreaterThanOrEqualTo(String propertyName, long literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThanOrEqualTo);
}
@Override
public FilterType propertyIsGreaterThanOrEqualTo(String propertyName, float literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThanOrEqualTo);
}
@Override
public FilterType propertyIsGreaterThanOrEqualTo(String propertyName, double literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsGreaterThanOrEqualTo);
}
@Override
public FilterType propertyIsLessThan(String propertyName, String literal) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsLessThan);
}
@Override
public FilterType propertyIsLessThan(String propertyName, Date literal) {
return buildPropertyIsFilterType(propertyName, convertDateToIso8601Format(literal),
PROPERTY_IS_OPS.PropertyIsLessThan);
}
@Override
public FilterType propertyIsLessThan(String propertyName, int literal) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsLessThan);
}
@Override
public FilterType propertyIsLessThan(String propertyName, short literal) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsLessThan);
}
@Override
public FilterType propertyIsLessThan(String propertyName, long literal) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsLessThan);
}
@Override
public FilterType propertyIsLessThan(String propertyName, float literal) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsLessThan);
}
@Override
public FilterType propertyIsLessThan(String propertyName, double literal) {
return buildPropertyIsFilterType(propertyName, literal, PROPERTY_IS_OPS.PropertyIsLessThan);
}
@Override
public FilterType propertyIsLessThanOrEqualTo(String propertyName, String literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsLessThanOrEqualTo);
}
@Override
public FilterType propertyIsLessThanOrEqualTo(String propertyName, Date literal) {
return buildPropertyIsFilterType(propertyName, convertDateToIso8601Format(literal),
PROPERTY_IS_OPS.PropertyIsLessThanOrEqualTo);
}
@Override
public FilterType propertyIsLessThanOrEqualTo(String propertyName, int literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsLessThanOrEqualTo);
}
@Override
public FilterType propertyIsLessThanOrEqualTo(String propertyName, short literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsLessThanOrEqualTo);
}
@Override
public FilterType propertyIsLessThanOrEqualTo(String propertyName, long literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsLessThanOrEqualTo);
}
@Override
public FilterType propertyIsLessThanOrEqualTo(String propertyName, float literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsLessThanOrEqualTo);
}
@Override
public FilterType propertyIsLessThanOrEqualTo(String propertyName, double literal) {
return buildPropertyIsFilterType(propertyName, literal,
PROPERTY_IS_OPS.PropertyIsLessThanOrEqualTo);
}
@Override
public FilterType propertyIsBetween(String propertyName, String lowerBoundary,
String upperBoundary) {
return buildPropertyIsBetweenFilterType(propertyName, lowerBoundary, upperBoundary);
}
@Override
public FilterType propertyIsBetween(String propertyName, Date lowerBoundary,
Date upperBoundary) {
return buildPropertyIsBetweenFilterType(propertyName,
convertDateToIso8601Format(lowerBoundary),
convertDateToIso8601Format(upperBoundary));
}
@Override
public FilterType during(String propertyName, Date startDate, Date endDate) {
return buildDuringFilterType(mapMetacardAttribute(propertyName),
convertDateToIso8601Format(startDate), convertDateToIso8601Format(endDate));
}
@Override
public FilterType relative(String propertyName, long duration) {
DateTime now = new DateTime();
DateTime startDate = now.minus(duration);
return buildDuringFilterType(mapMetacardAttribute(propertyName),
convertDateToIso8601Format(startDate), convertDateToIso8601Format(now));
}
@Override
public FilterType after(String propertyName, Date date) {
return buildAfterFilterType(mapMetacardAttribute(propertyName),
convertDateToIso8601Format(date));
}
@Override
public FilterType before(String propertyName, Date date) {
return buildBeforeFilterType(mapMetacardAttribute(propertyName),
convertDateToIso8601Format(date));
}
@Override
public FilterType propertyIsBetween(String propertyName, int lowerBoundary, int upperBoundary) {
return buildPropertyIsBetweenFilterType(propertyName, lowerBoundary, upperBoundary);
}
@Override
public FilterType propertyIsBetween(String propertyName, short lowerBoundary,
short upperBoundary) {
return buildPropertyIsBetweenFilterType(propertyName, lowerBoundary, upperBoundary);
}
@Override
public FilterType propertyIsBetween(String propertyName, long lowerBoundary,
long upperBoundary) {
return buildPropertyIsBetweenFilterType(propertyName, lowerBoundary, upperBoundary);
}
@Override
public FilterType propertyIsBetween(String propertyName, float lowerBoundary,
float upperBoundary) {
return buildPropertyIsBetweenFilterType(propertyName, lowerBoundary, upperBoundary);
}
@Override
public FilterType propertyIsBetween(String propertyName, double lowerBoundary,
double upperBoundary) {
return buildPropertyIsBetweenFilterType(propertyName, lowerBoundary, upperBoundary);
}
protected boolean isAfterFilter(FilterType filter) {
if (isFilterSet(filter)) {
return TEMPORAL_OPERATORS.After.toString()
.equals(filter.getTemporalOps().getName().getLocalPart());
} else {
return false;
}
}
protected boolean isBeforeFilter(FilterType filter) {
if (isFilterSet(filter)) {
return TEMPORAL_OPERATORS.Before.toString()
.equals(filter.getTemporalOps().getName().getLocalPart());
} else {
return false;
}
}
protected boolean isDuringFilter(FilterType filter) {
if (isFilterSet(filter)) {
return TEMPORAL_OPERATORS.During.toString()
.equals(filter.getTemporalOps().getName().getLocalPart());
} else {
return false;
}
}
private FilterType buildPropertyIsBetweenFilterType(String propertyName, Object lowerBoundary,
Object upperBoundary) {
if (!isValidInputParameters(propertyName, lowerBoundary, upperBoundary)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
FilterType filter = new FilterType();
if (featureMetacardType.getProperties().contains(propertyName)) {
FeatureAttributeDescriptor featureAttributeDescriptor = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(propertyName);
if (featureAttributeDescriptor.isIndexed()) {
filter.setComparisonOps(
createPropertyIsBetween(featureAttributeDescriptor.getPropertyName(),
lowerBoundary, upperBoundary));
} else {
throw new IllegalArgumentException(
String.format(PROPERTY_NOT_QUERYABLE, propertyName));
}
} else {
return null;
}
return filter;
}
private FilterType buildDuringFilterType(String propertyName, String startDate,
String endDate) {
if (!isTemporalOpSupported(TEMPORAL_OPERATORS.During)) {
throw new UnsupportedOperationException(
"Temporal Operator [" + TEMPORAL_OPERATORS.During + "] is not supported.");
}
TemporalOperand timePeriodTemporalOperand = new TemporalOperand();
timePeriodTemporalOperand
.setName(new QName(Wfs20Constants.GML_3_2_NAMESPACE, Wfs20Constants.TIME_PERIOD));
if (!isTemporalOperandSupported(timePeriodTemporalOperand)) {
throw new UnsupportedOperationException(
"Temporal Operand [" + timePeriodTemporalOperand.getName()
+ "] is not supported.");
}
if (!isValidInputParameters(propertyName, startDate, endDate)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (!isPropertyTemporalType(propertyName)) {
throw new IllegalArgumentException(
"Property [" + propertyName + "] is not of type " + timePeriodTemporalOperand
.getName() + ".");
}
FilterType filter = filterObjectFactory.createFilterType();
if (featureMetacardType.getProperties().contains(propertyName)) {
FeatureAttributeDescriptor featureAttributeDescriptor = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(propertyName);
if (featureAttributeDescriptor.isIndexed()) {
filter.setTemporalOps(createDuring(featureAttributeDescriptor.getPropertyName(),
featureMetacardType.getName(), startDate, endDate));
} else {
throw new IllegalArgumentException(
String.format(PROPERTY_NOT_QUERYABLE, propertyName));
}
} else {
return null;
}
return filter;
}
private FilterType buildAfterFilterType(String propertyName, String date) {
TemporalOperand timeInstantTemporalOperand = new TemporalOperand();
timeInstantTemporalOperand
.setName(new QName(Wfs20Constants.GML_3_2_NAMESPACE, Wfs20Constants.TIME_INSTANT));
if (!isTemporalOperandSupported(timeInstantTemporalOperand)) {
throw new UnsupportedOperationException(
"Temporal Operand [" + timeInstantTemporalOperand.getName()
+ "] is not supported.");
}
if (!isValidInputParameters(propertyName, date)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (!isPropertyTemporalType(propertyName)) {
throw new IllegalArgumentException(
"Property [" + propertyName + "] is not of type " + timeInstantTemporalOperand
.getName() + ".");
}
FilterType filter = filterObjectFactory.createFilterType();
if (featureMetacardType.getProperties().contains(propertyName)) {
FeatureAttributeDescriptor featureAttributeDescriptor = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(propertyName);
if (featureAttributeDescriptor.isIndexed()) {
filter.setTemporalOps(createAfter(featureAttributeDescriptor.getPropertyName(),
featureMetacardType.getName(), date));
} else {
throw new IllegalArgumentException(
String.format(PROPERTY_NOT_QUERYABLE, propertyName));
}
} else {
return null;
}
return filter;
}
private FilterType buildBeforeFilterType(String propertyName, String date) {
TemporalOperand timeInstantTemporalOperand = new TemporalOperand();
timeInstantTemporalOperand
.setName(new QName(Wfs20Constants.GML_3_2_NAMESPACE, Wfs20Constants.TIME_INSTANT));
if (!isTemporalOperandSupported(timeInstantTemporalOperand)) {
throw new UnsupportedOperationException(
"Temporal Operand [" + timeInstantTemporalOperand.getName()
+ "] is not supported.");
}
if (!isValidInputParameters(propertyName, date)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (!isPropertyTemporalType(propertyName)) {
throw new IllegalArgumentException(
"Property [" + propertyName + "] is not of type " + timeInstantTemporalOperand
.getName() + ".");
}
FilterType filter = filterObjectFactory.createFilterType();
if (featureMetacardType.getProperties().contains(propertyName)) {
FeatureAttributeDescriptor featureAttributeDescriptor = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(propertyName);
if (featureAttributeDescriptor.isIndexed()) {
filter.setTemporalOps(createBefore(featureAttributeDescriptor.getPropertyName(),
featureMetacardType.getName(), date));
} else {
throw new IllegalArgumentException(
String.format(PROPERTY_NOT_QUERYABLE, propertyName));
}
} else {
return null;
}
return filter;
}
private FilterType buildPropertyIsFilterType(String propertyName, Object literal,
PROPERTY_IS_OPS propertyIsType) {
if (!isValidInputParameters(propertyName, literal)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
FilterType returnFilter = new FilterType();
if (Metacard.CONTENT_TYPE.equals(propertyName)) {
return returnFilter;
}
// Special Case - If we get an ANY_TEXT we want to convert that to a
// series of OR's
if ((Metacard.ANY_TEXT.equalsIgnoreCase(propertyName))) {
if (CollectionUtils.isEmpty(featureMetacardType.getTextualProperties())) {
LOGGER.debug("Feature Type does not have Textual Properties to query.");
return null;
}
if (featureMetacardType.getTextualProperties().size() == 1) {
FeatureAttributeDescriptor attrDescriptor = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(featureMetacardType.getTextualProperties().get(0));
if (attrDescriptor.isIndexed()) {
returnFilter.setComparisonOps(
createPropertyIsFilter(attrDescriptor.getPropertyName(), literal,
propertyIsType));
} else {
LOGGER.debug(
"All textual properties have been blacklisted. Removing from query.");
return null;
}
} else {
List<FilterType> binaryCompOpsToBeOred = new ArrayList<FilterType>();
for (String property : featureMetacardType.getTextualProperties()) {
// only build filters for queryable properties
FeatureAttributeDescriptor attrDesc = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(property);
if (attrDesc.isIndexed()) {
FilterType filter = new FilterType();
filter.setComparisonOps(
createPropertyIsFilter(attrDesc.getPropertyName(), literal,
propertyIsType));
binaryCompOpsToBeOred.add(filter);
} else {
LOGGER.debug(String.format(PROPERTY_NOT_QUERYABLE, property));
}
}
if (!binaryCompOpsToBeOred.isEmpty()) {
returnFilter = or(binaryCompOpsToBeOred);
} else {
LOGGER.debug(
"All textual properties have been blacklisted. Removing from query.");
return null;
}
}
// filter is for a specific property; check to see if it is valid
} else if (featureMetacardType.getProperties().contains(propertyName)) {
FeatureAttributeDescriptor attrDesc = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(propertyName);
if (attrDesc.isIndexed()) {
returnFilter.setComparisonOps(
createPropertyIsFilter(attrDesc.getPropertyName(), literal,
propertyIsType));
} else {
// blacklisted property encountered
throw new IllegalArgumentException(
String.format(PROPERTY_NOT_QUERYABLE, propertyName));
}
} else if (Metacard.ID.equals(propertyName)) {
LOGGER.debug("feature id query for : {}", literal);
String[] idTokens = literal.toString().split("\\.");
if (idTokens.length > 1) {
if (idTokens[0].equals(featureMetacardType.getName())) {
LOGGER.debug("feature type matches metacard type; creating featureID filter");
returnFilter.getId().add(createFeatureIdFilter(literal.toString()));
} else {
LOGGER.debug("feature type does not match metacard type; invalidating filter");
return null;
}
} else {
returnFilter.getId().add(createFeatureIdFilter(literal.toString()));
}
} else {
return null;
}
return returnFilter;
}
private JAXBElement<? extends ComparisonOpsType> createPropertyIsFilter(String property,
Object literal, PROPERTY_IS_OPS operation) {
switch (operation) {
case PropertyIsEqualTo:
JAXBElement<BinaryComparisonOpType> propIsEqualTo = filterObjectFactory
.createPropertyIsEqualTo(new BinaryComparisonOpType());
propIsEqualTo.getValue().getExpression().add(createPropertyNameType(property));
propIsEqualTo.getValue().getExpression().add(createLiteralType(literal));
return propIsEqualTo;
case PropertyIsNotEqualTo:
JAXBElement<BinaryComparisonOpType> propIsNotEqualTo = filterObjectFactory
.createPropertyIsNotEqualTo(new BinaryComparisonOpType());
propIsNotEqualTo.getValue().getExpression().add(createPropertyNameType(property));
propIsNotEqualTo.getValue().getExpression().add(createLiteralType(literal));
return propIsNotEqualTo;
case PropertyIsGreaterThan:
JAXBElement<BinaryComparisonOpType> propIsGreaterThan = filterObjectFactory
.createPropertyIsGreaterThan(new BinaryComparisonOpType());
propIsGreaterThan.getValue().getExpression().add(createPropertyNameType(property));
propIsGreaterThan.getValue().getExpression().add(createLiteralType(literal));
return propIsGreaterThan;
case PropertyIsGreaterThanOrEqualTo:
JAXBElement<BinaryComparisonOpType> propIsGreaterThanOrEqualTo = filterObjectFactory
.createPropertyIsGreaterThanOrEqualTo(new BinaryComparisonOpType());
propIsGreaterThanOrEqualTo.getValue().getExpression()
.add(createPropertyNameType(property));
propIsGreaterThanOrEqualTo.getValue().getExpression().add(createLiteralType(literal));
return propIsGreaterThanOrEqualTo;
case PropertyIsLessThan:
JAXBElement<BinaryComparisonOpType> propIsLessThan = filterObjectFactory
.createPropertyIsLessThan(new BinaryComparisonOpType());
propIsLessThan.getValue().getExpression().add(createPropertyNameType(property));
propIsLessThan.getValue().getExpression().add(createLiteralType(literal));
return propIsLessThan;
case PropertyIsLessThanOrEqualTo:
JAXBElement<BinaryComparisonOpType> propIsLessThanOrEqualTo = filterObjectFactory
.createPropertyIsLessThanOrEqualTo(new BinaryComparisonOpType());
propIsLessThanOrEqualTo.getValue().getExpression()
.add(createPropertyNameType(property));
propIsLessThanOrEqualTo.getValue().getExpression().add(createLiteralType(literal));
return propIsLessThanOrEqualTo;
case PropertyIsLike:
JAXBElement<PropertyIsLikeType> propIsLike = filterObjectFactory
.createPropertyIsLike(new PropertyIsLikeType());
propIsLike.getValue().setEscapeChar(Wfs20Constants.ESCAPE);
propIsLike.getValue().setSingleChar(SINGLE_CHAR);
propIsLike.getValue().setWildCard(Wfs20Constants.WILD_CARD);
propIsLike.getValue().getExpression().add(createLiteralType(literal));
propIsLike.getValue().getExpression().add(createPropertyNameType(property));
return propIsLike;
default:
throw new UnsupportedOperationException("Unsupported Property Comparison Type");
}
}
private JAXBElement<PropertyIsBetweenType> createPropertyIsBetween(String property,
Object lowerBoundary, Object upperBoundary) {
PropertyIsBetweenType propertyIsBetween = new PropertyIsBetweenType();
propertyIsBetween.setLowerBoundary(createLowerBoundary(lowerBoundary));
propertyIsBetween.setUpperBoundary(createUpperBoundary(upperBoundary));
propertyIsBetween.setExpression(createPropertyNameType(property));
return filterObjectFactory.createPropertyIsBetween(propertyIsBetween);
}
private JAXBElement<BinaryTemporalOpType> createDuring(String property, String type,
String startDate, String endDate) {
JAXBElement<BinaryTemporalOpType> during = filterObjectFactory
.createDuring(createBinaryTemporalOpType(property, type, startDate, endDate));
return during;
}
private JAXBElement<BinaryTemporalOpType> createAfter(String property, String type,
String date) {
JAXBElement<BinaryTemporalOpType> after = filterObjectFactory
.createAfter(createBinaryTemporalOpType(property, type, date));
return after;
}
private JAXBElement<BinaryTemporalOpType> createBefore(String property, String type,
String date) {
JAXBElement<BinaryTemporalOpType> before = filterObjectFactory
.createBefore(createBinaryTemporalOpType(property, type, date));
return before;
}
private BinaryTemporalOpType createBinaryTemporalOpType(String property, String type,
String startDate, String endDate) {
BinaryTemporalOpType binaryTemporalOpType = filterObjectFactory
.createBinaryTemporalOpType();
binaryTemporalOpType.setValueReference(property);
binaryTemporalOpType.setExpression(gml320ObjectFactory
.createTimePeriod(createTimePeriodType(property, type, startDate, endDate)));
return binaryTemporalOpType;
}
private BinaryTemporalOpType createBinaryTemporalOpType(String property, String type,
String date) {
BinaryTemporalOpType binaryTemporalOpType = filterObjectFactory
.createBinaryTemporalOpType();
binaryTemporalOpType.setValueReference(property);
binaryTemporalOpType.setExpression(
gml320ObjectFactory.createTimeInstant(createTimeInstantType(property, type, date)));
return binaryTemporalOpType;
}
private TimePositionType createTimePositionType(String dateTime) {
TimePositionType timePosition = gml320ObjectFactory.createTimePositionType();
timePosition.getValue().add(dateTime);
return timePosition;
}
private TimePeriodType createTimePeriodType(String property, String type, String startDate,
String endDate) {
TimePeriodType timePeriodType = gml320ObjectFactory.createTimePeriodType();
timePeriodType.setBeginPosition(createTimePositionType(startDate));
timePeriodType.setEndPosition(createTimePositionType(endDate));
timePeriodType.setId(type + "." + System.currentTimeMillis());
return timePeriodType;
}
private TimeInstantType createTimeInstantType(String property, String type, String date) {
TimeInstantType timeInstantType = gml320ObjectFactory.createTimeInstantType();
timeInstantType.setTimePosition(createTimePositionType(date));
timeInstantType.setId(type + "." + System.currentTimeMillis());
return timeInstantType;
}
private JAXBElement<ResourceIdType> createFeatureIdFilter(final String id) {
ResourceIdType resId = new ResourceIdType();
resId.setRid(id);
ObjectFactory objFact = new ObjectFactory();
return objFact.createResourceId(resId);
}
private boolean isValidInputParameters(String propertyName, Object literal) {
if (literal == null || StringUtils.isEmpty(propertyName) || StringUtils
.isEmpty(literal.toString())) {
return false;
}
return true;
}
private boolean isValidInputParameters(String propertyName, String literal, double distance) {
boolean isValid = isValidInputParameters(propertyName, literal);
if (Double.valueOf(distance) < 0) {
isValid = false;
}
return isValid;
}
private boolean isValidInputParameters(String propertyName, Object lowerBoundary,
Object upperBoundary) {
if (lowerBoundary == null || upperBoundary == null) {
return false;
}
if (StringUtils.isEmpty(propertyName) || StringUtils.isEmpty(lowerBoundary.toString())
|| StringUtils.isEmpty(upperBoundary.toString())) {
return false;
}
return true;
}
private boolean isPropertyTemporalType(String propertyName) {
return featureMetacardType.getTemporalProperties().contains(propertyName);
}
private String convertDateToIso8601Format(DateTime inputDate) {
DateTimeFormatter dtf = ISODateTimeFormat.dateTimeNoMillis().withZone(DateTimeZone.UTC);
return dtf.print(inputDate).toString();
}
private String convertDateToIso8601Format(Date inputDate) {
DateTimeFormatter dtf = ISODateTimeFormat.dateTimeNoMillis().withZone(DateTimeZone.UTC);
return dtf.print(new DateTime(inputDate)).toString();
}
// spatial operators
@Override
public FilterType beyond(String propertyName, String wkt, double distance) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt, distance)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Beyond)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Beyond.toString(), propertyName, wkt,
distance);
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.DWithin)) {
return not(dwithin(propertyName, wkt, distance));
} else {
LOGGER.debug("WFS Source does not support Beyond filters");
return null;
}
}
@Override
public FilterType contains(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Contains)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Contains.toString(), propertyName,
wkt, null);
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.Within)) {
return not(within(propertyName, wkt));
} else {
LOGGER.debug("WFS Source does not support Contains filters");
return null;
}
}
@Override
public FilterType crosses(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Crosses)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Crosses.toString(), propertyName,
wkt, null);
} else {
LOGGER.debug("WFS Source does not support Crosses filters");
return null;
}
}
@Override
public FilterType disjoint(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Disjoint)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Disjoint.toString(), propertyName,
wkt, null);
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.BBOX)) {
return not(bbox(propertyName, wkt));
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.Intersects)) {
return not(intersects(propertyName, wkt));
} else {
LOGGER.debug("WFS Source does not support Disjoint or BBOX filters");
return null;
}
}
@Override
public FilterType dwithin(String propertyName, String wkt, double distance) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt, distance)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.DWithin)) {
return this
.buildGeospatialFilterType(SPATIAL_OPERATORS.DWithin.toString(), propertyName,
wkt, distance);
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.Beyond)) {
return not(beyond(propertyName, wkt, distance));
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.Intersects)) {
String bufferedWkt = bufferGeometry(wkt, distance);
return intersects(propertyName, bufferedWkt);
} else {
LOGGER.debug(
"WFS Source does not support the DWithin filter or any of its fallback filters (Not Beyond or Intersects).");
return null;
}
}
@Override
public FilterType intersects(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Intersects)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Intersects.toString(), propertyName,
wkt, null);
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.BBOX)) {
return bbox(propertyName, wkt);
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.Disjoint)) {
return not(disjoint(propertyName, wkt));
} else {
LOGGER.debug("WFS Source does not support Intersect or BBOX");
return null;
}
}
@Override
public FilterType overlaps(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Overlaps)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Overlaps.toString(), propertyName,
wkt, null);
} else {
LOGGER.debug("WFS Source does not support Overlaps filters");
return null;
}
}
@Override
public FilterType touches(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Touches)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Touches.toString(), propertyName,
wkt, null);
} else {
LOGGER.debug("WFS Source does not support Beyond filters");
return null;
}
}
@Override
public FilterType within(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.Within)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.Within.toString(), propertyName, wkt,
null);
} else if (spatialOps.containsKey(SPATIAL_OPERATORS.Contains)) {
return contains(propertyName, wkt);
} else {
LOGGER.debug("WFS Source does not support Within filters");
return null;
}
}
private FilterType bbox(String propertyName, String wkt) {
if (!isEpsg4326) {
return null;
}
if (!isValidInputParameters(propertyName, wkt)) {
throw new IllegalArgumentException(MISSING_PARAMETERS_MSG);
}
if (spatialOps.containsKey(SPATIAL_OPERATORS.BBOX)) {
return buildGeospatialFilterType(SPATIAL_OPERATORS.BBOX.toString(), propertyName, wkt,
null);
} else {
LOGGER.debug("WFS Source does not support BBOX filters");
return null;
}
}
private FilterType buildGeospatialFilterType(String spatialOpType, String propertyName,
String wkt, Double distance) {
FilterType returnFilter = new FilterType();
if (Metacard.ANY_GEO.equals(propertyName)) {
if (CollectionUtils.isEmpty(featureMetacardType.getGmlProperties())) {
LOGGER.debug("Feature Type does not have GEO properties to query");
return null;
}
if (featureMetacardType.getGmlProperties().size() == 1) {
FeatureAttributeDescriptor attrDesc = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(featureMetacardType.getGmlProperties().get(0));
if (attrDesc != null && attrDesc.isIndexed()) {
returnFilter.setSpatialOps(
createSpatialOpType(spatialOpType, attrDesc.getPropertyName(), wkt,
distance));
} else {
LOGGER.debug("All GEO properties have been blacklisted. Removing from query");
return null;
}
} else {
List<FilterType> filtersToBeOred = new ArrayList<FilterType>();
for (String property : featureMetacardType.getGmlProperties()) {
FeatureAttributeDescriptor attrDesc = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(property);
if (attrDesc != null && attrDesc.isIndexed()) {
FilterType filter = new FilterType();
filter.setSpatialOps(
createSpatialOpType(spatialOpType, attrDesc.getPropertyName(), wkt,
distance));
filtersToBeOred.add(filter);
} else {
LOGGER.debug(String.format(PROPERTY_NOT_QUERYABLE, property));
}
}
if (!filtersToBeOred.isEmpty()) {
returnFilter = or(filtersToBeOred);
} else {
LOGGER.debug("All GEO properties have been blacklisted. Removing from query.");
returnFilter = null;
}
}
} else if (featureMetacardType.getGmlProperties().contains(propertyName)) {
FeatureAttributeDescriptor attrDesc = (FeatureAttributeDescriptor) featureMetacardType
.getAttributeDescriptor(propertyName);
if (attrDesc != null && attrDesc.isIndexed()) {
FilterType filter = new FilterType();
filter.setSpatialOps(
createSpatialOpType(spatialOpType, attrDesc.getPropertyName(), wkt,
distance));
return filter;
} else {
// blacklisted property encountered
throw new IllegalArgumentException(
String.format(PROPERTY_NOT_QUERYABLE, propertyName));
}
} else {
return null;
}
return returnFilter;
}
private JAXBElement<? extends SpatialOpsType> createSpatialOpType(String operation,
String propertyName, String wkt, Double distance) {
switch (SPATIAL_OPERATORS.valueOf(operation)) {
case BBOX:
return buildBBoxType(propertyName, wkt);
case Beyond:
return buildDistanceBufferType(
filterObjectFactory.createBeyond(new DistanceBufferType()), propertyName, wkt,
distance);
case Contains:
return buildBinarySpatialOpType(
filterObjectFactory.createContains(new BinarySpatialOpType()), propertyName,
wkt);
case Crosses:
return buildBinarySpatialOpType(
filterObjectFactory.createCrosses(new BinarySpatialOpType()), propertyName,
wkt);
case Disjoint:
return buildBinarySpatialOpType(
filterObjectFactory.createDisjoint(new BinarySpatialOpType()), propertyName,
wkt);
case DWithin:
return buildDistanceBufferType(
filterObjectFactory.createDWithin(new DistanceBufferType()), propertyName, wkt,
distance);
case Intersects:
return buildBinarySpatialOpType(
filterObjectFactory.createIntersects(new BinarySpatialOpType()), propertyName,
wkt);
case Overlaps:
return buildBinarySpatialOpType(
filterObjectFactory.createOverlaps(new BinarySpatialOpType()), propertyName,
wkt);
case Touches:
return buildBinarySpatialOpType(
filterObjectFactory.createTouches(new BinarySpatialOpType()), propertyName,
wkt);
case Within:
return buildBinarySpatialOpType(
filterObjectFactory.createWithin(new BinarySpatialOpType()), propertyName, wkt);
default:
throw new UnsupportedOperationException(
"Unsupported geospatial filter type " + SPATIAL_OPERATORS.valueOf(operation)
+ " specified");
}
}
private JAXBElement<BinarySpatialOpType> buildBinarySpatialOpType(
JAXBElement<BinarySpatialOpType> bsot, String propertyName, String wkt) {
bsot.getValue().setValueReference(propertyName);
bsot.getValue().setExpression(createGeometryOperand(wkt));
return bsot;
}
private JAXBElement<DistanceBufferType> buildDistanceBufferType(
JAXBElement<DistanceBufferType> dbt, String propertyName, String wkt, double distance) {
MeasureType measureType = new MeasureType();
measureType.setValue(distance);
// the filter adapter normalizes all distances to meters
measureType.setUom(Wfs20Constants.METERS);
dbt.getValue().setDistance(measureType);
dbt.getValue().setExpression(filterObjectFactory.createValueReference(propertyName));
dbt.getValue().setAny(createGeometryOperand(wkt));
return dbt;
}
private JAXBElement<BBOXType> buildBBoxType(String propertyName, String wkt) {
BBOXType bboxType = new BBOXType();
bboxType.setExpression(filterObjectFactory.createValueReference(propertyName));
try {
bboxType.setAny(createEnvelope(getGeometryFromWkt(wkt)));
} catch (ParseException e) {
throw new UnsupportedOperationException("Unable to parse WKT Geometry [" + wkt + "]",
e);
}
return filterObjectFactory.createBBOX(bboxType);
}
private JAXBElement<?> createGeometryOperand(String wkt) {
String convertedWkt = convertWktToLatLonOrdering(wkt);
Geometry wktGeometry = null;
try {
wktGeometry = getGeometryFromWkt(convertedWkt);
} catch (ParseException e) {
throw new UnsupportedOperationException(
"Unable to parse WKT Geometry [" + convertedWkt + "]", e);
}
if (wktGeometry instanceof Polygon) {
GeometryOperand polygonOperand = new GeometryOperand();
polygonOperand.setName(Wfs20Constants.POLYGON);
if (isGeometryOperandSupported(polygonOperand)) {
return createPolygon(wktGeometry);
}
GeometryOperand envelopeOperand = new GeometryOperand();
envelopeOperand.setName(Wfs20Constants.ENVELOPE);
if (isGeometryOperandSupported(envelopeOperand)) {
return createEnvelope(wktGeometry);
}
} else if (wktGeometry instanceof Point) {
GeometryOperand pointOperand = new GeometryOperand();
pointOperand.setName(Wfs20Constants.POINT);
if (isGeometryOperandSupported(pointOperand)) {
return createPoint(wktGeometry);
}
} else if (wktGeometry instanceof LineString) {
GeometryOperand lineStringOperand = new GeometryOperand();
lineStringOperand.setName(Wfs20Constants.LINESTRING);
if (isGeometryOperandSupported(lineStringOperand)) {
return createLineString(wktGeometry);
}
}
throw new UnsupportedOperationException(
"Geometry Operand from WKT [" + convertedWkt + "] is not supported.");
}
private JAXBElement<PolygonType> createPolygon(Geometry geometry) {
PolygonType polygonType = gml320ObjectFactory.createPolygonType();
polygonType.setSrsName(Wfs20Constants.EPSG_4326_URN);
LinearRingType ring = gml320ObjectFactory.createLinearRingType();
ring.setCoordinates(createCoordinates(geometry));
AbstractRingPropertyType abstractRing = gml320ObjectFactory
.createAbstractRingPropertyType();
abstractRing.setAbstractRing(gml320ObjectFactory.createLinearRing(ring));
polygonType.setExterior(abstractRing);
return gml320ObjectFactory.createPolygon(polygonType);
}
private JAXBElement<EnvelopeType> createEnvelope(Geometry geometry) {
EnvelopeType envelopeType = gml320ObjectFactory.createEnvelopeType();
envelopeType.setSrsName(Wfs20Constants.EPSG_4326_URN);
Envelope envelope = geometry.getEnvelopeInternal();
DirectPositionType lowerCorner = gml320ObjectFactory.createDirectPositionType();
lowerCorner.getValue().add(envelope.getMinX());
lowerCorner.getValue().add(envelope.getMinY());
envelopeType.setLowerCorner(lowerCorner);
DirectPositionType upperCorner = gml320ObjectFactory.createDirectPositionType();
upperCorner.getValue().add(envelope.getMaxX());
upperCorner.getValue().add(envelope.getMaxY());
envelopeType.setUpperCorner(upperCorner);
return gml320ObjectFactory.createEnvelope(envelopeType);
}
private JAXBElement<LineStringType> createLineString(Geometry geometry) {
LineStringType lineStringType = gml320ObjectFactory.createLineStringType();
lineStringType.setSrsName(Wfs20Constants.EPSG_4326_URN);
lineStringType.setCoordinates(createCoordinates(geometry));
return gml320ObjectFactory.createLineString(lineStringType);
}
private CoordinatesType createCoordinates(Geometry geometry) {
CoordinatesType coords = gml320ObjectFactory.createCoordinatesType();
Coordinate[] coordinates = geometry.getCoordinates();
if (coordinates != null && coordinates.length > 0) {
StringBuffer coordString = new StringBuffer();
for (Coordinate coordinate : coordinates) {
coordString.append(coordinate.x).append(",").append(coordinate.y).append(" ");
}
coords.setValue(coordString.toString());
coords.setDecimal(".");
coords.setCs(",");
coords.setTs(" ");
coords.setValue(coordString.toString());
return coords;
} else {
throw new IllegalArgumentException(
"Unable to parse Geometry coordinates from WKT String");
}
}
private JAXBElement<PointType> createPoint(Geometry geometry) {
Coordinate[] coordinates = geometry.getCoordinates();
if (coordinates != null && coordinates.length > 0) {
StringBuilder coordString = new StringBuilder();
coordString.append(coordinates[0].x).append(",").append(coordinates[0].y);
CoordinatesType coordinatesType = new CoordinatesType();
coordinatesType.setValue(coordString.toString());
PointType point = new PointType();
point.setSrsName(srsName);
point.setCoordinates(coordinatesType);
return gml320ObjectFactory.createPoint(point);
} else {
throw new IllegalArgumentException("Unable to parse Point coordinates from WKT String");
}
}
private boolean isGeometryOperandSupported(GeometryOperand geoOperand) {
return geometryOperands.contains(geoOperand.getName());
}
private JAXBElement<LiteralType> createLiteralType(Object literalValue) {
JAXBElement<LiteralType> literalType = filterObjectFactory.createLiteral(new LiteralType());
literalType.getValue().getContent().add(literalValue.toString());
return literalType;
}
private JAXBElement<String> createPropertyNameType(String propertyNameValue) {
return filterObjectFactory.createValueReference(propertyNameValue);
}
private LowerBoundaryType createLowerBoundary(Object lowerBoundary) {
LowerBoundaryType lowerBoundaryType = new LowerBoundaryType();
lowerBoundaryType.setExpression(createLiteralType(lowerBoundary));
return lowerBoundaryType;
}
private UpperBoundaryType createUpperBoundary(Object upperBoundary) {
UpperBoundaryType upperBoundaryType = new UpperBoundaryType();
upperBoundaryType.setExpression(createLiteralType(upperBoundary));
return upperBoundaryType;
}
private Geometry getGeometryFromWkt(String wkt) throws ParseException {
return new WKTReader().read(wkt);
}
private String bufferGeometry(String wkt, double distance) {
LOGGER.debug("Buffering WKT {} by distance {} meter(s).", wkt, distance);
String bufferedWkt = null;
try {
Geometry geometry = getGeometryFromWkt(wkt);
double bufferInDegrees = metersToDegrees(distance);
LOGGER.debug("Buffering {} by {} degree(s).", geometry.getClass().getSimpleName(),
bufferInDegrees);
Geometry bufferedGeometry = geometry.buffer(bufferInDegrees);
bufferedWkt = new WKTWriter().write(bufferedGeometry);
LOGGER.debug("Buffered WKT: {}.", bufferedWkt);
} catch (ParseException e) {
throw new IllegalArgumentException("Unable to parse WKT String", e);
}
return bufferedWkt;
}
/**
* This method approximates the degrees in latitude for the given distance (in meters) using the
* formula for the meridian distance on Earth.
* <p/>
* degrees = distance in meters/radius of Earth in meters * 180.0/pi
* <p/>
* The approximate degrees in latitude can be used to compute a buffer around a given geometry
* (see bufferGeometry()).
*/
private double metersToDegrees(double distance) {
double degrees = (distance / Wfs20Constants.EARTH_MEAN_RADIUS_METERS)
* Wfs20Constants.RADIANS_TO_DEGREES;
LOGGER.debug("{} meter(s) is approximately {} degree(s) of latitude.", distance, degrees);
return degrees;
}
public boolean isLogicalOps() {
return logicalOps;
}
public Set<COMPARISON_OPERATORS> getComparisonOps() {
return comparisonOps;
}
public Map<SPATIAL_OPERATORS, SpatialOperatorType> getSpatialOps() {
return spatialOps;
}
public void setSpatialOps(SpatialOperatorsType spatialOperators) {
spatialOps = new ConcurrentHashMap<SPATIAL_OPERATORS, SpatialOperatorType>(
new EnumMap<SPATIAL_OPERATORS, SpatialOperatorType>(SPATIAL_OPERATORS.class));
for (SpatialOperatorType spatialOp : spatialOperators.getSpatialOperator()) {
LOGGER.debug("Adding key [spatialOp Name: {}]", spatialOp.getName());
spatialOps.put(SPATIAL_OPERATORS.valueOf(spatialOp.getName()), spatialOp);
LOGGER.debug("spatialOps Map: {}", spatialOps.toString());
}
}
public List<QName> getGeometryOperands() {
return geometryOperands;
}
public Map<TEMPORAL_OPERATORS, TemporalOperatorType> getTemporalOps() {
return temporalOps;
}
public void setTemporalOps(TemporalOperatorsType temporalOperators) {
temporalOps = new ConcurrentHashMap<TEMPORAL_OPERATORS, TemporalOperatorType>(
new EnumMap<TEMPORAL_OPERATORS, TemporalOperatorType>(TEMPORAL_OPERATORS.class));
for (TemporalOperatorType temporalOp : temporalOperators.getTemporalOperator()) {
LOGGER.debug("Adding key [temporalOp Name: {}]", temporalOp.getName());
temporalOps.put(TEMPORAL_OPERATORS.valueOf(temporalOp.getName()), temporalOp);
LOGGER.debug("temporalOps Map: {}", temporalOps.toString());
}
}
public List<QName> getTemporalOperands() {
return temporalOperands;
}
public boolean isSortingSupported() {
return this.isSortingSupported;
}
public Set<SortOrder> getAllowedSortOrders() {
return this.allowedSortOrders;
}
public String getSrsName() {
return srsName;
}
public boolean isEpsg4326() {
return isEpsg4326;
}
private boolean isTemporalOpSupported(TEMPORAL_OPERATORS temporalOp) {
return temporalOps.containsKey(temporalOp);
}
private boolean isTemporalOperandSupported(TemporalOperand temporalOperand) {
return temporalOperands.contains(temporalOperand.getName());
}
private void areLogicalOperationsSupported() {
if (!logicalOps) {
throw new UnsupportedOperationException("Logical Operations are not supported.");
}
}
private String mapMetacardAttribute(String metacardAttribute) {
String featureProperty = null;
if (this.metacardToFeatureMapper != null) {
featureProperty = this.metacardToFeatureMapper.getFeatureProperty(metacardAttribute);
} else {
LOGGER.debug("{} is null.", MetacardMapper.class.getSimpleName());
return metacardAttribute;
}
if (featureProperty == null) {
LOGGER.debug("Unable to find feature property for metacard attribute {}.",
metacardAttribute);
return metacardAttribute;
} else {
LOGGER.debug("Found feature property {} for metacard attribute {}.", featureProperty,
metacardAttribute);
return featureProperty;
}
}
/**
* The WKT passed into the spatial methods has the coordinates ordered in LON/LAT. This method
* will convert the WKT to LAT/LON ordering.
*/
private String convertWktToLatLonOrdering(String wktInLonLat) {
if (Wfs20Constants.LAT_LON_ORDER.equals(coordinateOrder)) {
LOGGER.debug(
"Converting WKT from LON/LAT coordinate ordering to LAT/LON coordinate ordering.");
// Normalize all whitespace in WKT before processing.
wktInLonLat = normalizeWhitespaceInWkt(wktInLonLat);
Matcher matcher = COORD_PATTERN.matcher(wktInLonLat);
StringBuffer stringBuffer = new StringBuffer();
while (matcher.find()) {
String lonLatCoord = matcher.group();
String latLonCoord = StringUtils.reverseDelimited(lonLatCoord, ' ');
LOGGER.debug("Converted LON/LAT coord: ({}) to LAT/LON coord: ({}).", lonLatCoord,
latLonCoord);
matcher.appendReplacement(stringBuffer, latLonCoord);
}
matcher.appendTail(stringBuffer);
String wktInLatLon = stringBuffer.toString();
LOGGER.debug("Original WKT with coords in LON/LAT ordering: {}", wktInLonLat);
LOGGER.debug("Converted WKT with coords in LAT/LON ordering: {}", wktInLatLon);
return wktInLatLon;
} else {
LOGGER.debug("The configured CSW source requires coordinates in LON/LAT ordering.");
return wktInLonLat;
}
}
private String normalizeWhitespaceInWkt(String wkt) {
String normalizedWkt = wkt.replaceAll("(\\s+)?([(|)|,])(\\s+)?", "$2");
normalizedWkt = normalizedWkt.replaceAll("\\s+", " ");
return normalizedWkt;
}
private static enum PROPERTY_IS_OPS {
PropertyIsEqualTo, PropertyIsLike, PropertyIsNotEqualTo, PropertyIsGreaterThan, PropertyIsGreaterThanOrEqualTo, PropertyIsLessThan, PropertyIsLessThanOrEqualTo;
}
}