Repository: gisaia/ARLAS-server (language: python)

Issue:
Add a new query param that allows to choose orientation of passed WKT
Currently , if the passed wkt is polygon, the orientation should be clock-wise . Otherwise, ARLAS-server will attempt to parse it as the "Complementary" Polygon on the other facet of the planet.

Specifications of WKT orientation suggest that exterior ring of a polygon should be counter clock-wise but tools like QGIS are clock-wise.

For ARLAS, we need to a add a new query param to specify the desired orientation
- [x] default to `counter clock-wise`
- [x] query param to be added to all endpoints having the parameter `f`
- [x] The behaviour after chosing the param would be : 

> Currently , if the passed wkt is polygon, the orientation should be `[chosen orientation]` . Otherwise, ARLAS-server will attempt to parse it as the "Complementary" Polygon on the other facet of the planet.


Discussion / hints:

Fix patch:
diff --git a/arlas-core/src/main/java/io/arlas/server/core/app/Documentation.java b/arlas-core/src/main/java/io/arlas/server/core/app/Documentation.java
index 7b6100069..3274bba56 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/app/Documentation.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/app/Documentation.java
@@ -100,6 +100,7 @@ public class Documentation {
 
     public static final String FILTER_PARAM_Q = "A full text search. Optionally, it's possible to search on a field using this syntax: {fieldname}:{text}";
     public static final String FILTER_DATE_FORMAT = "The format of dates. This parameter should be set only if a date field is queried in `f` param; when using `gt`, `lt`, `gte`, `lte` and `range` operations";
+    public static final String FILTER_RIGHT_HAND = "If righthand = true, the passed WKT should be counter clock-wise; otherwise, ARLAS-server will attempt to parse it as the \"Complementary\" Polygon on the other facet of the planet. \n Inversely, If righthand = false, the passed WKT should be clock-wise; otherwise, ARLAS-server will attempt to parse it as the \"Complementary\" Polygon on the other facet of the planet. ";
     public static final String AGGREGATION_OPERATION = "Aggregate the elements in the collection(s), given the filters and the aggregation parameters";
     public static final String AGGREGATION_PARAM_AGG = "- The agg parameter should be given in the following formats:  " +
             "\n \n" +
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java
index a79157869..b8c3eff9e 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticFluidSearch.java
@@ -111,18 +111,18 @@ public SearchResponse exec() throws ArlasException {
     }
 
     @Override
-    public FluidSearchService filter(MultiValueFilter<Expression> f, String dateFormat) throws ArlasException {
+    public FluidSearchService filter(MultiValueFilter<Expression> f, String dateFormat, Boolean righthand) throws ArlasException {
         BoolQueryBuilder orBoolQueryBuilder = QueryBuilders.boolQuery();
         for (Expression fFilter : f) {
             orBoolQueryBuilder = orBoolQueryBuilder
-                    .should(filter(fFilter, dateFormat));
+                    .should(filter(fFilter, dateFormat, righthand));
         }
         orBoolQueryBuilder = orBoolQueryBuilder.minimumShouldMatch(1);
         boolQueryBuilder = boolQueryBuilder.filter(orBoolQueryBuilder);
         return this;
     }
 
-    private BoolQueryBuilder filter(Expression expression, String dateFormat) throws ArlasException {
+    private BoolQueryBuilder filter(Expression expression, String dateFormat, Boolean righthand) throws ArlasException {
         BoolQueryBuilder ret = QueryBuilders.boolQuery();
         if (Strings.isNullOrEmpty(expression.field) || expression.op == null || Strings.isNullOrEmpty(expression.value)) {
             throw new InvalidParameterException(INVALID_PARAMETER_F);
@@ -210,7 +210,7 @@ private BoolQueryBuilder filter(Expression expression, String dateFormat) throws
                         }
                         break;
                     case GEO_SHAPE:
-                        orBoolQueryBuilder = orBoolQueryBuilder.should(filterGWithin(field, value));
+                        orBoolQueryBuilder = orBoolQueryBuilder.should(filterGWithin(field, value, righthand));
                         break;
                     default:
                         throw new ArlasException("'within' op on field '" + field + "' of type '" + wType + "' is not supported");
@@ -228,7 +228,7 @@ private BoolQueryBuilder filter(Expression expression, String dateFormat) throws
                         }
                         break;
                     case GEO_SHAPE:
-                        orBoolQueryBuilder2 = orBoolQueryBuilder2.should(filterGWithin(field, value));
+                        orBoolQueryBuilder2 = orBoolQueryBuilder2.should(filterGWithin(field, value, righthand));
                         break;
                     default:
                         throw new ArlasException("'notwithin' op on field '" + field + "' of type '" + type + "' is not supported");
@@ -236,10 +236,10 @@ private BoolQueryBuilder filter(Expression expression, String dateFormat) throws
                 ret = ret.mustNot(orBoolQueryBuilder2);
                 break;
             case intersects:
-                ret = ret.filter(filterGIntersect(field, value));
+                ret = ret.filter(filterGIntersect(field, value, righthand));
                 break;
             case notintersects:
-                ret = ret.mustNot(filterGIntersect(field, value));
+                ret = ret.mustNot(filterGIntersect(field, value, righthand));
                 break;
             default:
                 throw new InvalidParameterException(INVALID_OPERATOR);
@@ -367,18 +367,18 @@ public List<AbstractQueryBuilder> filterNotPWithin(String field, String notpwith
         return builderList;
     }
 
-    public GeoShapeQueryBuilder filterGWithin(String field, String geometry) throws ArlasException {
+    public GeoShapeQueryBuilder filterGWithin(String field, String geometry, Boolean righthand) throws ArlasException {
         try {
-            ShapeBuilder shapeBuilder = getShapeBuilder(geometry);
+            ShapeBuilder shapeBuilder = getShapeBuilder(geometry, righthand);
             return QueryBuilders.geoWithinQuery(field, shapeBuilder);
         } catch (IOException e) {
             throw new ArlasException("Exception while building geoWithinQuery: " + e.getMessage());
         }
     }
 
-    public GeoShapeQueryBuilder filterGIntersect(String field, String geometry) throws ArlasException {
+    public GeoShapeQueryBuilder filterGIntersect(String field, String geometry, Boolean righthand) throws ArlasException {
         try {
-            ShapeBuilder shapeBuilder = getShapeBuilder(geometry);
+            ShapeBuilder shapeBuilder = getShapeBuilder(geometry, righthand);
             return QueryBuilders.geoIntersectionQuery(field, shapeBuilder);
         } catch (IOException e) {
             throw new ArlasException("Exception while building geoIntersectionQuery: " + e.getMessage());
@@ -879,12 +879,13 @@ private void setOrder(Aggregation aggregationModel, ValuesSourceAggregationBuild
         }
     }
 
-    private PolygonBuilder createPolygonBuilder(Polygon polygon) {
+    private PolygonBuilder createPolygonBuilder(Polygon polygon, Boolean righthand) {
         // TODO: add interior holes
         CoordinatesBuilder coordinatesBuilder = new CoordinatesBuilder();
         List<Coordinate> coordinates = Arrays.asList(polygon.getCoordinates());
         coordinatesBuilder.coordinates(coordinates);
-        return new PolygonBuilder(coordinatesBuilder, Orientation.LEFT);
+
+        return new PolygonBuilder(coordinatesBuilder, righthand == Boolean.TRUE ? Orientation.RIGHT : Orientation.LEFT);
     }
 
     private PolygonBuilder createPolygonBuilder(double[] bbox) {
@@ -915,10 +916,10 @@ private PolygonBuilder createPolygonBuilder(double[] bbox) {
 
     }
 
-    private MultiPolygonBuilder createMultiPolygonBuilder(MultiPolygon multiPolygon) {
+    private MultiPolygonBuilder createMultiPolygonBuilder(MultiPolygon multiPolygon, Boolean righthand) {
         MultiPolygonBuilder multiPolygonBuilder = new MultiPolygonBuilder(Orientation.LEFT);
         for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
-            multiPolygonBuilder.polygon(createPolygonBuilder((Polygon) multiPolygon.getGeometryN(i)));
+            multiPolygonBuilder.polygon(createPolygonBuilder((Polygon) multiPolygon.getGeometryN(i), righthand));
         }
         return multiPolygonBuilder;
     }
@@ -935,7 +936,7 @@ private PointBuilder createPointBuilder(Point point) {
         return pointBuilder;
     }
 
-    private ShapeBuilder getShapeBuilder(String geometry) throws ArlasException {
+    private ShapeBuilder getShapeBuilder(String geometry, Boolean righthand) throws ArlasException {
         // test if geometry is 'west,south,east,north' or wkt string
         if (CheckParams.isBboxMatch(geometry)) {
             return createPolygonBuilder((double[]) CheckParams.toDoubles(geometry));
@@ -946,9 +947,9 @@ private ShapeBuilder getShapeBuilder(String geometry) throws ArlasException {
                 String geometryType = wktGeometry.getGeometryType().toUpperCase();
                 switch (geometryType) {
                     case "POLYGON":
-                        return createPolygonBuilder((Polygon) wktGeometry);
+                        return createPolygonBuilder((Polygon) wktGeometry, righthand);
                     case "MULTIPOLYGON":
-                        return createMultiPolygonBuilder((MultiPolygon) wktGeometry);
+                        return createMultiPolygonBuilder((MultiPolygon) wktGeometry, righthand);
                     case "LINESTRING":
                         return createLineStringBuilder((LineString) wktGeometry);
                     case "POINT":
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/model/SelectRequest.java b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/model/SelectRequest.java
index 39807d82f..b6b900db8 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/model/SelectRequest.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/model/SelectRequest.java
@@ -230,33 +230,33 @@ public String formatRangeCondition(String field, boolean incMin, String min, boo
                 AND, field, incMax ? LTE : LT, mapWhereParam(new ClauseParam(field, max)), RP);
     }
 
-    public String formatGeoCondition(String field, OperatorEnum op, Geometry jtsGeo) throws ArlasException {
+    public String formatGeoCondition(String field, OperatorEnum op, Geometry jtsGeo, Boolean righthand) throws ArlasException {
         switch (op) {
             case notintersects:
-                return formatGeoIntersectCondition(field, jtsGeo, true);
+                return formatGeoIntersectCondition(field, jtsGeo, true, righthand);
             case notwithin:
-                return formatGeoWithinCondition(field, jtsGeo, true);
+                return formatGeoWithinCondition(field, jtsGeo, true, righthand);
             case intersects:
-                return formatGeoIntersectCondition(field, jtsGeo, false);
+                return formatGeoIntersectCondition(field, jtsGeo, false, righthand);
             case within:
-                return formatGeoWithinCondition(field, jtsGeo, false);
+                return formatGeoWithinCondition(field, jtsGeo, false, righthand);
             default:
                 throw new ArlasException(op + " op on geo field '" + field + " is not supported");
         }
     }
 
-    public String formatGeoWithinCondition(String field, Geometry jtsGeo, boolean not) throws ArlasException {
+    public String formatGeoWithinCondition(String field, Geometry jtsGeo, boolean not, Boolean righthand) throws ArlasException {
         // ST_COVERS includes the borders while ST_WITHIN excludes them
-        return concat(not ? NOT : "", ST_COVERS, LP, mapWhereParam(new ClauseParam(field, jtsToDBgeo(jtsGeo))), COMMA, field, RP);
+        return concat(not ? NOT : "", ST_COVERS, LP, mapWhereParam(new ClauseParam(field, jtsToDBgeo(jtsGeo, righthand))), COMMA, field, RP);
     }
 
-    public String formatGeoIntersectCondition(String field, Geometry jtsGeo, boolean not) throws ArlasException {
-        return concat(not ? NOT : "", ST_INTERSECTS, LP, mapWhereParam(new ClauseParam(field, jtsToDBgeo(jtsGeo))), COMMA, field, RP);
+    public String formatGeoIntersectCondition(String field, Geometry jtsGeo, boolean not, Boolean righthand) throws ArlasException {
+        return concat(not ? NOT : "", ST_INTERSECTS, LP, mapWhereParam(new ClauseParam(field, jtsToDBgeo(jtsGeo, righthand))), COMMA, field, RP);
     }
 
     public String formatGeoDistance(String field, Geometry jtsGeo) throws ArlasException {
-        return concat(ST_DISTANCE, LP, field, mapWhereParam(new ClauseParam(field, jtsToDBgeo(jtsGeo))), RP);
+        return concat(ST_DISTANCE, LP, field, mapWhereParam(new ClauseParam(field, jtsToDBgeo(jtsGeo, null))), RP);
     }
 
-    protected abstract Object jtsToDBgeo(Geometry jtsGeo) throws ArlasException;
+    protected abstract Object jtsToDBgeo(Geometry jtsGeo, Boolean righthand) throws ArlasException;
 }
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/postgis/PostgisSelectRequest.java b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/postgis/PostgisSelectRequest.java
index 406741b90..27b4eca89 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/postgis/PostgisSelectRequest.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/postgis/PostgisSelectRequest.java
@@ -49,6 +49,8 @@ public class PostgisSelectRequest extends SelectRequest {
     public static final String G5 = "concat(";
     public static final String S1 = "to_tsvector(";
     public static final String S2 = ") @@ to_tsquery(";
+    public static final String ST_FORCEPOLYGONCW = "st_forcepolygoncw";
+    public static final String ST_FORCEPOLYGONCCW = "st_forcepolygonccw";
 
     public String getDateFieldAsEpochMilli(String field) {
         return concat(EPOCH_LP, field, EPOCH_RP_MILLI);
@@ -124,10 +126,17 @@ public String formatLikeCondition(String field, String value) {
         // to_tsvector(field) @@ to_tsquery('q');
         return concat(S1,field, S2, mapWhereParam(new ClauseParam("", value)), RP);
     }
-        @Override
-    protected Object jtsToDBgeo(Geometry jtsGeo) throws ArlasException {
+    @Override
+    protected Object jtsToDBgeo(Geometry jtsGeo, Boolean righthand) throws ArlasException {
         try {
-            return new PGgeometry(concat(SRID, jtsGeo.toText()));
+            if (righthand != null) {
+                String FORCED_ORIENTATION = righthand == Boolean.TRUE ? ST_FORCEPOLYGONCCW : ST_FORCEPOLYGONCW;
+                /** https://postgis.net/docs/ST_ForcePolygonCCW.html */
+                /** Non-polygonal geometries are returned unchanged. */
+                return new PGgeometry(concat(SRID, concat(FORCED_ORIENTATION, LP, jtsGeo.toText(), RP)));
+            } else {
+                return new PGgeometry(concat(SRID, jtsGeo.toText()));
+            }
         } catch (SQLException e) {
             throw new ArlasException("Could not create PGgeometry from JTS geometry", e);
         }
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java
index 8bb3c4c8a..0ea4490a8 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/jdbi/service/JdbiFluidSearch.java
@@ -36,6 +36,7 @@
 import io.arlas.server.core.utils.*;
 import org.apache.commons.lang3.tuple.Pair;
 import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.Polygon;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -74,16 +75,16 @@ public SelectRequest getRequest(boolean setSelectFields) throws ArlasException {
     }
 
     @Override
-    public FluidSearchService filter(MultiValueFilter<Expression> f, String dateFormat) throws ArlasException {
+    public FluidSearchService filter(MultiValueFilter<Expression> f, String dateFormat, Boolean righthand) throws ArlasException {
         List<String> orConditions = new ArrayList<>();
         for (Expression fFilter : f) {
-            orConditions.add(filter(fFilter, dateFormat));
+            orConditions.add(filter(fFilter, dateFormat, righthand));
         }
         req.addOrWhereClauses(orConditions);
         return this;
     }
 
-    private String filter(Expression e, String dateFormat) throws ArlasException {
+    private String filter(Expression e, String dateFormat, Boolean righthand) throws ArlasException {
         if (isNullOrEmpty(e.field) || e.op == null || isNullOrEmpty(e.value)) {
             throw new InvalidParameterException(INVALID_PARAMETER_F);
         }
@@ -149,10 +150,11 @@ private String filter(Expression e, String dateFormat) throws ArlasException {
                         if (CheckParams.isBboxMatch(value)) {
                             double[] tlbr = CheckParams.toDoubles(value);
                             geo = GeoTileUtil.toPolygon(new BoundingBox(tlbr[3], tlbr[1], tlbr[0], tlbr[2]));
+                            return req.formatGeoCondition(e.field, e.op, geo, null);
                         } else {
                             geo = GeoUtil.readWKT(value);
+                            return req.formatGeoCondition(e.field, e.op, geo, righthand);
                         }
-                        return req.formatGeoCondition(e.field, e.op, geo);
                     default:
                         throw new ArlasException(e.op + " op on field '" + field + "' of type '" + wType + "' is not supported");
                 }
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/request/Filter.java b/arlas-core/src/main/java/io/arlas/server/core/model/request/Filter.java
index be1ca000b..63b77ecd4 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/model/request/Filter.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/request/Filter.java
@@ -28,4 +28,9 @@ public class Filter implements Serializable {
     public List<MultiValueFilter<Expression>> f;
     public List<MultiValueFilter<String>> q;
     public String dateformat;
+    public Boolean righthand;
+
+    public Filter() {
+        this.righthand = true;
+    }
 }
diff --git a/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java b/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java
index d55ad8c84..e0193d70f 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/services/ExploreService.java
@@ -141,7 +141,7 @@ public void applyFilter(Filter filter, FluidSearchService fluidSearch) throws Ar
                     throw new BadRequestException("dateformat is specified but no date field is queried in f filter (gt, lt, gte, lte or range operations)");
                 }
                 for (MultiValueFilter<Expression> f : filter.f) {
-                    fluidSearch = fluidSearch.filter(f, filter.dateformat);
+                    fluidSearch = fluidSearch.filter(f, filter.dateformat, filter.righthand);
                 }
             }
             if (filter.q != null && !filter.q.isEmpty()) {
diff --git a/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java b/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java
index 65e89a002..211599dfd 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/services/FluidSearchService.java
@@ -189,7 +189,7 @@ public static String getAggregationName(String aggName) {
 
     public abstract FluidSearchService compute(String field, ComputationEnum metric) throws ArlasException;
 
-    abstract public FluidSearchService filter(MultiValueFilter<Expression> f, String dateFormat) throws ArlasException;
+    abstract public FluidSearchService filter(MultiValueFilter<Expression> f, String dateFormat, Boolean righthand) throws ArlasException;
 
     abstract public FluidSearchService filterQ(MultiValueFilter<String> q) throws ArlasException;
 
diff --git a/arlas-core/src/main/java/io/arlas/server/core/utils/GeoUtil.java b/arlas-core/src/main/java/io/arlas/server/core/utils/GeoUtil.java
index 466f4074f..40df99df8 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/utils/GeoUtil.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/utils/GeoUtil.java
@@ -253,6 +253,10 @@ public static double[] getBbox(GeoJsonObject geoJsonObject) {
 
     public static Geometry toClockwise(final GeoJsonObject geojson) throws JsonProcessingException, ParseException {
         Geometry geometry = reader.read(writer.writeValueAsString(geojson));
+        return toClockwise(geometry);
+    }
+
+    public static Geometry toClockwise(final Geometry geometry) {
         final GeometryFactory factory = geometry.getFactory();
         if (geometry instanceof MultiPolygon || geometry instanceof Polygon) {
             boolean isMultiPolygon = geometry instanceof MultiPolygon;
@@ -293,6 +297,47 @@ public static Geometry toClockwise(final GeoJsonObject geojson) throws JsonProce
         }
     }
 
+    public static Geometry toCounterClockwise(final Geometry geometry) {
+        final GeometryFactory factory = geometry.getFactory();
+        if (geometry instanceof MultiPolygon || geometry instanceof Polygon) {
+            boolean isMultiPolygon = geometry instanceof MultiPolygon;
+            int nbPolygon = geometry.getNumGeometries();
+
+            final Polygon[] ps = new Polygon[nbPolygon];
+            for (int i = 0; i < nbPolygon; i++) {
+                final Polygon p = isMultiPolygon ? (Polygon) geometry.getGeometryN(i) : (Polygon) geometry;
+                final LinearRing[] holes = new LinearRing[p.getNumInteriorRing()];
+                LinearRing outer = p.getExteriorRing();
+                if (!Orientation.isCCW(outer.getCoordinateSequence())) {
+                    outer = reverse(factory, p.getExteriorRing());
+                }
+
+                for (int t = 0, tt = p.getNumInteriorRing(); t < tt; t++) {
+                    holes[t] = p.getInteriorRingN(t);
+                    if (Orientation.isCCW(holes[t].getCoordinateSequence())) {
+                        holes[t] = reverse(factory, holes[t]);
+                    }
+                }
+                ps[i] = factory.createPolygon(outer, holes);
+            }
+
+            Geometry reversed = isMultiPolygon ? factory.createMultiPolygon(ps) : ps[0];
+            reversed.setSRID(geometry.getSRID());
+            reversed.setUserData(geometry.getUserData());
+            return reversed;
+
+        } else if (geometry instanceof LinearRing) {
+            LinearRing lr = (LinearRing) geometry;
+            if (!Orientation.isCCW(lr.getCoordinateSequence())) {
+                lr = reverse(factory, lr);
+            }
+            return lr;
+
+        } else {
+            return geometry;
+        }
+    }
+
     private static LinearRing reverse(final GeometryFactory factory, final LinearRing ring) {
         final CoordinateSequence cs = ring.getCoordinateSequence().copy();
         CoordinateSequences.reverse(cs);
diff --git a/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java b/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java
index 6abb9fc61..24ae9dc16 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/utils/ParamsParser.java
@@ -37,6 +37,7 @@
 import org.joda.time.format.DateTimeFormat;
 import org.locationtech.jts.algorithm.Orientation;
 import org.locationtech.jts.geom.*;
+import org.locationtech.jts.io.ParseException;
 import org.locationtech.jts.io.WKTReader;
 import org.locationtech.jts.operation.valid.IsValidOp;
 import org.locationtech.jts.operation.valid.RepeatedPointTester;
@@ -251,10 +252,18 @@ public static Filter getFilter(CollectionReference collectionReference, String s
         if (serializedFilter != null) {
             try {
                 Map<String, Filter> pf = objectMapper.readValue(serializedFilter, new TypeReference<Map<String, Filter>>() {});
-                return pf.get(collectionReference.collectionName);
+                Filter f = pf.get(collectionReference.collectionName);
+                if (f.righthand == null) {
+                    f.righthand = Boolean.TRUE;
+                }
+                return f;
             } catch (IOException e) {
                 try {
-                    return objectMapper.readValue(serializedFilter, Filter.class);
+                    Filter f = objectMapper.readValue(serializedFilter, Filter.class);
+                    if (f.righthand == null) {
+                        f.righthand = Boolean.TRUE;
+                    }
+                    return f;
                 } catch (JsonProcessingException ex) {
                 }
                 throw new InvalidParameterException(INVALID_FILTER + ": '" + serializedFilter + "'");
@@ -265,8 +274,11 @@ public static Filter getFilter(CollectionReference collectionReference, String s
     }
 
     public static Filter getFilter(CollectionReference collectionReference,
-                                   List<String> filters, List<String> q, String dateFormat) throws ArlasException {
-        return getFilter(collectionReference, filters, q, dateFormat, null, null);
+                                   List<String> filters, List<String> q, String dateFormat, Boolean righthand) throws ArlasException {
+        if (righthand == null) {
+            righthand = Boolean.TRUE;
+        }
+        return getFilter(collectionReference, filters, q, dateFormat, righthand, null, null);
     }
 
     /**
@@ -281,7 +293,7 @@ public static Filter getFilter(CollectionReference collectionReference,
      * @throws ArlasException
      */
     public static Filter getFilter(CollectionReference collectionReference,
-                                   List<String> filters, List<String> q, String dateFormat,
+                                   List<String> filters, List<String> q, String dateFormat, Boolean righthand,
                                    BoundingBox tileBbox, Expression pwithinBbox) throws ArlasException {
         Filter filter = new Filter();
         filter.f = new ArrayList<>();
@@ -297,7 +309,7 @@ public static Filter getFilter(CollectionReference collectionReference,
                     String value = String.join(":", Arrays.copyOfRange(operands, 2, operands.length));
 
                     if (GEO_OP.contains(OperatorEnum.valueOf(operands[1]))) {
-                        value = getValidGeometry(value);
+                        value = getValidGeometry(value, righthand);
                         if(tileBbox != null && collectionReference != null) {
                             boolean isPwithin = isPwithin(collectionReference, operands[0], operands[1]);
                             if (isPwithin){
@@ -323,6 +335,7 @@ public static Filter getFilter(CollectionReference collectionReference,
         }
         filter.q = getStringMultiFilter(q);
         filter.dateformat = dateFormat;
+        filter.righthand = righthand;
         return filter;
     }
 
@@ -354,28 +367,32 @@ public static Filter getFilterWithValidGeos(CollectionReference collectionRefere
         Filter newFilter = new Filter();
         newFilter.q = filter.q;
         newFilter.dateformat = filter.dateformat;
+        newFilter.righthand = filter.righthand;
         if (filter.f != null) {
             newFilter.f = new ArrayList<>();
             for (MultiValueFilter<Expression> orFiltersList : filter.f) {
                 MultiValueFilter<Expression> newOrFiltersList = new MultiValueFilter<>();
                 for (Expression orCond : orFiltersList) {
-                    newOrFiltersList.add(getValidGeoFilter(collectionReference, orCond));
+                    newOrFiltersList.add(getValidGeoFilter(collectionReference, orCond, newFilter.righthand ));
                 }
                 newFilter.f.add(newOrFiltersList);
             }
         }
+        if (newFilter.righthand == null) {
+            newFilter.righthand = Boolean.TRUE;
+        }
         return newFilter;
     }
 
-    public static Expression getValidGeoFilter(CollectionReference collectionReference, Expression expression) throws ArlasException {
+    public static Expression getValidGeoFilter(CollectionReference collectionReference, Expression expression, Boolean righthand) throws ArlasException {
         intersectsToWithin(collectionReference, expression);
         if (GEO_OP.contains(expression.op)) {
-            expression.value = getValidGeometry(expression.value);
+            expression.value = getValidGeometry(expression.value, righthand);
         }
         return expression;
     }
 
-    public static String getValidGeometry(String geo) throws ArlasException{
+    public static String getValidGeometry(String geo, Boolean righthand) throws ArlasException {
         if (CheckParams.isBboxMatch(geo)) {
             CheckParams.checkBbox(geo);
             return geo;
@@ -387,37 +404,22 @@ public static String getValidGeometry(String geo) throws ArlasException{
                 for (int i = 0; i < wkt.getNumGeometries(); i++) {
                     Polygon subWkt = (Polygon) wkt.getGeometryN(i);
                     if (Orientation.isCCW(subWkt.getCoordinates())) {
-                        // By convention the passed queryGeometry must be interpreted as CW.
-                        // If the orientation is CCW, we try to build the WKT that goes the other side of the planet.
-                        // If the topology of the resulted geometry is not valid, an exception is thrown
-                        Polygon tmpGeometry = (Polygon) subWkt.copy();
-                        Envelope tmpEnvelope = tmpGeometry.getEnvelopeInternal();
-                        // east is the minX and west is the maxX
-                        double east = tmpEnvelope.getMinX();
-                        double west = tmpEnvelope.getMaxX();
-                        if (west > east && ((east < -180 && west >= -180) || (west > 180 && east <= 180))) {
-                            // It means west > 180 or east < -180
-                            if (west >= 180) {
-                                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, false, 180);
-                            } else if (east <= -180) {
-                                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, true, -180);
-                            }
+                        if (righthand != Boolean.TRUE) {
+                            polygonList.addAll(getClockwisePolygons(subWkt));
                         } else {
-                            if (west >= 0) {
-                                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, false, east);
-                            } else {
-                                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, true, west);
-                            }
-                        }
-                        IsValidOp validOp = new IsValidOp(tmpGeometry);
-                        TopologyValidationError err = validOp.getValidationError();
-                        if (err != null) {
-                            throw new InvalidParameterException("A Polygon of the given WKT is right oriented. Unable to reverse the orientation of the polygon : " + err);
+                            polygonList.addAll(GeoUtil.splitPolygon(subWkt)._1().stream().map(p -> (Polygon)GeoUtil.toCounterClockwise(p)).toList());
                         }
-                        polygonList.addAll(GeoUtil.splitPolygon((Polygon) GeoUtil.readWKT(tmpGeometry.toString()))._1());
+
                     } else {
-                        polygonList.addAll(GeoUtil.splitPolygon(subWkt)._1());
+                        // the wkt is CW
+                        if (righthand == Boolean.TRUE) {
+                            polygonList.addAll(getClockwisePolygons(subWkt).stream().map(p -> (Polygon)GeoUtil.toCounterClockwise(p)).toList());
+                        } else {
+                            polygonList.addAll(GeoUtil.splitPolygon(subWkt)._1());
+                        }
                     }
+
+
                 }
                 if (polygonList.size() == 1) {
                     return polygonList.get(0).toString();
@@ -430,6 +432,38 @@ public static String getValidGeometry(String geo) throws ArlasException{
         }
     }
 
+    /** returns a list of CW WKT Polygons were longitudes are between -180 and 180*/
+    private static List<Polygon> getClockwisePolygons(Polygon wkt) throws ArlasException {
+        // the passed queryGeometry must be interpreted as CCW (righthand = true).
+        // If the orientation is CW, we try to build the WKT that goes the other side of the planet.
+        // If the topology of the resulted geometry is not valid, an exception is thrown
+        Polygon tmpGeometry = (Polygon) wkt.copy();
+        Envelope tmpEnvelope = tmpGeometry.getEnvelopeInternal();
+        // east is the minX and west is the maxX
+        double east = tmpEnvelope.getMinX();
+        double west = tmpEnvelope.getMaxX();
+        if (west > east && ((east < -180 && west >= -180) || (west > 180 && east <= 180))) {
+            // It means west > 180 or east < -180
+            if (west >= 180) {
+                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, false, 180);
+            } else if (east <= -180) {
+                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, true, -180);
+            }
+        } else {
+            if (west >= 0) {
+                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, false, east);
+            } else {
+                GeoUtil.translateLongitudesWithCondition(tmpGeometry, 360, true, west);
+            }
+        }
+        IsValidOp validOp = new IsValidOp(tmpGeometry);
+        TopologyValidationError err = validOp.getValidationError();
+        if (err != null) {
+            throw new InvalidParameterException("A Polygon of the given WKT is right oriented. Unable to reverse the orientation of the polygon : " + err);
+        }
+        return GeoUtil.splitPolygon((Polygon) GeoUtil.readWKT(tmpGeometry.toString()))._1();
+    }
+
     public static List<String> toSemiColonsSeparatedStringList(List<MultiValueFilter<String>> multiValueFilters) {
         return multiValueFilters == null ?  null :
                 multiValueFilters.stream().map(multiValueFilter -> String.join(";", multiValueFilter)).collect(Collectors.toList());
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java
index e05919c34..8bde4b1d2 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java
@@ -95,6 +95,12 @@ public Response aggregate(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -124,7 +130,7 @@ public Response aggregate(
             throw new NotFoundException(collection);
         }
         AggregationsRequest aggregationsRequest = new AggregationsRequest();
-        aggregationsRequest.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat);
+        aggregationsRequest.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand);
         aggregationsRequest.aggregations = ParamsParser.getAggregations(collectionReference, agg);
         exploreService.setValidGeoFilters(collectionReference, aggregationsRequest);
 
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
index 2f842f936..eca3428bd 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
@@ -108,6 +108,10 @@ public Response geoaggregate(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
 
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
@@ -140,7 +144,7 @@ public Response geoaggregate(
         }
 
         return geoaggregate(collectionReference,
-                ParamsParser.getFilter(collectionReference, f, q, dateformat),
+                ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand),
                 partitionFilter, columnFilter, flat, agg, maxagecache, Optional.empty(), false);
 
     }
@@ -188,6 +192,11 @@ public Response shapeaggregate(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    value = Documentation.FILTER_RIGHT_HAND,
+                    defaultValue = "true")
+            @QueryParam(value = "righthand") Boolean righthand,
+
 
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
@@ -216,7 +225,7 @@ public Response shapeaggregate(
         }
 
         return geoaggregate(collectionReference,
-                ParamsParser.getFilter(collectionReference, f, q, dateformat),
+                ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand),
                 partitionFilter, columnFilter, true, agg, maxagecache, Optional.empty(), true);
 
     }
@@ -267,6 +276,11 @@ public Response geohashgeoaggregate(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -314,7 +328,7 @@ public Response geohashgeoaggregate(
                             + String.format(Locale.ROOT, "%.8f", b.getEast() - GEOHASH_EPSILON) + ","
                             + String.format(Locale.ROOT,"%.8f", b.getNorth() - GEOHASH_EPSILON));
             MixedRequest request = getGeoaggregateRequest(collectionReference,
-                    ParamsParser.getFilter(collectionReference, f, q, dateformat, b, pwithinBbox)
+                    ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand, b, pwithinBbox)
                     , partitionFilter, columnFilter, agg);
             aggType = ((AggregationsRequest) request.basicRequest).aggregations.get(0).type;
 
@@ -392,6 +406,11 @@ public Response geotilegeoaggregate(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -436,7 +455,7 @@ public Response geotilegeoaggregate(
                             + String.format(Locale.ROOT, "%.8f", b.getEast() - GEOHASH_EPSILON) + ","
                             + String.format(Locale.ROOT, "%.8f", b.getNorth() - GEOHASH_EPSILON));
             MixedRequest request = getGeoaggregateRequest(collectionReference,
-                    ParamsParser.getFilter(collectionReference, f, q, dateformat, b, pwithinBbox)
+                    ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand, b, pwithinBbox)
                     , partitionFilter, columnFilter, agg);
             aggType = ((AggregationsRequest) request.basicRequest).aggregations.get(0).type;
 
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java
index 5418d59e1..630713be8 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java
@@ -93,6 +93,11 @@ public Response compute(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -119,7 +124,7 @@ public Response compute(
             throw new NotFoundException(collection);
         }
         ComputationRequest computationRequest = new ComputationRequest();
-        computationRequest.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat);
+        computationRequest.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand);
         computationRequest.field = field;
         computationRequest.metric = ComputationEnum.fromValue(metric);
         exploreService.setValidGeoFilters(collectionReference, computationRequest);
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java
index 0ae210ee6..d088c380f 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java
@@ -80,6 +80,11 @@ public Response count(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionfilter,
 
@@ -106,7 +111,7 @@ public Response count(
         }
 
         Count count = new Count();
-        count.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat);
+        count.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand);
         exploreService.setValidGeoFilters(collectionReference, count);
 
         ColumnFilterUtil.assertRequestAllowed(columnFilter, collectionReference, count);
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java
index 91c9fcff0..fc71ab96c 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java
@@ -86,6 +86,11 @@ public Response geosearch(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -172,7 +177,7 @@ public Response geosearch(
         }
 
         return geosearch(collectionReference,
-                ParamsParser.getFilter(collectionReference, f, q, dateformat), partitionFilter, columnFilter,
+                ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand), partitionFilter, columnFilter,
                 flat, include, exclude, size, from, sort, after, before, maxagecache, returned_geometries, false);
     }
 
@@ -208,6 +213,11 @@ public Response shapesearch(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -289,7 +299,7 @@ public Response shapesearch(
         }
 
         return geosearch(collectionReference,
-                ParamsParser.getFilter(collectionReference, f, q, dateformat), partitionFilter, columnFilter,
+                ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand), partitionFilter, columnFilter,
                 true, include, exclude, size, from, sort, after, before, maxagecache, returned_geometries, true);
     }
 
@@ -338,6 +348,11 @@ public Response tiledgeosearch(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -429,7 +444,7 @@ public Response tiledgeosearch(
                 bbox.getWest() + "," + bbox.getSouth() + "," + bbox.getEast() + "," + bbox.getNorth());
 
         return geosearch(collectionReference,
-                ParamsParser.getFilter(collectionReference, f, q, dateformat, bbox, pwithinBbox),
+                ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand, bbox, pwithinBbox),
                 partitionFilter, columnFilter, flat, include, exclude, size, from, sort, after, before, maxagecache, returned_geometries, false);
     }
 
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java
index d09baa2b4..55979a234 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java
@@ -87,6 +87,11 @@ public Response search(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    defaultValue = "true",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -174,7 +179,7 @@ public Response search(
         CheckParams.checkReturnedGeometries(collectionReference, include, exclude, returned_geometries);
 
         Search search = new Search();
-        search.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat);
+        search.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand);
         search.page = ParamsParser.getPage(size, from, sort, after, before);
         search.projection = ParamsParser.getProjection(include, exclude);
         search.returned_geometries = returned_geometries;
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java
index 8a9090c27..f5d9ece6a 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java
@@ -105,6 +105,10 @@ public Response tiledgeosearch(
                     value = Documentation.FILTER_DATE_FORMAT)
             @QueryParam(value = "dateformat") String dateformat,
 
+            @ApiParam(name = "righthand",
+                    value = Documentation.FILTER_RIGHT_HAND)
+            @QueryParam(value = "righthand") Boolean righthand,
+
             @ApiParam(hidden = true)
             @HeaderParam(value = "partition-filter") String partitionFilter,
 
@@ -180,7 +184,7 @@ public Response tiledgeosearch(
 
         BoundingBox bbox = GeoTileUtil.getBoundingBox(new Tile(x, y, z));
         Search search = new Search();
-        search.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat);
+        search.filter = ParamsParser.getFilter(collectionReference, f, q, dateformat, righthand);
         search.page = ParamsParser.getPage(size, from, sort, after,before);
         search.projection = ParamsParser.getProjection(collectionReference.params.rasterTileURL.idPath+","+collectionReference.params.geometryPath, null);
 
diff --git a/docs/arlas-api-exploration.md b/docs/arlas-api-exploration.md
index 378319ecf..9783db9b9 100644
--- a/docs/arlas-api-exploration.md
+++ b/docs/arlas-api-exploration.md
@@ -172,24 +172,26 @@ For **_geoaggregate** service, the first (main) aggregation must be geohash,or g
 
 The `filter` url part allows the following parameters to be specified:
 
-| Parameter         | Default value | Values                         | Description                              | Multiple |
-| ----------------- | ------------- | ------------------------------ | ---------------------------------------- | -------- |
-| **f**             | None          | `{fieldName}{operator}{value}` | A triplet for filtering the result. Multiple filter can be provided. The order does not matter. A triplet is composed of a field name, a comparison operator and a value. The **AND** operator is applied between filters. For the **`:eq:`** and **`:range:`** filters, values can be comma separated (field`:eq:`v1,v2) which stands for an **OR**. For the **`:ne:`**  filter, values can be comma separated (field`:ne:`v1,v2) which stands for an **AND** | true     |
-| **q**             | None          | `{text}` or `{fieldname}:{text}` | A full text search. Optionally, it's possible to search the text on a specific field                       | false    |
-| **dateformat**    | None          | [Joda time pattern](https://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html)                       | A date format pattern that respects the Joda-time syntax | false    |
-
-!!! info "Important"
+| Parameter      | Default value | Values                                                                                               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Multiple |
+|----------------|---------------|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------- |
+| **f**          | None          | `{fieldName}{operator}{value}`                                                                       | A triplet for filtering the result. Multiple filter can be provided. The order does not matter. A triplet is composed of a field name, a comparison operator and a value. The **AND** operator is applied between filters. For the **`:eq:`** and **`:range:`** filters, values can be comma separated (field`:eq:`v1,v2) which stands for an **OR**. For the **`:ne:`**  filter, values can be comma separated (field`:ne:`v1,v2) which stands for an **AND** | true     |
+| **q**          | None          | `{text}` or `{fieldname}:{text}`                                                                     | A full text search. Optionally, it's possible to search the text on a specific field                                                                                                                                                                                                                                                                                                                                                                           | false    |
+| **dateformat** | None          | [Joda time pattern](https://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html) | A date format pattern that respects the Joda-time syntax                                                                                                                                                                                                                                                                                                                                                                                                       | false    |
+| **righthand**  | false         | Boolean                                                                                              | A parameter that indicates how to consider the given WKT orientation. (Check **Important 2**)                                                                                                                                                                                                                                                                                                                                                                  | false    |
+
+!!! info "Important 1"
     The given BBOX must respect the following rules :
     - `west` and `east` must be between -180 and 180 inclusive
     - `west` must be different from `east` (west=east is invalid)
     - You can specify `west`>`east` which means the bbox crosses the dateline
     - `south` and `north` must be between -90 and 90 inclusive and `south`<`north`
 
-!!! info "Important"
-    For parameters that accept WKT, in case of a **Polygon** or **MultiPolygon**, the orientation should be clock-wise (left-hand rule). Otherwise, ARLAS-server will attempt to parse it as the "Complementary" Polygon on the other facet of the planet.
-    For instance the polygon `POLYGON  ((-170 -10, -175 10, 175 10, 170 -10, -170 -10))` is counter clock-wise. If it's passed as a parameter, ARLAS-server will interpret it as the Polygon that crosses the dateline `POLYGON  ((-170 -10, -175 10, 185 10, 180 -10, -170 -10))` which is clock-wise.
+!!! info "Important 2"
+    For parameters that accept WKT, in case of a **Polygon** or **MultiPolygon**, ARLAS-server will treat the orientation of the WKT according to the value of **righthand** parameter.
+    If `righthand = true`, the passed WKT should be counter clock-wise; otherwise, ARLAS-server will attempt to parse it as the "Complementary" Polygon on the other facet of the planet.
+    Inversely, if `righthand = false`, the passed WKT should be clock-wise; otherwise, ARLAS-server will attempt to parse it as the "Complementary" Polygon on the other facet of the planet.
 
-!!! info "Important"
+!!! info "Important 3"
     Coordinates of the given **WKT** must be contained in the Envelope -360, 360, -180, 180
 
 #### Filter parameters algebra
diff --git a/stac-api/src/main/java/io/arlas/server/stac/api/StacRESTService.java b/stac-api/src/main/java/io/arlas/server/stac/api/StacRESTService.java
index 4eaa348a6..c51611850 100644
--- a/stac-api/src/main/java/io/arlas/server/stac/api/StacRESTService.java
+++ b/stac-api/src/main/java/io/arlas/server/stac/api/StacRESTService.java
@@ -21,6 +21,8 @@
 
 import com.ethlo.time.ITU;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
 import io.arlas.server.core.app.STACConfiguration;
 import io.arlas.server.core.exceptions.ArlasException;
 import io.arlas.server.core.exceptions.InvalidParameterException;
@@ -45,6 +47,7 @@
 import org.geojson.Polygon;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.io.ParseException;
+import org.locationtech.jts.io.geojson.GeoJsonReader;
 
 import javax.ws.rs.Path;
 import javax.ws.rs.core.MediaType;
@@ -70,6 +73,9 @@ public abstract class StacRESTService {
     protected CollectionReferenceService collectionReferenceService;
     protected STACConfiguration configuration;
     protected ResponseCacheManager responseCacheManager;
+    private static final ObjectMapper objectMapper = new ObjectMapper();
+    public static final GeoJsonReader reader = new GeoJsonReader();
+    public static final ObjectWriter writer = objectMapper.writer();
 
     public StacRESTService(STACConfiguration configuration,
                            int arlasRestCacheTimeout,
@@ -220,7 +226,7 @@ protected StacFeatureCollection getStacFeatureCollection(CollectionReference col
                                                              String method,
                                                              boolean isOgc) throws ArlasException {
         Search search = new Search();
-        search.filter = ParamsParser.getFilter(collectionReference, filter, null, null);
+        search.filter = ParamsParser.getFilter(collectionReference, filter, null, null, true);
         if (body != null) {
             search.page = ParamsParser.getPage(new IntParam(body.getLimit().toString()),
                     new IntParam(body.getFrom().toString()),
@@ -343,7 +349,8 @@ protected String getDateFilter(String datetime, CollectionReference collectionRe
     protected String getGeoFilter(GeoJsonObject geojson, CollectionReference collectionReference) throws ArlasException {
         if (geojson != null) {
             try {
-                Geometry geometry = GeoUtil.toClockwise(geojson);
+                /** righthand parameter is forced for STAC; therefore, passed righthand WKTs will be used correctly; **/
+                Geometry geometry = reader.read(writer.writeValueAsString(geojson));
                 return StringUtil.concat(collectionReference.params.centroidPath, ":", OperatorEnum.intersects.name(), ":",
                         geometry.toText());
             } catch (ParseException | JsonProcessingException e) {


Test patch:
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithCollection.java b/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithCollection.java
index f38ef834f..a9c9ec81b 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithCollection.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithCollection.java
@@ -38,6 +38,7 @@ public abstract class AbstractTestWithCollection extends AbstractTestContext {
 
     static {
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @BeforeClass
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithDataSet.java b/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithDataSet.java
index 7b4110ae3..89a7be5e7 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithDataSet.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/AbstractTestWithDataSet.java
@@ -34,6 +34,7 @@ public abstract class AbstractTestWithDataSet extends AbstractTestContext {
 
     static {
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @BeforeClass
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java
index df0e00a9c..9da0b0f14 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractAggregatedTest.java
@@ -43,6 +43,7 @@ public abstract class AbstractAggregatedTest extends AbstractFormattedTest {
     public void setUpAggregationRequest() {
         aggregationRequest = new AggregationsRequest();
         aggregationRequest.filter = new Filter();
+        aggregationRequest.filter.righthand = false;
         aggregationRequest.aggregations = new ArrayList<>();
         aggregationModel = new Aggregation();
         aggregationRequest.aggregations.add(aggregationModel);
@@ -1051,6 +1052,7 @@ public void testInvalidAggregateParameters() throws Exception {
         handleInvalidParameters(get("foobar:params.job"));
         InvalidAggregationsRequest invalidAggregationRequest = new InvalidAggregationsRequest();
         invalidAggregationRequest.filter = new Filter();
+        invalidAggregationRequest.filter.righthand = false;
         invalidAggregationRequest.invalidAggregations = new ArrayList<>();
         invalidAggregationRequest.invalidAggregations.add(new InvalidAggregation());
         invalidAggregationRequest.invalidAggregations.get(0).type = "foobar";
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractComputationTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractComputationTest.java
index 42569d05e..acd21860a 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractComputationTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractComputationTest.java
@@ -37,6 +37,7 @@ public abstract class AbstractComputationTest extends AbstractFilteredTest {
     public void setUpComputationRequest() {
         computationRequest = new ComputationRequest();
         computationRequest.filter = new Filter();
+        computationRequest.filter.righthand =false;
         request = computationRequest;
     }
 
@@ -78,6 +79,7 @@ public void testComputeRequest() throws Exception {
 
         /** SUM **/
         computationRequest.filter =  new Filter();
+        computationRequest.filter.righthand = false;
         computationRequest.metric = ComputationEnum.SUM;
         handleComputationRequest(post(computationRequest), 271, 374900);
         handleComputationRequest(get(computationRequest.field, computationRequest.metric.value()), 271, 374900);
@@ -101,6 +103,7 @@ public void testComputeRequest() throws Exception {
         handleComputationEmptyResponse(post(computationRequest));
         handleComputationEmptyResponse(get(computationRequest.field, computationRequest.metric.value(), "f", computationRequest.filter.f.get(0).get(0).toString()));
         computationRequest.filter =  new Filter();
+        computationRequest.filter.righthand = false;
 
         /** EMPTY RESPONSE : NON-EXISTING FIELD**/
         computationRequest.field = "params.foo";
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractFilteredTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractFilteredTest.java
index e4a5f7d79..494192dbc 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractFilteredTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractFilteredTest.java
@@ -49,6 +49,7 @@ public abstract class AbstractFilteredTest extends AbstractTestWithCollection {
     public void setUpFilter() {
         request = new Request();
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     //----------------------------------------------------------------
@@ -245,6 +246,18 @@ public void testPwithinFilter() throws Exception {
         handleMatchingGeometryFilter(post(request, "column-filter", "fullname"), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString(), "column-filter", "fullname"), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        /** counter clock-wise WKT with right_hand true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "POLYGON((-5 -5, 5 -5, 5 5, -5 5, -5 -5))")));
+        request.filter.righthand = true;
+        handleMatchingGeometryFilter(post(request, "column-filter", "fullname"), 1, everyItem(equalTo("0,0")));
+        ValidatableResponse getWithRightHand = this.getExtraParamsRequest().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .header("column-filter", "fullname")
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(getWithRightHand, 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        request.filter.righthand = false;
 
         /** west > east bbox*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "5,-5,-5,5")));
@@ -257,6 +270,17 @@ public void testPwithinFilter() throws Exception {
         handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        /** clock-wise WKT with right_hand true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "POLYGON((-5 -5, -5 5, 5 5, 5 -5, -5 -5))")));
+        request.filter.righthand = true;
+        handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
+        getWithRightHand = this.getExtraParamsRequest().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(getWithRightHand, 34, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** west > east bbox*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "180,0,-165,5")));
@@ -269,6 +293,18 @@ public void testPwithinFilter() throws Exception {
         handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,-170")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 1, everyItem(equalTo("0,-170")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,-170")));
+        /** clock-wise WKT with righthand =true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "POLYGON((180 5, 180 -5, -165 -5, -165 5, 180 5))")));
+        request.filter.righthand = true;
+        handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,-170")));
+        getWithRightHand = this.getExtraParamsRequest().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(getWithRightHand, 1, everyItem(equalTo("0,-170")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,-170")));
+        request.filter.righthand = false;
+
 
         /** west < east bbox*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "175,85,180,90")));
@@ -281,6 +317,17 @@ public void testPwithinFilter() throws Exception {
         handleNotMatchingPwithinFilter(post(request));
         handleNotMatchingPwithinFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingPwithinFilter(header(request.filter));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "POLYGON((175 85, 180 85, 180 90, 175 90, 175 85))")));
+        request.filter.righthand = true;
+        handleNotMatchingPwithinFilter(post(request));
+        getWithRightHand = this.getExtraParamsRequest().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingPwithinFilter(getWithRightHand);
+        handleNotMatchingPwithinFilter(header(request.filter));
+        request.filter.righthand = false;
 
         /** west < east bbox*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "-5,-50,180,50")),
@@ -301,6 +348,24 @@ public void testPwithinFilter() throws Exception {
                         new ImmutablePair<>("f", request.filter.f.get(1).get(0).toString()))),
                 10, everyItem(endsWith("0")));
         handleMatchingGeometryFilter(header(request.filter), 10, everyItem(endsWith("0")));
+        /** counter clock-wise WKT with righthand=true AND west < east bbox*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "POLYGON((-5 -50, 180 -50, 180 50, -5 50, -5 -50))")),
+                new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "-180,-50,5,50")));
+        request.filter.righthand = true;
+        handleMatchingGeometryFilter(post(request), 10, everyItem(endsWith("0")));
+        getWithRightHand = this.getExtraParamsRequest().param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(
+                getWithRightHand,
+                10, everyItem(endsWith("0")));
+        handleMatchingGeometryFilter(header(request.filter), 10, everyItem(endsWith("0")));
+        request.filter.righthand = false;
+
+
 
         request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.centroid", OperatorEnum.within, "-5,-5,5,5"),
                 new Expression("geo_params.centroid", OperatorEnum.within, "5,5,15,15"))));
@@ -308,6 +373,8 @@ public void testPwithinFilter() throws Exception {
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString()), 2, everyItem(isOneOf("0,0", "10,10")));
         handleMatchingGeometryFilter(header(request.filter), 2, everyItem(isOneOf("0,0", "10,10")));
 
+        /** --- NOT WITHIN --- **/
+
         /** west < east bbox*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "-170,-85,175,85")));
         handleMatchingGeometryFilter(post(request), 17, everyItem(endsWith("170")));
@@ -319,12 +386,34 @@ public void testPwithinFilter() throws Exception {
         handleMatchingGeometryFilter(post(request), 17, everyItem(endsWith("170")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 17, everyItem(endsWith("170")));
         handleMatchingGeometryFilter(header(request.filter), 17, everyItem(endsWith("170")));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-170 -85, 175 -85, 175 85, -170 85, -170 -85))")));
+        request.filter.righthand = true;
+        handleMatchingGeometryFilter(post(request), 17, everyItem(endsWith("170")));
+        getWithRightHand = this.getExtraParamsRequest().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(getWithRightHand, 17, everyItem(endsWith("170")));
+        handleMatchingGeometryFilter(header(request.filter), 17, everyItem(endsWith("170")));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-2 -2, 2 -2, 2 2,-2 2, -2 -2))")));
         handleMatchingGeometryFilter(post(request), 561, everyItem(notNullValue()));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 561, everyItem(notNullValue()));
         handleMatchingGeometryFilter(header(request.filter), 561, everyItem(notNullValue()));
+        /** clock-wise WKT with righthand =true*/
+        request.filter.righthand = true;
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-2 -2, -2 2, 2 2, 2 -2, -2 -2))")));
+        handleMatchingGeometryFilter(post(request), 561, everyItem(notNullValue()));
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(getWithRightHand, 561, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(header(request.filter), 561, everyItem(notNullValue()));
+        request.filter.righthand = false;
 
         /** west < east bbox*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "-175,-85,175,85")));
@@ -337,18 +426,52 @@ public void testPwithinFilter() throws Exception {
         handleNotMatchingNotPwithinFilter(post(request));
         handleNotMatchingNotPwithinFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingNotPwithinFilter(header(request.filter));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-175 -85, 175 -85, 175 85, -175 85, -175 -85))")));
+        request.filter.righthand = true;
+        handleNotMatchingNotPwithinFilter(post(request));
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingNotPwithinFilter(getWithRightHand);
+        handleNotMatchingNotPwithinFilter(header(request.filter));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((160 -2, 160 2, -160 2, -160 -2, 160 -2))")));
         handleMatchingGeometryFilter(post(request), 592, everyItem(notNullValue()));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 592, notNullValue());
         handleMatchingGeometryFilter(header(request.filter), 592, everyItem(notNullValue()));
+        /** clock-wise WKT with righthand=true */
+        request.filter.righthand = true;
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((160 -2,  -160 -2, -160 2, 160 2, 160 -2))")));
+        handleMatchingGeometryFilter(post(request), 592, everyItem(notNullValue()));
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(getWithRightHand, 592, notNullValue());
+        handleMatchingGeometryFilter(header(request.filter), 592, everyItem(notNullValue()));
+        request.filter.righthand = false;
+
 
-        /** multi polygon **/
+        /** clock-wise multi polygon **/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "MULTIPOLYGON (((-180 -90, -180 90, -5 90, -5 -90, -180 -90)), ((5 -90, 5 90, 180 90, 180 -90, 5 -90)))")));
         handleMatchingGeometryFilter(post(request), 17, everyItem(endsWith("0")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 17, everyItem(endsWith("0")));
         handleMatchingGeometryFilter(header(request.filter), 17, everyItem(endsWith("0")));
+        /** counter clock-wise multi polygon with righthand=true**/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "MULTIPOLYGON (((-180 -90, -5 -90, -5 90, -180 90, -180 -90)), ((5 -90, 180 -90, 180 90, 5 90, 5 -90)))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 17, everyItem(endsWith("0")));
+        handleMatchingGeometryFilter(getWithRightHand, 17, everyItem(endsWith("0")));
+        handleMatchingGeometryFilter(header(request.filter), 17, everyItem(endsWith("0")));
+        request.filter.righthand = false;
 
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "-180,-90,-5,90")),
                 new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "5,-90,180,90")));
@@ -359,6 +482,7 @@ public void testPwithinFilter() throws Exception {
                 17, everyItem(endsWith("0")));
         handleMatchingGeometryFilter(header(request.filter), 17, everyItem(endsWith("0")));
 
+        /** clock wise with BBOX*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-180 -90, -180 90, -5 90, -5 -90, -180 -90))")),
                 new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "5,-90,180,90")));
         handleMatchingGeometryFilter(post(request), 17, everyItem(endsWith("0")));
@@ -367,6 +491,21 @@ public void testPwithinFilter() throws Exception {
                         new ImmutablePair<>("f", request.filter.f.get(1).get(0).toString()))),
                 17, everyItem(endsWith("0")));
         handleMatchingGeometryFilter(header(request.filter), 17, everyItem(endsWith("0")));
+        /** counter clock wise with righthand=true && BBOX*/
+        request.filter.righthand = true;
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-180 -90, -5 -90, -5 90, -180 90, -180 -90))")),
+                new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "5,-90,180,90")));
+        handleMatchingGeometryFilter(post(request), 17, everyItem(endsWith("0")));
+        getWithRightHand = this.getExtraParamsRequest().param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(getWithRightHand,
+                17, everyItem(endsWith("0")));
+        handleMatchingGeometryFilter(header(request.filter), 17, everyItem(endsWith("0")));
+        request.filter.righthand = false;
 
         request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.centroid", OperatorEnum.notwithin, "-180,-90,-5,90"),
                 new Expression("geo_params.centroid", OperatorEnum.notwithin, "5,-90,180,90"))));
@@ -374,11 +513,25 @@ public void testPwithinFilter() throws Exception {
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString()), 595, notNullValue());
         handleMatchingGeometryFilter(header(request.filter), 595, everyItem(notNullValue()));
 
+        /** clock wise OR BBOX*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-180 -90, -180 90, -5 90, -5 -90, -180 -90))"),
                 new Expression("geo_params.centroid", OperatorEnum.notwithin, "5,-90,180,90"))));
         handleMatchingGeometryFilter(post(request), 595, everyItem(notNullValue()));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString()), 595, notNullValue());
         handleMatchingGeometryFilter(header(request.filter), 595, everyItem(notNullValue()));
+        /** counter clock wise (with righthand=true) OR BBOX*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.centroid", OperatorEnum.notwithin, "POLYGON((-180 -90, -5 -90, -5 90, -180 90, -180 -90))"),
+                new Expression("geo_params.centroid", OperatorEnum.notwithin, "5,-90,180,90"))));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 595, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(getWithRightHand, 595, notNullValue());
+        handleMatchingGeometryFilter(header(request.filter), 595, everyItem(notNullValue()));
+        request.filter.righthand = false;
 
         //TODO support correct 10,-10,-10,10 bounding box
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "-11,-11,11,11")),
@@ -438,12 +591,35 @@ public void testGwithinFilter() throws Exception {
         handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-2 -2, 2 -2, 2 2, -2 2, -2 -2))")));
+        request.filter.righthand = true;
+        ValidatableResponse getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(getWithRightHand, 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        request.filter.righthand = false;
+
 
         /** clock-wise WKT*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-170 -2, -170 2, 170 2, 170 -2, -170 -2))")));
         handleMatchingGeometryFilter(post(request), 33, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 33, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 33, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.righthand = true;
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-170 -2, 170 -2, 170 2, -170 2, -170 -2))")));
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 33, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 33, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 33, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
 
         /** counter clock-wise WKT*/
@@ -451,57 +627,160 @@ public void testGwithinFilter() throws Exception {
         handleNotMatchingGwithinFilter(post(request));
         handleNotMatchingGwithinFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingGwithinFilter(header(request.filter));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.righthand = true;
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-170 -2, -170 2, 170 2, 170 -2, -170 -2))")));
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingGwithinFilter(post(request));
+        handleNotMatchingGwithinFilter(getWithRightHand);
+        handleNotMatchingGwithinFilter(header(request.filter));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-2 -2, 2 -2, 2 2,-2 2, -2 -2))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-2 -2, 2 -2, 2 2, -2 2, -2 -2))")));
         handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-2 -2, -2 2, 2 2, 2 -2, -2 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 34, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-330 -2, -330 -1, -290 -1, -290 2, -15 2, -15 1, -40 1, -40 -2, -330 -2))")));
         handleMatchingGeometryFilter(post(request), 24, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 24, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 24, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-330 -2, -40 -2, -40 1, -15 1, -15 2, -290 2, -290 -1, -330 -1, -330 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 24, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 24, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 24, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-330 -2, -40 -2, -40 1, -15 1, -15 2, -290 2, -290 -1, -330 -1, -330 -2))")));
         handleMatchingGeometryFilter(post(request), 10, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 10, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 10, everyItem(startsWith("0,")));
+        /** clock-wise WKT with righthand=true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-330 -2, -330 -1, -290 -1, -290 2, -15 2, -15 1, -40 1, -40 -2, -330 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 10, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 10, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 10, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((10 -2, 10 2, 350 2, 350 0, 300 0, 350 -2, 10 -2))")));
         handleMatchingGeometryFilter(post(request), 32, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 32, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 32, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((10 -2, 350 -2, 300 0, 350 0, 350 2, 10 2, 10 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 32, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 32, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 32, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((10 -2, 350 -2, 300 0, 350 0, 350 2, 10 2, 10 -2))")));
         handleMatchingGeometryFilter(post(request), 1, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 1, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(startsWith("0,")));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((10 -2, 10 2, 350 2, 350 0, 300 0, 350 -2, 10 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 1, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 1, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
-        /** clock-wise WKT with with a point longitude > 180*/
+
+        /** clock-wise WKT with a point longitude > 180*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-130 -2, -130 2, 220 2, 220 -2, -130 -2))")));
         handleMatchingGeometryFilter(post(request), 33, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 33, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 33, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with a point longitude > 180 with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-130 -2, 220 -2, 220 2, -130 2,  -130 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 33, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 33, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 33, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** counter clock-wise WKT with a point longitude > 180*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-130 -2, 220 -2, 220 2, -130 2,  -130 -2))")));
         handleNotMatchingGwithinFilter(post(request));
         handleNotMatchingGwithinFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingGwithinFilter(header(request.filter));
+        /** clock-wise WKT with a point longitude > 180 with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((-130 -2, -130 2, 220 2, 220 -2, -130 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingGwithinFilter(post(request));
+        handleNotMatchingGwithinFilter(getWithRightHand);
+        handleNotMatchingGwithinFilter(header(request.filter));
+        request.filter.righthand = false;
+
 
         /** clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1,1 2,2 2,2 1,1 1))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1, 1 2, 2 2, 2 1, 1 1))")));
         handleNotMatchingGwithinFilter(post(request));
         handleNotMatchingGwithinFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingGwithinFilter(header(request.filter));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1, 2 1, 2 2, 1 2, 1 1))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingGwithinFilter(post(request));
+        handleNotMatchingGwithinFilter(getWithRightHand);
+        handleNotMatchingGwithinFilter(header(request.filter));
+        request.filter.righthand = false;
 
-        /** west < east bbox along with a clock-wise WKT*/
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1,10 -20,-10 -20,-10 20,1 1))")),
+
+        /** west < east bbox along with a clock-wise WKT (AND OP)*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1, 10 -20, -10 -20, -10 20, 1 1))")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "-2,-2,2,2")));
         handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(
@@ -509,39 +788,122 @@ public void testGwithinFilter() throws Exception {
                         new ImmutablePair<>("f", request.filter.f.get(1).get(0).toString()))),
                 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        /** west < east bbox along with a counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1, -10 20, -10 -20, 10 -20, 1 1))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "-2,-2,2,2")));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(
+                getWithRightHand,
+                1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        request.filter.righthand = false;
 
-        /** west < east bbox along with a clock-wise WKT*/
+
+        /** west < east bbox along with a clock-wise WKT (OR OP)*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.within, "-2,-2,2,2"),
-                new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1,10 -20,-10 -20,-10 20,1 1))"))));
+                new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1, 10 -20, -10 -20, -10 20, 1 1))"))));
         handleMatchingGeometryFilter(post(request), 2, everyItem(isOneOf("-10,0", "0,0")));
         handleMatchingGeometryFilter(
                 get("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString()),
                 2, everyItem(isOneOf("-10,0", "0,0")));
         handleMatchingGeometryFilter(header(request.filter), 2, everyItem(isOneOf("-10,0", "0,0")));
+        /** west < east bbox along with a counter clock-wise WKT with righthand=true (OR OP)*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.within, "-2,-2,2,2"),
+                new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((1 1, -10 20, -10 -20, 10 -20, 1 1))"))));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 2, everyItem(isOneOf("-10,0", "0,0")));
+        handleMatchingGeometryFilter(
+                getWithRightHand,
+                2, everyItem(isOneOf("-10,0", "0,0")));
+        handleMatchingGeometryFilter(header(request.filter), 2, everyItem(isOneOf("-10,0", "0,0")));
+        request.filter.righthand = false;
+
+
+        /** --- NOTWITHIN --- */
 
         /** clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, 180 -70, 160 -70, 160 -90,-180 -90,-180 90,180 90))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, 180 -70, 160 -70, 160 -90, -180 -90, -180 90, 180 90))")));
         handleMatchingGeometryFilter(post(request), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
         handleMatchingGeometryFilter(header(request.filter), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, -180 90, -180 -90, 160 -90, 160 -70, 180 -70, 180 90))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        handleMatchingGeometryFilter(getWithRightHand, 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        handleMatchingGeometryFilter(header(request.filter), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((-2 -2, 2 -2, 2 2,-2 2, -2 -2))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((-2 -2, 2 -2, 2 2, -2 2, -2 -2))")));
         handleMatchingGeometryFilter(post(request), 561, everyItem(notNullValue()));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 561, everyItem(notNullValue()));
         handleMatchingGeometryFilter(header(request.filter), 561, everyItem(notNullValue()));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((-2 -2, -2 2, 2 2, 2 -2, -2 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 561, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(getWithRightHand, 561, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(header(request.filter), 561, everyItem(notNullValue()));
+        request.filter.righthand = false;
+
 
         /** clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90,180 -90,-180 -90,-180 90,180 90))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, 180 -90, -180 -90, -180 90, 180 90))")));
         handleNotMatchingNotGwithinFilter(post(request));
         handleNotMatchingNotGwithinFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingNotGwithinFilter(header(request.filter));
+        /** counter clock-wise WKT with righthand=true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, -180 90, -180 -90, 180 -90, 180 90))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingNotGwithinFilter(post(request));
+        handleNotMatchingNotGwithinFilter(getWithRightHand);
+        handleNotMatchingNotGwithinFilter(header(request.filter));
+        request.filter.righthand = false;
+
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((160 -2, 160 2, -160 2, -160 -2, 160 -2))")));
         handleMatchingGeometryFilter(post(request), 593, everyItem(notNullValue()));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 593, notNullValue());
         handleMatchingGeometryFilter(header(request.filter), 593, everyItem(notNullValue()));
+        /** clock-wise WKT with righthand =true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((160 -2, -160 -2, -160 2, 160 2, 160 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 593, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(getWithRightHand, 593, notNullValue());
+        handleMatchingGeometryFilter(header(request.filter), 593, everyItem(notNullValue()));
+        request.filter.righthand = false;
 
         /** clock-wise WKTs */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90,180 -70,100 -70,100 -80,-180 -80,-180 90,180 90))")),
@@ -552,26 +914,72 @@ public void testGwithinFilter() throws Exception {
                         new ImmutablePair<>("f", request.filter.f.get(1).get(0).toString()))),
                 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
         handleMatchingGeometryFilter(header(request.filter), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        /** counter clock-wise WKTs with righthand=true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, -180 90, -180 -80, 100 -80, 100 -70, 180 -70, 180 90))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, -180 90, -180 -90, 160 -90, 160 -70, 180 -70, 180 90))")));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        handleMatchingGeometryFilter(getWithRightHand,
+                4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        handleMatchingGeometryFilter(header(request.filter), 4, hasItems("-70,170", "-80,170", "-70,160", "-80,160"));
+        request.filter.righthand = false;
 
-        /** clock-wise WKTs */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90,180 -68,99 -68,99 -80,-180 -80,-180 90,180 90))"),
-                new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, 180 -70, 160 -70, 160 -90,-180 -90,-180 90,180 90))"))));
+        /** clock-wise WKTs (OR OP)*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, 180 -68, 99 -68, 99 -80, -180 -80, -180 90, 180 90))"),
+                new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, 180 -70, 160 -70, 160 -90, -180 -90, -180 90, 180 90))"))));
         handleMatchingGeometryFilter(post(request), 43, everyItem(notNullValue()));
         handleMatchingGeometryFilter(
                 get("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString()),
                 43, everyItem(notNullValue()));
         handleMatchingGeometryFilter(header(request.filter), 43, everyItem(notNullValue()));
+        /** counter clock-wise WKTs with righthand=true (OR OP)*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, -180 90, -180 -80, 99 -80, 99 -68, 180 -68, 180 90))"),
+                new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((180 90, -180 90, -180 -90, 160 -90, 160 -70, 180 -70, 180 90))"))));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 43, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(getWithRightHand,
+                43, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(header(request.filter), 43, everyItem(notNullValue()));
+        request.filter.righthand = false;
 
         /** west < east bbox along with a clock-wise WKT*/
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((12 12,12 -12,-12 -12,-12 12,12 12))")),
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((12 12, 12 -12, -12 -12, -12 12, 12 12))")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "-8,-8,8,8")));
         handleMatchingGeometryFilter(post(request), 8, hasItems("10,0", "10,-10", "10,10", "10,10", "10,0", "10,-10", "0,10", "0,-10"));
         handleMatchingGeometryFilter(
-                givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                this.getExtraParamsRequest()
+                        .param("f", request.filter.f.get(0).get(0).toString())
                         .param("f", request.filter.f.get(1).get(0).toString())
+                        .param("righthand", request.filter.righthand)
                         .when().get(getUrlPath("geodata"))
                         .then(), 8, hasItems("10,0", "10,-10", "10,10", "10,10", "10,0", "10,-10", "0,10", "0,-10"));
         handleMatchingGeometryFilter(header(request.filter), 8, hasItems("10,0", "10,-10", "10,10", "10,10", "10,0", "10,-10", "0,10", "0,-10"));
+        /** west < east bbox along with a counter clock-wise WKT with righthand*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((12 12, -12 12, -12 -12, 12 -12, 12 12))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "-8,-8,8,8")));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 8, hasItems("10,0", "10,-10", "10,10", "10,10", "10,0", "10,-10", "0,10", "0,-10"));
+        handleMatchingGeometryFilter(
+                getWithRightHand, 8, hasItems("10,0", "10,-10", "10,10", "10,10", "10,0", "10,-10", "0,10", "0,-10"));
+        handleMatchingGeometryFilter(header(request.filter), 8, hasItems("10,0", "10,-10", "10,10", "10,10", "10,0", "10,-10", "0,10", "0,-10"));
+        request.filter.righthand = false;
 
         /** west < east bbox*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "-12,-12,12,12")),
@@ -618,84 +1026,263 @@ public void testGintersectFilter() throws Exception {
         handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        /** counter clock-wise WKT*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-2 -2, 2 -2, 2 2, -2 2, -2 -2))")));
+        request.filter.righthand = true;
+        ValidatableResponse getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(getWithRightHand, 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        request.filter.righthand = false;
+
 
         /** clock-wise WKT*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-170 -2, -170 2, 170 2, 170 -2, -170 -2))")));
         handleMatchingGeometryFilter(post(request), 35, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 35, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 35, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-170 -2, 170 -2, 170 2, -170 2, -170 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 35, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 35, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 35, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** counter clock-wise WKT*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-170 -2, 170 -2, 170 2, -170 2, -170 -2))")));
         handleMatchingGeometryFilter(post(request), 2, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 2, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 2, everyItem(startsWith("0,")));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-170 -2, -170 2, 170 2, 170 -2, -170 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 2, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 2, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 2, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-2 -2, 2 -2, 2 2,-2 2, -2 -2))")));
         handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        /** clock-wise WKT with righthand=true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-2 -2, -2 2, 2 2, 2 -2, -2 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 34, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-330 -2, -330 -1, -290 -1, -290 2, -15 2, -15 1, -40 1, -40 -2, -330 -2))")));
         handleMatchingGeometryFilter(post(request), 31, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 31, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 31, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with righthand=true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-330 -2, -40 -2, -40 1, -15 1, -15 2, -290 2, -290 -1, -330 -1, -330 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 31, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 31, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 31, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-330 -2, -40 -2, -40 1, -15 1, -15 2, -290 2, -290 -1, -330 -1, -330 -2))")));
         handleMatchingGeometryFilter(post(request), 12, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 12, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 12, everyItem(startsWith("0,")));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-330 -2, -330 -1, -290 -1, -290 2, -15 2, -15 1, -40 1, -40 -2, -330 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 12, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 12, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 12, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 -2, 10 2, 350 2, 350 0, 300 0, 350 -2, 10 -2))")));
         handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 34, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with righthand=true */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 -2, 350 -2, 300 0, 350 0, 350 2, 10 2, 10 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 34, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 34, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 34, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 -2, 350 -2, 300 0, 350 0, 350 2, 10 2, 10 -2))")));
         handleMatchingGeometryFilter(post(request), 8, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 8, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 8, everyItem(startsWith("0,")));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 -2, 10 2, 350 2, 350 0, 300 0, 350 -2, 10 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 8, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 8, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 8, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** clock-wise WKT with with a point longitude > 180*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-130 -2, -130 2, 220 2, 220 -2, -130 -2))")));
         handleMatchingGeometryFilter(post(request), 35, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 35, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 35, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with a point longitude > 180 with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-130 -2, 220 -2, 220 2, -130 2,  -130 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 35, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 35, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 35, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** counter clock-wise WKT with a point longitude > 180*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-130 -2, 220 -2, 220 2, -130 2,  -130 -2))")));
         handleMatchingGeometryFilter(post(request), 2, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 2, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 2, everyItem(startsWith("0,")));
+        /** counter clock-wise WKT with a point longitude > 180*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-130 -2, -130 2, 220 2, 220 -2, -130 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 2, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 2, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 2, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
 
         /** clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((0 1,1 1,1 -1,0 -1,0 1))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((0 1, 1 1, 1 -1, 0 -1, 0 1))")));
         handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 1, everyItem(equalTo("0,0")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        /** counter clock-wise WKT with righthand*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((0 1, 0 -1, 1 -1, 1 1, 0 1))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(getWithRightHand, 1, everyItem(equalTo("0,0")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("0,0")));
+        request.filter.righthand = false;
+
 
         /** clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((2 2,2 3,3 3,3 2,2 2))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((2 2, 2 3, 3 3, 3 2, 2 2))")));
         handleNotMatchingGintersectFilter(post(request));
         handleNotMatchingGintersectFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingGintersectFilter(header(request.filter));
+        /** counter clock-wise WKT */
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((2 2, 3 2, 3 3, 2 3, 2 2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingGintersectFilter(post(request));
+        handleNotMatchingGintersectFilter(getWithRightHand);
+        handleNotMatchingGintersectFilter(header(request.filter));
+        request.filter.righthand = false;
+
 
         /** WKT Linestring that crosses the dateline*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "LINESTRING(50 0, 340 0)")));
         handleMatchingGeometryFilter(post(request), 29, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 29, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 29, everyItem(startsWith("0,")));
+        /** WKT Linestring that crosses the dateline with righthand = true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "LINESTRING(50 0, 340 0)")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 29, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 29, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 29, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** WKT Linestring that doesn't cross the dateline*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "LINESTRING(50 0, -20 0)")));
         handleMatchingGeometryFilter(post(request), 8, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 8, everyItem(startsWith("0,")));
         handleMatchingGeometryFilter(header(request.filter), 8, everyItem(startsWith("0,")));
+        /** WKT Linestring that doesn't cross the dateline with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "LINESTRING(50 0, -20 0)")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 8, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(getWithRightHand, 8, everyItem(startsWith("0,")));
+        handleMatchingGeometryFilter(header(request.filter), 8, everyItem(startsWith("0,")));
+        request.filter.righthand = false;
+
 
         /** west < east bbox */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "-12,-12,12,12")),
@@ -709,77 +1296,205 @@ public void testGintersectFilter() throws Exception {
 
         /** west < east bbox along with a clock-wise WKT*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.intersects, "-12,-12,12,12"),
-                new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((0 1,1 1,1 -1,0 -1,0 1))"))));
+                new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((0 1, 1 1, 1 -1, 0 -1, 0 1))"))));
         handleMatchingGeometryFilter(post(request), 9, everyItem(notNullValue()));
         handleMatchingGeometryFilter(
                 get("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString()),
                 9, everyItem(notNullValue()));
         handleMatchingGeometryFilter(header(request.filter), 9, everyItem(notNullValue()));
+        /** west < east bbox along with a counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.intersects, "-12,-12,12,12"),
+                new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((0 1, 0 -1, 1 -1, 1 1, 0 1))"))));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 9, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(
+                getWithRightHand,
+                9, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(header(request.filter), 9, everyItem(notNullValue()));
+        request.filter.righthand = false;
+
+        /** --- NOTINTERSECTS --- */
 
         /** clock-wise wkt*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, 180 -70, 160 -70, 160 -90,-180 -90,-180 90,180 90))")));
         handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("-80,170")));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 1, everyItem(equalTo("-80,170")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("-80,170")));
+        /** counter clock-wise wkt with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, -180 90, -180 -90, 160 -90, 160 -70, 180 -70, 180 90))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("-80,170")));
+        handleMatchingGeometryFilter(getWithRightHand, 1, everyItem(equalTo("-80,170")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("-80,170")));
+        request.filter.righthand = false;
 
         /** counter clock-wise WKT */
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((-2 -2, 2 -2, 2 2,-2 2, -2 -2))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((-2 -2, 2 -2, 2 2, -2 2, -2 -2))")));
         handleMatchingGeometryFilter(post(request), 561, everyItem(notNullValue()));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 561, everyItem(notNullValue()));
         handleMatchingGeometryFilter(header(request.filter), 561, everyItem(notNullValue()));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((-2 -2, -2 2, 2 2, 2 -2, -2 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 561, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(getWithRightHand, 561, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(header(request.filter), 561, everyItem(notNullValue()));
+        request.filter.righthand = false;
 
         /** clock-wise wkt*/
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90,180 -90,-180 -90,-180 90,180 90))")));
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, 180 -90, -180 -90, -180 90, 180 90))")));
         handleNotMatchingNotGintersectFilter(post(request));
         handleNotMatchingNotGintersectFilter(get("f", request.filter.f.get(0).get(0).toString()));
         handleNotMatchingNotGintersectFilter(header(request.filter));
+        /** counter clock-wise wkt with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, -180 90, -180 -90, 180 -90, 180 90))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingNotGintersectFilter(post(request));
+        handleNotMatchingNotGintersectFilter(getWithRightHand);
+        handleNotMatchingNotGintersectFilter(header(request.filter));
+        request.filter.righthand = false;
+
 
         /** counter clock-wise WKT */
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((160 -2, 160 2, -160 2, -160 -2, 160 -2))")));
         handleMatchingGeometryFilter(post(request), 591, everyItem(notNullValue()));
         handleMatchingGeometryFilter(get("f", request.filter.f.get(0).get(0).toString()), 591, notNullValue());
         handleMatchingGeometryFilter(header(request.filter), 591, everyItem(notNullValue()));
+        /** clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((160 -2, -160 -2, -160 2, 160 2, 160 -2))")));
+        request.filter.righthand = true;
+        getWithRightHand = givenFilterableRequestParams()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 591, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(getWithRightHand, 591, notNullValue());
+        handleMatchingGeometryFilter(header(request.filter), 591, everyItem(notNullValue()));
+        request.filter.righthand = false;
+
 
         /** west < east bbox along with a clock-wise WKT*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "-12,-12,12,12")),
-                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, 180 -70, 160 -70, 160 -90,-180 -90,-180 90,180 90))")));
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, 180 -70, 160 -70, 160 -90, -180 -90, -180 90, 180 90))")));
         handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("-80,170")));
         handleMatchingGeometryFilter(
                 get(Arrays.asList(new ImmutablePair<>("notintersects", request.filter.f.get(0).get(0).toString()),
                         new ImmutablePair<>("f", request.filter.f.get(1).get(0).toString()))),
                 1, everyItem(equalTo("-80,170")));
         handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("-80,170")));
+        /** west < east bbox along with a counter clock-wise WKT with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "-12,-12,12,12")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, -180 90, -180 -90, 160 -90, 160 -70, 180 -70, 180 90))")));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 1, everyItem(equalTo("-80,170")));
+        handleMatchingGeometryFilter(getWithRightHand,
+                1, everyItem(equalTo("-80,170")));
+        handleMatchingGeometryFilter(header(request.filter), 1, everyItem(equalTo("-80,170")));
+        request.filter.righthand = false;
+
 
         /** clock-wise wkts*/
-        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((12 12,12 -12,-12 -12,-12 12,12 12))"),
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((12 12, 12 -12, -12 -12, -12 12, 12 12))"),
                 new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, 180 -70, 160 -70, 160 -90,-180 -90,-180 90,180 90))"))));
         handleMatchingGeometryFilter(post(request), 586, everyItem(notNullValue()));
         handleMatchingGeometryFilter(
                 get("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString()),
                 586, everyItem(notNullValue()));
         handleMatchingGeometryFilter(header(request.filter), 586, everyItem(notNullValue()));
+        /** counter clock-wise wkts with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(Arrays.asList(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((12 12, -12 12, -12 -12, 12 -12, 12 12))"),
+                new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((180 90, -180 90, -180 -90, 160 -90, 160 -70, 180 -70, 180 90))"))));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString() + ";" + request.filter.f.get(0).get(1).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 586, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(getWithRightHand,
+                586, everyItem(notNullValue()));
+        handleMatchingGeometryFilter(header(request.filter), 586, everyItem(notNullValue()));
+        request.filter.righthand = false;
 
         /** clock-wise wkts*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 10,10 -10,-10 -10,-10 10,10 10))")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((10 10,10 -10,0 -10,0 10,10 10))")));
         handleMatchingGeometryFilter(post(request), 3, hasItems("10,-10", "0,-10", "-10,-10"));
         handleMatchingGeometryFilter(
-                givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                givenFilterableRequestParams()
+                        .param("f", request.filter.f.get(0).get(0).toString())
                         .param("f", request.filter.f.get(1).get(0).toString())
+                        .param("righthand", request.filter.righthand)
                         .when().get(getUrlPath("geodata"))
                         .then(), 3, hasItems("10,-10", "0,-10", "-10,-10"));
         handleMatchingGeometryFilter(header(request.filter), 3, hasItems("10,-10", "0,-10", "-10,-10"));
+        /** counter clock-wise wkts with righthand=true*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 10, -10 10, -10 -10, 10 -10, 10 10))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((10 10,0 10,0 -10,10 -10,10 10))")));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleMatchingGeometryFilter(post(request), 3, hasItems("10,-10", "0,-10", "-10,-10"));
+        handleMatchingGeometryFilter(getWithRightHand, 3, hasItems("10,-10", "0,-10", "-10,-10"));
+        handleMatchingGeometryFilter(header(request.filter), 3, hasItems("10,-10", "0,-10", "-10,-10"));
+        request.filter.righthand = false;
 
         /** clock-wise wkts*/
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 10,10 -10,-10 -10,-10 10,10 10))")),
-                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((11 11,11 -11,-11 -11,-11 11,11 11))")));
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((11 11, 11 -11, -11 -11, -11 11, 11 11))")));
         handleNotMatchingGintersectComboFilter(post(request));
         handleNotMatchingGintersectComboFilter(
-                givenFilterableRequestParams().param("f", request.filter.f.get(0).get(0).toString())
+                givenFilterableRequestParams()
+                        .param("f", request.filter.f.get(0).get(0).toString())
                         .param("f", request.filter.f.get(1).get(0).toString())
                         .when().get(getUrlPath("geodata"))
                         .then());
         handleNotMatchingGintersectComboFilter(header(request.filter));
+        /** counter clock-wise wkts*/
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((10 10, -10 10, -10 -10, 10 -10, 10 10))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((11 11, -11 11, -11 -11, 11 -11, 11 11))")));
+        request.filter.righthand = true;
+        getWithRightHand = this.getExtraParamsRequest()
+                .param("f", request.filter.f.get(0).get(0).toString())
+                .param("f", request.filter.f.get(1).get(0).toString())
+                .param("righthand", request.filter.righthand)
+                .when().get(getUrlPath("geodata"))
+                .then();
+        handleNotMatchingGintersectComboFilter(post(request));
+        handleNotMatchingGintersectComboFilter(getWithRightHand);
+        handleNotMatchingGintersectComboFilter(header(request.filter));
+        request.filter.righthand = false;
         request.filter.f = null;
     }
 
@@ -834,7 +1549,7 @@ public void testComplexFilter() throws Exception {
                 .when().get(getUrlPath("geodata")).then(), 0, 772000, 2);
         handleMatchingTimestampRangeFilter(header(request.filter), 0, 772000, 2);
         request.filter.dateformat = null;
-
+        request.filter.righthand = false;
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("params.job", OperatorEnum.like, "Architect")),//"job:eq:Architect"
                 new MultiValueFilter<>(new Expression("params.startdate", OperatorEnum.range, "[1009799<1009801]")),
                 new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "-50,-50,50,50")),
