terriajs
Version:
Geospatial data visualization platform.
210 lines (174 loc) • 8 kB
JavaScript
'use strict';
/*global require*/
var CatalogFunction = require('./CatalogFunction');
var defined = require('terriajs-cesium/Source/Core/defined');
var defineProperties = require('terriajs-cesium/Source/Core/defineProperties');
// var extendLoad = require('./extendLoad');
var GeoJsonCatalogItem = require('./GeoJsonCatalogItem');
var inherit = require('../Core/inherit');
var invokeTerriaAnalyticsService = require('./invokeTerriaAnalyticsService');
var TerriaError = require('../Core/TerriaError');
var RegionDataParameter = require('./RegionDataParameter');
var RegionParameter = require('./RegionParameter');
var RegionTypeParameter = require('./RegionTypeParameter');
// var updateRectangleFromRegion = require('./updateRectangleFromRegion');
var when = require('terriajs-cesium/Source/ThirdParty/when');
/**
* A Terria Spatial Inference function to determines the characteristics by which a particular region is _most different_
* from all other regions.
*
* @alias WhyAmISpecialCatalogFunction
* @constructor
* @extends CatalogFunction
*
* @param {Terria} terria The Terria instance.
*/
var WhyAmISpecialCatalogFunction = function(terria) {
CatalogFunction.call(this, terria);
this.url = undefined;
this.name = 'What makes this region unique or special?';
this.description = 'Determines the characteristics by which a particular region is _most different_ from all other regions.';
this._regionTypeParameter = new RegionTypeParameter({
terria: this.terria,
catalogFunction: this,
id: 'regionType',
name: 'Region Type',
description: 'The type of region to analyze.'
});
this._regionParameter = new RegionParameter({
terria: this.terria,
catalogFunction: this,
id: 'region',
name: 'Region',
description: 'The region to analyze. The analysis will determine the characteristics by which this region is most different from all others.',
regionProvider: this._regionTypeParameter
});
this._dataParameter = new RegionDataParameter({
terria: this.terria,
catalogFunction: this,
id: 'data',
name: 'Characteristics',
description: 'The region characteristics to include in the analysis.',
regionProvider: this._regionTypeParameter
});
this._parameters = [
this._regionTypeParameter,
this._regionParameter,
this._dataParameter
];
};
inherit(CatalogFunction, WhyAmISpecialCatalogFunction);
defineProperties(WhyAmISpecialCatalogFunction.prototype, {
/**
* Gets the type of data member represented by this instance.
* @memberOf WhyAmISpecialCatalogFunction.prototype
* @type {String}
*/
type : {
get : function() {
return 'why-am-i-special-function';
}
},
/**
* Gets a human-readable name for this type of data source, 'Why Am I Special?'.
* @memberOf WhyAmISpecialCatalogFunction.prototype
* @type {String}
*/
typeName : {
get : function() {
return 'Why Am I Special?';
}
},
/**
* Gets the parameters used to {@link CatalogProcess#invoke} to this function.
* @memberOf WhyAmISpecialCatalogFunction
* @type {CatalogProcessParameters[]}
*/
parameters : {
get : function() {
return this._parameters;
}
}
});
WhyAmISpecialCatalogFunction.prototype._load = function() {
};
/**
* Invokes the function.
* @return {AsyncProcessResultCatalogItem} The result of invoking this process. Because the process typically proceeds asynchronously, the result is a temporary
* catalog item that resolves to the real one once the process finishes.
*/
WhyAmISpecialCatalogFunction.prototype.invoke = function() {
var region = this._regionParameter.value;
var data = this._dataParameter.getRegionDataValue();
if (!defined(region)) {
throw new TerriaError({
title: 'Region not selected',
message: 'You must select a Region.'
});
}
var regionProvider = this._regionTypeParameter.value;
var request = {
algorithm: 'whyamispecial',
boundaries_name: regionProvider.regionType,
region_codes: data.regionCodes,
columns: data.columnHeadings,
table: data.table,
parameters: {
query: region.id
}
};
var regionIndex = regionProvider.regions.indexOf(region);
var regionName = region.id;
if (regionIndex >= 0) {
regionName = regionProvider.regionNames[regionIndex] || regionName;
}
var name = 'Why is ' + regionName + ' special?';
var geoJsonPromise = regionProvider.getRegionFeature(this.terria, region);
var invocationPromise = invokeTerriaAnalyticsService(this.terria, name, this.url, request);
var that = this;
return when.all([geoJsonPromise, invocationPromise]).then(function(allResults) {
var geoJson = allResults[0];
var invocationResult = allResults[1];
var result = invocationResult.result;
var catalogItem = new GeoJsonCatalogItem(that.terria);
catalogItem.name = name;
catalogItem.data = geoJson;
catalogItem.dataUrl = invocationResult.url;
var description = 'This is the result of invoking "' + that.name + '" at ' + invocationResult.startDate + ' with these parameters:\n\n';
description += ' * ' + regionProvider.regionType + ' Region: ' + regionName + ' (' + region.id + ')\n';
description += ' * Characteristics: ' + data.columnHeadings.join(', ') + '\n';
catalogItem.description = description;
var shortReport = '<p>These are the top characteristics that make ' + regionName + ' unique or special:</p>';
for (var i = 0; i < 5 && i < result.columns.length; ++i) {
var columnName = data.columnHeadings[result.columns[i]];
var histogram = result.histograms[i];
var binCounts = histogram.bin_counts;
var binEdges = histogram.bin_edges;
var chartData = [['Value', 'Count']];
for (var j = 0; j < binCounts.length; ++j) {
var leftEdge = binEdges[j];
var rightEdge = binEdges[j + 1];
if (!defined(rightEdge)) {
// Assume equally-spaced bins.
rightEdge = leftEdge + (binEdges[1] - binEdges[0]);
}
var center = (leftEdge + rightEdge) * 0.5;
chartData.push([center, binCounts[j]]);
}
var percentile = result.percentiles[i];
var percentileDescription = 'lower';
if (percentile > 50) {
percentile = 100 - percentile;
percentileDescription = 'higher';
}
shortReport += '<collapsible title="' + columnName + '" open="' + (i === 0 ? 'true' : 'false') + '">\n';
shortReport += '<p style="font-size: 0.8em">The value of ' + columnName + ' in ' + regionName + ' is ' + result.values[i] + '.</p>\n';
shortReport += '<p style="font-size: 0.8em">Only ' + percentile.toFixed(1) + '% of regions have an equal or ' + percentileDescription + ' value.</p>\n';
shortReport += '<chart title="Distribution of ' + columnName + ' across ' + regionProvider.regionType + ' regions" id="' + columnName + '" data=\'' + JSON.stringify(chartData) + '\' column-names="," y-column="1" styling="histogram" highlight-x="' + result.values[i] + '" hide-buttons="true"></chart>\n';
shortReport += '</collapsible>\n';
}
catalogItem.shortReport = shortReport;
catalogItem.isEnabled = true;
});
};
module.exports = WhyAmISpecialCatalogFunction;