diff --git a/client/apollo/js/JSONUtils.js b/client/apollo/js/JSONUtils.js index 0bd009f219..6dd06de974 100644 --- a/client/apollo/js/JSONUtils.js +++ b/client/apollo/js/JSONUtils.js @@ -1,7 +1,7 @@ define([ 'dojo/_base/declare', 'dojo/_base/array', 'JBrowse/Util', - 'JBrowse/Model/SimpleFeature', + 'JBrowse/Model/SimpleFeature', 'WebApollo/SequenceOntologyUtils' ], function( declare, array, Util, SimpleFeature, SeqOnto ) { @@ -38,7 +38,7 @@ var JAFeature = declare( SimpleFeature, { constructor: function( afeature, parent ) { this.afeature = afeature; if (parent) { this._parent = parent; } - + // get the main data var loc = afeature.location; var pfeat = this; @@ -49,21 +49,21 @@ var JAFeature = declare( SimpleFeature, { name: afeature.name, parent_id: afeature.parent_id, parent_type: afeature.parent_type ? afeature.parent_type.name : undefined, - type: afeature.type.name, + type: afeature.type.name, properties: afeature.properties }; - if (this.data.type === "CDS") { - this.data.type = "wholeCDS"; + if (this.data.type === "CDS") { + this.data.type = "wholeCDS"; } else if (this.data.type === "stop_codon_read_through") { parent.data.readThroughStopCodon = true; } - + this._uniqueID = afeature.uniquename; // this doesn't work, since can be multiple properties with same CV term (comments, for example) - // could create arrray for each flattened cv-name for multiple values, but not sure what the point would be over + // could create arrray for each flattened cv-name for multiple values, but not sure what the point would be over // just making sure can access via get('properties') via above assignment into data object // parse the props /* var props = afeature.properties; @@ -95,7 +95,7 @@ var JAFeature = declare( SimpleFeature, { } } } - + if (!parent) { if (afeature.children) { var descendants = []; @@ -116,16 +116,16 @@ var JAFeature = declare( SimpleFeature, { afeature.children = [ child ]; } } - + // moved subfeature assignment to bottom of feature construction, since subfeatures may need to call method on their parent // only thing subfeature constructor won't have access to is parent.data.subfeatures - // get the subfeatures + // get the subfeatures this.data.subfeatures = array.map( afeature.children, function(s) { return new JAFeature( s, pfeat); } ); }, - + getUniqueName: function() { if (this.parent() && this.parent().get("cloned_subfeatures")) { return this.parent().id(); @@ -152,18 +152,18 @@ JSONUtils.flattenFeature = function(feature, descendants) { /** - * takes any JBrowse feature, returns a SimpleFeature "copy", + * takes any JBrowse feature, returns a SimpleFeature "copy", * for which all properties returned by tags() are mutable (has set() method) * needed since JBrowse features no longer necessarily mutable * feature requirements: * functions: id, parent, tags, get * if subfeatures, then returned as array by feature.get('subfeatures') - * + * */ JSONUtils.makeSimpleFeature = function(feature, parent) { var result = new SimpleFeature({id: feature.id(), parent: (parent ? parent : feature.parent()) }); var ftags = feature.tags(); - for (var tindex = 0; tindex < ftags.length; tindex++) { + for (var tindex = 0; tindex < ftags.length; tindex++) { var tag = ftags[tindex]; // forcing lower case, since still having case issues with NCList features result.set(tag.toLowerCase(), feature.get(tag.toLowerCase())); @@ -187,7 +187,7 @@ JSONUtils.makeSimpleFeature = function(feature, parent) { * afeature: sequence alteration in ApolloEditorService JSON format, */ JSONUtils.createJBrowseSequenceAlteration = function( afeature ) { - var loc = afeature.location; + var loc = afeature.location; var uid = afeature.uniquename; var justification; for (var i = 0; i < afeature.properties.length; i++) { @@ -211,42 +211,122 @@ JSONUtils.createJBrowseSequenceAlteration = function( afeature ) { }); }; +JSONUtils.isAlignment = function(feature){ + return feature.data.cigar !== undefined ; +}; + +JSONUtils.parseCigar = function( cigar ) { + return array.map( cigar.toUpperCase().match(/\d+\D/g), function( op ) { + return [ op.match(/\D/)[0], parseInt( op ) ]; + }); +}; + +/** +* Generate exon subfeatures only from M vs N. +* @param feature +*/ +JSONUtils.generateFeaturesFromCigar = function(feature){ + var cigarData = feature.data.cigar ; + // var baseObject = {}; + + + // 12M3N5M9N4M + // split Cigar + var ops = this.parseCigar(cigarData); + var currOffset = 0; + // var mismatches = []; + // {"track":"ctgA","features":[{"location":{"fmin":17399,"fmax":23000,"strand":1},"type":{"cv":{"name":"sequence"},"name":"mRNA"},"name":"Apple3","children":[{"location":{"fmin":17999,"fmax":21200,"strand":1},"type":{"cv":{"name":"sequence"},"name":"CDS"}},{"location":{"fmin":20999,"fmax":21200,"strand":1},"type":{"cv":{"name":"sequence"},"name":"exon"}},{"location":{"fmin":18999,"fmax":19500,"strand":1},"type":{"cv":{"name":"sequence"},"name":"exon"}},{"location":{"fmin":17999,"fmax":18800,"strand":1},"type":{"cv":{"name":"sequence"},"name":"exon"}}]}],"operation":"add_transcript","clientToken":"85401581821119996091324637603"} + console.log('ffeature',feature); + // feature.children = feature.children ? feature.children : []; + // console.log('childrens: '+feature.children); + // console.log('subfeatures: ' + feature.subfeature) + // feature.children = feature.children ? feature.children : []; + feature.children = []; + // baseObject.children= []; + var start = feature.data.start ; + array.forEach( ops, function( oprec ) { + var op = oprec[0]; + var len = oprec[1]; + if( op === 'M' || op === '=' || op === 'E' ) { + // TODO: create an exon subfeature for each + // add subfeature + + var exon = new SimpleFeature({parent: feature}); + exon.set('start', currOffset+start); + exon.set('end', currOffset + len+start); + exon.set('strand', feature.strand); + exon.set('type', 'exon'); + // feature.children().push(JSONUtils.createApolloFeature(exon, "exon")); + feature.children.push(JSONUtils.createApolloFeature(exon, "exon")); + // baseObject.children.push(JSONUtils.createApolloFeature(exon, "exon")); + + } + // if( op == 'I' ) + // // GAH: shouldn't length of insertion really by 0, since JBrowse internally uses zero-interbase coordinates? + // mismatches.push( { start: currOffset, type: 'insertion', base: ''+len, length: 1 }); + // else if( op == 'D' ) + // mismatches.push( { start: currOffset, type: 'deletion', base: '*', length: len }); + // else if( op == 'N' ) + // mismatches.push( { start: currOffset, type: 'skip', base: 'N', length: len }); + // else if( op == 'X' ) + // mismatches.push( { start: currOffset, type: 'mismatch', base: 'X', length: len }); + // else if( op == 'H' ) + // mismatches.push( { start: currOffset, type: 'hardclip', base: 'H'+len, length: 1 }); + // else if( op == 'S' ) + // mismatches.push( { start: currOffset, type: 'softclip', base: 'S'+len, cliplen: len, length: 1 }); + // if( op != 'I' && op != 'S' && op != 'H' ) + currOffset += len; + }); + feature.data.children = feature.children; + console.log('output',feature) + return feature ; +}; -/** +/** * creates a feature in ApolloEditorService JSON format * takes as argument: -* jfeature: a feature in JBrowse JSON format, +* jfeature: a feature in JBrowse JSON format, * fields: array specifying order of fields in jfeature * subfields: array specifying order of fields in subfeatures of jfeature * specified_type (optional): type passed in that overrides type info for jfeature * ApolloEditorService format: -* { -* "location" : { "fmin": fmin, "fmax": fmax, "strand": strand }, +* { +* "location" : { "fmin": fmin, "fmax": fmax, "strand": strand }, * "type": { "cv": { "name":, cv }, // typical cv name: "SO" (Sequence Ontology) * "name": cvterm }, // typical name: "transcript" * "children": { __recursive ApolloEditorService feature__ } * } -* -* For ApolloEditorService "add_feature" call to work, need to have "gene" as toplevel feature, +* +* For ApolloEditorService "add_feature" call to work, need to have "gene" as toplevel feature, * then "transcript", then ??? -* +* * JBrowse JSON fields example: ["start", "end", "strand", "id", "subfeatures"] * * type handling * if specified_type arg present, it determines type name * else if fields has a "type" field, use that to determine type name -* else don't include type +* else don't include type * * ignoring JBrowse ID / name fields for now -* currently, for features with lazy-loaded children, ignores children +* currently, for features with lazy-loaded children, ignores children */ JSONUtils.createApolloFeature = function( jfeature, specified_type, useName, specified_subtype ) { + console.log('creating apollo feature') var diagnose = (JSONUtils.verbose_conversion && jfeature.children() && jfeature.children().length > 0); - if (diagnose) { - console.log("converting JBrowse feature to Apollo feture, specified type: " + specified_type); + if (diagnose) { + console.log("converting JBrowse feature to Apollo feture, specified type: " + specified_type); console.log(jfeature); } + if(this.isAlignment(jfeature)){ + console.log('is an alignment') + jfeature = this.generateFeaturesFromCigar(jfeature) + } + else{ + console.log('just regular input') + } + + var afeature = new Object(); var astrand; // Apollo feature strand must be an integer @@ -261,7 +341,7 @@ JSONUtils.createApolloFeature = function( jfeature, specified_type, useName, spe default: astrand = 0; // either not stranded or strand is uknown } - + afeature.location = { "fmin": jfeature.get('start'), "fmax": jfeature.get('end'), @@ -295,7 +375,7 @@ JSONUtils.createApolloFeature = function( jfeature, specified_type, useName, spe // using 'id' attribute in the absence of 'name' attribute name !== undefined ? afeature.name = name : afeature.name = id; } - + /* afeature.properties = []; var property = { value : "source_id=" + jfeature.get('id'), @@ -314,14 +394,14 @@ JSONUtils.createApolloFeature = function( jfeature, specified_type, useName, spe // use filteredsubs if present instead of subfeats? // if (jfeature.filteredsubs) { subfeats = jfeature.filteredsubs; } // else { subfeats = jfeature.get('subfeatures'); } - subfeats = jfeature.get('subfeatures'); + subfeats = jfeature.get('subfeatures'); if( subfeats && subfeats.length ) { afeature.children = []; var slength = subfeats.length; var cds; var cdsFeatures = []; var foundExons = false; - + var updateCds = function(subfeat) { if (!cds) { cds = new SimpleFeature({id: "cds", parent: jfeature}); @@ -339,14 +419,14 @@ JSONUtils.createApolloFeature = function( jfeature, specified_type, useName, spe } } }; - + for (var i=0; i