@@ -853,10 +1568,39 @@ public void testComplexFilter() throws Exception {
                         .param("f", request.filter.f.get(5).get(0).toString())
                         .param("f", request.filter.f.get(6).get(0).toString())
                         .param("f", request.filter.f.get(7).get(0).toString())
+                        .param("righthand", request.filter.righthand)
+                        .when().get(getUrlPath("geodata"))
+                        .then());
+        handleComplexFilter(header(request.filter));
+
+        /** counter clock-wise with righthand=true */
+        request.filter.righthand = true;
+        request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("params.job", OperatorEnum.like, "Architect")),//"job:eq:Architect"
+                new MultiValueFilter<>(new Expression("params.startdate", OperatorEnum.range, "[1009799<1009801]")),
+                new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "-50,-50,50,50")),
+                new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "20,-50,60,50")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "POLYGON((30 30,-30 30,-30 -30,30 -30,30 30))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((-50 50, -50 -50, -20 -50, -20 50, -50 50))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-20 20, -20 -20, 20 -20, 20 20, -20 20))")),
+                new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((-30 -10, -30 -30, 30 -30, 30 10, -30 -10))")));
+        handleComplexFilter(post(request));
+        handleComplexFilter(
+                givenFilterableRequestParams()
+                        .param("f", request.filter.f.get(0).get(0).toString())
+                        .param("f", "params.startdate:range:[1009799<1009801]")
+                        .param("f", request.filter.f.get(2).get(0).toString())
+                        .param("f", request.filter.f.get(3).get(0).toString())
+                        .param("f", request.filter.f.get(4).get(0).toString())
+                        .param("f", request.filter.f.get(5).get(0).toString())
+                        .param("f", request.filter.f.get(6).get(0).toString())
+                        .param("f", request.filter.f.get(7).get(0).toString())
+                        .param("righthand", request.filter.righthand)
                         .when().get(getUrlPath("geodata"))
                         .then());
         handleComplexFilter(header(request.filter));
         request.filter = new Filter();
