X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fext%2Fensembl%2FEnsemblGene.java;h=919134ccb3e267b974c6135d7648666409f518eb;hb=17ff1f476e009b3a3c7e892e416edc2a4af8a2bc;hp=10841bd8cd26278b01ef4172ecbd77c967bb1e5a;hpb=550c391f0c113658e540783dc89034a34280ef18;p=jalview.git diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index 10841bd..919134c 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -1,14 +1,41 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.ext.ensembl; +import jalview.api.FeatureColourI; +import jalview.api.FeatureSettingsModelI; import jalview.datamodel.AlignmentI; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.datamodel.features.SequenceFeatures; import jalview.io.gff.SequenceOntologyFactory; import jalview.io.gff.SequenceOntologyI; +import jalview.schemes.FeatureColour; +import jalview.schemes.FeatureSettingsAdapter; import jalview.util.MapList; -import jalview.util.StringUtils; +import java.awt.Color; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -35,10 +62,28 @@ public class EnsemblGene extends EnsemblSeqProxy EnsemblFeatureType.exon, EnsemblFeatureType.cds, EnsemblFeatureType.variation }; + /** + * Default constructor (to use rest.ensembl.org) + */ + public EnsemblGene() + { + super(); + } + + /** + * Constructor given the target domain to fetch data from + * + * @param d + */ + public EnsemblGene(String d) + { + super(d); + } + @Override public String getDbName() { - return "ENSEMBL (GENE)"; + return "ENSEMBL"; } @Override @@ -53,6 +98,12 @@ public class EnsemblGene extends EnsemblSeqProxy return EnsemblSeqType.GENOMIC; } + @Override + protected String getObjectType() + { + return OBJECT_TYPE_GENE; + } + /** * Returns an alignment containing the gene(s) for the given gene or * transcript identifier, or external identifier (e.g. Uniprot id). If given a @@ -65,7 +116,8 @@ public class EnsemblGene extends EnsemblSeqProxy *
  • resolves an external identifier by looking up xref-ed gene ids
  • *
  • fetches the gene sequence
  • *
  • fetches features on the sequence
  • - *
  • identifies "transcript" features whose Parent is the requested gene
  • + *
  • identifies "transcript" features whose Parent is the requested + * gene
  • *
  • fetches the transcript sequence for each transcript
  • *
  • makes a mapping from the gene to each transcript
  • *
  • copies features from gene to transcript sequences
  • @@ -76,79 +128,87 @@ public class EnsemblGene extends EnsemblSeqProxy * * * @param query - * one or more identifiers separated by a space - * @return an alignment containing one or more genes, and possibly - * transcripts, or null + * a single gene or transcript identifier or gene name + * @return an alignment containing a gene, and possibly transcripts, or null */ @Override public AlignmentI getSequenceRecords(String query) throws Exception { - // todo: tidy up handling of one or multiple accession ids - String[] queries = query.split(getAccessionSeparator()); - /* - * if given a transcript id, look up its gene parent + * convert to a non-duplicated list of gene identifiers */ - if (isTranscriptIdentifier(query)) + List geneIds = getGeneIds(query); + + AlignmentI al = null; + for (String geneId : geneIds) { - // we are assuming all transcripts have the same gene parent here - query = new EnsemblLookup().getParent(queries[0]); - if (query == null) + /* + * fetch the gene sequence(s) with features and xrefs + */ + AlignmentI geneAlignment = super.getSequenceRecords(geneId); + if (geneAlignment == null) { - return null; + continue; } - } - - /* - * if given a gene or other external name, lookup and fetch - * the corresponding gene for all model organisms - */ - if (!isGeneIdentifier(query)) - { - List geneIds = new EnsemblSymbol().getIds(query); - if (geneIds.isEmpty()) + if (geneAlignment.getHeight() == 1) { - return null; + getTranscripts(geneAlignment, geneId); + } + if (al == null) + { + al = geneAlignment; + } + else + { + al.append(geneAlignment); } - String theIds = StringUtils.listToDelimitedString(geneIds, - getAccessionSeparator()); - return getSequenceRecords(theIds); - } - - AlignmentI al = super.getSequenceRecords(query); - - /* - * if we retrieved a single gene, get its transcripts as well - */ - if (al.getHeight() == 1) - { - getTranscripts(al, query); } - return al; } /** - * Attempts to get Ensembl stable identifiers for model organisms for a gene - * name by calling the xrefs symbol REST service to resolve the gene name. + * Converts a query, which may contain one or more gene, transcript, or + * external (to Ensembl) identifiers, into a non-redundant list of gene + * identifiers. * - * @param query + * @param accessions * @return */ - protected String getGeneIdentifiersForName(String query) + List getGeneIds(String accessions) { - List ids = new EnsemblSymbol().getIds(query); - if (ids != null) + List geneIds = new ArrayList<>(); + + for (String acc : accessions.split(getAccessionSeparator())) { - for (String id : ids) + /* + * First try lookup as an Ensembl (gene or transcript) identifier + */ + String geneId = new EnsemblLookup(getDomain()).getGeneId(acc); + if (geneId != null) + { + if (!geneIds.contains(geneId)) + { + geneIds.add(geneId); + } + } + else { - if (isGeneIdentifier(id)) + /* + * if given a gene or other external name, lookup and fetch + * the corresponding gene for all model organisms + */ + List ids = new EnsemblSymbol(getDomain(), getDbSource(), + getDbVersion()).getGeneIds(acc); + for (String id : ids) { - return id; + if (!geneIds.contains(id)) + { + geneIds.add(id); + } } } } - return null; + return geneIds; } /** @@ -171,6 +231,33 @@ public class EnsemblGene extends EnsemblSeqProxy { makeTranscript(transcriptFeature, al, gene); } + + clearGeneFeatures(gene); + } + + /** + * Remove unwanted features (transcript, exon, CDS) from the gene sequence + * after we have used them to derive transcripts and transfer features + * + * @param gene + */ + protected void clearGeneFeatures(SequenceI gene) + { + /* + * Note we include NMD_transcript_variant here because it behaves like + * 'transcript' in Ensembl, although strictly speaking it is not + * (it is a sub-type of sequence_variant) + */ + String[] soTerms = new String[] { + SequenceOntologyI.NMD_TRANSCRIPT_VARIANT, + SequenceOntologyI.TRANSCRIPT, SequenceOntologyI.EXON, + SequenceOntologyI.CDS }; + List sfs = gene.getFeatures().getFeaturesByOntology( + soTerms); + for (SequenceFeature sf : sfs) + { + gene.deleteFeature(sf); + } } /** @@ -186,8 +273,8 @@ public class EnsemblGene extends EnsemblSeqProxy * the parent gene sequence, with features * @return */ - SequenceI makeTranscript(SequenceFeature transcriptFeature, - AlignmentI al, SequenceI gene) + SequenceI makeTranscript(SequenceFeature transcriptFeature, AlignmentI al, + SequenceI gene) { String accId = getTranscriptId(transcriptFeature); if (accId == null) @@ -219,11 +306,12 @@ public class EnsemblGene extends EnsemblSeqProxy { splices = findFeatures(gene, SequenceOntologyI.CDS, parentId); } + SequenceFeatures.sortFeatures(splices, true); int transcriptLength = 0; final char[] geneChars = gene.getSequence(); int offset = gene.getStart(); // to convert to 0-based positions - List mappedFrom = new ArrayList(); + List mappedFrom = new ArrayList<>(); for (SequenceFeature sf : splices) { @@ -235,11 +323,27 @@ public class EnsemblGene extends EnsemblSeqProxy mappedFrom.add(new int[] { sf.getBegin(), sf.getEnd() }); } - Sequence transcript = new Sequence(accId, seqChars, 1, transcriptLength); - String geneName = (String) transcriptFeature.getValue(NAME); - if (geneName != null) + Sequence transcript = new Sequence(accId, seqChars, 1, + transcriptLength); + + /* + * Ensembl has gene name as transcript Name + * EnsemblGenomes doesn't, but has a url-encoded description field + */ + String description = (String) transcriptFeature.getValue(NAME); + if (description == null) { - transcript.setDescription(geneName); + description = (String) transcriptFeature.getValue(DESCRIPTION); + } + if (description != null) + { + try + { + transcript.setDescription(URLDecoder.decode(description, "UTF-8")); + } catch (UnsupportedEncodingException e) + { + e.printStackTrace(); // as if + } } transcript.createDatasetSequence(); @@ -249,21 +353,22 @@ public class EnsemblGene extends EnsemblSeqProxy * transfer features to the new sequence; we use EnsemblCdna to do this, * to filter out unwanted features types (see method retainFeature) */ - List mapTo = new ArrayList(); + List mapTo = new ArrayList<>(); mapTo.add(new int[] { 1, transcriptLength }); MapList mapping = new MapList(mappedFrom, mapTo, 1, 1); - new EnsemblCdna().transferFeatures(gene.getSequenceFeatures(), + EnsemblCdna cdna = new EnsemblCdna(getDomain()); + cdna.transferFeatures(gene.getFeatures().getPositionalFeatures(), transcript.getDatasetSequence(), mapping, parentId); /* * fetch and save cross-references */ - super.getCrossReferences(transcript); + cdna.getCrossReferences(transcript); /* * and finally fetch the protein product and save as a cross-reference */ - new EnsemblCdna().addProteinProduct(transcript); + cdna.addProteinProduct(transcript); return transcript; } @@ -282,6 +387,12 @@ public class EnsemblGene extends EnsemblSeqProxy /** * Returns a list of the transcript features on the sequence whose Parent is * the gene for the accession id. + *

    + * Transcript features are those of type "transcript", or any of its sub-types + * in the Sequence Ontology e.g. "mRNA", "processed_transcript". We also + * include "NMD_transcript_variant", because this type behaves like a + * transcript identifier in Ensembl, although strictly speaking it is not in + * the SO. * * @param accId * @param geneSequence @@ -290,23 +401,21 @@ public class EnsemblGene extends EnsemblSeqProxy protected List getTranscriptFeatures(String accId, SequenceI geneSequence) { - List transcriptFeatures = new ArrayList(); + List transcriptFeatures = new ArrayList<>(); String parentIdentifier = GENE_PREFIX + accId; - SequenceFeature[] sfs = geneSequence.getSequenceFeatures(); - if (sfs != null) + List sfs = geneSequence.getFeatures() + .getFeaturesByOntology(SequenceOntologyI.TRANSCRIPT); + sfs.addAll(geneSequence.getFeatures().getPositionalFeatures( + SequenceOntologyI.NMD_TRANSCRIPT_VARIANT)); + + for (SequenceFeature sf : sfs) { - for (SequenceFeature sf : sfs) + String parent = (String) sf.getValue(PARENT); + if (parentIdentifier.equals(parent)) { - if (isTranscript(sf.getType())) - { - String parent = (String) sf.getValue(PARENT); - if (parentIdentifier.equals(parent)) - { - transcriptFeatures.add(sf); - } - } + transcriptFeatures.add(sf); } } @@ -341,7 +450,8 @@ public class EnsemblGene extends EnsemblSeqProxy if (SequenceOntologyFactory.getInstance().isA(sf.getType(), SequenceOntologyI.GENE)) { - String id = (String) sf.getValue(ID); + // NB features as gff use 'ID'; rest services return as 'id' + String id = (String) sf.getValue("ID"); if ((GENE_PREFIX + accId).equals(id)) { return true; @@ -360,13 +470,13 @@ public class EnsemblGene extends EnsemblSeqProxy @Override protected boolean retainFeature(SequenceFeature sf, String accessionId) { - if (SequenceOntologyFactory.getInstance().isA(sf.getType(), - SequenceOntologyI.GENE)) + SequenceOntologyI so = SequenceOntologyFactory.getInstance(); + String type = sf.getType(); + if (so.isA(type, SequenceOntologyI.GENE)) { return false; } - - if (isTranscript(sf.getType())) + if (isTranscript(type)) { String parent = (String) sf.getValue(PARENT); if (!(GENE_PREFIX + accessionId).equals(parent)) @@ -388,15 +498,6 @@ public class EnsemblGene extends EnsemblSeqProxy return false; } - @Override - protected List getCrossReferenceDatabases() - { - // found these for ENSG00000157764 on 30/01/2016: - // return new String[] {"Vega_gene", "OTTG", "ENS_LRG_gene", "ArrayExpress", - // "EntrezGene", "HGNC", "MIM_GENE", "MIM_MORBID", "WikiGene"}; - return super.getCrossReferenceDatabases(); - } - /** * Override to do nothing as Ensembl doesn't return a protein sequence for a * gene identifier @@ -412,4 +513,84 @@ public class EnsemblGene extends EnsemblSeqProxy return ACCESSION_REGEX; } + /** + * Returns a descriptor for suitable feature display settings with + *

      + *
    • only exon or sequence_variant features (or their subtypes in the + * Sequence Ontology) visible
    • + *
    • variant features coloured red
    • + *
    • exon features coloured by label (exon name)
    • + *
    • variants displayed above (on top of) exons
    • + *
    + */ + @Override + public FeatureSettingsModelI getFeatureColourScheme() + { + return new FeatureSettingsAdapter() + { + SequenceOntologyI so = SequenceOntologyFactory.getInstance(); + + @Override + public boolean isFeatureDisplayed(String type) + { + return (so.isA(type, SequenceOntologyI.EXON) + || so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT)); + } + + @Override + public FeatureColourI getFeatureColour(String type) + { + if (so.isA(type, SequenceOntologyI.EXON)) + { + return new FeatureColour() + { + @Override + public boolean isColourByLabel() + { + return true; + } + }; + } + if (so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT)) + { + return new FeatureColour() + { + + @Override + public Color getColour() + { + return Color.RED; + } + }; + } + return null; + } + + /** + * order to render sequence_variant after exon after the rest + */ + @Override + public int compare(String feature1, String feature2) + { + if (so.isA(feature1, SequenceOntologyI.SEQUENCE_VARIANT)) + { + return +1; + } + if (so.isA(feature2, SequenceOntologyI.SEQUENCE_VARIANT)) + { + return -1; + } + if (so.isA(feature1, SequenceOntologyI.EXON)) + { + return +1; + } + if (so.isA(feature2, SequenceOntologyI.EXON)) + { + return -1; + } + return 0; + } + }; + } + }