NLeSC/PattyVis

View on GitHub
app/scripts/core/sites.service.js

Summary

Maintainability
D
2 days
Test Coverage
/**
 * @namespace core
 */
(function() {
  'use strict';

  /**
   * Sites data model service.
   * Fetches and stores a collection of sites.
   * The collection of sites can be filtered.
   *
   * @constructs core.SitesService
   * @memberOf core
   */
  function SitesService($http, $q, $rootScope, pattyConf, Messagebus) {

    function onLoad(data) {
      service.all = data;
      service.filtered = data;
      service.searched = [];
      deferred.resolve(service.all);
      service.onSitesChanged();
    }
    var deferred = $q.defer();

    var service = /** @lends core.SitesService */ {
      _query: '',
      /**
       * List of all sites.
       *
       * @type {Array}
       */
      all: [],
      /**
       * List of filtered sites. When query is empty then it will contain all sites.
       *
       * @type {Array}
       */
      filtered: [],
      /**
       * List of searched sites. When query is empty then it will contain no sites.
       *
       * @type {Array}
       */
      searched: [],
      /**
       * Promise for loading the sites remotely.
       * Can be used to perform action when loading sites has been completed.
       *
       * @type {Promise}
       */
      ready: deferred.promise,
      /**
       * Fetches sites from server
       *
       * @returns {Promise}
       */
      load: function() {
        $http.get(pattyConf.SITES_JSON_URL)
          .success(onLoad)
          .error(function(data, status, headers, config) {
            deferred.reject(data, status, headers, config);
          });
      },
      onLoad: onLoad,
      /**
       * Get a site by it's identifier.
       *
       * @param {Number} id Site identifier
       * @returns {Object|undefined} Site object or undefined when site was not found.
       */
      getById: function(id) {
        var sites = this.all.filter(function(d) {
          return d.id === id;
        });
        return sites[0];
      },
      /**
       * Select a site.
       *
       * @param {Site} site
       */
      selectSite: function(site) {
        this.query = 'site:' + site.id;
      },
      /**
       * Clears the site selection
       *
       */
      clearSiteSelection: function() {
        this.query = '';
      },
      /**
       * @fires sitesChanged
       */
      onSitesChanged: function() {
        /**
         * Sites changed event
         * @event sitesChanged
         */
        Messagebus.publish('sitesChanged');

        if(this.searched.length===1){
          Messagebus.publish('singleSite', this.searched[0]);
        }

        // angular does not know that SitesService.searched and SitesService.filtered when query has been changed
        // trigger a $digest to let angular detect changes
        if (!$rootScope.$$phase) {
          $rootScope.$digest();
        }
      },
      // Methods for one site
      /**
       * Determines bounding box based on footprint.
       *
       * @param {Site} site
       * @return {array} [minlon, minlat, minalt, maxlon, maxlat, maxalt]
       */
      getBoundingBoxOfFootprint: function(site) {
        var minlon = Number.POSITIVE_INFINITY;
        var minlat = Number.POSITIVE_INFINITY;
        var minalt = site.footprint_altitude[0]; // jshint ignore:line
        var maxlon = Number.NEGATIVE_INFINITY;
        var maxlat = Number.NEGATIVE_INFINITY;
        var maxalt = site.footprint_altitude[1]; // jshint ignore:line

        site.footprint.forEach(function(polygon) {
          polygon.forEach(function(ring) {
            ring.forEach(function(point) {
              if (point[0] < minlon) {
                minlon = point[0];
              }
              if (point[0] > maxlon) {
                maxlon = point[0];
              }
              if (point[1] < minlat) {
                minlat = point[1];
              }
              if (point[1] > maxlat) {
                maxlat = point[1];
              }
            });
          });
        });

        var bbox = [minlon, minlat, minalt, maxlon, maxlat, maxalt];
        return bbox;
      },
      /**
       * Center position of site based on it's bounding box.
       *
       * @param {Site} site
       * @returns {Array} [lon, lat, alt]
       */
      centerOfSite: function(site) {
        var bbox = this.getBoundingBox(site);
        return [
          ((bbox[3] - bbox[0]) / 2) + bbox[0], ((bbox[4] - bbox[1]) / 2) + bbox[1], ((bbox[5] - bbox[2]) / 2) + bbox[2]
        ];
      },
      /**
       * If site has footprint and pointcloud then returns bbox of pointcloud.
       * If site has footprint and no pointcloud then returns bbox of footprint.
       *
       * @param {Site} site [description]
       * @return {array} [minlon, minlat, minalt, maxlon, maxlat, maxalt]
       */
      getBoundingBox: function(site) {
        if (site.pointcloud !== undefined && site.pointcloud.length !== 0 && 'bbox' in site.pointcloud[0]) {
          return site.pointcloud[0].bbox;
        } else {
          return this.getBoundingBoxOfFootprint(site);
        }
      },
      /**
       * Size of bounding box
       *
       * @param {Site} site
       * @returns {array} [sizelon, sizelat, sizealt]
       */
      getBoundingBoxSize: function(site) {
        var bbox = this.getBoundingBox(site);
        return [
          ((bbox[3] - bbox[0]) / 2), ((bbox[4] - bbox[1]) / 2), ((bbox[5] - bbox[2]) / 2)
        ];
      }
    };
    /**
     * Query string, to search/filter the collection of sites.
     *
     * @name query
     * @type {string}
     * @memberOf core.SitesService#
     */
    Object.defineProperty(service, 'query', {
      get: function() {
        return this._query;
      },
      set: function(query) {
        if (this._query === query) {
          return;
        }
        this._query = query;
        if (query) {
          var re = new RegExp(query, 'i');

          this.filtered = this.searched = this.all.filter(function(site) {
            var descriptionSite = site.description_site; // jshint ignore:line
            var siteInterpretation = site.site_interpretation; // jshint ignore:line
            var siteContext = site.site_context; // jshint ignore:line
            var allText = descriptionSite +
              ' ' + siteInterpretation +
              ' ' + siteContext;

            var allMaterialText = '';
            var allTimeText = '';
            var allConditionText = '';

            for (var i = 0; i < site.objects.length; i++) {
              var object = site.objects[i];
              allText += ' ' + object.description_restorations; // jshint ignore:line
              allText += ' ' + object.object_interpretation; // jshint ignore:line
              allText += ' ' + object.object_type; // jshint ignore:line
              allText += ' ' + object.description_object; // jshint ignore:line

              allTimeText += ' ' + object.date_specific; // jshint ignore:line
              allTimeText += ' ' + object.period;

              allConditionText += ' ' + object.condition;

              for (var j = 0; j < object.object_material.length; j++) { // jshint ignore:line
                var objectMaterial = object.object_material[j]; // jshint ignore:line
                allMaterialText += ' ' + objectMaterial.material_subtype; // jshint ignore:line
                allMaterialText += ' ' + objectMaterial.material_type; // jshint ignore:line
                allMaterialText += ' ' + objectMaterial.material_technique; // jshint ignore:line
              }
            }
            allText += ' ' + allMaterialText;
            allText += ' ' + allTimeText;
            allText += ' ' + allConditionText;

            // The following three ifs do specific field searches using three
            // search "tags":
            //    'time:'
            //    'material:'
            //    'condition:'
            // Note: currently the only thing that is included is the word
            // right after the search-tag! If you want to search for multiple
            // words with the same (or different) tags, you have to add the tag
            // before every word.
            var queryLowCase = query.toLowerCase();

            var timeMatched = false;
            if (queryLowCase.indexOf('time:') !== -1) {
              var timeSearches = queryLowCase.split('time:').slice(1);
              timeMatched = timeSearches.some(function(timeSearch) {
                var taggedWord = timeSearch.trim().split(' ')[0];
                return allTimeText.toLowerCase().indexOf(taggedWord) !== -1;
              });
            }

            var materialMatched = false;
            if (query.toLowerCase().indexOf('material:') !== -1) {
              var materialSearches = queryLowCase.split('material:').slice(1);
              materialMatched = materialSearches.some(function(materialSearch) {
                var taggedWord = materialSearch.trim().split(' ')[0];
                return allMaterialText.toLowerCase().indexOf(taggedWord) !== -1;
              });
            }

            var conditionMatched = false;
            if (query.toLowerCase().indexOf('condition:') !== -1) {
              var conditionSearches = queryLowCase.split('condition:').slice(1);
              conditionMatched = conditionSearches.some(function(conditionSearch) {
                var taggedWord = conditionSearch.trim().split(' ')[0];
                return allConditionText.toLowerCase().indexOf(taggedWord) !== -1;
              });
            }

            return (re.test(allText) ||
                    'site:' + site.id === query ||
                    timeMatched ||
                    materialMatched ||
                    conditionMatched);
          }, this);
        } else {
          this.searched = [];
          this.filtered = this.all;
        }
        this.onSitesChanged();
      },
      enumerable: true,
      configurable: true
    });

    return service;
  }

  angular.module('pattyApp.core')
    .factory('SitesService', SitesService);
})();