Source: models/filters/SpatialFilter.js

define(["underscore", "jquery", "backbone", "models/filters/Filter"],
    function(_, $, Backbone, Filter) {

        /**
        *A SpatialFilter represents a spatial constraint on the query to be executed,
        *and stores the geohash strings for all of the geohash tiles that coincide with the
        * search bounding box at the given zoom level.
        * @class SpatialFilter
        * @name SpatialFilter
        * @constructs
        * @extends Filter
        */
        var SpatialFilter = Filter.extend(
          /** @lends SpatialFilter.prototype */{

            /* The type of the filter */
            type: "SpatialFilter",

            /**
            * @type {object}
            * @property {string} icon - The icon to display with the Spatial filter
            * @property {string} label - A short intuitive label
            */
            defaults: function() {
                return _.extend(Filter.prototype.defaults(), {

                    /* The array of geohashes used to spatially constrain the search*/
                    geohashes: [],

                    /* The easternmost longitude of the represented bounding box */
                    east: null,

                    /* The westernmost longitude of the represented bounding box */
                    west: null,

                    /* The northernmost latitude of the represented bounding box */
                    north: null,

                    /* The southernmost latitude of the represented bounding box */
                    south: null,

                    /* The default precision level of the geohash-based search */
                    geohashLevel: null,

                    /* The list of grouped geohashes, as complete or incomplete 32 tiles */
                    groupedGeohashes: {},

                    label: "Limit search to the map area",
                    icon: "globe"
                });
            },

            /* Standard events and callbacks for this model */
            events: {
                "change geohashes": this.groupGeohashes
            },

            /**
             * Initialize the model, calling super
             */
            initialize: function(attributes, options) {
                this.on("change:geohashes", this.groupGeohashes);
            },

            /**
             * Builds a query string that represents this spatial filter
             * @return queryFragment - the query string representing the geohash constraints
             */
            getQuery: function() {
                var queryFragment = "";
                var geohashes = this.get("geohashes");
                var groups = this.get("geohashGroups");
                var geohashList;

                // Only return geohash query fragments when they are enabled in the filter
                if ( (typeof geohashes !== "undefined") && geohashes.length > 0 ) {
                    if ( (typeof groups !== "undefined") &&
                        Object.keys(groups).length > 0
                    ) {
                        // Group the Solr query fragment
                        queryFragment += "+(";

                        // Append geohashes at each level up to a fixed query string length
                        _.each(Object.keys(groups), function(level) {
                            geohashList = groups[level];
                            queryFragment += "geohash_" + level + ":(";
                            _.each(geohashList, function(geohash) {
                                if ( queryFragment.length < 7900 ) {
                                    queryFragment += geohash + "%20OR%20";
                                }
                            });
                            // Remove the last OR
                            queryFragment =
                                queryFragment.substring(0, (queryFragment.length - 8));
                            queryFragment += ")%20OR%20";
                        });
                        // Remove the last OR
                        queryFragment = queryFragment.substring(0, (queryFragment.length - 8));
                        // Ungroup the Solr query fragment
                        queryFragment += ")";

                    }
                }
                return queryFragment;
            },

            /**
             *  Consolidate geohashes into a parent tile if all are present
             */
            groupGeohashes: function() {
                var geohashGroups = {};
                var sortedGeohashes = this.get("geohashes").sort();
                var groupedGeohashes = _.groupBy(sortedGeohashes, function(geohash) {
                    return geohash.substring(0, geohash.length - 1);
                });
                //Find groups of geohashes that makeup a complete geohash tile (32)
                // so we can shorten the query
                var completeGroups = _.filter(Object.keys(groupedGeohashes), function(group) {
                    return groupedGeohashes[group].length == 32;
                });

                // Find groups that fall short of 32 tiles
                var incompleteGroups = [];
                _.each(
                    _.filter(Object.keys(groupedGeohashes), function(group) {
                        return (groupedGeohashes[group].length < 32)
                    }), function(incomplete) {
                        incompleteGroups.push(groupedGeohashes[incomplete]);
                    }
                );
                incompleteGroups = _.flatten(incompleteGroups);

                // Add both complete and incomplete groups to the instance property
                if((typeof incompleteGroups !== "undefined") && (incompleteGroups.length > 0)) {
                    geohashGroups[incompleteGroups[0].length.toString()] = incompleteGroups;
                }
                if((typeof completeGroups !== "undefined") && (completeGroups.length > 0)) {
                    geohashGroups[completeGroups[0].length.toString()] = completeGroups;
                }
                this.set("geohashGroups", geohashGroups); // Triggers a change event
            },
        });
        return SpatialFilter;
});