blob: e68094f1b9359bb1bdfbc1f22fa6a1ab17d27349 [file] [log] [blame]
'use strict';
// Controller for the world map
function WorldMapCtrl($rootScope, $scope, selectedDatasetInformation, regionSelectParams) {
$scope.datasets = selectedDatasetInformation.getDatasets();
$scope.regionParams = regionSelectParams.getParameters();
$scope.updateMap = function() {
// Clear Group of layers from map if it exists
if ("rectangleGroup" in $rootScope) {
$rootScope.rectangleGroup.clearLayers();
}
// Don't process if we don't have any datasets added!!
if ($scope.datasets.length == 0)
return;
if ("map" in $rootScope) {
// Create Group to add all rectangles to map
$rootScope.rectangleGroup = L.layerGroup();
// Loop through datasets and add rectangles to Group
var i = 0;
angular.forEach($scope.datasets, function(dataset) {
// Get bounds from dataset
var maplatlon = dataset.latlonVals;
var bounds = [[maplatlon.latMax, maplatlon.lonMin],
[maplatlon.latMin, maplatlon.lonMax]];
var polygon = L.rectangle(bounds,{
stroke: false,
fillColor: $rootScope.fillColors[i],
fillOpacity: 0.3
});
// Add layer to Group
$rootScope.rectangleGroup.addLayer(polygon);
i++;
});
// Draw user selected region
if ($scope.regionParams.latMin != "" && $scope.regionParams.latMax != "" &&
$scope.regionParams.lonMin != "" && $scope.regionParams.lonMax != "") {
var bounds = [[$scope.regionParams.latMax, $scope.regionParams.lonMin],
[$scope.regionParams.latMin, $scope.regionParams.lonMax]];
var polygon = L.rectangle(bounds, {
color: '#000000',
opacity: 1.0,
fill: false,
});
$rootScope.rectangleGroup.addLayer(polygon);
}
// Add rectangle Group to map
$rootScope.rectangleGroup.addTo($rootScope.map);
}
};
$scope.$watch('datasets', function() {
$scope.updateMap();
}, true);
$scope.$watch('regionParams', function() {
$scope.updateMap();
}, true);
};
// Controller for dataset parameter selection/modification
function ParameterSelectCtrl($rootScope, $scope, $http, $timeout, selectedDatasetInformation, regionSelectParams) {
$scope.datasets = selectedDatasetInformation.getDatasets();
// The min/max lat/lon values from the selected datasets
$scope.latMin = -90;
$scope.latMax = 90;
$scope.lonMin = -180;
$scope.lonMax = 180;
$scope.start = "1980-01-01 00:00:00";
$scope.end = "2030-01-01 00:00:00";
// The min/max lat/lon values entered by the user
$scope.enteredLatMin = "";
$scope.enteredLatMax = "";
$scope.enteredLonMin = "";
$scope.enteredLonMax = "";
$scope.enteredStart = "";
$scope.enteredEnd = "";
// The min/max lat/lon values that are displayed
$scope.displayParams = regionSelectParams.getParameters();
$scope.runningEval = false;
var updateDisplayValues = function() {
// Update the displayed lat/lon values. We give precedence to users entered values assuming
// they're valid given the current set of datasets selected.
//
// If the user has entered a value for latMin
if ($scope.enteredLatMin != "") {
// If it's not a valid value...
if ($scope.enteredLatMin < $scope.latMin) {
// Reset enteredLatMin to the "unmodified" state and display the correct value.
$scope.displayParams.latMin = $scope.latMin;
} else {
$scope.displayParams.latMin = $scope.enteredLatMin;
}
// Otherwise, just display the value.
} else {
$scope.displayParams.latMin = $scope.latMin;
}
// Update latMax
if ($scope.enteredLatMin != "") {
if ($scope.enteredLatMax > $scope.latMax) {
$scope.displayParams.latMax = $scope.latMax;
} else {
$scope.displayParams.latMax = $scope.enteredLatMax;
}
} else {
$scope.displayParams.latMax = $scope.latMax;
}
// Update lonMin
if ($scope.enteredLonMin != "") {
if ($scope.enteredLonMin < $scope.lonMin) {
$scope.displayParams.lonMin = $scope.lonMin;
} else {
$scope.displayParams.lonMin = $scope.enteredLonMin;
}
} else {
$scope.displayParams.lonMin = $scope.lonMin;
}
// Update lonMax
if ($scope.enteredLonMax != "") {
if ($scope.enteredLonMax > $scope.lonMax) {
$scope.displayParams.lonMax = $scope.lonMax;
} else {
$scope.displayParams.lonMax = $scope.enteredLonMax;
}
} else {
$scope.displayParams.lonMax = $scope.lonMax;
}
// Update Start time
if ($scope.enteredStart != "") {
if ($scope.enteredStart < $scope.start) {
$scope.displayParams.start = $scope.start;
} else {
$scope.displayParams.start = $scope.enteredStart;
}
} else {
$scope.displayParams.start = $scope.start;
}
// Update End time
if ($scope.enteredEnd != "") {
if ($scope.enteredEnd > $scope.end) {
$scope.displayParams.end = $scope.end;
} else {
$scope.displayParams.end = $scope.enteredEnd;
}
} else {
$scope.displayParams.end = $scope.end;
}
}
$scope.shouldDisableControls = function() {
return (selectedDatasetInformation.getDatasetCount() < 2);
}
$scope.shouldDisableEvaluate = function() {
return ($scope.shouldDisableControls() || $scope.runningEval);
}
$scope.shouldDisableClearButton = function() {
return (selectedDatasetInformation.getDatasetCount() == 0);
}
$scope.shouldDisableResultsView = function() {
var res = false;
if ($rootScope.evalResults == "")
res = true;
return res;
}
$scope.clearDatasets = function() {
selectedDatasetInformation.clearDatasets();
}
$scope.runEvaluation = function() {
$scope.runningEval = true;
// TODO
// Currently this has the 1 model, 1 observation format hard coded in. This shouldn't
// be the long-term case! This needs to be changed!!!!!!!!
var obsIndex = -1,
modelIndex = -1;
for (var i = 0; i < $scope.datasets.length; i++) {
if ($scope.datasets[i]['isObs'] == 1)
obsIndex = i;
else
modelIndex = i;
}
// You might wonder why this is using a jQuery ajax call instead of a built
// in $http.post call. The reason would be that it wasn't working with the
// $http.post call but it is with this. So...there you go! This should be
// changed eventually!!
$.ajax({
type: 'POST',
url: $rootScope.baseURL + '/rcmes/run/',
data: {
'obsDatasetId' : $scope.datasets[obsIndex]['id'],
'obsParameterId' : $scope.datasets[obsIndex]['param'],
'startTime' : $scope.displayParams.start,
'endTime' : $scope.displayParams.end,
'latMin' : $scope.displayParams.latMin,
'latMax' : $scope.displayParams.latMax,
'lonMin' : $scope.displayParams.lonMin,
'lonMax' : $scope.displayParams.lonMax,
'filelist' : $scope.datasets[modelIndex]['id'],
'modelVarName' : $scope.datasets[modelIndex]['param'],
'modelTimeVarName' : $scope.datasets[modelIndex]['time'],
'modelLatVarName' : $scope.datasets[modelIndex]['lat'],
'modelLonVarName' : $scope.datasets[modelIndex]['lon'],
'regridOption' : 'model',
'timeRegridOption' : 'monthly',
'metricOption' : 'bias',
},
success: function(data) {
var comp = data['comparisonPath'].split('/');
var model = data['modelPath'].split('/');
var obs = data['obsPath'].split('/');
$rootScope.evalResults = {};
$rootScope.evalResults.comparisonPath = comp[comp.length - 1];
$rootScope.evalResults.modelPath = model[model.length - 1];
$rootScope.evalResults.obsPath = obs[obs.length - 1];
$scope.runningEval = false;
$timeout(function() {
$('#evaluationResults').trigger('modalOpen', true, true);
}, 100);
},
error: function(xhr, status, error) {
$scope.runningEval = false;
},
});
}
$scope.updateParameters = function() {
// Save the user input, even if it isn't valid.
$scope.enteredLatMin = $scope.displayParams.latMin;
$scope.enteredLatMax = $scope.displayParams.latMax;
$scope.enteredLonMin = $scope.displayParams.lonMin;
$scope.enteredLonMax = $scope.displayParams.lonMax;
$scope.enteredStart = $scope.displayParams.start;
$scope.enteredEnd = $scope.displayParams.end;
// Check if the user values are valid and update the display values.
updateDisplayValues();
}
$scope.$watch('datasets',
function() {
var numDatasets = $scope.datasets.length;
if (numDatasets) {
var latMin = -90,
latMax = 90,
lonMin = -180,
lonMax = 180,
start = "1980-01-01 00:00:00",
end = "2030-01-01 00:00:00";
// Get the valid lat/lon range in the selected datasets.
for (var i = 0; i < numDatasets; i++) {
var curDataset = $scope.datasets[i];
latMin = (curDataset['latlonVals']['latMin'] > latMin) ? curDataset['latlonVals']['latMin'] : latMin;
latMax = (curDataset['latlonVals']['latMax'] < latMax) ? curDataset['latlonVals']['latMax'] : latMax;
lonMin = (curDataset['latlonVals']['lonMin'] > lonMin) ? curDataset['latlonVals']['lonMin'] : lonMin;
lonMax = (curDataset['latlonVals']['lonMax'] < lonMax) ? curDataset['latlonVals']['lonMax'] : lonMax;
start = (curDataset['timeVals']['start'] > start) ? curDataset['timeVals']['start'] : start;
end = (curDataset['timeVals']['end'] < end) ? curDataset['timeVals']['end'] : end;
}
}
$scope.latMin = latMin;
$scope.latMax = latMax;
$scope.lonMin = lonMin;
$scope.lonMax = lonMax;
$scope.start = start;
$scope.end = end;
updateDisplayValues();
}, true);
}
// Controller for dataset display
function DatasetDisplayCtrl($rootScope, $scope, selectedDatasetInformation) {
$scope.datasets = selectedDatasetInformation.getDatasets();
$scope.removeDataset = function($index) {
selectedDatasetInformation.removeDataset($index);
}
}
// Controller for observation selection in modal
function ObservationSelectCtrl($rootScope, $scope, $http, $q, $timeout, selectedDatasetInformation) {
// Grab a copy of the datasets so we can display a count to the user!
$scope.datasetCount = selectedDatasetInformation.getDatasets();
// Initalize the option arrays and default to the first element
$scope.params = ["Please select a file above"];
$scope.paramSelect = $scope.params[0];
$scope.lats = ["Please select a file above"];
$scope.latsSelect = $scope.lats[0];
$scope.lons = ["Please select a file above"];
$scope.lonsSelect = $scope.lons[0];
$scope.times = ["Please select a file above"];
$scope.timeSelect = $scope.times[0];
// Grab the path leader information that the webserver is using to limit directory access.
$scope.pathLeader = 'False';
$http.jsonp($rootScope.baseURL + '/getPathLeader/?callback=JSON_CALLBACK').
success(function(data) {
$scope.pathLeader = data.leader;
});
// Toggle load button view depending on upload state of selected files
$scope.loadingFile = false;
// Toggle display of a confirmation when loading a dataset
$scope.fileAdded = false;
$scope.latLonVals = [];
$scope.timeVals = [];
$scope.localSelectForm = {};
$scope.uploadLocalFile = function() {
$scope.loadingFile = true;
// TODO: Need to try to validate the input a bit. At least make sure we're not
// pointing at a directory perhaps?
// TODO: Two-way binding with ng-model isn't being used here because it fails to update
// properly with the auto-complete that we're using on the input box. So we're doing
// it the wrong way temporarily...
var input = $('#observationFileInput').val();
// If the backend is limiting directory access we need to add that leader to our path
// so it remains valid!
if ($scope.pathLeader != 'False') {
input = $scope.pathLeader + input
}
// TODO: We're not really handling the case where there is a failure here at all.
// Should check for fails and allow the user to make changes.
//
// Get model variables
var varsPromise = $http.jsonp($rootScope.baseURL + '/list/vars/"' + input + '"?callback=JSON_CALLBACK');
// Get Lat and Lon variables
var latlonPromise = $http.jsonp($rootScope.baseURL + '/list/latlon/"' + input + '"?callback=JSON_CALLBACK');
// Get Time variables
var timesPromise = $http.jsonp($rootScope.baseURL + '/list/time/"' + input + '"?callback=JSON_CALLBACK');
$q.all([varsPromise, latlonPromise, timesPromise]).then(
// Handle success fetches!
function(arrayOfResults) {
$scope.loadingFile = false;
// Handle lat/lon results
var data = arrayOfResults[1].data;
$scope.lats = [data.latname];
$scope.lons = [data.lonname];
$scope.latLonVals = [data.latMin, data.latMax, data.lonMin, data.lonMax];
// If there is more than one option for the user, tell them they need to pick one!
if ($scope.lats.length > 1) $scope.lats.splice(0, 0, "Please select an option");
if ($scope.lons.length > 1) $scope.lons.splice(0, 0, "Please select an option");
// Default the display to the first available option.
$scope.latsSelect = $scope.lats[0];
$scope.lonsSelect = $scope.lons[0];
// Handle time results
var data = arrayOfResults[2].data
$scope.times = [data.timename];
if ($scope.times.length > 1) $scope.times.splice(0, 0, "Please select an option");
$scope.timeSelect = $scope.times[0];
// Handle parameter results
var data = arrayOfResults[0].data.variables;
$scope.params = (data instanceof Array) ? data : [data];
$scope.params = $.grep($scope.params,
function(val) {
return ($.inArray(val, $scope.lats) != 0 &&
$.inArray(val, $scope.lons) != 0 &&
$.inArray(val, $scope.times) != 0);
});
if ($scope.params.length > 1) $scope.params.splice(0, 0, "Please select an option");
$scope.paramSelect = $scope.params[0];
},
// Uh oh! AT LEAST on of our fetches failed
function(arrayOfFailure) {
$scope.loadingFile = false;
$scope.params = ["Unable to load variable(s)"];
$scope.paramSelect = $scope.params[0];
$scope.lats = ["Unable to load variable(s)"];
$scope.latsSelect = $scope.lats[0];
$scope.lons = ["Unable to load variable(s)"];
$scope.lonsSelect = $scope.lons[0];
$scope.times = ["Unable to load variable(s)"];
$scope.timeSelect = $scope.times[0];
}
);
};
$scope.addDataSet = function() {
// TODO: Need to verify that all the variables selected are correct!!!
// TODO: We shouldn't allow different parameters to match the same variables!!
var newDataset = {};
var input = $('#observationFileInput').val();
// If the backend is limiting directory access we need to add that leader to our path
// so it remains valid!
if ($scope.pathLeader != 'False') {
input = $scope.pathLeader + input
}
newDataset['isObs'] = 0;
// Save the model path. Note that the path is effectively the "id" for the model.
newDataset['id'] = input;
// Grab the file name later for display purposes.
var splitFilePath = input.split('/');
newDataset['name'] = splitFilePath[splitFilePath.length - 1];
// Save the model parameter variable. We save it twice for consistency and display convenience.
newDataset['param'] = $scope.paramSelect;
newDataset['paramName'] = newDataset['param'];
// Save the lat/lon information
newDataset['lat'] = $scope.latsSelect;
newDataset['lon'] = $scope.lonsSelect;
newDataset['latlonVals'] = {"latMin": $scope.latLonVals[0], "latMax": $scope.latLonVals[1],
"lonMin": $scope.latLonVals[2], "lonMax": $scope.latLonVals[3]};
// Get the time information
newDataset['time'] = $scope.timeSelect;
newDataset['timeVals'] = {"start": $scope.timeVals[0], "end": $scope.timeVals[1]};
selectedDatasetInformation.addDataset(newDataset);
// Reset all the fields!!
$scope.params = ["Please select a file above"];
$scope.paramSelect = $scope.params[0];
$scope.lats = ["Please select a file above"];
$scope.latsSelect = $scope.lats[0];
$scope.lons = ["Please select a file above"];
$scope.lonsSelect = $scope.lons[0];
$scope.times = ["Please select a file above"];
$scope.timeSelect = $scope.times[0];
$scope.latLonVals = [];
$scope.timeVals = [];
// Clear the input box
$('#observationFileInput').val("");
// Display a confirmation message for a little bit
$scope.fileAdded = true;
$timeout(function() {
$scope.fileAdded = false;
}, 2000);
}
$scope.shouldDisableLoadButton = function() {
return $scope.loadingFile;
}
}
function RcmedSelectionCtrl($rootScope, $scope, $http, $timeout, selectedDatasetInformation) {
// Grab a copy of the datasets so we can display a count to the user!
$scope.datasetCount = selectedDatasetInformation.getDatasets();
$scope.fileAdded = false;
var getObservations = function() {
$http.jsonp($rootScope.baseURL + '/getObsDatasets?callback=JSON_CALLBACK').
success(function(data) {
$scope.availableObs = data;
$scope.availableObs.splice(0, 0, {longname: 'Please select an option'});
$scope.datasetSelection = $scope.availableObs[0];
}).
error(function(data) {
$scope.availableObs = ["Unable to query RCMED"]
});
};
var getObservationTimeRange = function(datasetID) {
var times = {
'1' : {'start' : '1989-01-01 00:00:00','end' : '2009-12-31 00:00:00'}, // ERA-Interim
'2' : {'start' : '2002-08-31 00:00:00','end' : '2010-01-01 00:00:00'}, // AIRS
'3' : {'start' : '1998-01-01 00:00:00','end' : '2010-01-01 00:00:00'}, // TRMM
'4' : {'start' : '1948-01-01 00:00:00','end' : '2010-01-01 00:00:00'}, // URD
'5' : {'start' : '2000-02-24 00:00:00','end' : '2010-05-30 00:00:00'}, // MODIS
'6' : {'start' : '1901-01-01 00:00:00','end' : '2006-12-01 00:00:00'} // CRU
};
return ((datasetID in times) ? times[datasetID] : false);
};
$scope.dataSelectUpdated = function() {
var urlString = $rootScope.baseURL + '/getDatasetParam?dataset=' +
$scope.datasetSelection["shortname"] +
"&callback=JSON_CALLBACK";
$http.jsonp(urlString).
success(function(data) {
$scope.retrievedObsParams = data;
if ($scope.retrievedObsParams.length > 1)
$scope.retrievedObsParams.splice(0, 0, {shortname: 'Please select a parameter'});
$scope.parameterSelection = $scope.retrievedObsParams[0];
});
};
$scope.addObservation = function() {
// This is a horrible hack for temporarily getting a valid time range
// for the selected observation. Eventually we need to handle this more
// elegantly than indexing into an array...
var timeRange = getObservationTimeRange($scope.datasetSelection["dataset_id"]);
var newDataset = {};
newDataset['isObs'] = 1;
// Save the dataset id (the important part) and name (for display purposes)
newDataset['id'] = $scope.datasetSelection['dataset_id'];
newDataset['name'] = $scope.datasetSelection['longname'];
// Save the parameter id (the important part) and name (for display purposes)
newDataset['param'] = $scope.parameterSelection['parameter_id'];
newDataset['paramName'] = $scope.parameterSelection['longname'];
// Save the (fake) lat/lon information. Our datasets cover the entire globe (I think...)
newDataset['latlonVals'] = {"latMin": -90, "latMax": 90, "lonMin": -180, "lonMax": 180};
// Set some defaults for lat/lon variable names. This just helps us display stuff later.
newDataset['lat'] = "N/A";
newDataset['lon'] = "N/A";
// Save time range information. If we don't have saved data for this observation then
// we set the values to extreme values so they'll be ignored when calculating overlaps.
newDataset['timeVals'] = {"start": (timeRange) ? timeRange['start'] : "1901-01-01 00:00:00",
"end": (timeRange) ? timeRange['end'] : "2050-01-01 00:00:00"};
// Set a default for the time variable names for display convenience.
newDataset['time'] = "N/A";
selectedDatasetInformation.addDataset(newDataset);
// Clear the user selections by requery-ing RCMED. This is really hacky, but it works for now...
$scope.availableObs = [];
$scope.retrievedObsParams = [];
getObservations();
// Display a confirmation message for a little bit
$scope.fileAdded = true;
$timeout(function() {
$scope.fileAdded = false;
}, 2000);
};
// Grab the available observations from RCMED
getObservations();
}