JAL-2755 add lookup of cds/cdna gene loci
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 19 Oct 2017 14:01:16 +0000 (15:01 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 19 Oct 2017 14:01:16 +0000 (15:01 +0100)
src/jalview/ext/ensembl/EnsemblMap.java

index 05cc897..56657e0 100644 (file)
@@ -2,11 +2,15 @@ package jalview.ext.ensembl;
 
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
+import jalview.datamodel.GeneLociI;
+import jalview.util.MapList;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -17,6 +21,13 @@ import org.json.simple.parser.ParseException;
 
 public class EnsemblMap extends EnsemblRestClient
 {
+  private static final String MAPPED = "mapped";
+
+  private static final String MAPPINGS = "mappings";
+
+  private static final String CDS = "cds";
+
+  private static final String CDNA = "cdna";
 
   /**
    * Default constructor (to use rest.ensembl.org)
@@ -62,7 +73,7 @@ public class EnsemblMap extends EnsemblRestClient
    * @return
    * @throws MalformedURLException
    */
-  protected URL getUrl(String species, String chromosome, String fromRef,
+  protected URL getAssemblyMapUrl(String species, String chromosome, String fromRef,
           String toRef, int startPos, int endPos)
           throws MalformedURLException
   {
@@ -77,13 +88,7 @@ public class EnsemblMap extends EnsemblRestClient
             "%s/map/%s/%s/%s:%d..%d:%s/%s?content-type=application/json",
             getDomain(), species, fromRef, chromosome, start, end, strand,
             toRef);
-    try
-    {
-      return new URL(url);
-    } catch (MalformedURLException e)
-    {
-      return null;
-    }
+    return new URL(url);
   }
 
   @Override
@@ -110,7 +115,19 @@ public class EnsemblMap extends EnsemblRestClient
     return null; // not used
   }
 
-  public int[] getMapping(String species, String chromosome,
+  /**
+   * Calls the REST /map service to get the chromosomal coordinates (start/end)
+   * in 'toRef' that corresponding to the (start/end) queryRange in 'fromRef'
+   * 
+   * @param species
+   * @param chromosome
+   * @param fromRef
+   * @param toRef
+   * @param queryRange
+   * @return
+   * @see http://rest.ensemblgenomes.org/documentation/info/assembly_map
+   */
+  public int[] getAssemblyMapping(String species, String chromosome,
           String fromRef, String toRef, int[] queryRange)
   {
     URL url = null;
@@ -118,21 +135,32 @@ public class EnsemblMap extends EnsemblRestClient
 
     try
     {
-      url = getUrl(species, chromosome, fromRef, toRef, queryRange[0],
+      url = getAssemblyMapUrl(species, chromosome, fromRef, toRef, queryRange[0],
               queryRange[1]);
-      // System.out.println("Calling " + url);
       br = getHttpResponse(url, null);
-      return (parseResponse(br));
+      return (parseAssemblyMappingResponse(br));
     } catch (Throwable t)
     {
       System.out.println("Error calling " + url + ": " + t.getMessage());
       return null;
+    } finally
+    {
+      if (br != null)
+      {
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
+      }
     }
   }
 
   /**
-   * Parses the JSON response from the /map REST service. The format is (with
-   * some fields omitted)
+   * Parses the JSON response from the /map/&lt;species&gt;/ REST service. The
+   * format is (with some fields omitted)
    * 
    * <pre>
    *  {"mappings": 
@@ -145,7 +173,7 @@ public class EnsemblMap extends EnsemblRestClient
    * @param br
    * @return
    */
-  protected int[] parseResponse(BufferedReader br)
+  protected int[] parseAssemblyMappingResponse(BufferedReader br)
   {
     int[] result = null;
     JSONParser jp = new JSONParser();
@@ -153,14 +181,14 @@ public class EnsemblMap extends EnsemblRestClient
     try
     {
       JSONObject parsed = (JSONObject) jp.parse(br);
-      JSONArray mappings = (JSONArray) parsed.get("mappings");
+      JSONArray mappings = (JSONArray) parsed.get(MAPPINGS);
 
       Iterator rvals = mappings.iterator();
       while (rvals.hasNext())
       {
         // todo check for "mapped"
         JSONObject val = (JSONObject) rvals.next();
-        JSONObject mapped = (JSONObject) val.get("mapped");
+        JSONObject mapped = (JSONObject) val.get(MAPPED);
         int start = Integer.parseInt(mapped.get("start").toString());
         int end = Integer.parseInt(mapped.get("end").toString());
         String strand = mapped.get("strand").toString();
@@ -180,4 +208,215 @@ public class EnsemblMap extends EnsemblRestClient
     return result;
   }
 
+  /**
+   * Calls the REST /map/cds/id service, and returns a DBRefEntry holding the
+   * returned chromosomal coordinates, or returns null if the call fails
+   * 
+   * @param division
+   *          e.g. Ensembl, EnsemblMetazoa
+   * @param accession
+   *          e.g. ENST00000592782, Y55B1AR.1.1
+   * @param start
+   * @param end
+   * @return
+   */
+  public GeneLociI getCdsMapping(String division, String accession,
+          int start, int end)
+  {
+    return getIdMapping(division, accession, start, end, CDS);
+  }
+
+  /**
+   * Calls the REST /map/cdna/id service, and returns a DBRefEntry holding the
+   * returned chromosomal coordinates, or returns null if the call fails
+   * 
+   * @param division
+   *          e.g. Ensembl, EnsemblMetazoa
+   * @param accession
+   *          e.g. ENST00000592782, Y55B1AR.1.1
+   * @param start
+   * @param end
+   * @return
+   */
+  public GeneLociI getCdnaMapping(String division, String accession,
+          int start, int end)
+  {
+    return getIdMapping(division, accession, start, end, CDNA);
+  }
+
+  GeneLociI getIdMapping(String division, String accession, int start,
+          int end, String cdsOrCdna)
+  {
+    URL url = null;
+    BufferedReader br = null;
+
+    try
+    {
+      String domain = new EnsemblInfo().getDomain(division);
+      if (domain != null)
+      {
+        url = getIdMapUrl(domain, accession, start, end, cdsOrCdna);
+        br = getHttpResponse(url, null);
+        return (parseIdMappingResponse(br, accession, domain));
+      }
+      return null;
+    } catch (Throwable t)
+    {
+      System.out.println("Error calling " + url + ": " + t.getMessage());
+      return null;
+    } finally
+    {
+      if (br != null)
+      {
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+          // ignore
+        }
+      }
+    }
+  }
+
+  /**
+   * Constructs a URL to the /map/cds/<id> or /map/cdna/<id> REST service. The
+   * REST call is to either ensembl or ensemblgenomes, as determined from the
+   * division, e.g. Ensembl or EnsemblProtists.
+   * 
+   * @param domain
+   * @param accession
+   * @param start
+   * @param end
+   * @param cdsOrCdna
+   * @return
+   * @throws MalformedURLException
+   */
+  URL getIdMapUrl(String domain, String accession, int start, int end,
+          String cdsOrCdna) throws MalformedURLException
+  {
+    String url = String
+            .format("%s/map/%s/%s/%d..%d?include_original_region=1&content-type=application/json",
+                    domain, cdsOrCdna, accession, start, end);
+    return new URL(url);
+  }
+
+  /**
+   * Parses the JSON response from the /map/cds/ or /map/cdna REST service. The
+   * format is
+   * 
+   * <pre>
+   * {"mappings":
+   *   [
+   *    {"assembly_name":"TAIR10","end":2501311,"seq_region_name":"1","gap":0,
+   *     "strand":-1,"coord_system":"chromosome","rank":0,"start":2501114},
+   *    {"assembly_name":"TAIR10","end":2500815,"seq_region_name":"1","gap":0,
+   *     "strand":-1,"coord_system":"chromosome","rank":0,"start":2500714}
+   *   ]
+   * }
+   * </pre>
+   * 
+   * @param br
+   * @param accession
+   * @param domain
+   * @return
+   */
+  GeneLociI parseIdMappingResponse(BufferedReader br, String accession,
+          String domain)
+  {
+    JSONParser jp = new JSONParser();
+
+    try
+    {
+      JSONObject parsed = (JSONObject) jp.parse(br);
+      JSONArray mappings = (JSONArray) parsed.get(MAPPINGS);
+
+      Iterator rvals = mappings.iterator();
+      String assembly = null;
+      String chromosome = null;
+      int fromEnd = 0;
+      List<int[]> regions = new ArrayList<>();
+
+      while (rvals.hasNext())
+      {
+        JSONObject val = (JSONObject) rvals.next();
+        JSONObject original = (JSONObject) val.get("original");
+        fromEnd = Integer.parseInt(original.get("end").toString());
+
+        JSONObject mapped = (JSONObject) val.get(MAPPED);
+        int start = Integer.parseInt(mapped.get("start").toString());
+        int end = Integer.parseInt(mapped.get("end").toString());
+        String ass = mapped.get("assembly_name").toString();
+        if (assembly != null && !assembly.equals(ass))
+        {
+          System.err
+                  .println("EnsemblMap found multiple assemblies - can't resolve");
+          return null;
+        }
+        assembly = ass;
+        String chr = mapped.get("seq_region_name").toString();
+        if (chromosome != null && !chromosome.equals(chr))
+        {
+          System.err
+                  .println("EnsemblMap found multiple chromosomes - can't resolve");
+          return null;
+        }
+        chromosome = chr;
+        String strand = mapped.get("strand").toString();
+        if ("-1".equals(strand))
+        {
+          regions.add(new int[] { end, start });
+        }
+        else
+        {
+          regions.add(new int[] { start, end });
+        }
+      }
+
+      /*
+       * processed all mapped regions on chromosome, assemble the result,
+       * having first fetched the species id for the accession
+       */
+      final String species = new EnsemblLookup(domain)
+              .getSpecies(accession);
+      final String as = assembly;
+      final String chr = chromosome;
+      List<int[]> fromRange = Collections.singletonList(new int[] { 1,
+          fromEnd });
+      final MapList map = new MapList(fromRange, regions, 1, 1);
+      return new GeneLociI()
+      {
+
+        @Override
+        public String getSpeciesId()
+        {
+          return species == null ? "" : species;
+        }
+
+        @Override
+        public String getAssemblyId()
+        {
+          return as;
+        }
+
+        @Override
+        public String getChromosomeId()
+        {
+          return chr;
+        }
+
+        @Override
+        public MapList getMap()
+        {
+          return map;
+        }
+      };
+    } catch (IOException | ParseException | NumberFormatException e)
+    {
+      // ignore
+    }
+
+    return null;
+  }
+
 }