diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/hexagram.js
--- a/hexagram/hexagram.js Wed Jun 19 15:04:17 2013 -0400
+++ b/hexagram/hexagram.js Wed Oct 16 18:48:28 2013 -0400
@@ -23,6 +23,9 @@
// This is a list of layer names maintained in sorted order.
var layer_names_sorted = [];
+// This is a list of the map-layour names mantained in order of entry
+var layout_names = [];
+
// This holds an array of layer names that the user has added to the "shortlist"
// They can be quickly selected for display.
var shortlist = [];
@@ -31,6 +34,52 @@
// elements, so we can efficiently tell if e.g. one is selected.
var shortlist_ui = {};
+// This is a list of layer names whose intersection checkbox has been selected.
+var shortlist_intersection = [];
+
+//This is the number of intersection checkboxes that have been selected.
+var shortlist_intersection_num = 0;
+
+// This is a list of layer names whose union checkbox has been selected.
+var shortlist_union = [];
+
+//This is the number of union checkboxes that have been selected.
+var shortlist_union_num = 0;
+
+//This is a list of layer names whose set difference checkbox has been selected.
+var shortlist_set_difference = [];
+
+// This is the number of set difference checkboxes that have been selected.
+var shortlist_set_difference_num = 0;
+
+// This is a list of the layer names whose symmetric difference checkbox
+// has been selected.
+var shortlist_symmetric_difference = [];
+
+// This is the number of symmetric difference checkboxes that have been
+// selected.
+var shortlist_symmetric_difference_num = 0;
+
+// This is an array containing the layer whose absolute complement checkbox
+// has been selected.
+var shortlist_absolute_complement = [];
+
+// This is the number of absolute complement checkboxes that have been selected.
+var shortlist_absolute_complement_num = 0;
+
+// Records number of set-operation clicks
+var set_operation_clicks = 0;
+
+// Boolean stating whether this is the first time the set operation popup
+// has been created so that "Select Layer" Default is added only once
+var first_opening = true;
+
+// Boolean for Creating Layer from Filter
+var created = false;
+
+// Stores the Name of Current Layer Displayed
+var current_layout_name;
+
// This holds colormaps (objects from layer values to category objects with a
// name and color). They are stored under the name of the layer they apply to.
var colormaps = {}
@@ -118,6 +167,10 @@
"mousemove"
];
+// This is a global variable that keeps track of the current Goolge Map zoom
+// This is needed to keep viewing consistent across layouts
+var global_zoom = 0;
+
function print(text) {
// Print some logging text to the browser console
@@ -130,7 +183,7 @@
function complain(text) {
// Display a temporary error message to the user.
$("#error-notification").text(text);
- $(".error").show().delay(1000).fadeOut(400);
+ $(".error").show().delay(1250).fadeOut(1000);
if(console && console.error) {
// Inform the browser console of this problem.as
@@ -400,7 +453,9 @@
data: data,
magnitude: undefined
};
-
+
+ var check_layer_exists = layers[layer_name];
+
for(var name in attributes) {
// Copy over each specified attribute
layers[layer_name][name] = attributes[name];
@@ -422,6 +477,7 @@
// First get what we have stored for the layer
var layer = layers[layer_name];
+ var data_val = layer.data;
if(layer.data == undefined) {
// We need to download the layer.
print("Downloading \"" + layer.url + "\"");
@@ -571,6 +627,7 @@
// Return a jQuery element representing the layer with the given name in the
// shortlist UI.
+
// This holds the root element for this shortlist UI entry
var root = $("").addClass("shortlist-entry");
root.data("layer", layer_name);
@@ -610,14 +667,15 @@
fill_layer_metadata(metadata_holder, layer_name);
contents.append(metadata_holder);
-
- // Add a div to hold the filtering stuff so it wraps together.
+
+ // Add a div to hold the filtering stuff so it wraps together.
var filter_holder = $("").addClass("filter-holder");
// Add an image label for the filter control.
- // TODO: put this in a label
- var filter_image = $("").attr("src", "filter.svg")
+ // TODO: put this in a label
+ var filter_image = $("").attr("src", "filter.svg");
filter_image.addClass("control-icon");
+ filter_image.addClass("filter-image");
filter_image.attr("title", "Filter on Layer");
filter_image.addClass("filter");
@@ -636,9 +694,16 @@
// Add a select input to pick from a discrete list of values to filter on
var filter_value = $("").addClass("filter-value");
- filter_holder.append(filter_value);
-
- contents.append(filter_holder);
+ filter_holder.append(filter_value);
+
+ // Add a image for the save function
+ var save_filter = $("").attr("src", "save.svg");
+ save_filter.addClass("save-filter");
+ save_filter.attr("title", "Save Filter as Layer");
+
+ contents.append(filter_holder);
+ contents.append(save_filter);
+
if(layers[layer_name].selection) {
// We can do statistics on this layer.
@@ -738,17 +803,18 @@
// Remove this from the DOM
root.remove();
-
+
// Make the UI match the list.
update_shortlist_ui();
-
+
if(checkbox.is(":checked") || filter_control.is(":checked")) {
// Re-draw the view since we were selected (as coloring or filter)
// before removal.
refresh();
}
+
});
-
+
// Functionality for turning filtering on and off
filter_control.change(function() {
if(filter_control.is(":checked")) {
@@ -793,14 +859,43 @@
filter_threshold.show();
}
- // Now that the right controls are there, assume they have
+ save_filter.show ();
+
+ save_filter.button().click(function() {
+ // Configure Save Filter Buttons
+
+ // Get selected value
+ var selected = filter_value.prop("selectedIndex");
+ var value = filter_value.val();
+
+ var signatures = [];
+
+ // Gather Tumor-ID Signatures with value and push to "signatures"
+ for (hex in polygons){
+ if (layer.data[hex] == value){
+ signatures.push(hex);
+ }
+ }
+
+ // Create Layer
+ if (created == false) {
+ select_list (signatures, "user selection");
+ created = true;
+ }
+ created = false;
+ });
+
+
+ // Now that the right controls are there, assume they have
refresh();
+
});
} else {
+ created = false;
// Hide the filtering settings
filter_value.hide();
filter_threshold.hide();
-
+ save_filter.hide();
// Draw view since we're no longer filtering on this layer.
refresh();
}
@@ -845,6 +940,331 @@
return root;
}
+// ____________________________________________________________________________
+// Replacement Set Operation Code
+// ____________________________________________________________________________
+function get_set_operation_selection () {
+ // For the new dop-down GUI for set operation selection
+ // we neeed a function to determine which set operation is selected.
+ // This way we can display the appropriate divs.
+
+ // Drop Down List & Index for Selected Element
+ var drop_down = document.getElementById("set-operations-list");
+ var index = drop_down.selectedIndex;
+ var selection = drop_down.options[index];
+
+ return selection;
+}
+
+function show_set_operation_drop_down () {
+ // Show Set Operation Drop Down Menu
+ document.getElementsByClassName("set-operation-col")[0].style.visibility="visible";
+ document.getElementsByClassName("set-operation-panel-holder")[0].style.visibility="visible";
+ document.getElementsByClassName("set-operation-panel")[0].style.visibility="visible";
+ document.getElementById("set-operations").style.visibility="visible";
+ document.getElementsByClassName("set-operation-panel-title")[0].style.visibility="visible";
+ document.getElementsByClassName("set-operation-panel-contents")[0].style.visibility="visible";
+
+}
+
+function hide_set_operation_drop_down () {
+ // Hide Set Operation Drop Down Menu
+ document.getElementsByClassName("set-operation-col")[0].style.visibility="hidden";
+ document.getElementsByClassName("set-operation-panel-holder")[0].style.visibility="hidden";
+ document.getElementsByClassName("set-operation-panel")[0].style.visibility="hidden";
+ document.getElementById("set-operations").style.visibility="hidden";
+ document.getElementsByClassName("set-operation-panel-title")[0].style.visibility="hidden";
+ document.getElementsByClassName("set-operation-panel-contents")[0].style.visibility="hidden";
+
+ // Hide the Data Values for the Selected Layers
+ var drop_downs_layer_values = document.getElementsByClassName("set-operation-layer-value");
+ for (var i = 0; i < drop_downs_layer_values.length; i++) {
+ drop_downs_layer_values[i].style.visibility="hidden";
+ }
+
+ // Hide the Compute Button
+ var compute_button = document.getElementsByClassName("compute-button");
+ compute_button[0].style.visibility = "hidden";
+
+ // Set the "Select Layer" drop down to the default value
+ var list = document.getElementById("set-operations-list");
+ list.selectedIndex = 0;
+
+ var list_value = document.getElementsByClassName("set-operation-value");
+ list_value[0].selectedIndex = 0;
+ list_value[1].selectedIndex = 0;
+
+ // Remove all elements from drop downs holding the data values for the
+ // selected layers. This way there are no values presented when the user
+ // clicks on the set operation button to open it again.
+ var set_operation_layer_values = document.getElementsByClassName("set-operation-layer-value");
+ var length = set_operation_layer_values[0].options.length;
+ do{
+ set_operation_layer_values[0].remove(0);
+ length--;
+ }
+ while (length > 0);
+
+ var length = set_operation_layer_values[1].options.length;
+ do{
+ set_operation_layer_values[1].remove(0);
+ length--;
+ }
+ while (length > 0);
+
+}
+
+function create_set_operation_ui () {
+ // Returns a Jquery element that is then prepended to the existing
+ // set theory drop-down menu
+
+ // This holds the root element for this set operation UI
+ var root = $("").addClass("set-operation-entry");
+
+ // Add Drop Downs to hold the selected layers and and selected data values
+ var set_theory_value1 = $("").addClass("set-operation-value");
+ var set_theory_layer_value1 = $("").addClass("set-operation-layer-value");
+ var set_theory_value2 = $("").addClass("set-operation-value");
+ var set_theory_layer_value2 = $("").addClass("set-operation-layer-value");
+
+ var compute_button = $("").attr("type", "button");
+ compute_button.addClass ("compute-button");
+
+ // Append to Root
+ root.append (set_theory_value1);
+ root.append (set_theory_layer_value1);
+ root.append (set_theory_value2);
+ root.append (set_theory_layer_value2);
+ root.append (compute_button);
+
+ return root;
+}
+
+function update_set_operation_drop_down () {
+ // This is the onchange command for the drop down displaying the
+ // different set operation functions. It is called whenever the user changes
+ // the selected set operation.
+
+ // Get the value of the set operation selection made by the user.
+ var selection = get_set_operation_selection();
+ var value = selection.value;
+ // Check if the selectin value is that of one of set operation functions
+ if (selection.value == 1 || selection.value == 2
+ || selection.value == 3 || selection.value == 4
+ || selection.value == 5){
+ // Make the drop downs that hold layer names and data values visible
+ var drop_downs = document.getElementsByClassName("set-operation-value");
+ var drop_downs_layer_values = document.getElementsByClassName("set-operation-layer-value");
+
+ for (var i = 0; i < drop_downs.length; i++) {
+ drop_downs[i].style.visibility="visible";
+ }
+
+ for (var i = 0; i < drop_downs_layer_values.length; i++) {
+ drop_downs_layer_values[i].style.visibility="visible";
+ }
+
+ var compute_button = document.getElementsByClassName("compute-button");
+ compute_button[0].style.visibility = "visible";
+ compute_button[0].value = "Compute Set Operation";
+
+ if (first_opening == true) {
+ // Set the default value for the drop down, holding the selected layers
+ var default_value = document.createElement("option");
+ default_value.text = "Select Layer 1";
+ default_value.value = 0;
+ drop_downs[0].add(default_value);
+
+ var default_value2 = document.createElement("option");
+ default_value2.text = "Select Layer 2";
+ default_value2.value = 0;
+ drop_downs[1].add(default_value2);
+
+ // Prevent from adding the default value again
+ first_opening = false;
+ }
+
+ // Hide the second set of drop downs if "Not:" is selected
+ if (selection.value == 5) {
+ drop_downs[1].style.visibility="hidden";
+ drop_downs_layer_values[1].style.visibility="hidden";
+ }
+ }
+ else {
+ // If the user has the default value selected, hide all drop downs
+ var drop_downs = document.getElementsByClassName("set-operation-value");
+ for (var i = 0; i < drop_downs.length; i++) {
+ drop_downs[i].style.visibility="hidden";
+ }
+ var drop_downs_layer_values = document.getElementsByClassName("set-operation-layer-value");
+ for (var i = 0; i < drop_downs_layer_values.length; i++) {
+ drop_downs_layer_values[i].style.visibility="hidden";
+ }
+ var compute_button = document.getElementsByClassName("compute-button");
+ compute_button[0].style.visibility = "hidden";
+ }
+}
+
+function update_set_operation_selections () {
+ // This function is called when the shorlist is changed.
+ // It appropriately updates the drop down containing the list of layers
+ // to match the layers found in the shortlist.
+
+ // Get the list of all layers
+ var layers = [];
+ $("#shortlist").children().each(function(index, element) {
+ // Get the layer name
+ var layer_name = $(element).data("layer");
+ layers.push(layer_name);
+ });
+
+ // Get a list of all drop downs that contain layer names
+ var drop_downs = document.getElementsByClassName("set-operation-value");
+
+ // Remove all existing layer names from both dropdowns
+ var length = drop_downs[0].options.length;
+ do{
+ drop_downs[0].remove(0);
+ length--;
+ }
+ while (length > 0);
+ var length = drop_downs[1].options.length;
+ do{
+ drop_downs[1].remove(0);
+ length--;
+ }
+ while (length > 0);
+
+ // Add the default values that were stripped in the last step.
+ var default_value = document.createElement("option");
+ default_value.text = "Select Layer 1";
+ default_value.value = 0;
+ drop_downs[0].add(default_value);
+
+ var default_value2 = document.createElement("option");
+ default_value2.text = "Select Layer 2";
+ default_value2.value = 0;
+ drop_downs[1].add(default_value2);
+
+ first_opening = false;
+
+ // Add the layer names from the shortlist to the drop downs that store
+ // layer names.
+ for (var i = 0; i < drop_downs.length; i++){
+ for (var j = 0; j < layers.length; j++) {
+ var option = document.createElement("option");
+ option.text = layers[j];
+ option.value = j+1;
+ drop_downs[i].add(option);
+ }
+ }
+
+ // Remove all elements from drop downs holding the data values for the
+ // selected layers. This way there are no values presented when the user
+ // clicks on the set operation button to open it again.
+ var set_operation_layer_values = document.getElementsByClassName("set-operation-layer-value");
+ var length = set_operation_layer_values[0].options.length;
+ do{
+ set_operation_layer_values[0].remove(0);
+ length--;
+ }
+ while (length > 0);
+
+ var length = set_operation_layer_values[1].options.length;
+ do{
+ set_operation_layer_values[1].remove(0);
+ length--;
+ }
+ while (length > 0);
+
+ // Call the function containing onchange commands for these dropdowns.
+ // This way the data values are updated according the the selected layer.
+ update_set_operation_data_values ();
+}
+
+function update_set_operation_data_values () {
+ // Define the onchange commands for the drop downs that hold layer names.
+ // This way the data values are updated according the the selected layer.
+
+ // Get all drop down elements
+ var selected_function = document.getElementById ("set-operations-list");
+ var drop_downs = document.getElementsByClassName("set-operation-value");
+ var set_operation_layer_values = document.getElementsByClassName("set-operation-layer-value");
+
+ // The "Select Layer1" Dropdown onchange function
+ drop_downs[0].onchange = function(){
+ // Strip current values of the data value dropdown
+ var length = set_operation_layer_values[0].options.length;
+ do{
+ set_operation_layer_values[0].remove(0);
+ length--;
+ }
+ while (length > 0);
+
+ // Add the data values depending on the selected layer
+ var selectedIndex = drop_downs[0].selectedIndex;
+ var layer_name = drop_downs[0].options[selectedIndex].text;
+ var set_operation_data_value_select = set_operation_layer_values[0];
+ create_set_operation_pick_list(set_operation_data_value_select, layer_name);
+ };
+
+ // The "Select Layer2" Dropdown onchange function
+ drop_downs[1].onchange = function(){
+ // Strip current values of the data value dropdown
+ var length = set_operation_layer_values[1].options.length;
+ do{
+ set_operation_layer_values[1].remove(0);
+ length--;
+ }
+ while (length > 0);
+
+ // Add the data values depending on the selected layer
+ var selectedIndex = drop_downs[1].selectedIndex;
+ var layer_name = drop_downs[1].options[selectedIndex].text;
+ var set_operation_data_value_select = set_operation_layer_values[1];
+ create_set_operation_pick_list(set_operation_data_value_select, layer_name);
+ };
+
+}
+
+function create_set_operation_pick_list(value,layer_object) {
+
+ // We must create a drop down containing the data values for the selected
+ // layer.
+
+ // The Javascript "select" element that contains the data values
+ // is passed as "value" and the selected layer is passed as "layer_object".
+
+ // First, figure out what kind of filter settings we take based on
+ // what kind of layer we are.
+ with_layer(layer_object, function(layer) {
+
+ // No options available. We have to add them.
+ for(var i = 0; i < layer.magnitude + 1; i++) {
+ // Make an option for each value;
+ var option = document.createElement("option");
+ option.value = i;
+
+ if(colormaps[layer_object].hasOwnProperty(i)) {
+ // We have a real name for this value
+ option.text = (colormaps[layer_object][i].name);
+ } else {
+ // No name. Use the number.
+ option.text = i;
+ }
+ value.add(option);
+
+ // Select the last option, so that 1 on 0/1 layers will
+ // be selected by default.
+ var last_index = value.options.length - 1;
+ value.selectedIndex = last_index;
+ }
+ // Now that the right controls are there, assume they have
+ refresh();
+ });
+}
+
+
function update_shortlist_ui() {
// Go through the shortlist and make sure each layer there has an entry in
// the shortlist UI, and that each UI element has an entry in the shortlist.
@@ -896,9 +1316,207 @@
// Sort by the part with the lines icon, so we can still select text.
handle: ".shortlist-controls"
});
-
+
+ update_set_operation_selections ();
+}
+
+function uncheck_checkbox (checkbox_class) {
+ // Unchecks chekboxes after the function has been completed.
+ var checkboxArray = new Array ();
+ checkboxArray = document.getElementsByClassName(checkbox_class);
+ for (var i = 0; i < checkboxArray.length; i++)
+ {
+ checkboxArray[i].checked = false;
+ }
+}
+
+function hide_values (set_theory_function) {
+ // Hides pick lists for set theory functions after function has been
+ // completed.
+ var value_type = set_theory_function + '-value';
+
+ var values = new Array ();
+
+ values = document.getElementsByClassName(value_type);
+
+ var length = values.length;
+
+ for (var i = 0; i < length; i++)
+ {
+ values[i].style.display = 'none';
+ }
+ refresh();
+}
+
+function compute_intersection (values, intersection_layer_names, text) {
+ // A function that will take a list of layer names
+ // that have been selected for the intersection utility.
+ // Fetches the respective layers and list of tumor ids.
+ // Then compares data elements of the same tumor id
+ // between both layers. Adds these hexes to a new layer
+ // for visualization
+
+ //Array of signatures that intersect
+ var intersection_signatures = [];
+
+ with_layers (intersection_layer_names, function (intersection_layers) {
+
+ // Gather Tumor-ID Signatures.
+ for (hex in polygons)
+ {
+ if (intersection_layers[0].data[hex] == values[0] && intersection_layers[1].data[hex] == values[1]){
+ intersection_signatures.push(hex);
+ }
+ }
+ });
+
+ for (var i = 0; i < intersection_layer_names.length; i++){
+ intersection_layer_names[i] = intersection_layer_names[i] + " [" + text[i] + "]";
+ }
+ var intersection_function = "intersection";
+ select_list (intersection_signatures, intersection_function, intersection_layer_names);
+ uncheck_checkbox ('intersection-checkbox');
+ hide_values('intersection');
+}
+
+function compute_union (values, union_layer_names, text) {
+ // A function that will take a list of layer names
+ // that have been selected for the union utility.
+ // Fetches the respective layers and list of tumor ids.
+ // Then compares data elements of the same tumor id
+ // between both layers. Adds these hexes to a new layer
+ // for visualization
+
+ //Array of signatures
+ var union_signatures = [];
+
+ with_layers (union_layer_names, function (union_layers) {
+
+ // Gather Tumor-ID Signatures.
+ for (hex in polygons)
+ {
+ // Union Function
+ if (union_layers[0].data[hex] == values[0] || union_layers[1].data[hex] == values[1]){
+ union_signatures.push(hex);
+ }
+ }
+ });
+
+ for (var i = 0; i < union_layer_names.length; i++){
+ union_layer_names[i] = union_layer_names[i] + " [" + text[i] + "]";
+ }
+
+ var union_function = "union";
+ select_list (union_signatures, union_function, union_layer_names);
+ uncheck_checkbox ('union-checkbox');
+ hide_values('union');
}
+function compute_set_difference (values, set_difference_layer_names, text) {
+ // A function that will take a list of layer names
+ // that have been selected for the set difference utility.
+ // Fetches the respective layers and list of tumor ids.
+ // Then compares data elements of the same tumor id
+ // between both layers. Adds these hexes to a new layer
+ // for visualization
+
+ //Array of signatures
+ var set_difference_signatures = [];
+
+ with_layers (set_difference_layer_names, function (set_difference_layers) {
+
+ // Gather Tumor-ID Signatures.
+ for (hex in polygons)
+ {
+ // Set Difference Function
+ if (set_difference_layers[0].data[hex] == values[0] &&
+ set_difference_layers[1].data[hex] != values[1]){
+ set_difference_signatures.push(hex);
+ }
+ }
+ });
+
+ for (var i = 0; i < set_difference_layer_names.length; i++){
+ set_difference_layer_names[i] = set_difference_layer_names[i] + " [" + text[i] + "]";
+ }
+
+ var set_difference_function = "set difference";
+ select_list (set_difference_signatures, set_difference_function, set_difference_layer_names);
+ uncheck_checkbox ('set-difference-checkbox');
+ hide_values('set-difference');
+}
+
+function compute_symmetric_difference (values, symmetric_difference_layer_names, text) {
+ // A function that will take a list of layer names
+ // that have been selected for the set difference utility.
+ // Fetches the respective layers and list of tumor ids.
+ // Then compares data elements of the same tumor id
+ // between both layers. Adds these hexes to a new layer
+ // for visualization
+
+ //Array of signatures
+ var symmetric_difference_signatures = [];
+
+ with_layers (symmetric_difference_layer_names, function (symmetric_difference_layers) {
+
+ // Gather Tumor-ID Signatures.
+ for (hex in polygons)
+ {
+ // Symmetric Difference Function
+ if (symmetric_difference_layers[0].data[hex] == values[0] &&
+ symmetric_difference_layers[1].data[hex] != values[1]){
+ symmetric_difference_signatures.push(hex);
+ }
+ if (symmetric_difference_layers[0].data[hex] != values[0] &&
+ symmetric_difference_layers[1].data[hex] == values[1]){
+ symmetric_difference_signatures.push(hex);
+ }
+ }
+ });
+
+ for (var i = 0; i < symmetric_difference_layer_names.length; i++){
+ symmetric_difference_layer_names[i] = symmetric_difference_layer_names[i] + " [" + text[i] + "]";
+ }
+
+ var symmetric_difference_function = "symmetric difference";
+ select_list (symmetric_difference_signatures, symmetric_difference_function, symmetric_difference_layer_names);
+ uncheck_checkbox ('symmetric-difference-checkbox');
+ hide_values('symmetric-difference');
+}
+
+function compute_absolute_complement (values, absolute_complement_layer_names, text) {
+ // A function that will take a list of layer names
+ // that have been selected for the set difference utility.
+ // Fetches the respective layers and list of tumor ids.
+ // Then compares data elements of the same tumor id
+ // between both layers. Adds these hexes to a new layer
+ // for visualization
+
+ //Array of signatures
+ var absolute_complement_signatures = [];
+
+ with_layers (absolute_complement_layer_names, function (absolute_complement_layers) {
+
+ // Gather Tumor-ID Signatures.
+ for (hex in polygons)
+ {
+ // Absolute Complement Function
+ if (absolute_complement_layers[0].data[hex] != values[0]) {
+ absolute_complement_signatures.push(hex);
+ }
+ }
+ });
+
+ for (var i = 0; i < absolute_complement_layer_names.length; i++){
+ absolute_complement_layer_names[i] = absolute_complement_layer_names[i] + " [" + text[i] + "]";
+ }
+ var absolute_complement_function = "absolute complement";
+ select_list (absolute_complement_signatures, absolute_complement_function, absolute_complement_layer_names);
+ uncheck_checkbox ('absolute-complement-checkbox');
+ hide_values('absolute-complement');
+}
+
+
function layer_sort_order(a, b) {
// A sort function defined on layer names.
// Return <0 if a belongs before b, >0 if a belongs after
@@ -1010,7 +1628,7 @@
}
// We couldn't find a difference in selection status, p-value, or clumpiness
- // score, or the binary layer minor value frequency, or whether eqach layer
+ // score, or the binary layer minor value frequency, or whether each layer
// *had* a binary layer minor value frequency, so use lexicographic ordering
// on the name.
return a.localeCompare(b);
@@ -1072,7 +1690,9 @@
// Do some transformations to make the displayed labels make more sense
lookup = {
n: "Number of non-empty values",
- positives: "Number of ones"
+ positives: "Number of ones",
+ inside_yes: "Ones in A",
+ outside_yes: "Ones in background"
}
if(lookup[attribute]) {
@@ -1089,6 +1709,21 @@
}
}
+function make_toggle_layout_ui(layout_name) {
+ // Returns a jQuery element to represent the layer layout the given name in
+ // the toggle layout panel.
+
+ // This holds a jQuery element that's the root of the structure we're
+ // building.
+ var root = $("").addClass("layout-entry");
+ root.data("layout-name", layout_name);
+
+ // Put in the layer name in a div that makes it wrap.
+ root.append($("").addClass("layout-name").text(layout_name));
+
+ return root;
+}
+
function make_browse_ui(layer_name) {
// Returns a jQuery element to represent the layer with the given name in
// the browse panel.
@@ -1270,6 +1905,134 @@
return current_filters;
}
+function get_current_layers() {
+ // Returns an array of the string names of the layers that are currently
+ // supposed to be displayed, according to the shortlist UI.
+ // Not responsible for enforcing maximum selected layers limit.
+
+ // This holds a list of the string names of the currently selected layers,
+ // in order.
+ var current_layers = [];
+
+ $("#shortlist").children().each(function(index, element) {
+ // This holds the checkbox that determines if we use this layer
+ var checkbox = $(element).find(".layer-on");
+ if(checkbox.is(":checked")) {
+ // Put the layer in if its checkbox is checked.
+ current_layers.push($(element).data("layer"));
+ }
+ });
+
+ // Return things in reverse order relative to the UI.
+ // Thus, layer-added layers will be "secondary", and e.g. selecting
+ // something with only tissue up behaves as you might expect, highlighting
+ // those things.
+ current_layers.reverse();
+
+ return current_layers;
+}
+
+function get_current_set_theory_layers(function_type) {
+ // Returns an array of layer names that have been selected.
+ // This function only looks at the layers that are listed on the shortlist.
+
+ var current_set_theory_layers = [];
+
+ // Initialize global variables that hold the number of checkboxes selected
+ // for set theory functions to zero so that the new number is calculated
+ // each time this function is called.
+
+ if (function_type == "intersection"){
+ shortlist_intersection_num = 0;
+ }
+
+ if (function_type == "union"){
+ shortlist_union_num = 0;
+ }
+
+ if (function_type == "set difference"){
+ shortlist_set_difference_num = 0;
+ }
+
+ if (function_type == "symmetric difference"){
+ shortlist_symmetric_difference_num = 0;
+ }
+
+ if (function_type == "absolute complement"){
+ shortlist_absolute_complement_num = 0;
+ }
+
+ $("#shortlist").children().each(function(index, element) {
+ // Go through all the shortlist entries.
+
+ // This holds the checkbox that determines if we use this layer
+ // The class name depends on the function_type.
+
+ // If intersection function look for intersection-checkbox.
+ if (function_type == "intersection"){
+ var checkbox = $(element).find(".intersection-checkbox");
+ }
+
+ // If union function look for union-checkbox.
+ if (function_type == "union"){
+ var checkbox = $(element).find(".union-checkbox");
+ }
+
+ // If set difference function look for set-difference-checkbox.
+ if (function_type == "set difference"){
+ var checkbox = $(element).find(".set-difference-checkbox");
+ }
+
+ // If symmetric difference function look for
+ // symmetric-difference-checkbox.
+ if (function_type == "symmetric difference"){
+ var checkbox = $(element).find(".symmetric-difference-checkbox");
+ }
+
+ if (function_type == "absolute complement"){
+ var checkbox = $(element).find(".absolute-complement-checkbox");
+ }
+
+ if(checkbox.is(":checked")) {
+ // Put the layer in if its checkbox is checked.
+
+
+ // Get the layer name
+ var layer_name = $(element).data("layer");
+
+ // Add the layer_name to the list of current_set_theory_layers.
+ current_set_theory_layers.push(layer_name);
+
+ // Add to the global "num" variables to keep track of the number
+ // of selected checkboxes.
+
+ if (function_type == "intersection"){
+ shortlist_intersection_num++;
+ }
+
+ if (function_type == "union"){
+ shortlist_union_num++;
+ }
+
+ if (function_type == "set difference"){
+ shortlist_set_difference_num++;
+ }
+
+ if (function_type == "symmetric difference"){
+ shortlist_symmetric_difference_num++;
+ }
+
+ if (function_type == "absolute complement"){
+ shortlist_absolute_complement_num++;
+ }
+
+ }
+ });
+
+ return current_set_theory_layers;
+}
+
+
function with_filtered_signatures(filters, callback) {
// Takes an array of filters, as produced by get_current_filters. Signatures
// pass a filter if the filter's layer has a value >0 for that signature.
@@ -1322,10 +2085,18 @@
});
}
-function select_list(to_select) {
+function select_list(to_select, function_type, layer_names) {
// Given an array of signature names, add a new selection layer containing
// just those hexes. Only looks at hexes that are not filtered out by the
// currently selected filters.
+
+ // function_type is an optional parameter. If no variable is passed for the
+ // function_type undefined then the value will be undefined and the
+ // default "selection + #" title will be assigned to the shortlist element.
+ // If layer_names is undefined, the "selection + #" will also apply as a
+ // default. However, if a value i.e. "intersection" is passed
+ // for function_type, the layer_names will be used along with the
+ // function_type to assign the correct title.
// Make the requested signature list into an object for quick membership
// checking. This holds true if a signature was requested, undefined
@@ -1365,10 +2136,57 @@
}
}
- // Make up a name for the layer
- var layer_name = "Selection " + selection_next_id;
- selection_next_id++;
+ // Make up a name for the layer
+ var layer_name;
+
+ // Default Values for Optional Parameters
+ if (function_type == undefined && layer_names == undefined){
+ layer_name = "Selection " + selection_next_id;
+ selection_next_id++;
+ }
+
+ if (function_type == "user selection"){
+ var text = prompt("Please provide a label for your selection",
+ "Selection Label Text");
+ if (text != null){
+ layer_name = text;
+ }
+ if (!text)
+ {
+ return;
+ }
+ }
+
+ // intersection for layer name
+ if (function_type == "intersection"){
+ layer_name = "(" + layer_names[0] + " ∩ " + layer_names[1] + ")";
+ }
+
+ // union for layer name
+ if (function_type == "union"){
+ layer_name = "(" + layer_names[0] + " U " + layer_names[1] + ")";
+ }
+
+ // set difference for layer name
+ if (function_type == "set difference"){
+ layer_name = "(" + layer_names[0] + " \\ " + layer_names[1] + ")";
+ }
+
+ // symmetric difference for layer name
+ if (function_type == "symmetric difference"){
+ layer_name = "(" + layer_names[0] + " ∆ " + layer_names[1] + ")";
+ }
+ // absolute complement for layer name
+ if (function_type == "absolute complement"){
+ layer_name = "Not: " + "(" + layer_names[0] + ")";
+ }
+
+ // saved filter for layer name
+ if (function_type == "save"){
+ layer_name = "(" + layer_names[0] + ")";
+ }
+
// Add the layer. Say it is a selection
add_layer_data(layer_name, data, {
selection: true,
@@ -1434,7 +2252,8 @@
// Now we have an array of the signatures that ought to be in the selection
// (if they pass filters). Hand it off to select_list.
- select_list(in_box);
+ var select_function_type = "user selection";
+ select_list(in_box, select_function_type);
}
@@ -1551,8 +2370,14 @@
// This holds a callback for setting the layer's p_value to the result of
// the statistics.
- var callback = function(result) {
- layers[layer_name].p_value = result;
+ var callback = function(results) {
+
+ // The statistics code really sends back a dict of updated metadata for
+ // each layer. Copy it over.
+ for(var metadata in results) {
+ layers[layer_name][metadata] = results[metadata];
+ }
+
if(jobs_running == 0) {
// All statistics are done!
// TODO: Unify this code with similar callback below.
@@ -1592,8 +2417,11 @@
// The return value is p values by layer name
for(var layer_name in result) {
- // Copy over p values
- layers[layer_name].p_value = result[layer_name];
+ // The statistics code really sends back a dict of updated metadata
+ // for each layer. Copy it over.
+ for(var metadata in result[layer_name]) {
+ layers[layer_name][metadata] = result[layer_name][metadata];
+ }
}
if(jobs_running == 0) {
@@ -1705,7 +2533,7 @@
" column " + error.column);
}
-function initialize_view() {
+function initialize_view(initial_zoom) {
// Initialize the global Google Map.
// Configure a Google map
@@ -1713,7 +2541,7 @@
// Look at the center of the map
center: get_LatLng(128, 128),
// Zoom all the way out
- zoom: 0,
+ zoom: initial_zoom,
mapTypeId: "blank",
// Don't show a map type picker.
mapTypeControlOptions: {
@@ -2408,13 +3236,196 @@
new google.maps.Point(x, y));
}
+function clearMap() {
+
+}
+
+function drl_values(layout_index) {
+
+ // Download the DrL position data, and make it into a layer
+ $.get("drl"+ layout_index +".tab", function(tsv_data) {
+ // This is an array of rows, which are arrays of values:
+ // id, x, y
+ // Only this time X and Y are Cartesian coordinates.
+ var parsed = $.tsv.parseRows(tsv_data);
+
+ // Compute two layers: one for x position, and one for y position.
+ var layer_x = {};
+ var layer_y = {};
+
+ for(var i = 0; i < parsed.length; i++) {
+ // Pull out the parts of the TSV entry
+ var label = parsed[i][0];
+
+ if(label == "") {
+ // DrL ends its output with a blank line, which we skip
+ // here.
+ continue;
+ }
+
+ var x = parseFloat(parsed[i][1]);
+ // Invert the Y coordinate since we do that in the hex grid
+ var y = -parseFloat(parsed[i][2]);
+
+ // Add x and y to the appropriate layers
+ layer_x[label] = x;
+ layer_y[label] = y;
+ }
+
+ // Register the layers with no priorities. By default they are not
+ // selections.
+ add_layer_data("DrL X Position", layer_x);
+ add_layer_data("DrL Y Position", layer_y);
+
+ // Make sure the layer browser has the up-to-date layer list
+ update_browse_ui();
+
+ }, "text");
+}
+
+function assignment_values (layout_index, spacing) {
+ // Download the signature assignments to hexagons and fill in the global
+ // hexagon assignment grid.
+ $.get("assignments" + layout_index +".tab", function(tsv_data) {
+ // This is an array of rows, which are arrays of values:
+ // id, x, y
+ var parsed = $.tsv.parseRows(tsv_data);
+
+ // This holds the maximum observed x
+ var max_x = 0;
+ // And y
+ var max_y = 0;
+
+ // Fill in the global signature grid and ploygon grid arrays.
+ for(var i = 0; i < parsed.length; i++) {
+ // Get the label
+ var label = parsed[i][0];
+
+ if(label == "") {
+ // Blank line
+ continue;
+ }
+
+ // Get the x coord
+ var x = parseInt(parsed[i][1]);
+ // And the y coord
+ var y = parseInt(parsed[i][2]);
+
+ x = x * spacing;
+ y = y * spacing;
+
+
+ // Update maxes
+ max_x = Math.max(x, max_x);
+ max_y = Math.max(y, max_y);
+
+
+ // Make sure we have a row
+ if(signature_grid[y] == null) {
+ signature_grid[y] = [];
+ // Pre-emptively add a row to the polygon grid.
+ polygon_grid[y] = [];
+ }
+
+ // Store the label in the global signature grid.
+ signature_grid[y][x] = label;
+ }
+
+ // We need to fit this whole thing into a 256x256 grid.
+ // How big can we make each hexagon?
+ // TODO: Do the algrbra to make this exact. Right now we just make a
+ // grid that we know to be small enough.
+ // Divide the space into one column per column, and calculate
+ // side length from column width. Add an extra column for dangling
+ // corners.
+ var side_length_x = (256)/ (max_x + 2) * (2.0 / 3.0);
+
+ print("Max hexagon side length horizontally is " + side_length_x);
+
+ // Divide the space into rows and calculate the side length
+ // from hex height. Remember to add an extra row for wggle.
+ var side_length_y = ((256)/(max_y + 2)) / Math.sqrt(3);
+
+ print("Max hexagon side length vertically is " + side_length_y);
+
+ // How long is a hexagon side in world coords?
+ // Shrink it from the biggest we can have so that we don't wrap off the
+ // edges of the map.
+ var hexagon_side_length = Math.min(side_length_x, side_length_y) / 2.0;
+
+ // Store this in the global hex_size, so we can later calculate the hex
+ // size in pixels and make borders go away if we are too zoomed out.
+ hex_size = hexagon_side_length;
+
+ // How far in should we move the whole grid from the top left corner of
+ // the earth?
+ // Let's try leaving a 1/4 Earth gap at least, to stop wrapping in
+ // longitude that we can't turn off.
+ // Since we already shrunk the map to half max size, this would put it
+ // 1/4 of the 256 unit width and height away from the top left corner.
+ grid_offset = (256) / 4;
+
+ // Loop through again and draw the polygons, now that we know how big
+ // they have to be
+ for(var i = 0; i < parsed.length; i++) {
+ // TODO: don't re-parse this info
+ // Get the label
+ var label = parsed[i][0];
+
+ if(label == "") {
+ // Blank line
+ continue;
+ }
+
+ // Get the x coord
+ var x = parseInt(parsed[i][1]);
+ // And the y coord
+ var y = parseInt(parsed[i][2]);
+
+ x = x * spacing;
+ y = y * spacing;
+
+ // Make a hexagon on the Google map and store that.
+ var hexagon = make_hexagon(y, x, hexagon_side_length, grid_offset);
+ // Store by x, y in grid
+ polygon_grid[y][x] = hexagon;
+ // Store by label
+ polygons[label] = hexagon;
+
+ // Set the polygon's signature so we can look stuff up for it when
+ // it's clicked.
+ set_hexagon_signature(hexagon, label);
+
+ }
+
+ // Now that the ploygons exist, do the initial redraw to set all their
+ // colors corectly. In case someone has messed with the controls.
+ // TODO: can someone yet have messed with the controlls?
+ refresh();
+
+
+ }, "text");
+}
+
+// Function to create a new map based upon the the layout_name argument
+// Find the index of the layout_name and pass it as the index to the
+// drl_values and assignment_values functions as these files are indexed
+// according to the appropriate layout
+function recreate_map(layout_name, spacing) {
+
+ var layout_index = layout_names.indexOf(layout_name);
+ drl_values(layout_index);
+ assignment_values(layout_index, spacing);
+
+}
+
$(function() {
// Set up the RPC system for background statistics
rpc_initialize();
// Set up the Google Map
- initialize_view();
+ initialize_view(0);
// Set up the layer search
$("#search").select2({
@@ -2474,7 +3485,7 @@
// We want our dropdown to be big enough to browse.
dropdownCssClass: "results-dropdown"
});
-
+
// Handle result selection
$("#search").on("select2-selecting", function(event) {
// The select2 id of the thing clicked (the layer's name) is event.val
@@ -2521,164 +3532,84 @@
// Find everything passing the filters and run the statistics.
recalculate_statistics(signatures);
});
- });
-
- // Download the signature assignments to hexagons and fill in the global
- // hexagon assignment grid.
- $.get("assignments.tab", function(tsv_data) {
- // This is an array of rows, which are arrays of values:
- // id, x, y
- var parsed = $.tsv.parseRows(tsv_data);
-
- // This holds the maximum observed x
- var max_x = 0;
- // And y
- var max_y = 0;
-
- // Fill in the global signature grid and ploygon grid arrays.
- for(var i = 0; i < parsed.length; i++) {
- // Get the label
- var label = parsed[i][0];
-
- if(label == "") {
- // Blank line
- continue;
- }
-
- // Get the x coord
- var x = parseInt(parsed[i][1]);
- // And the y coord
- var y = parseInt(parsed[i][2]);
-
-
- // Update maxes
- max_x = Math.max(x, max_x);
- max_y = Math.max(y, max_y);
-
-
- // Make sure we have a row
- if(signature_grid[y] == null) {
- signature_grid[y] = [];
- // Pre-emptively add a row to the polygon grid.
- polygon_grid[y] = [];
- }
-
- // Store the label in the global signature grid.
- signature_grid[y][x] = label;
- }
-
- // We need to fit this whole thing into a 256x256 grid.
- // How big can we make each hexagon?
- // TODO: Do the algrbra to make this exact. Right now we just make a
- // grid that we know to be small enough.
- // Divide the space into one column per column, and calculate
- // side length from column width. Add an extra column for dangling
- // corners.
- var side_length_x = 256 / (max_x + 2) * (2.0 / 3.0);
-
- print("Max hexagon side length horizontally is " + side_length_x);
-
- // Divide the space into rows and calculate the side length
- // from hex height. Remember to add an extra row for wggle.
- var side_length_y = (256 / (max_y + 2)) / Math.sqrt(3);
-
- print("Max hexagon side length vertically is " + side_length_y);
-
- // How long is a hexagon side in world coords?
- // Shrink it from the biggest we can have so that we don't wrap off the
- // edges of the map.
- var hexagon_side_length = Math.min(side_length_x, side_length_y) / 2.0;
-
- // Store this in the global hex_size, so we can later calculate the hex
- // size in pixels and make borders go away if we are too zoomed out.
- hex_size = hexagon_side_length;
-
- // How far in should we move the whole grid from the top left corner of
- // the earth?
- // Let's try leaving a 1/4 Earth gap at least, to stop wrapping in
- // longitude that we can't turn off.
- // Since we already shrunk the map to half max size, this would put it
- // 1/4 of the 256 unit width and height away from the top left corner.
- grid_offset = 256 / 4;
-
- // Loop through again and draw the polygons, now that we know how big
- // they have to be
- for(var i = 0; i < parsed.length; i++) {
- // TODO: don't re-parse this info
- // Get the label
- var label = parsed[i][0];
-
- if(label == "") {
- // Blank line
- continue;
- }
-
- // Get the x coord
- var x = parseInt(parsed[i][1]);
- // And the y coord
- var y = parseInt(parsed[i][2]);
-
- // Make a hexagon on the Google map and store that.
- var hexagon = make_hexagon(y, x, hexagon_side_length, grid_offset);
- // Store by x, y in grid
- polygon_grid[y][x] = hexagon;
- // Store by label
- polygons[label] = hexagon;
-
- // Set the polygon's signature so we can look stuff up for it when
- // it's clicked.
- set_hexagon_signature(hexagon, label);
-
- }
-
- // Now that the ploygons exist, do the initial redraw to set all their
- // colors corectly. In case someone has messed with the controls.
- // TODO: can someone yet have messed with the controlls?
- refresh();
-
-
- }, "text");
-
- // Download the DrL position data, and make it into a layer
- $.get("drl.tab", function(tsv_data) {
- // This is an array of rows, which are arrays of values:
- // id, x, y
- // Only this time X and Y are Cartesian coordinates.
- var parsed = $.tsv.parseRows(tsv_data);
-
- // Compute two layers: one for x position, and one for y position.
- var layer_x = {};
- var layer_y = {};
-
- for(var i = 0; i < parsed.length; i++) {
- // Pull out the parts of the TSV entry
- var label = parsed[i][0];
-
- if(label == "") {
- // DrL ends its output with a blank line, which we skip
- // here.
- continue;
- }
-
- var x = parseFloat(parsed[i][1]);
- // Invert the Y coordinate since we do that in the hex grid
- var y = -parseFloat(parsed[i][2]);
-
- // Add x and y to the appropriate layers
- layer_x[label] = x;
- layer_y[label] = y;
- }
-
- // Register the layers with no priorities. By default they are not
- // selections.
- add_layer_data("DrL X Position", layer_x);
- add_layer_data("DrL Y Position", layer_y);
-
- // Make sure the layer browser has the up-to-date layer list
- update_browse_ui();
-
- }, "text");
-
+ });
+
+ // Temporary Inflate Button
+ $("#inflate").button().click(function() {
+ initialize_view (0);
+ recreate_map(current_layout_name, 2);
+ refresh ();
+ });
+
+ // Create Pop-Up UI for Set Operations
+ $("#set-operations").prepend(create_set_operation_ui ());
+
+ // Action handler for display of set operation pop-up
+ $("#set-operation").button().click(function() {
+ set_operation_clicks++;
+ if (set_operation_clicks % 2 != 0){
+ show_set_operation_drop_down ();
+ }
+ else {
+ hide_set_operation_drop_down ();
+ var drop_downs = document.getElementsByClassName("set-operation-value");
+ for (var i = 0; i < drop_downs.length; i++) {
+ drop_downs[i].style.visibility="hidden";
+ }
+ }
+
+ });
+
+ // Coputation of Set Operations
+ var compute_button = document.getElementsByClassName ("compute-button");
+ compute_button[0].onclick = function () {
+ var layer_names = [];
+ var layer_values = [];
+ var layer_values_text = [];
+
+ var drop_down_layers = document.getElementsByClassName("set-operation-value");
+ var drop_down_data_values = document.getElementsByClassName("set-operation-layer-value");
+
+ var function_type = document.getElementById("set-operations-list");
+ var selected_function = function_type.selectedIndex;
+
+ var selected_index = drop_down_layers[0].selectedIndex;
+ layer_names.push(drop_down_layers[0].options[selected_index].text);
+
+ var selected_index = drop_down_data_values[0].selectedIndex;
+ layer_values.push(drop_down_data_values[0].options[selected_index].value);
+ layer_values_text.push(drop_down_data_values[0].options[selected_index].text);
+
+ if (selected_function != 5) {
+ var selected_index = drop_down_data_values[1].selectedIndex;
+ layer_values.push(drop_down_data_values[1].options[selected_index].value);
+ layer_values_text.push(drop_down_data_values[1].options[selected_index].text);
+ var selected_index = drop_down_layers[1].selectedIndex;
+ layer_names.push(drop_down_layers[1].options[selected_index].text);
+ }
+
+
+ switch (selected_function) {
+ case 1:
+ compute_intersection(layer_values, layer_names, layer_values_text);
+ break;
+ case 2:
+ compute_union(layer_values, layer_names, layer_values_text);
+ break;
+ case 3:
+ compute_set_difference(layer_values, layer_names, layer_values_text);
+ break;
+ case 4:
+ compute_symmetric_difference(layer_values, layer_names, layer_values_text);
+ break;
+ case 5:
+ compute_absolute_complement(layer_values, layer_names, layer_values_text);
+ break
+ default:
+ complain ("Set Theory Error");
+ }
+ };
+
// Download the layer index
$.get("layers.tab", function(tsv_data) {
// Layer index is \t\t
@@ -2796,5 +3727,106 @@
refresh();
}, "text");
+
+// Download the Matrix Names and pass it to the layout_names array
+ $.get("matrixnames.tab", function(tsv_data) {
+ // This is an array of rows, which are strings of matrix names
+ var parsed = $.tsv.parseRows(tsv_data);
+
+ for(var i = 0; i < parsed.length; i++) {
+ // Pull out the parts of the TSV entry
+ var label = parsed[i][0];
+
+ if(label == "") {
+ // Skip any blank lines
+ continue;
+ }
+ // Add layout names to global array of names
+ layout_names.push(label);
+ }
+ }, "text");
+
+ $("#layout-search").select2({
+ placeholder: "Select a Layout...",
+ query: function(query) {
+ // Given a select2 query object, call query.callback with an object
+ // with a "results" array.
+
+ // This is the array of result objects we will be sending back.
+ var results = [];
+
+ // Get where we should start in the layer list, from select2's
+ // infinite scrolling.
+ var start_position = 0;
+ if(query.context != undefined) {
+ start_position = query.context;
+ }
+
+ for(var i = start_position; i < layout_names.length; i++) {
+ // For each possible result
+ if(layout_names[i].toLowerCase().indexOf(
+ query.term.toLowerCase()) != -1) {
+
+ // Query search term is in this layer's name. Add a select2
+ // record to our results. Don't specify text: our custom
+ // formatter looks up by ID and makes UI elements
+ // dynamically.
+ results.push({
+ id: layout_names[i]
+ });
+
+ if(results.length >= SEARCH_PAGE_SIZE) {
+ // Page is full. Send it on.
+ break;
+ }
+
+ }
+ }
+
+ // Give the results back to select2 as the results parameter.
+ query.callback({
+ results: results,
+ // Say there's more if we broke out of the loop.
+ more: i < layout_names.length,
+ // If there are more results, start after where we left off.
+ context: i + 1
+ });
+ },
+ formatResult: function(result, container, query) {
+ // Given a select2 result record, the element that our results go
+ // in, and the query used to get the result, return a jQuery element
+ // that goes in the container to represent the result.
+
+ // Get the layer name, and make the browse UI for it.
+ return make_toggle_layout_ui(result.id);
+ },
+ // We want our dropdown to be big enough to browse.
+ dropdownCssClass: "results-dropdown"
+ });
+
+ // Handle result selection
+ $("#layout-search").on("select2-selecting", function(event) {
+ // The select2 id of the thing clicked (the layout's name) is event.val
+ var layout_name = event.val;
+
+ var current_layout = "Current Layout: " + layout_name;
+
+ document.getElementById('current-layout').innerHTML=current_layout;
+ initialize_view (0);
+ recreate_map(layout_name, 1);
+ refresh ();
+ // Don't actually change the selection.
+ // This keeps the dropdown open when we click.
+ event.preventDefault();
+
+ current_layout_name = layout_name;
+ });
+
+ drl_values(layout_names[0]);
+ assignment_values (layout_names[0], 1);
+ current_layout_name = layout_names[0];
+
});
+
+
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/hexagram.py
--- a/hexagram/hexagram.py Wed Jun 19 15:04:17 2013 -0400
+++ b/hexagram/hexagram.py Wed Oct 16 18:48:28 2013 -0400
@@ -7,7 +7,7 @@
layer/score data. It produces an HTML file (and several support files) that
provide an interactive visualization of the items clustered on a hexagonal grid.
-This script depends on the DrL graph alyout package, binaries for which must be
+This script depends on the DrL graph layout package, binaries for which must be
present in your PATH.
Re-uses sample code and documentation from
@@ -15,10 +15,15 @@
"""
import argparse, sys, os, itertools, math, numpy, subprocess, shutil, tempfile
-import collections, scipy.stats, multiprocessing, traceback, numpy.ma
+import collections, multiprocessing, traceback, numpy
+import scipy.stats, scipy.linalg
import os.path
import tsv
+# Global variable to hold opened matrices files
+matrices = [];
+
+
def parse_args(args):
"""
Takes in the command-line arguments list (args), and returns a nice argparse
@@ -42,8 +47,10 @@
# Now add all the options to it
# Options match the ctdHeatmap tool options as much as possible.
- parser.add_argument("similarities", type=argparse.FileType("r"),
- help="the TSV file with the similarities for signatures we're using")
+ parser.add_argument("similarity", type=str, nargs='+',
+ help="the unopened files of similarity matrices")
+ parser.add_argument("--names", type=str, action="append", default=[],
+ help="the unopened files of similarity matrices")
parser.add_argument("--scores", type=str,
action="append", default=[],
help="a TSV to read scores for each signature from")
@@ -80,7 +87,7 @@
scale is a float specifying hexagon side length.
The origin in coordinate space is defined as the upper left corner of the
- bounding box of the hexagon wityh indices x=0 and y=0.
+ bounding box of the hexagon with indices x=0 and y=0.
Returns a tuple of floats.
"""
@@ -640,124 +647,164 @@
except:
# Put all exception text into an exception and raise that
raise Exception(traceback.format_exc())
-
-def main(args):
- """
- Parses command line arguments, and makes visualization.
- "args" specifies the program arguments, with args[0] being the executable
- name. The return value should be used as the program's exit code.
+
+def open_matrices(names):
+ """
+ The argument parser now take multiple similarity matrices as input and
+ saves their file name as strings. We want to store the names of these
+ strings for display later in hexagram.js in order to allow the user to
+ navigate and know what type of visualization map they are looking at -
+ gene expression, copy number, etc.
+
+ Since, the parser no longer opens the files automatically we must, do it
+ in this function.
+ """
+
+ # For each file name, open the file and add it to the matrices list
+ # 'r' is the argument stating that the file will be read-only
+ for similarity_filename in names:
+ print "Opening Matrices..."
+ matrix_file = tsv.TsvReader(open(similarity_filename, "r"))
+ matrices.append(matrix_file)
+
+def compute_beta (coords, matrix, axis, index, options):
+ """
+ Compute and return a beta matrix from coords * matrix.
+ Then print the matrix to a file to be read on clientside.
"""
-
- options = parse_args(args) # This holds the nicely-parsed options object
-
- # Test our picking
- x, y = hexagon_center(0, 0)
- if hexagon_pick(x, y) != (0, 0):
- raise Exception("Picking is broken!")
-
- # First bit of stdout becomes annotation in Galaxy
-
- # Make sure our output directory exists.
- if not os.path.exists(options.directory):
- # makedirs is the right thing to use here: recursive
- os.makedirs(options.directory)
-
- # Work in a temporary directory
- drl_directory = tempfile.mkdtemp()
+ beta = coords * matrix
+ return beta
+ # Must add writing function
+
+def drl_similarity_functions(matrix, index, options):
+ """
+ Performs all the functions needed to format a similarity matrix into a
+ tsv format whereby the DrL can take the values. Then all of the DrL
+ functions are performed on the similarity matrix.
+
+ Options is passed to access options.singletons and other required apsects
+ of the parsed args.
+ """
+
+ # Work in a temporary directory
+ # If not available, create the directory.
+ drl_directory = tempfile.mkdtemp()
# This is the base name for all the files that DrL uses to do the layout
# We're going to put it in a temporary directory.
- drl_basename = os.path.join(drl_directory, "layout")
-
- # We can just pass our similarity matrix to DrL's truncate
+ # index added to extension in order to keep track of
+ # respective layouts
+ drl_basename = os.path.join(drl_directory, "layout" + str(index))
+
+ # We can just pass our similarity matrix to DrL's truncate
# But we want to run it through our tsv parser to strip comments and ensure
# it's valid
# This holds a reader for the similarity matrix
- sim_reader = tsv.TsvReader(options.similarities)
+ sim_reader = matrix
# This holds a writer for the sim file
- sim_writer = tsv.TsvWriter(open(drl_basename + ".sim", "w"))
+ sim_writer = tsv.TsvWriter(open(drl_basename + ".sim", "w"))
- print "Regularizing similarity matrix..."
- sys.stdout.flush()
+ print "Regularizing similarity matrix..."
+ sys.stdout.flush()
# This holds a list of all unique signature names in the similarity matrix.
# We can use it to add edges to keep singletons.
- signatures = set()
-
- for parts in sim_reader:
+ signatures = set()
+
+ print "Reach for parts in sim_reader"
+ for parts in sim_reader:
# Keep the signature names used
- signatures.add(parts[0])
- signatures.add(parts[1])
+ signatures.add(parts[0])
+ signatures.add(parts[1])
# Save the line to the regularized file
- sim_writer.list_line(parts)
+ sim_writer.list_line(parts)
- if options.singletons:
+ if options.singletons:
# Now add a self-edge on every node, so we don't drop nodes with no
# other strictly positive edges
- for signature in signatures:
- sim_writer.line(signature, signature, 1)
+ for signature in signatures:
+ sim_writer.line(signature, signature, 1)
- sim_reader.close()
- sim_writer.close()
+ sim_reader.close()
+ sim_writer.close()
# Now our input for DrL is prepared!
# Do DrL truncate.
# TODO: pass a truncation level
- print "DrL: Truncating..."
- sys.stdout.flush()
- subprocess.check_call(["truncate", "-t", str(options.truncation_edges),
+ print "DrL: Truncating..."
+ sys.stdout.flush()
+ subprocess.check_call(["truncate", "-t", str(options.truncation_edges),
drl_basename])
# Run the DrL layout engine.
- print "DrL: Doing layout..."
- sys.stdout.flush()
- subprocess.check_call(["layout", drl_basename])
+ print "DrL: Doing layout..."
+ sys.stdout.flush()
+ subprocess.check_call(["layout", drl_basename])
# Put the string names back
- print "DrL: Restoring names..."
- sys.stdout.flush()
- subprocess.check_call(["recoord", drl_basename])
+ print "DrL: Restoring names..."
+ sys.stdout.flush()
+ subprocess.check_call(["recoord", drl_basename])
# Now DrL has saved its coordinates as \t\t rows in
# .coord
# We want to read that.
# This holds a reader for the DrL output
- coord_reader = tsv.TsvReader(open(drl_basename + ".coord", "r"))
+ coord_reader = tsv.TsvReader(open(drl_basename + ".coord", "r"))
# This holds a dict from signature name string to (x, y) float tuple. It is
# also our official collection of node names that made it through DrL, and
# therefore need their score data sent to the client.
- nodes = {}
-
- print "Reading DrL output..."
- sys.stdout.flush()
- for parts in coord_reader:
- nodes[parts[0]] = (float(parts[1]), float(parts[2]))
-
- coord_reader.close()
+ nodes = {}
+
+ print "Reading DrL output..."
+ sys.stdout.flush()
+ for parts in coord_reader:
+ nodes[parts[0]] = (float(parts[1]), float(parts[2]))
+
+ coord_reader.close()
# Save the DrL coordinates in our bundle, to be displayed client-side for
# debugging.
- coord_writer = tsv.TsvWriter(open(
- os.path.join(options.directory, "drl.tab"), "w"))
+
+ # index added to drl.tab extension in order to keep track of
+ # respective drl.tabs
+ coord_writer = tsv.TsvWriter(open(
+ os.path.join(options.directory, "drl" + str(index) + ".tab"), "w"))
- for signature_name, (x, y) in nodes.iteritems():
+ for signature_name, (x, y) in nodes.iteritems():
# Write a tsv with names instead of numbers, like what DrL recoord would
# have written. This is what the Javascript on the client side wants.
- coord_writer.line(signature_name, x, y)
+ coord_writer.line(signature_name, x, y)
- coord_writer.close()
-
+ coord_writer.close()
+
+ # Delete our temporary directory.
+ shutil.rmtree(drl_directory)
+
+ # Return nodes dict back to main method for further processes
+ return nodes
+
+def compute_hexagram_assignments (nodes, index, options):
+ """
+ Now that we are taking multiple similarity matrices as inputs, we must
+ compute hexagram assignments for each similarity matrix. These assignments
+ are based up on the nodes ouput provided by the DrL function.
+
+ Index relates each matrix name with its drl output, nodes, assignments, etc.
+ Options contains the parsed arguments that are present in the main method.
+ """
# Do the hexagon layout
# We do the squiggly rows setup, so express everything as integer x, y
# This is a defaultdict from (x, y) integer tuple to id that goes there, or
# None if it's free.
+ global hexagons
hexagons = collections.defaultdict(lambda: None)
# This holds the side length that we use
@@ -790,13 +837,13 @@
# The hexagons have been assigned. Make hexagons be a dict instead of a
# defaultdict, so it pickles.
# TODO: I should change it so I don't need to do this.
- hexagons = dict(hexagons)
-
+ hexagons = dict(hexagons)
+
# Now dump the hexagon assignments as an id, x, y tsv. This will be read by
# the JavaScript on the static page and be used to produce the
# visualization.
hexagon_writer = tsv.TsvWriter(open(os.path.join(options.directory,
- "assignments.tab"), "w"))
+ "assignments"+ str(index) + ".tab"), "w"))
# First find the x and y offsets needed to make all hexagon positions
# positive
@@ -807,7 +854,79 @@
# Write this hexagon assignment, converted to all-positive coordinates.
hexagon_writer.line(name, coords[0] - min_x, coords[1] - min_y)
hexagon_writer.close()
+
+ # Hand placement_badness dict to main method so that it can be used else
+ # where.
+ return placement_badnesses
+
+def write_matrix_names (options):
+ """
+ Write the names of the similarity matrices so that hexagram.js can
+ process the names and create the toggle layout GUI.
+ We pass options to access the parsed args and thus the matrix names.
+ """
+ name_writer = tsv.TsvWriter(open(os.path.join(options.directory,
+ "matrixnames.tab"), "w"))
+ for i in options.names:
+ name_writer.line(i)
+
+ name_writer.close()
+
+def main(args):
+ """
+ Parses command line arguments, and makes visualization.
+ "args" specifies the program arguments, with args[0] being the executable
+ name. The return value should be used as the program's exit code.
+ """
+ options = parse_args(args) # This holds the nicely-parsed options object
+
+ print "Created Options"
+
+ # Test our picking
+ x, y = hexagon_center(0, 0)
+ if hexagon_pick(x, y) != (0, 0):
+ raise Exception("Picking is broken!")
+
+ # First bit of stdout becomes annotation in Galaxy
+ # Make sure our output directory exists.
+ if not os.path.exists(options.directory):
+ # makedirs is the right thing to use here: recursive
+ os.makedirs(options.directory)
+
+ print "Writing matrix names..."
+ # We must write the file names for hexagram.js to access.
+ write_matrix_names(options)
+
+ print "About to open matrices..."
+
+ # We have file names stored in options.similarities
+ # We must open the files and store them in matrices list for access
+ open_matrices(options.similarity)
+
+ print "Opened matrices..."
+
+ # The nodes list stores the list of nodes for each matrix
+ # We must keep track of each set of nodes
+ nodes_multiple = []
+
+ print "Created nodes_multiple list..."
+
+ # Index for drl.tab and drl.layout file naming. With indexes we can match
+ # file names, to matrices, to drl output files.
+ for index, i in enumerate (matrices):
+ nodes_multiple.append (drl_similarity_functions(i, index, options))
+
+ # Compute Hexagam Assignments for each similarity matrix's drl output,
+ # which is found in nodes_multiple.
+
+ # placement_badnesses_multiple list is required to store the placement
+ # badness dicts that are returned by the compute_hexagram_assignments
+ # function.
+ placement_badnesses_multiple = []
+ for index, i in enumerate (nodes_multiple):
+ placement_badnesses_multiple.append (compute_hexagram_assignments (i, index, options))
+
# Now that we have hex assignments, compute layers.
# In addition to making per-layer files, we're going to copy all the score
@@ -863,7 +982,7 @@
# This is the signature that this line is about
signature_name = parts[0]
- if signature_name not in nodes:
+ if signature_name not in nodes_multiple[0]:
# This signature wasn't in our DrL output. Don't bother
# putting its layer data in our visualization. This saves
# space and makes the client-side layer counts accurate for
@@ -903,7 +1022,7 @@
# Stick our placement badness layer on the end
layer_names.append("Placement Badness")
- layers["Placement Badness"] = placement_badnesses
+ layers["Placement Badness"] = placement_badnesses_multiple[0]
# Now we need to write layer files.
@@ -995,11 +1114,16 @@
index_writer.close()
+ # Sahil will implement linear regression code here
+
+ # We must create a m * n matrix of samples * genes
+ # In order to create this matrix we first must know the number of hexes
+ # and mantain them in a certain order. The order is important so that
+ # we populate the matrix with the data values in the proper row (sample).
+
# Copy over the user-specified colormaps file, or make an empty TSV if it's
# not specified.
-
-
# This holds a writer for the sim file. Creating it creates the file.
colormaps_writer = tsv.TsvWriter(open(os.path.join(options.directory,
"colormaps.tab"), "w"))
@@ -1035,6 +1159,9 @@
"filter.svg",
"statistics.svg",
"right.svg",
+ "set.svg",
+ "save.svg",
+ "inflate.svg",
"throbber.svg",
# jQuery itself is pulled from a CDN.
@@ -1074,12 +1201,9 @@
# Copy the HTML file to our output file. It automatically knows to read
# assignments.tab, and does its own TSV parsing
shutil.copy2(os.path.join(tool_root, "hexagram.html"), options.html)
-
- # Delete our temporary directory.
- shutil.rmtree(drl_directory)
-
+
print "Visualization generation complete!"
-
+
return 0
if __name__ == "__main__" :
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/hexagram.xml
--- a/hexagram/hexagram.xml Wed Jun 19 15:04:17 2013 -0400
+++ b/hexagram/hexagram.xml Wed Oct 16 18:48:28 2013 -0400
@@ -1,5 +1,5 @@
-
+Interactive hex grid clustering visualization
- hexagram.py "$similarity"
+ hexagram.py
+ #for $i, $s in enumerate( $similarity )
+ "${s.similarity_matrix.file_name}"
+ #end for
+ #for $i, $s in enumerate ($similarity)
+ --names "${s.similarity_matrix.name}"
+ #end for
#for $i, $s in enumerate( $scores )
--scores "${s.score_matrix.file_name}"
#end for
@@ -39,8 +45,10 @@
#end if
-
+
+
+
@@ -58,7 +66,7 @@
label="Skip calculation of heatmap clumpiness statistics"/>
-
@@ -84,10 +92,10 @@
The tool takes three types of input files:
-Similarity Matrix
-+++++++++++++++++
+Similarity Matrices
++++++++++++++++++++
-The only required input file is a *similarity matrix*, which contains similarity information over a set of "samples" or "signatures". This file is a sparse matrix represented as three tab-delimited columns; the first two columns of each row contain the names of two signatures, and the last column contains a nonzero, non-negative floating-point "similarity" between them. No headers are used. Self-edges are permitted, and self-edges with a similarity of 1 will be added to every node if "Keep unconnected singleton signatures" is checked. The input similarity matrix need not describe a similarity graph that is connected, and similarity need not be transitive in any way.
+The only required input file is at least one *similarity matrix*, which contains similarity information over a set of "samples" or "signatures". This file is a sparse matrix represented as three tab-delimited columns; the first two columns of each row contain the names of two signatures, and the last column contains a nonzero, non-negative floating-point "similarity" between them. No headers are used. Self-edges are permitted, and self-edges with a similarity of 1 will be added to every node if "Keep unconnected singleton signatures" is checked. The input similarity matrix need not describe a similarity graph that is connected, and similarity need not be transitive in any way.
Score Matrices
++++++++++++++
@@ -103,3 +111,4 @@
+
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/inflate.svg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hexagram/inflate.svg Wed Oct 16 18:48:28 2013 -0400
@@ -0,0 +1,179 @@
+
+
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/save.svg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hexagram/save.svg Wed Oct 16 18:48:28 2013 -0400
@@ -0,0 +1,192 @@
+
+
+
+
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/set.svg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hexagram/set.svg Wed Oct 16 18:48:28 2013 -0400
@@ -0,0 +1,201 @@
+
+
+
+
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/statistics.js
--- a/hexagram/statistics.js Wed Jun 19 15:04:17 2013 -0400
+++ b/hexagram/statistics.js Wed Oct 16 18:48:28 2013 -0400
@@ -251,9 +251,9 @@
// Given the data of a continuous layer object (an object from signature
// name to float (or undefined)), and arrays of the names of "in" and "out"
// signatures, do a t test test for whether the in signatures differ from
- // the out signatures. Returns the p value of the test (two-tailed), or NaN
- // if the test cannot be performed (due to, e.g. fewer than 2 samples in one
- // category).
+ // the out signatures. Returns an object of metadata, with "p_value" set to
+ // either the p value of the test (two-tailed), or NaN if the test cannot be
+ // performed (due to, e.g. fewer than 2 samples in one category).
// Go through the in list and calculate all the summary statistics
// How many non-NaN values?
@@ -331,10 +331,11 @@
var p_value = t_test(mean_in, unbiased_variance_in, number_in, mean_out,
unbiased_variance_out, number_out);
- print("p=" + p_value);
-
- // And return it
- return p_value;
+ // And return it in a dict with other metadata.
+ // We don't really have any other metadata.
+ return {
+ p_value: p_value
+ };
}
function t_test(mean_in, unbiased_variance_in, number_in, mean_out,
@@ -347,11 +348,6 @@
// https://en.wikipedia.org/wiki/Student%27s_t-test
// Assumes we have enough samples to actually perform the test.
- print("Running t test with mean " + mean_in + " and variance " +
- unbiased_variance_in + " at n=" + number_in + " versus mean " +
- mean_out + " and variance " + unbiased_variance_out + " at n=" +
- number_out);
-
// First, calculate the t statistic, which is where our observations fall on
// the t distribution.
var t_statistic = (mean_in - mean_out) / Math.sqrt((unbiased_variance_in /
@@ -365,8 +361,6 @@
((Math.pow(unbiased_variance_in / number_in, 2) / (number_in - 1)) +
(Math.pow(unbiased_variance_out / number_out, 2) / (number_out - 1)));
- print("t = " + t_statistic + ", DoF = " + degrees_of_freedom);
-
// Now we have to compare the t statistic to the t test CDF available via
// the totally undocumented jstat.pt = function(q, df, ncp, lower_tail, log)
// where:
@@ -394,12 +388,13 @@
// 0 or 1 (or undefined)), and arrays of the names of "in" and "out"
// signatures, do a binomial test for whether the in signatures differ from
// the out signatures. Uses a number of pseudocount trials as specified in
- // the global constant BINOMIAL_PSEUDOCOUNTS Return the p value, or NaN if
- // it cannot be calculated. all_list specifies the names of all signatures
- // that figure into the analysis at all (i.e. those which the user hasn't
- // filtered out), which we use when calculating how many of our pseudocounts
- // should be successes. Signature names appearing in all_list but with no
- // data in layer_data are not counted.
+ // the global constant BINOMIAL_PSEUDOCOUNTS Returns an object of metadata,
+ // with "p_value" set to either the p value of the test (two-tailed), or NaN
+ // if the test cannot be performed. all_list specifies the names of all
+ // signatures that figure into the analysis at all (i.e. those which the
+ // user hasn't filtered out), which we use when calculating how many of our
+ // pseudocounts should be successes. Signature names appearing in all_list
+ // but with no data in layer_data are not counted.
// Work out the distribution from the out list
@@ -492,8 +487,14 @@
outside_no) + " with " + pseudo_yes + " out of " +
BINOMIAL_PSEUDOCOUNTS + " pseudocounts.");
}
-
- return p;
+
+ // Return our p value as "p_value", and also how many non-pseudocount
+ // successes were in the in_list and the out_list.
+ return {
+ p_value: p,
+ inside_yes: inside_yes,
+ outside_yes: outside_yes
+ };
}
function binomial_test(trials, successes, success_probability) {
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/tools.js
--- a/hexagram/tools.js Wed Jun 19 15:04:17 2013 -0400
+++ b/hexagram/tools.js Wed Oct 16 18:48:28 2013 -0400
@@ -370,3 +370,20 @@
});
});
});
+
+$(function() {
+ // Set up the link to this page control
+ add_tool("link-to-page", "Link to this Page...", function() {
+
+ // We will provide the user with an alert box with the link to the
+ // hexagrap visualization map.
+
+ var link = (window.location.protocol + "//" + window.location.host
+ + "/" + window.location.pathname);
+
+ alert(link);
+ selected_tool = undefined;
+
+ });
+});
+
diff -r 6b6c1a0a452e -r a9213a30d0f9 hexagram/tsv.pyc
Binary file hexagram/tsv.pyc has changed