chado_feature__residues.inc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <?php
  2. class chado_feature__residues extends TripalField {
  3. /**
  4. * Implements hook_info() for fields.
  5. *
  6. * This is a hook provided by the tripal_chado module for offloading the
  7. * hook_field_info() hook for each field to specify.
  8. */
  9. function field_info() {
  10. return array(
  11. 'label' => t('Sequences'),
  12. 'description' => t('A field for managing nucleotide and protein residues.'),
  13. 'default_widget' => 'chado_feature__residues_widget',
  14. 'default_formatter' => 'chado_feature__residues_formatter',
  15. 'settings' => array(),
  16. 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0),
  17. 'storage' => array(
  18. 'type' => 'field_chado_storage',
  19. 'module' => 'tripal_chado',
  20. 'active' => TRUE
  21. ),
  22. );
  23. }
  24. /**
  25. * Implements hook_attach_info().
  26. *
  27. * This is a hook provided by the tripal_Chado module. It allows the field
  28. * to specify which bundles it will attach to and to specify thee settings.
  29. *
  30. * @param $entity_type
  31. * @param $entity
  32. * @param $term
  33. *
  34. * @return
  35. * A field array
  36. */
  37. function attach_info($entity_type, $bundle, $settings) {
  38. $field_info = array();
  39. $table_name = $settings['data_table'];
  40. $type_table = $settings['type_table'];
  41. $type_field = $settings['field'];
  42. $cv_id = $settings['cv_id'];
  43. $cvterm_id = $settings['cvterm_id'];
  44. // If this is not the feature table then we don't want to attach.
  45. if ($table_name != 'feature') {
  46. return $field_info;
  47. }
  48. $field_info = array(
  49. 'field_name' => 'feature__residues',
  50. 'field_type' => 'chado_feature__residues',
  51. 'widget_type' => 'chado_feature__residues_widget',
  52. 'description' => 'An IUPAC compatible residues for this feature.',
  53. 'label' => 'Sequences',
  54. 'is_required' => 0,
  55. 'storage' => 'field_chado_storage',
  56. 'widget_settings' => array(
  57. 'display_label' => 1
  58. ),
  59. 'field_settings' => array(
  60. 'chado_table' => $table_name,
  61. 'chado_column' => 'residues',
  62. 'semantic_web' => array(
  63. 'name' => 'sequence_feature',
  64. 'accession' => '0000110',
  65. 'ns' => 'SO',
  66. 'nsurl' => 'http://www.sequenceontology.org',
  67. ),
  68. ),
  69. );
  70. return $field_info;
  71. }
  72. /**
  73. * Implements hook_widget_info.
  74. *
  75. * This is a hook provided by the tripal_chado module for offloading
  76. * the hook_field_widget_info() hook for each field to specify.
  77. */
  78. function widget_info() {
  79. return array(
  80. 'label' => t('Sequences'),
  81. 'field types' => array('chado_feature__residues'),
  82. );
  83. }
  84. /**
  85. * Implements hook_formatter_info.
  86. *
  87. * This is a hook provided by the tripal_chado module for
  88. * offloading the hook_field_formatter_info() for each field
  89. * to specify.
  90. *
  91. */
  92. function formatter_info() {
  93. return array(
  94. 'label' => t('Sequences'),
  95. 'field types' => array('chado_feature__residues'),
  96. );
  97. }
  98. /**
  99. *
  100. * @param unknown $entity_type
  101. * @param unknown $entity
  102. * @param unknown $field
  103. * @param unknown $instance
  104. * @param unknown $langcode
  105. * @param unknown $items
  106. * @param unknown $display
  107. */
  108. function formatter_view(&$element, $entity_type, $entity, $field,
  109. $instance, $langcode, $items, $display) {
  110. $num_bases = 50;
  111. $feature = $entity->chado_record;
  112. foreach ($items as $delta => $item) {
  113. $residues = $item['value']['residues'];
  114. $label = $item['value']['label'];
  115. $defline = $item['value']['defline'];
  116. $content = '<p>' . $label . '<p>';
  117. $content .= '<pre class="residues-formatter">';
  118. $content .= '>' . $defline . "<br>";
  119. $content .= wordwrap($residues, $num_bases, "<br>", TRUE);
  120. $content .= '</pre>';
  121. $element[$delta] = array(
  122. // We create a render array to produce the desired markup,
  123. '#type' => 'markup',
  124. '#markup' => $content,
  125. );
  126. }
  127. }
  128. /**
  129. * @see TripalField::widget_form()
  130. */
  131. function widget_form(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
  132. $widget['value'] = array(
  133. '#type' => 'textarea',
  134. '#title' => $element['#title'],
  135. '#description' => $element['#description'],
  136. '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
  137. '#default_value' => count($items) > 0 ? $items[0]['value'] : '',
  138. '#delta' => $delta,
  139. '#element_validate' => array('chado_feature__residues_widget_validate'),
  140. '#cols' => 30,
  141. );
  142. }
  143. /**
  144. * @see TripalField::load()
  145. */
  146. function load($field, $entity, $details) {
  147. $field_name = $field['field_name'];
  148. $feature = $details['record'];
  149. $num_seqs = 0;
  150. // we don't want to get the sequence for traditionally large types. They are
  151. // too big, bog down the web browser, take longer to load and it's not
  152. // reasonable to print them on a page.
  153. if(strcmp($feature->type_id->name,'scaffold') !=0 and
  154. strcmp($feature->type_id->name,'chromosome') !=0 and
  155. strcmp($feature->type_id->name,'supercontig') !=0 and
  156. strcmp($feature->type_id->name,'pseudomolecule') !=0) {
  157. $feature = chado_expand_var($feature,'field','feature.residues');
  158. if ($feature->residues) {
  159. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  160. '@type' => 'SO:0000110',
  161. 'type' => 'sequence_feature',
  162. 'label' => 'Sequence',
  163. 'defline' => tripal_get_fasta_defline($feature, '', NULL, '', strlen($feature->residues)),
  164. 'residues' => $feature->residues,
  165. );
  166. }
  167. }
  168. // Add in the protein sequences
  169. $values = array(
  170. 'object_id' => $feature->feature_id,
  171. 'subject_id' => array(
  172. 'type_id' => array(
  173. 'name' => 'polypeptide'
  174. ),
  175. ),
  176. 'type_id' => array(
  177. 'name' => 'derives_from',
  178. ),
  179. );
  180. $options = array(
  181. 'return_array' => 1,
  182. 'include_fk' => array(
  183. 'subject_id' => 1
  184. ),
  185. );
  186. $protein_rels = chado_generate_var('feature_relationship', $values, $options);
  187. foreach ($protein_rels as $protein_rel) {
  188. $protein_rel = chado_expand_var($protein_rel, 'field', 'feature.residues');
  189. if ($protein_rel->subject_id->residues) {
  190. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  191. '@type' => 'SO:0000104',
  192. 'type' => 'polypeptide',
  193. 'label' => 'Protein Sequence',
  194. 'defline' => tripal_get_fasta_defline($protein_rel->subject_id, '', NULL, '', strlen($protein_rel->subject_id->residues)),
  195. 'residues' => $protein_rel->subject_id->residues,
  196. );
  197. }
  198. }
  199. // Add in sequences from alignments.
  200. $options = array(
  201. 'return_array' => 1,
  202. 'include_fk' => array(
  203. 'srcfeature_id' => array(
  204. 'type_id' => 1
  205. ),
  206. 'feature_id' => array(
  207. 'type_id' => 1
  208. ),
  209. ),
  210. );
  211. $feature = chado_expand_var($feature, 'table', 'featureloc', $options);
  212. $featureloc_sequences = $this->get_featureloc_sequences($feature->feature_id, $feature->featureloc->feature_id);
  213. // Add in the coding sequences.
  214. $values = array(
  215. 'object_id' => $feature->feature_id,
  216. 'subject_id' => array(
  217. 'type_id' => array(
  218. 'name' => 'CDS'
  219. ),
  220. ),
  221. 'type_id' => array(
  222. 'name' => 'part_of',
  223. ),
  224. );
  225. $options = array(
  226. 'order_by' => array('rank' => 'ASC'),
  227. 'return_array' => 1,
  228. 'include_fk' => array(
  229. 'subject_id' => 1
  230. ),
  231. );
  232. $cds_rels = chado_generate_var('feature_relationship', $values, $options);
  233. $coding_seq = '';
  234. foreach ($cds_rels as $cds_rel) {
  235. $cds_rel = chado_expand_var($cds_rel, 'field', 'feature.residues');
  236. if ($cds_rel->subject_id->residues) {
  237. $coding_seq .= $cds_rel->subject_id->residues;
  238. }
  239. }
  240. if ($coding_seq) {
  241. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  242. '@type' => 'SO:0000316',
  243. 'type' => 'coding_sequence',
  244. 'label' => 'Coding sequence (CDS)',
  245. 'defline' => tripal_get_fasta_defline($feature, '', $feature->featureloc->feature_id[0], '', strlen($coding_seq)),
  246. 'residues' => $coding_seq,
  247. );
  248. }
  249. foreach($featureloc_sequences as $src => $attrs){
  250. // the $attrs array has the following keys
  251. // * id: a unique identifier combining the feature id with the cvterm id
  252. // * type: the type of sequence (e.g. mRNA, etc)
  253. // * location: the alignment location
  254. // * defline: the definition line
  255. // * formatted_seq: the formatted sequences
  256. // * featureloc: the feature object aligned to
  257. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  258. 'residues' => $attrs['residues'],
  259. '@type' => 'SO:0000110',
  260. 'type' => 'sequence_feature',
  261. 'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', strlen($attrs['residues'])),
  262. 'label' => 'Sequence from alignment at ' . $attrs['location'],
  263. );
  264. // check to see if this alignment has any CDS. If so, generate a CDS sequence
  265. $cds_sequence = tripal_get_feature_sequences(
  266. array(
  267. 'feature_id' => $feature->feature_id,
  268. 'parent_id' => $attrs['featureloc']->srcfeature_id->feature_id,
  269. 'name' => $feature->name,
  270. 'featureloc_id' => $attrs['featureloc']->featureloc_id,
  271. ),
  272. array(
  273. 'derive_from_parent' => 1, // CDS are in parent-child relationships so we want to use the sequence from the parent
  274. 'aggregate' => 1, // we want to combine all CDS for this feature into a single sequence
  275. 'sub_feature_types' => array('CDS'), // we're looking for CDS features
  276. 'is_html' => 0
  277. )
  278. );
  279. if (count($cds_sequence) > 0) {
  280. // the tripal_get_feature_sequences() function can return multiple sequences
  281. // if a feature is aligned to multiple places. In the case of CDSs we expect
  282. // that one mRNA is only aligned to a single location on the assembly so we
  283. // can access the CDS sequence with index 0.
  284. if ($cds_sequence[0]['residues']) {
  285. $entity->{$field_name}['und'][$num_seqs++]['value'] = array(
  286. 'residues' => $cds_sequence[0]['residues'],
  287. '@type' => 'SO:0000316',
  288. 'type' => 'coding_sequence',
  289. 'defline' => tripal_get_fasta_defline($feature, '', $attrs['featureloc'], 'CDS', $cds_sequence[0]['length']),
  290. 'label' => 'Coding sequence (CDS) from alignment at ' . $attrs['location'],
  291. );
  292. }
  293. }
  294. }
  295. }
  296. /**
  297. *
  298. * @param unknown $feature_id
  299. * @param unknown $featurelocs
  300. * @return multitype:|Ambigous <multitype:, an>
  301. */
  302. private function get_featureloc_sequences($feature_id, $featurelocs) {
  303. // if we don't have any featurelocs then no point in continuing
  304. if (!$featurelocs) {
  305. return array();
  306. }
  307. // get the list of relationships (including any aggregators) and iterate
  308. // through each one to find information needed to color-code the reference sequence
  309. $relationships = $this->get_aggregate_relationships($feature_id);
  310. if (!$relationships) {
  311. return array();
  312. }
  313. // iterate through each of the realtionships features and get their
  314. // locations
  315. foreach ($relationships as $rindex => $rel) {
  316. // get the featurelocs for each of the relationship features
  317. $rel_featurelocs = $this->get_featurelocs($rel->subject_id, 'as_child', 0);
  318. foreach ($rel_featurelocs as $rfindex => $rel_featureloc) {
  319. // keep track of this unique source feature
  320. $src = $rel_featureloc->src_feature_id . "-" . $rel_featureloc->src_cvterm_id;
  321. // copy over the results to the relationship object. Since there can
  322. // be more than one feature location for each relationship feature we
  323. // use the '$src' variable to keep track of these.
  324. $rel->featurelocs = new stdClass();
  325. $rel->featurelocs->$src = new stdClass();
  326. $rel->featurelocs->$src->src_uniquename = $rel_featureloc->src_uniquename;
  327. $rel->featurelocs->$src->src_cvterm_id = $rel_featureloc->src_cvterm_id;
  328. $rel->featurelocs->$src->src_cvname = $rel_featureloc->src_cvname;
  329. $rel->featurelocs->$src->fmin = $rel_featureloc->fmin;
  330. $rel->featurelocs->$src->fmax = $rel_featureloc->fmax;
  331. $rel->featurelocs->$src->src_name = $rel_featureloc->src_name;
  332. // keep track of the individual parts for each relationship
  333. $start = $rel->featurelocs->$src->fmin;
  334. $end = $rel->featurelocs->$src->fmax;
  335. $type = $rel->subject_type;
  336. $rel_locs[$src]['parts'][$start][$type]['start'] = $start;
  337. $rel_locs[$src]['parts'][$start][$type]['end'] = $end;
  338. $rel_locs[$src]['parts'][$start][$type]['type'] = $type;
  339. }
  340. }
  341. // the featurelocs array provided to the function contains the locations
  342. // where this feature is found. We want to get the sequence for each
  343. // location and then annotate it with the parts found from the relationships
  344. // locations determiend above.
  345. $floc_sequences = array();
  346. foreach ($featurelocs as $featureloc) {
  347. // build the src name so we can keep track of the different parts for each feature
  348. $src = $featureloc->srcfeature_id->feature_id . "-" . $featureloc->srcfeature_id->type_id->cvterm_id;
  349. // orient the parts to the beginning of the feature sequence
  350. if (!empty($rel_locs[$src]['parts'])) {
  351. $parts = $rel_locs[$src]['parts'];
  352. $rparts = array(); // we will fill this up if we're on the reverse strand
  353. foreach ($parts as $start => $types) {
  354. foreach ($types as $type_name => $type) {
  355. if ($featureloc->strand >= 0) {
  356. // this is on the forward strand. We need to convert the start on the src feature to the
  357. // start on this feature's sequence
  358. $parts[$start][$type_name]['start'] = $parts[$start][$type_name]['start'] - $featureloc->fmin;
  359. $parts[$start][$type_name]['end'] = $parts[$start][$type_name]['end'] - $featureloc->fmin;
  360. $parts[$start][$type_name]['type'] = $type_name;
  361. }
  362. else {
  363. // this is on the reverse strand. We need to swap the start and stop and calculate from the
  364. // begining of the reverse sequence
  365. $size = ($featureloc->fmax - $featureloc->fmin);
  366. $start_orig = $parts[$start][$type_name]['start'];
  367. $end_orig = $parts[$start][$type_name]['end'];
  368. $new_start = $size - ($end_orig - $featureloc->fmin);
  369. $new_end = $size - ($start_orig - $featureloc->fmin);
  370. $rparts[$new_start][$type_name]['start'] = $new_start;
  371. $rparts[$new_start][$type_name]['end'] = $new_end;
  372. $rparts[$new_start][$type_name]['type'] = $type_name;
  373. }
  374. }
  375. }
  376. // now sort the parts
  377. // if we're on the reverse strand we need to resort
  378. if ($featureloc->strand >= 0) {
  379. usort($parts, 'chado_feature__residues_sort_rel_parts_by_start');
  380. }
  381. else {
  382. usort($rparts, 'chado_feature__residues_sort_rel_parts_by_start');
  383. $parts = $rparts;
  384. }
  385. $floc_sequences[$src]['id'] = $src;
  386. $floc_sequences[$src]['type'] = $featureloc->feature_id->type_id->name;
  387. $args = array(':feature_id' => $featureloc->srcfeature_id->feature_id);
  388. $start = $featureloc->fmin + 1;
  389. $size = $featureloc->fmax - $featureloc->fmin;
  390. // TODO: fix the hard coded $start and $size
  391. // the $start and $size variables are hard-coded in the SQL statement
  392. // because the db_query function places quotes around all placeholders
  393. // (e.g. :start & :size) and screws up the substring function
  394. $sql = "
  395. SELECT substring(residues from $start for $size) as residues
  396. FROM {feature}
  397. WHERE feature_id = :feature_id
  398. ";
  399. $sequence = chado_query($sql, $args)->fetchObject();
  400. $residues = $sequence->residues;
  401. if ($featureloc->strand < 0) {
  402. $residues = tripal_reverse_compliment_sequence($residues);
  403. }
  404. $strand = '.';
  405. if ($featureloc->strand == 1) {
  406. $strand = '+';
  407. }
  408. elseif ($featureloc->strand == -1) {
  409. $strand = '-';
  410. }
  411. $floc_sequences[$src]['location'] = tripal_get_location_string($featureloc);
  412. $floc_sequences[$src]['defline'] = tripal_get_fasta_defline($featureloc->feature_id, '', $featureloc, '', strlen($residues));
  413. $floc_sequences[$src]['featureloc'] = $featureloc;
  414. $floc_sequences[$src]['residues'] = $residues;
  415. //$floc_sequences[$src]['formatted_seq'] = tripal_feature_color_sequence($residues, $parts, $floc_sequences[$src]['defline']);
  416. }
  417. }
  418. return $floc_sequences;
  419. }
  420. /**
  421. * Get features related to the current feature to a given depth. Recursive function.
  422. *
  423. * @param $feature_id
  424. * @param $substitute
  425. * @param $levels
  426. * @param $base_type_id
  427. * @param $depth
  428. *
  429. * @ingroup tripal_feature
  430. */
  431. private function get_aggregate_relationships($feature_id, $substitute=1,
  432. $levels=0, $base_type_id=NULL, $depth=0) {
  433. // we only want to recurse to as many levels deep as indicated by the
  434. // $levels variable, but only if this variable is > 0. If 0 then we
  435. // recurse until we reach the end of the relationships tree.
  436. if ($levels > 0 and $levels == $depth) {
  437. return NULL;
  438. }
  439. // first get the relationships for this feature
  440. return $this->get_relationships($feature_id, 'as_object');
  441. }
  442. /**
  443. * Get the relationships for a feature.
  444. *
  445. * @param $feature_id
  446. * The feature to get relationships for
  447. * @param $side
  448. * The side of the relationship this feature is (ie: 'as_subject' or 'as_object')
  449. *
  450. * @ingroup tripal_feature
  451. */
  452. private function get_relationships($feature_id, $side = 'as_subject') {
  453. // get the relationships for this feature. The query below is used for both
  454. // querying the object and subject relationships
  455. $sql = "
  456. SELECT
  457. FS.name as subject_name, FS.uniquename as subject_uniquename,
  458. CVTS.name as subject_type, CVTS.cvterm_id as subject_type_id,
  459. FR.subject_id, FR.type_id as relationship_type_id, FR.object_id, FR.rank,
  460. CVT.name as rel_type,
  461. FO.name as object_name, FO.uniquename as object_uniquename,
  462. CVTO.name as object_type, CVTO.cvterm_id as object_type_id
  463. FROM {feature_relationship} FR
  464. INNER JOIN {cvterm} CVT ON FR.type_id = CVT.cvterm_id
  465. INNER JOIN {feature} FS ON FS.feature_id = FR.subject_id
  466. INNER JOIN {feature} FO ON FO.feature_id = FR.object_id
  467. INNER JOIN {cvterm} CVTO ON FO.type_id = CVTO.cvterm_id
  468. INNER JOIN {cvterm} CVTS ON FS.type_id = CVTS.cvterm_id
  469. ";
  470. if (strcmp($side, 'as_object')==0) {
  471. $sql .= " WHERE FR.object_id = :feature_id";
  472. }
  473. if (strcmp($side, 'as_subject')==0) {
  474. $sql .= " WHERE FR.subject_id = :feature_id";
  475. }
  476. $sql .= " ORDER BY FR.rank";
  477. // get the relationships
  478. $results = chado_query($sql, array(':feature_id' => $feature_id));
  479. // iterate through the relationships, put these in an array and add
  480. // in the Drupal node id if one exists
  481. $i=0;
  482. $esql = "
  483. SELECT entity_id
  484. FROM {chado_entity}
  485. WHERE data_table = 'feature' AND record_id = :feature_id";
  486. $relationships = array();
  487. while ($rel = $results->fetchObject()) {
  488. $entity = db_query($esql, array(':feature_id' => $rel->subject_id))->fetchObject();
  489. if ($entity) {
  490. $rel->subject_entity_id = $entity->entity_id;
  491. }
  492. $entity = db_query($esql, array(':feature_id' => $rel->object_id))->fetchObject();
  493. if ($entity) {
  494. $rel->object_entity_id = $entity->entity_id;
  495. }
  496. $relationships[$i++] = $rel;
  497. }
  498. return $relationships;
  499. }
  500. /**
  501. * Load the locations for a given feature
  502. *
  503. * @param $feature_id
  504. * The feature to look up locations for
  505. * @param $side
  506. * Whether the feature is the scrfeature, 'as_parent', or feature, 'as_child'
  507. * @param $aggregate
  508. * Whether or not to get the locations for related features
  509. *
  510. * @ingroup tripal_feature
  511. */
  512. private function get_featurelocs($feature_id, $side = 'as_parent', $aggregate = 1) {
  513. $sql = "
  514. SELECT
  515. F.name, F.feature_id, F.uniquename,
  516. FS.name as src_name, FS.feature_id as src_feature_id, FS.uniquename as src_uniquename,
  517. CVT.name as cvname, CVT.cvterm_id,
  518. CVTS.name as src_cvname, CVTS.cvterm_id as src_cvterm_id,
  519. FL.fmin, FL.fmax, FL.is_fmin_partial, FL.is_fmax_partial,FL.strand, FL.phase
  520. FROM {featureloc} FL
  521. INNER JOIN {feature} F ON FL.feature_id = F.feature_id
  522. INNER JOIN {feature} FS ON FS.feature_id = FL.srcfeature_id
  523. INNER JOIN {cvterm} CVT ON F.type_id = CVT.cvterm_id
  524. INNER JOIN {cvterm} CVTS ON FS.type_id = CVTS.cvterm_id
  525. ";
  526. if (strcmp($side, 'as_parent')==0) {
  527. $sql .= "WHERE FL.srcfeature_id = :feature_id ";
  528. }
  529. if (strcmp($side, 'as_child')==0) {
  530. $sql .= "WHERE FL.feature_id = :feature_id ";
  531. }
  532. $flresults = chado_query($sql, array(':feature_id' => $feature_id));
  533. // copy the results into an array
  534. $i=0;
  535. $featurelocs = array();
  536. while ($loc = $flresults->fetchObject()) {
  537. // if a drupal node exists for this feature then add the nid to the
  538. // results object
  539. $loc->feid = tripal_get_chado_entity_id('feature', $loc->feature_id);
  540. $loc->seid = tripal_get_chado_entity_id('feature', $loc->src_feature_id);
  541. // add the result to the array
  542. $featurelocs[$i++] = $loc;
  543. }
  544. // Add the relationship feature locs if aggregate is turned on
  545. if ($aggregate and strcmp($side, 'as_parent')==0) {
  546. // get the relationships for this feature without substituting any children
  547. // for the parent. We want all relationships
  548. $relationships = tripal_feature_get_aggregate_relationships($feature_id, 0);
  549. foreach ($relationships as $rindex => $rel) {
  550. // get the featurelocs for each of the relationship features
  551. $rel_featurelocs = tripal_feature_load_featurelocs($rel->subject_id, 'as_child', 0);
  552. foreach ($rel_featurelocs as $findex => $rfloc) {
  553. $featurelocs[$i++] = $rfloc;
  554. }
  555. }
  556. }
  557. usort($featurelocs, 'chado_feature__residues_sort_locations');
  558. return $featurelocs;
  559. }
  560. }
  561. /**
  562. * Callback function for validating the chado_feature__residues_widget.
  563. */
  564. function chado_feature__residues_widget_validate($element, &$form_state) {
  565. $field_name = $element['#parents'][0];
  566. // Remove any white spaces.
  567. $residues = tripal_chado_get_field_form_values($field_name, $form_state);
  568. if ($residues) {
  569. $residues = preg_replace('/\s/', '', $residues);
  570. tripal_chado_set_field_form_values($field_name, $form_state, $residues);
  571. }
  572. }
  573. /**
  574. * Used to sort the list of relationship parts by start position
  575. *
  576. * @ingroup tripal_feature
  577. */
  578. function chado_feature__residues_sort_rel_parts_by_start($a, $b) {
  579. foreach ($a as $type_name => $details) {
  580. $astart = $a[$type_name]['start'];
  581. break;
  582. }
  583. foreach ($b as $type_name => $details) {
  584. $bstart = $b[$type_name]['start'];
  585. break;
  586. }
  587. return strnatcmp($astart, $bstart);
  588. }
  589. /**
  590. * Used to sort the feature locs by start position
  591. *
  592. * @param $a
  593. * One featureloc record (as an object)
  594. * @param $b
  595. * The other featureloc record (as an object)
  596. *
  597. * @return
  598. * Which feature location comes first
  599. *
  600. * @ingroup tripal_feature
  601. */
  602. function chado_feature__residues_sort_locations($a, $b) {
  603. return strnatcmp($a->fmin, $b->fmin);
  604. }