+        request.filter.righthand = false;
+
     }
 
 
@@ -879,6 +1623,7 @@ public void testCollectionFilter() throws Exception {
                         .then()
         );
         request.filter = new Filter();
+        request.filter.righthand = false;
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("params.job", OperatorEnum.eq, DataSetTool.jobs[0])));//("job:eq:" + DataSetTool.jobs[0]);
         req = givenFilterableRequestBody();
 
@@ -901,6 +1646,7 @@ public void testCollectionFilter() throws Exception {
                         .then(),59,"Actor"
         );
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @Test
@@ -912,12 +1658,13 @@ public void testMixedFilter() throws Exception {
                 new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "-50,-50,50,50")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.within, "-30,-30,30,30")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.intersects, "POLYGON((-20 20, 20 20, 20 -20, -20 -20, -20 20))")));
-
+        request.filter.righthand = false;
         Filter filterHeader = new Filter();
         filterHeader.f = Arrays.asList(new MultiValueFilter<>(new Expression("params.startdate", OperatorEnum.range, "[0<1009801]")),
                 new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "20,-50,60,50")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((-50 50,-20 50, -20 -50, -50 -50,-50 50))")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((-30 -10,30 10, 30 -30, -30 -30,-30 -10))")));
+        filterHeader.righthand = false;
         handleComplexFilter(
                 givenFilterableRequestParams()
                         .header("partition-filter", objectMapper.writeValueAsString(filterHeader))
@@ -927,6 +1674,7 @@ public void testMixedFilter() throws Exception {
                         .param("f", request.filter.f.get(2).get(0).toString())
                         .param("f", request.filter.f.get(3).get(0).toString())
                         .param("f", request.filter.f.get(4).get(0).toString())
+                        .param("righthand", request.filter.righthand)
                         .when().get(getUrlPath("geodata"))
                         .then());
 
@@ -953,12 +1701,14 @@ public void testMixedFilter() throws Exception {
                         .when().post(getUrlPath("geodata"))
                         .then());
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @Test
     public void testMixedFilterMultiCollectionFormat() throws Exception {
         // valid bbox from WFS OGC SPEC = lower longitude , lower latitude , upper longitude  , upper latitude
         // valid bbox for ARLAS classic bbox = lat top,  long left,  lat bottom,  long right
+        request.filter.righthand = false;
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("params.job", OperatorEnum.eq, "Architect")),//"job:eq:Architect"
                 new MultiValueFilter<>(new Expression("params.startdate", OperatorEnum.range, "[1009799<2000000]")),
                 new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.within, "-50,-50,50,50")),
