JAL-3076 fetch Ensembl sequence as JSON instead of Fasta
[jalview.git] / src / jalview / ext / ensembl / EnsemblMap.java
1 package jalview.ext.ensembl;
2
3 import jalview.datamodel.AlignmentI;
4 import jalview.datamodel.DBRefSource;
5
6 import java.io.BufferedReader;
7 import java.io.IOException;
8 import java.net.MalformedURLException;
9 import java.net.URL;
10 import java.util.Iterator;
11 import java.util.List;
12
13 import org.json.simple.JSONArray;
14 import org.json.simple.JSONObject;
15 import org.json.simple.parser.JSONParser;
16 import org.json.simple.parser.ParseException;
17
18 public class EnsemblMap extends EnsemblRestClient
19 {
20   private static final String MAPPED = "mapped";
21
22   private static final String MAPPINGS = "mappings";
23
24   /**
25    * Default constructor (to use rest.ensembl.org)
26    */
27   public EnsemblMap()
28   {
29     super();
30   }
31
32   /**
33    * Constructor given the target domain to fetch data from
34    * 
35    * @param
36    */
37   public EnsemblMap(String domain)
38   {
39     super(domain);
40   }
41
42   @Override
43   public String getDbName()
44   {
45     return DBRefSource.ENSEMBL;
46   }
47
48   @Override
49   public AlignmentI getSequenceRecords(String queries) throws Exception
50   {
51     return null; // not used
52   }
53
54   /**
55    * Constructs a URL of the format <code>
56    * http://rest.ensembl.org/map/human/GRCh38/17:45051610..45109016:1/GRCh37?content-type=application/json
57    * </code>
58    * 
59    * @param species
60    * @param chromosome
61    * @param fromRef
62    * @param toRef
63    * @param startPos
64    * @param endPos
65    * @return
66    * @throws MalformedURLException
67    */
68   protected URL getAssemblyMapUrl(String species, String chromosome, String fromRef,
69           String toRef, int startPos, int endPos)
70           throws MalformedURLException
71   {
72     /*
73      * start-end might be reverse strand - present forwards to the service
74      */
75     boolean forward = startPos <= endPos;
76     int start = forward ? startPos : endPos;
77     int end = forward ? endPos : startPos;
78     String strand = forward ? "1" : "-1";
79     String url = String.format(
80             "%s/map/%s/%s/%s:%d..%d:%s/%s?content-type=application/json",
81             getDomain(), species, fromRef, chromosome, start, end, strand,
82             toRef);
83     return new URL(url);
84   }
85
86   @Override
87   protected boolean useGetRequest()
88   {
89     return true;
90   }
91
92   @Override
93   protected URL getUrl(List<String> ids) throws MalformedURLException
94   {
95     return null; // not used
96   }
97
98   /**
99    * Calls the REST /map service to get the chromosomal coordinates (start/end)
100    * in 'toRef' that corresponding to the (start/end) queryRange in 'fromRef'
101    * 
102    * @param species
103    * @param chromosome
104    * @param fromRef
105    * @param toRef
106    * @param queryRange
107    * @return
108    * @see http://rest.ensemblgenomes.org/documentation/info/assembly_map
109    */
110   public int[] getAssemblyMapping(String species, String chromosome,
111           String fromRef, String toRef, int[] queryRange)
112   {
113     URL url = null;
114     BufferedReader br = null;
115
116     try
117     {
118       url = getAssemblyMapUrl(species, chromosome, fromRef, toRef, queryRange[0],
119               queryRange[1]);
120       br = getHttpResponse(url, null);
121       return (parseAssemblyMappingResponse(br));
122     } catch (Throwable t)
123     {
124       System.out.println("Error calling " + url + ": " + t.getMessage());
125       return null;
126     } finally
127     {
128       if (br != null)
129       {
130         try
131         {
132           br.close();
133         } catch (IOException e)
134         {
135           // ignore
136         }
137       }
138     }
139   }
140
141   /**
142    * Parses the JSON response from the /map/&lt;species&gt;/ REST service. The
143    * format is (with some fields omitted)
144    * 
145    * <pre>
146    *  {"mappings": 
147    *    [{
148    *       "original": {"end":45109016,"start":45051610},
149    *       "mapped"  : {"end":43186384,"start":43128978} 
150    *  }] }
151    * </pre>
152    * 
153    * @param br
154    * @return
155    */
156   protected int[] parseAssemblyMappingResponse(BufferedReader br)
157   {
158     int[] result = null;
159     JSONParser jp = new JSONParser();
160
161     try
162     {
163       JSONObject parsed = (JSONObject) jp.parse(br);
164       JSONArray mappings = (JSONArray) parsed.get(MAPPINGS);
165
166       Iterator rvals = mappings.iterator();
167       while (rvals.hasNext())
168       {
169         // todo check for "mapped"
170         JSONObject val = (JSONObject) rvals.next();
171         JSONObject mapped = (JSONObject) val.get(MAPPED);
172         int start = Integer.parseInt(mapped.get("start").toString());
173         int end = Integer.parseInt(mapped.get("end").toString());
174         String strand = mapped.get("strand").toString();
175         if ("1".equals(strand))
176         {
177           result = new int[] { start, end };
178         }
179         else
180         {
181           result = new int[] { end, start };
182         }
183       }
184     } catch (IOException | ParseException | NumberFormatException e)
185     {
186       // ignore
187     }
188     return result;
189   }
190
191   /**
192    * Constructs a URL to the /map/cds/<id> or /map/cdna/<id> REST service. The
193    * REST call is to either ensembl or ensemblgenomes, as determined from the
194    * division, e.g. Ensembl or EnsemblProtists.
195    * 
196    * @param domain
197    * @param accession
198    * @param start
199    * @param end
200    * @param cdsOrCdna
201    * @return
202    * @throws MalformedURLException
203    */
204   URL getIdMapUrl(String domain, String accession, int start, int end,
205           String cdsOrCdna) throws MalformedURLException
206   {
207     String url = String
208             .format("%s/map/%s/%s/%d..%d?include_original_region=1&content-type=application/json",
209                     domain, cdsOrCdna, accession, start, end);
210     return new URL(url);
211   }
212
213 }