@WorkerThread
        MapSearchResultSet execute() {
            final Parameters params = new Parameters();

            if (box != null) {
                // on empty viewport silently log stacktrace + return empty searchresult without calling search provider
                if (box.isJustADot()) {
                    try {
                        throw new RuntimeException("searching map with empty viewport");
                    } catch (RuntimeException e) {
                        Log.d("searching map with empty viewport: " + ExceptionUtils.getStackTrace(e));
                    }
                    return new MapSearchResultSet();
                }
                params.put("box", String.valueOf(this.box.getLatitudeMax()) + ',' + this.box.getLongitudeMin() +
                        ',' + this.box.getLatitudeMin() + ',' + this.box.getLongitudeMax());

                //6.4.24: seems like gc.com adds a strange "rad=16000" parameter to every box search. Always 16000, regardless of zoom level.
                // Don't know why yet...
                params.put("rad", "16000");

                //set origin to middle of viewport (will be overridden if origin is set explicitely later)
                params.put("origin", String.valueOf(this.box.getCenter().getLatitude()) + ',' + this.box.getCenter().getLongitude());
            }

            if (origin != null) {
                params.put("origin", String.valueOf(origin.getLatitude()) + ',' + origin.getLongitude());
            }

            if (!this.cacheTypes.isEmpty()) {
                params.put("ct", CollectionStream.of(this.cacheTypes).map(ct -> ct.wptTypeId).toJoinedString(","));
            }

            if (!this.cacheSizes.isEmpty()) {
                params.put("cs", CollectionStream.of(this.cacheSizes).filter(cs -> CacheSize.getGcIdsForSize(cs).length > 0)
                        .map(cs -> CollectionStream.of(ArrayUtils.toObject(CacheSize.getGcIdsForSize(cs))).toJoinedString(",")).toJoinedString(","));
            }

            if (!this.cacheAttributes.isEmpty()) {
                params.put("att", CollectionStream.of(this.cacheAttributes).map(ct -> ct.gcid).toJoinedString(","));
            }

            //Hide owned/hide found caches, only works for premium members
            if (this.statusOwn != null) {
                params.put("ho", this.statusOwn ? "0" : "1");
            }

            if (this.statusFound != null) {
                params.put("hf", this.statusFound ? "0" : "1");
            }

            if (this.statusMembership != null) {
                params.put("sp", this.statusMembership ? "0" : "1");
            }

            if (this.statusEnabled != null) {
                params.put("sd", this.statusEnabled ? "0" : "1");
            }
            if (this.showArchived) {
                params.put("sa", "1");
            }

            if (this.statusCorrectedCoordinates != null) {
                params.put("cc", this.statusCorrectedCoordinates ? "0" : "1");
            }

            if (this.hiddenBy != null) {
                params.put("hb", this.hiddenBy);
            }

            for (String notFoundBy : this.notFoundBy) {
                params.put("nfb", notFoundBy);
            }

            for (String foundBy : this.foundBy) {
                params.put("fb", foundBy);
            }

            if (this.minFavoritePoints > 0) {
                params.put("fp", "" + this.minFavoritePoints);
            }

            if (this.difficulty != null) {
                params.put("d", this.difficulty);
            }

            if (this.terrain != null) {
                params.put("t", this.terrain);
            }

            if (this.difficultyTerrainCombis != null) {
                params.put("m", this.difficultyTerrainCombis);
            }

            if (this.placedFrom != null || this.placedTo != null) {
                // after: pad
                // between: psd - ped
                // before: pbd
                // on: pod (not used by us)
                if (this.placedFrom == null) {
                    params.put("pbd", this.placedTo);
                } else if (this.placedTo == null) {
                    params.put("pad", this.placedFrom);
                } else {
                    params.put("psd", this.placedFrom);
                    params.put("ped", this.placedTo);
                }
            }

            if (this.keywords != null) {
                params.put("cn", this.keywords);
            }

            //special
            //6.4.24: seems like "properties=callernote" is now ALWAYS set
            //if (deliverLastFoundDateOfFoundBy && foundBy.size() == 1) {
            params.put("properties", "callernote");
            //}

            //paging / result size
            params.put("take", "" + take);
            params.put("skip", "" + skip);

            //sort
            if (sort != null) {
                params.put("sort", sort.keyword);
                if (sort == SortType.DISTANCE && this.box == null) {
                    //to sort by distance we need to set an origin of distance measurement
                    //6.4.24: gc.com seems to avoid dorigin param when box is set. So let's avoid it too...
                    final Geopoint dOrigin = origin != null ? origin : LocationDataProvider.getInstance().currentGeo().getCoords();
                    params.put("dorigin", String.valueOf(dOrigin.getLatitude()) + ',' + dOrigin.getLongitude());
                }
                params.put("asc", "" + sortAsc);
            }


            //ALWAYS send cgeo as an identifier
            params.put("app", "cgeo"); //identify us towards Groundspeak due to gentlemens agreement

            final HttpRequest request = apiProxyReq().uri("/web/search/v2").uriParams(params);
            Log.iForce("GCWEBAPI: request: " + request.getRequestUrl());
            return request.requestJson(MapSearchResultSet.class).blockingGet();
        }
Returns a string specifying a range from 1-5 (in 0.5-steps) as used for parameters difficulty and terrain
main/src/main/java/cgeo/geocaching/connector/gc/GCWebAPI.java