app/scripts/core/sites.service.js
/**
* @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);
})();