@@ -968,11 +1718,14 @@ public void testMixedFilterMultiCollectionFormat() throws Exception {
         Map<String, Filter> filterHeader = new HashMap<String, Filter>();
 
         Filter filter = new Filter();
+        filter.righthand = false;
         filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("params.startdate", OperatorEnum.range, "[0<1009801]")),
                 new MultiValueFilter<>(new Expression("geo_params.centroid", OperatorEnum.notwithin, "20,-50,60,50")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notwithin, "POLYGON((-50 50,-20 50, -20 -50, -50 -50,-50 50))")),
                 new MultiValueFilter<>(new Expression("geo_params.geometry", OperatorEnum.notintersects, "POLYGON((-30 -10,30 10, 30 -30, -30 -30,-30 -10))")));
+
         filterHeader.put("geodata", filter);
+
         handleComplexFilter(
                 givenFilterableRequestParams()
                         .header("partition-filter", objectMapper.writeValueAsString(filterHeader))
@@ -982,6 +1735,7 @@ public void testMixedFilterMultiCollectionFormat() throws Exception {
                         .param("f", request.filter.f.get(2).get(0).toString())
                         .param("f", request.filter.f.get(3).get(0).toString())
                         .param("f", request.filter.f.get(4).get(0).toString())
+                        .param("righthand", request.filter.righthand)
                         .when().get(getUrlPath("geodata"))
                         .then());
 
@@ -1009,13 +1763,14 @@ public void testMixedFilterMultiCollectionFormat() throws Exception {
                         .when().post(getUrlPath("geodata"))
                         .then());
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @Test
     public void testFieldFilterWithUnavailableColumns() throws Exception {
         request.filter = new Filter();
         request.filter.f = Arrays.asList(new MultiValueFilter<>(new Expression("params.job", OperatorEnum.eq, "Architect")));
-
+        request.filter.righthand = false;
         handleUnavailableColumn(
                 givenFilterableRequestParams()
                         .header("column-filter", "params.city")
@@ -1035,6 +1790,7 @@ public void testFieldFilterWithUnavailableColumns() throws Exception {
         handleUnavailableColumn(get("f", request.filter.f.get(0).get(0).toString(), "column-filter", "fullname"));
 
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @Test
@@ -1054,11 +1810,13 @@ public void testFieldFilterWithCollectionBasedColumnFiltering() throws Exception
         handleUnavailableCollection(get("f", request.filter.f.get(0).get(0).toString(), "column-filter", "notExisting:params.job"));
 
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @Test
     public void testQueryFilterWithUnavailableColumns() throws Exception {
         request.filter = new Filter();
+        request.filter.righthand = false;
         request.filter.q = Arrays.asList(new MultiValueFilter<>("My name is"));
         handleUnavailableColumn(post(request, "column-filter", "params.city"));
         handleUnavailableColumn(get("q", request.filter.q.get(0).get(0), "column-filter", "params.city"));
@@ -1077,11 +1835,13 @@ public void testQueryFilterWithUnavailableColumns() throws Exception {
         handleUnavailableColumn(get("q", request.filter.q.get(0).get(0), "column-filter", "fullname.anything"));
 
         request.filter = new Filter();
+        request.filter.righthand = false;
     }
 
     @Test
     public void testQueryFilterWithCollectionBasedColumnFiltering() throws Exception {
         request.filter = new Filter();
+        request.filter.righthand = false;
         request.filter.q = Arrays.asList(new MultiValueFilter<>("fullname:My name is"));
 
         handleMatchingQueryFilter(post(request, "column-filter", "fullname*"), 595);
@@ -1444,36 +2204,36 @@ protected void handleNotMatchingGintersectComboFilter(ValidatableResponse then)
     //---------------------- ValidatableResponse ------------------
     //----------------------------------------------------------------
 
-    private ValidatableResponse get(String param, Object paramValue) {
+    private RequestSpecification getExtraParamsRequest() {
         RequestSpecification req = givenFilterableRequestParams();
         for (Pair<String, String> extraParam : this.extraParams) {
             req = req.param(extraParam.getKey(), extraParam.getValue());
         }
+        return req;
+    }
+    private ValidatableResponse get(String param, Object paramValue) {
+        RequestSpecification req = getExtraParamsRequest();
         return req.param(param, paramValue)
+                .param("righthand", false)
                 .when().get(getUrlPath("geodata"))
                 .then();
     }
 
     private ValidatableResponse get(String param, Object paramValue, String headerkey, String headerValue) {
-        RequestSpecification req = givenFilterableRequestParams();
-        for (Pair<String, String> extraParam : this.extraParams) {
-            req = req.param(extraParam.getKey(), extraParam.getValue());
-        }
+        RequestSpecification req = getExtraParamsRequest();
         return req.param(param, paramValue)
+                .param("righthand", false)
                 .header(headerkey, headerValue)
                 .when().get(getUrlPath("geodata"))
                 .then();
     }
 
-    private ValidatableResponse get(List<Pair<String, String>> params) {
-        RequestSpecification req = givenFilterableRequestParams();
-        for (Pair<String, String> extraParam : this.extraParams) {
-            req = req.param(extraParam.getKey(), extraParam.getValue());
-        }
-        for (Pair<String, String> param : params) {
+    private ValidatableResponse get(List<Pair<String, Object>> params) {
+        RequestSpecification req = getExtraParamsRequest();
+        for (Pair<String, Object> param : params) {
             req = req.param(param.getKey(), param.getValue());
         }
-        return req
+        return req.param("righthand", false)
                 .when().get(getUrlPath("geodata"))
                 .then();
     }
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractPaginatedTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractPaginatedTest.java
index 86a0f918f..c0d9687e7 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractPaginatedTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractPaginatedTest.java
@@ -42,6 +42,7 @@ public abstract class AbstractPaginatedTest extends AbstractFormattedTest{
     public void setUpSearch() {
         search.page = new Page();
         search.filter = new Filter();
+        search.filter.righthand = false;
     }
 
     //----------------------------------------------------------------
@@ -92,6 +93,7 @@ public void testPageFrom() throws Exception {
     public void testPageSort() throws Exception {
 
         search.filter = new Filter();
+        search.filter.righthand = false;
         search.page.sort = "-params.job";
         //an empty column filter is not considered
         handleSortParameter(post(search, ""), "Dancer");
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractProjectedTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractProjectedTest.java
index e865ff1cc..3c2270dce 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractProjectedTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractProjectedTest.java
@@ -37,6 +37,7 @@ public abstract class AbstractProjectedTest extends AbstractPaginatedTest {
     public void setUpSearch() {
         search.page = new Page();
         search.filter = new Filter();
+        search.filter.righthand = false;
         search.projection = new Projection();
     }
 
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractXYZTiledTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractXYZTiledTest.java
index 7e4c13241..e6f0f4c0b 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractXYZTiledTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractXYZTiledTest.java
@@ -33,9 +33,9 @@ public abstract class AbstractXYZTiledTest extends AbstractProjectedTest {
     @Before
     public void setUpSearch() {
         search.filter = new Filter();
+        search.filter.righthand = false;
         search.page = new Page();
         search.projection = new Projection();
-        search.filter = new Filter();
     }
 
     @Test