trash/to_port/concept-lookup-services.js.txt
(function() {
var util = jassa.util;
var facete = jassa.facete;
var geo = jassa.geo;
var sponate = jassa.sponate;
var facete = jassa.facete;
var ns = jassa.service;
/**
* A data service only provides a single method for retrieving data based on some 'key' (thing)
* The key can be an arbitrary object that identifies a collection (e.g. a tag), a sparql concept, etc...
*/
ns.DataService = Class.create({
fetchData: function(thing) {
console.log('Not implemented');
throw 'Not implemented';
}
});
/**
* A list service supports fetching ranges of items and supports thresholded counting.
*/
ns.ListService = Class.create({
fetchItems: function(thing, limit, offset) {
console.log('Not implemented');
throw 'Not implemented';
},
fetchCount: function(thing, threshold) {
console.log('Not implemented');
throw 'Not implemented';
}
});
ns.ListServiceBbox = Class.create(ns.ListService, {
initialize: function(sparqlService, geoMapFactory, concept) {
this.sparqlService = sparqlService;
this.geoMapFactory = geoMapFactory;
this.concept = concept;
// this.fnGetBBox = fnGetBBox || defaultDocWktExtractorFn;
// TODO How to augment the data provided by the geoMapFactory?
},
createFlow: function(bounds) {
var store = new sponate.StoreFacade(this.sparqlService); // ,
// prefixes);
var geoMap = this.geoMapFactory.createMap(bounds);
store.addMap(geoMap, 'geoMap');
return store.geoMap;
},
fetchItems: function(bounds, limit, offset) {
var loadFlow = this.createFlow(bounds).find().concept(this.concept).limit(limit).offset(offset);
var result = loadFlow.asList(true);
return result;
// var promises = _(boundsArray).each(function(bounds) {
// var loadFlow = self.createFlow(bounds).find().concept(self.concept).limit(limit).offset(offset);
// var r = loadFlow.asList(true);
// return r;
// });
// var result = this.zip(boundsArray, promises);
// return result;
},
fetchCount: function(bounds, threshold) {
var countFlow = this.createFlow(bounds).find().concept(this.concept).limit(threshold);
var result = countFlow.count();
return result;
// var self = this;
// var promises = _(boundsArray).each(function(bounds) {
// var countFlow = self.createFlow(bounds).find().concept(self.concept).limit(countThreshold);
// var r = countFlow.count();
// return r;
// });
//
// var result = this.zip(boundsArray, promises);
// return result;
}
});
/*
ns.ListServiceBBoxStrategy = Class.create(ns.ListService, {
initialize: function(strategy) {
this.strategy = strategy;
},
fetchItems: function() {
},
fetchCount: function() {
strategy.runWorkflow()
}
});
*/
/*
ns.ClusterService = Class.create({
fetchClusters: function(bounds, limit, offset) {
}
});
*/
/**
* Adds a quad tree cache to the lookup service
*/
ns.DataServiceBboxCache = Class.create({
initialize: function(listServiceBbox, maxGlobalItemCount, maxItemsPerTileCount, aquireDepth) {
this.listServiceBbox = listServiceBbox;
var maxBounds = new geo.Bounds(-180.0, -90.0, 180.0, 90.0);
this.quadTree = new geo.QuadTree(maxBounds, 18, 0);
this.maxItemsPerTileCount = maxItemsPerTileCount || 25;
this.maxGlobalItemCount = maxGlobalItemCount || 50;
this.aquireDepth = aquireDepth || 2;
},
// TODO: limit and offset currently ignored
fetchData: function(bounds) {
var result = this.runWorkflow(bounds).pipe(function(nodes) {
var arrayOfDocs = _(nodes).map(function(node) {
return node.data.docs;
});
// Remove null items
var docs = _(arrayOfDocs).compact();
docs = _(docs).flatten(true);
// Add clusters as regular items to the list???
_(nodes).each(function(node) {
if(node.isLoaded) {
return;
}
var wkt = geo.GeoExprUtils.boundsToWkt(node.getBounds());
var cluster = {
id: wkt,
//type: 'cluster',
//isZoomCluster: true,
zoomClusterBounds: node.getBounds(),
wkt: rdf.NodeFactory.createPlainLiteral(wkt)
};
docs.push(cluster);
});
return docs;
});
return result;
},
/*
fetchCount: function(bounds, threshold) {
var result = this.listServiceBbox.fetchCount(bounds, threshold);
return result;
},
*/
runCheckGlobal: function() {
var result;
var rootNode = this.quadTree.getRootNode();
var self = this;
if(!rootNode.checkedGlobal) {
var countTask = this.listServiceBbox.fetchCount(null, this.maxGlobalItemCount);
// var countFlow =
// this.createFlowForGlobal().find().concept(this.concept).limit(self.maxGlobalItemCount);
// var countTask = countFlow.count();
var globalCheckTask = countTask.pipe(function(countInfo) {
var canUseGlobal = !countInfo.hasMoreItems;
console.log("Global check counts", countInfo);
rootNode.canUseGlobal = canUseGlobal;
rootNode.checkedGlobal = true;
return canUseGlobal;
});
result = globalCheckTask;
} else {
var deferred = $.Deferred();
deferred.resolve(rootNode.canUseGlobal);
result = deferred.promise();
}
return result;
},
runWorkflow: function(bounds) {
var deferred = $.Deferred();
var rootNode = this.quadTree.getRootNode();
var self = this;
this.runCheckGlobal().pipe(function(canUseGlobal) {
console.log('Can use global? ', canUseGlobal);
var task;
if(canUseGlobal) {
task = self.runGlobalWorkflow(rootNode);
} else {
task = self.runTiledWorkflow(bounds);
}
task.done(function(nodes) {
deferred.resolve(nodes);
}).fail(function() {
deferred.fail();
});
}).fail(function() {
deferred.fail();
});
var result = deferred.promise();
return result;
},
runGlobalWorkflow: function(node) {
var self = this;
var result = this.listServiceBbox.fetchItems(null).pipe(function(docs) {
// console.log("Global fetching: ", geomToFeatureCount);
self.loadTaskAction(node, docs);
return [node];
});
/*
* loadTask.done(function() {
* $.when(self.postProcess([node])).done(function() {
* //console.log("Global workflow completed.");
* //console.debug("Workflow completed. Resolving deferred.");
* result.resolve([node]); }).fail(function() { result.fail(); });
* }).fail(function() { result.fail(); });
*/
return result;
},
/**
* This method implements the primary workflow for tile-based fetching
* data.
*
* globalGeomCount = number of geoms - facets enabled, bounds disabled.
* if(globalGeomCount > threshold) {
*
*
* nodes = aquire nodes. foreach(node in nodes) { fetchGeomCount in the
* node - facets TODO enabled or disabled?
*
* nonFullNodes = nodes where geomCount < threshold foreach(node in
* nonFullNodes) { fetch geomToFeatureCount - facets enabled
*
* fetch all positions of geometries in that area -- Optionally:
* fetchGeomToFeatureCount - facets disabled - this can be cached per
* type of interest!! } } }
*
*/
runTiledWorkflow: function(bounds) {
var self = this;
//console.log("Aquiring nodes for " + bounds);
var nodes = this.quadTree.aquireNodes(bounds, this.aquireDepth);
// console.log('Done aquiring');
// Init the data attribute if needed
_(nodes).each(function(node) {
if(!node.data) {
node.data = {};
}
});
// Mark empty nodes as loaded
_(nodes).each(function(node) {
if(node.isCountComplete() && node.infMinItemCount === 0) {
node.isLoaded = true;
}
});
var uncountedNodes = _(nodes).filter(function(node) { return self.isCountingNeeded(node); });
// console.log("# uncounted nodes", uncountedNodes.length);
// The deferred is only resolved once the whole workflow completed
var result = $.Deferred();
var countTasks = this.createCountTasks(uncountedNodes);
$.when.apply(window, countTasks).done(function() {
var nonLoadedNodes = _(nodes).filter(function(node) { return self.isLoadingNeeded(node); });
// console.log("# non loaded nodes", nonLoadedNodes.length,
// nonLoadedNodes);
var loadTasks = self.createLoadTasks(nonLoadedNodes);
$.when.apply(window, loadTasks).done(function() {
// ns.QuadTreeCache.finalizeLoading(nodes);
result.resolve(nodes);
/*
$.when(self.postProcess(nodes)).then(function() {
// self.isLocked = false;
// console.debug("Workflow completed. Resolving
// deferred.");
result.resolve(nodes);
});
*/
});
}).fail(function() {
result.fail();
});
return result;
},
createCountTask: function(node) {
var self = this;
var threshold = self.maxItemsPerTileCount; // ? self.maxItemsPerTileCount + 1 : null;
var countPromise = this.listServiceBbox.fetchCount(node.getBounds(), threshold);
var result = countPromise.pipe(function(itemCountInfo) {
var itemCount = itemCountInfo.count;
node.setMinItemCount(itemCountInfo.count);
// If the value is 0, also mark the node as loaded
if(itemCount === 0) {
// self.initNode(node);
node.isLoaded = true;
}
});
// var countFlow =
// this.createFlowForBounds(node.getBounds()).find().concept(this.concept).limit(limit);
// var result = countFlow.count().pipe(function(itemCountInfo) {
return result;
},
/**
* If either the minimum number of items in the node is above the
* threshold or all children have been counted, then there is NO need
* for counting
*
*/
isCountingNeeded: function(node) {
// console.log("Node To Count:", node, node.isCountComplete());
return !(this.isTooManyGeoms(node) || node.isCountComplete());
},
/**
* Loading is needed if NONE of the following criteria applies: . node
* was already loaded . there are no items in the node . there are to
* many items in the node
*
*/
isLoadingNeeded: function(node) {
// (node.data && node.data.isLoaded)
var noLoadingNeeded = node.isLoaded || (node.isCountComplete() && node.infMinItemCount === 0) || this.isTooManyGeoms(node);
return !noLoadingNeeded;
},
isTooManyGeoms: function(node) {
// console.log("FFS", node.infMinItemCount, node.getMinItemCount());
return node.infMinItemCount >= this.maxItemsPerTileCount;
},
createCountTasks: function(nodes) {
var self = this;
var result = _(nodes).chain()
.map(function(node) {
return self.createCountTask(node);
})
.compact() // Remove null entries
.value();
/*
* var result = []; $.each(nodes, function(i, node) { var task =
* self.createCountTask(node); if(task) { result.push(task); } });
*/
return result;
},
/**
* Sets the node's state to loaded, attaches the geomToFeatureCount to
* it.
*
* @param node
* @param geomToFeatureCount
*/
loadTaskAction: function(node, docs) {
// console.log('Data for ' + node.getBounds() + ': ', docs);
node.data.docs = docs;
node.isLoaded = true;
},
createLoadTasks: function(nodes) {
var self = this;
var result = [];
//promises = this.lookupServiceBBox.fetchDataBounds()
// $.each(nodes, function(index, node) {
var result = _(nodes).map(function(node) {
// console.debug("Inferred minimum item count: ",
// node.infMinItemCount);
// if(node.data.absoluteGeomToFeatureCount)
// var loadFlow =
// self.createFlowForBounds(node.getBounds()).find().concept(self.concept);
//var loadTask = loadFlow.asList(true).pipe(function(docs) {
var loadTask = self.listServiceBbox.fetchItems(node.getBounds()).pipe(function(docs) {
self.loadTaskAction(node, docs);
});
return loadTask;
});
return result;
},
/**
* TODO Finishing this method at some point to merge nodes together
* could be useful
*
*/
finalizeLoading: function(nodes) {
// Restructure all nodes that have been completely loaded,
var parents = [];
$.each(nodes, function(index, node) {
if(node.parent) {
parents.push(node.parent);
}
});
parents = _.uniq(parents);
var change = false;
do {
change = false;
for(var i in parents) {
var p = parents[i];
var children = p.children;
var didMerge = ns.tryMergeNode(p);
if(!didMerge) {
continue;
}
change = true;
$.each(children, function(i, child) {
var indexOf = _.indexOf(nodes, child);
if(indexOf >= 0) {
nodes[indexOf] = undefined;
}
});
nodes.push(p);
if(p.parent) {
parents.push(p.parent);
}
break;
}
} while(change == true);
_.compact(nodes);
/*
* $.each(nodes, function(i, node) { node.isLoaded = true; });
*/
// console.log("All done");
// self._setNodes(nodes, bounds);
// callback.success(nodes, bounds);
}
});
/**
*
*
*/
ns.ListServiceConceptKeyLookup = Class.create(ns.ListService, {
// initialize: function(conceptLookupService, keyLookupService) {
initialize: function(sparqlService, keyLookupService) {
this.sparqlService = sparqlService;
this.keyLookupService = keyLookupService;
},
fetchItems: function(concept, limit, offset) {
var query = facete.ConceptUtils.createQueryList(concept, limit, offset);
var deferred = jQuery.Deferred();
var self = this;
ns.ServiceUtils.fetchList(query, concept.getVar()).pipe(function(items) {
self.keyLookupService.lookup(items).pipe(function(map) {
//deferred.resolve(map);
var entries = map.entries();
var r = _(entries).values();
deferred.resolve(map);
}).fail(function() {
deferred.reject();
});
}).fail(function() {
deferred.reject();
});
return deferred.promise();
},
fetchCount: function(concept, threshold) {
var result = ns.ServiceUtils.fetchCountConcept(concept, threshold);
return result;
}
});
/**
* If bounds is null, no restriction on the bbox is assumed
*/
ns.BBoxLookupService = Class.create({
lookupBBox: function(bounds) {
console.log('Not implemented');
throw 'Not implemented';
}
});
ns.BBoxLookupService = Class.create(ns.BBoxLookupService, {
});
ns.LookupServiceUtils = {
/**
* Yields a promise resolving to an empty array if lookupService or keys are null
*
*/
lookup: function(lookupService, keys) {
var result;
if(!lookupService || !keys) {
var deferred = jQuery.Deferred();
deferred.resolve([]);
result = deferred.promise();
} else {
result = lookupService.lookup(keys);
}
return result;
},
/**
* Create a new promise from a list of keys and corresponding
* valuePromises
*/
zip: function(keys, valuePromises) {
var result = jQuery.when.apply(window, valuePromises).pipe(function() {
var r = new util.HashMap();
for(var i = 0; i < keys.length; ++i) {
var bounds = keys[i];
var docs = arguments[i];
r.put(bounds, docs);
}
return r;
});
return result;
},
unmapKeys: function(keys, fn, map) {
var result = new util.HashMap();
_(keys).each(function(key) {
var k = fn(key);
var v = map.get(k);
r.put(key, v);
});
return result;
},
/**
* Performs a lookup by mapping the keys first
*/
fetchItemsMapped: function(lookupService, keys, fn) {
var ks = _(keys).map(fn);
var result = lookupService.fetchItems(ks).pipe(function(map) {
var r = ns.LookupServiceUtils.unmapKeys(keys, fn, map);
return r;
});
return result;
},
fetchCountsMapped: function(lookupService, keys, fn) {
var ks = _(keys).map(fn);
var result = lookupService.fetchCounts(ks).pipe(function(map) {
var r = ns.LookupServiceUtils.unmapKeys(keys, fn, map);
return r;
});
return result;
}
};
/**
* Fetches initial data based on a bbox, and uses this to request additional
* data from a key lookup service
*
*/
ns.BBoxLookupServiceKeys = Class.create(ns.BBoxLookupService, {
initialize: function() {
}
});
/**
*
*/
ns.BBoxLookupServiceSupplierDynamic = Class.create({
initialize: function(sparqlServiceSupplier, geoMapSupplier, conceptSupplier, quadTreeConfig) {
this.sparqlServiceSupplier = sparqlServiceSupplier;
this.geoMapSupplier = geoMapSupplier;
this.conceptSupplier = conceptSupplier;
this.quadTreeConfig = quadTreeConfig;
// this.conceptToService = {};
this.hashToCache = {};
},
fetchItems: function(bounds) {
// var sparqlService = this.sparqlService;
// var geoMapFactory = this.geoMapFactory;
// var conceptFactory = this.conceptFactory;
quadTreeConfig = quadTreeConfig || {};
_(quadTreeConfig).defaults(ns.ViewStateFetcher.defaultQuadTreeConfig);
// quadTreeConfig =
// var concept = conceptFactory.createConcept();
// TODO Make this configurable
var geoMap = geoMapFactory.createMapForGlobal();
// TODO This should be a concept, I assume
// var geoConcept = geoMap.createConcept();
var hash = ns.ViewStateUtils.createStateHash(sparqlService, geoMap, concept);
// TODO Combine the concept with the geoConcept...
// var serviceHash = sparqlService.getStateHash();
// var geoConceptHash =
// geoMap.getElementFactory().createElement().toString();
// console.log("[DEBUG] Query hash (including facets): " + hash);
var cacheEntry = this.hashToCache[hash];
if(!cacheEntry) {
cacheEntry = new ns.QuadTreeCache(sparqlService, geoMapFactory, concept, null, quadTreeConfig);
this.hashToCache[hash] = cacheEntry;
}
var nodePromise = cacheEntry.fetchData(bounds);
// Create a new view state object
var result = nodePromise.pipe(function(nodes) {
var r = new ns.ViewState(sparqlService, geoMap, concept, bounds, nodes);
return r;
});
return result;
}
});
/*
ns.ViewStateFetcher.defaultQuadTreeConfig = {
maxItemsPerTileCount: 1000,
maxGlobalItemCount: 5000
};
*/
/**
*
*/
/*
ns.LookupServicePremapKeys = Class.create({
initialize: function(lookupService, itemToKeyFn) {
this.lookupService = lookupService;
this.itemToKeyFn = itemToKeyFn;
},
lookup: function(keys) {
var promise = ns.LookupServiceUtils.fetchDataMapped(this.lookupService, keys, this.itemToKeyFn);
var result = promise.pipe(function(map) {
var r = ns.LookupServiceUtils.unmapKeys(keys, fn, map);
return r;
});
return result;
}
});
*/
})();