Skip to content

Commit b261022

Browse files
committed
Apply upstream BEDPE support changes mostly
2 parents 7a0160d + 9fad72b commit b261022

18 files changed

+146
-67
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jdk: openjdk11
77
install:
88
- ./gradlew assemble
99
script:
10-
- ./gradlew check --debug
10+
- ./gradlew check
1111
- ./gradlew createMacAppDistZip
1212
- ./gradlew createLinuxDistZip
1313
- ./gradlew createWinDistZip

build.gradle

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
// THE SOFTWARE.
2323

2424
apply plugin: 'java'
25+
apply plugin: 'maven'
2526
apply plugin: 'application'
2627

2728
import org.apache.tools.ant.filters.ReplaceTokens
@@ -190,19 +191,19 @@ tasks.withType(Test) {
190191
systemProperties['ignore.ioexceptions'] = 'false'
191192
maxHeapSize = '2g'
192193
maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
193-
194-
195-
194+
195+
196+
196197
}
197198

198199
compileTestJava {
199200
inputs.property("moduleName", moduleName)
200201
doFirst {
201202
options.compilerArgs = [
202-
'--module-path', classpath.asPath,
203+
'--module-path', classpath.asPath,
203204
'--add-modules', 'junit',
204205
'--add-modules', 'fest.swing',
205-
'--patch-module', "$moduleName=" + files(sourceSets.test.java.srcDirs).asPath,
206+
'--patch-module', "$moduleName=" + files(sourceSets.test.java.srcDirs).asPath,
206207
]
207208
classpath = files()
208209
}
@@ -377,7 +378,26 @@ task signWinExeDist(type: Exec, dependsOn: createWinExeDist) {
377378
}
378379
}
379380

380-
tasks.build.dependsOn { [createDistZip, createLinuxDistZip, createMacAppDistZip, createWinDistZip] }
381+
task fullJar(type: Jar, dependsOn: jar) {
382+
// Based on https://discuss.gradle.org/t/removing-dependencies-from-a-jar-file-during-jar-task/5521/3
383+
from {
384+
((configurations.compile - configurations.default) + "${buildDir}/libs/igv.jar").collect {
385+
zipTree(it)
386+
}
387+
} {
388+
exclude "META-INF/**"
389+
}
390+
391+
manifest {
392+
attributes(
393+
"Permissions": "all-permissions",
394+
"Application-Name": "IGV",
395+
"Built-By": System.getProperty('user.name'),
396+
"Main-Class": mainClassName,
397+
"Class-Path": configurations.default.collect { it.getName() }.join(' ')
398+
)
399+
}
400+
}
381401

382402
// XXX: META-INF versions support seem to clash with proguard at this point in time:
383403
// https://sourceforge.net/p/proguard/bugs/665/

docs/AWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Amazon Web Services support for IGV desktop
2+
3+
Please refer to the following step by step tutorial to setup an AWS backend that provides
4+
access to S3 for IGV:
5+
6+
https://umccr.org/blog/igv-amazon-backend-setup/

src/main/java/org/broad/igv/Globals.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.apache.log4j.Logger;
2929
import org.broad.igv.renderer.SequenceRenderer;
3030

31-
import java.io.FileReader;
3231
import java.io.IOException;
3332
import java.text.DecimalFormat;
3433
import java.util.*;
@@ -132,7 +131,7 @@ public class Globals {
132131
TIMESTAMP = properties.getProperty("timestamp", "???");
133132
BEDtoolsPath = System.getProperty("BEDtoolsPath", BEDtoolsPath);
134133

135-
//Runtime property overrides compile-time property, if both exist.
134+
//Runtime property overrides compile-time property, if both exist.
136135
//If neither exist we default to false
137136
final String developmentProperty = System.getProperty("development", properties.getProperty("development", "false"));
138137
development = Boolean.parseBoolean(developmentProperty);

src/main/java/org/broad/igv/batch/CommandListener.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public void run() {
128128
try {
129129
clientSocket.close();
130130
clientSocket = null;
131+
// We do NOT set isListening = false here, otherwise logout/login state change falls back to OOB
131132
} catch (IOException e) {
132133
log.error("Error in client socket loop", e);
133134
isListening = false;
@@ -168,6 +169,7 @@ private void processClientSession(CommandExecutor cmdExe) throws IOException {
168169

169170

170171
while (!halt && (inputLine = in.readLine()) != null) {
172+
171173
String cmd = inputLine;
172174
if (cmd.startsWith("GET")) {
173175

@@ -180,11 +182,8 @@ private void processClientSession(CommandExecutor cmdExe) throws IOException {
180182
if (tokens.length == 2) {
181183
headers.put(tokens[0].trim(), tokens[1].trim());
182184
}
183-
log.info("Tokens (as in *tokenized* headers, not oauth tokens): "+Arrays.toString(tokens));
184185
}
185-
186-
log.info("Headers: "+headers);
187-
log.info("Command: "+cmd);
186+
log.info(cmd);
188187

189188
String command = null;
190189
Map<String, String> params = null;
@@ -195,13 +194,11 @@ private void processClientSession(CommandExecutor cmdExe) throws IOException {
195194
} else {
196195
String[] parts = tokens[1].split("\\?");
197196
command = parts[0];
198-
log.debug("Parts of the request: "+Arrays.toString(parts));
199197
params = parts.length < 2 ? new HashMap() : parseParameters(parts[1]);
200198
}
201199

202200
// Detect google oauth callback
203201
if (command.equals("/oauthCallback")) {
204-
log.debug("Response parameters: " + params.toString());
205202
if (params.containsKey("code")) {
206203
OAuthUtils.getInstance().setAuthorizationCode(params.get("code"));
207204
} else if (params.containsKey("token")) {

src/main/java/org/broad/igv/bedpe/BedPEFeature.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class BedPEFeature implements BedPE {
1515
int start2;
1616
int end2;
1717
String name;
18+
String scoreString = "";
1819
double score;
1920
Color color;
2021
int thickness = 1;
@@ -52,12 +53,12 @@ public int getEnd() {
5253
return Math.max(end1, end2);
5354
}
5455

55-
public int getMidStart() {
56-
return Math.min ((start1 + end1) / 2, (start2 + end2) / 2);
56+
public double getMidStart() {
57+
return Math.min ((start1 + end1) / 2.0, (start2 + end2) / 2.0);
5758
}
5859

59-
public int getMidEnd() {
60-
return Math.max ((start1 + end1) / 2, (start2 + end2) / 2);
60+
public double getMidEnd() {
61+
return Math.max ((start1 + end1) / 2.0, (start2 + end2) / 2.0);
6162
}
6263

6364
@Override
@@ -114,7 +115,7 @@ public String getValueString() {
114115
}
115116
buf.append(locus1);
116117
buf.append("<br>" + locus2);
117-
buf.append("<br>Score: " + score);
118+
buf.append("<br>Score: " + scoreString);
118119
if(attributes != null) {
119120
buf.append("<br><hr>");
120121
for (Map.Entry<String, String> entry : attributes.entrySet()) {

src/main/java/org/broad/igv/bedpe/BedPEParser.java

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,17 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc
2929
int thicknessColumn = -1;
3030
DatasetType type = DatasetType.UNKNOWN;
3131
boolean parsedHeader = true;
32-
String[] columns;
32+
33+
// Default column headers from BedPE spec. Can be overriden
34+
String[] columns = {"chrom1", "start1", "stop1", "chrom2", "start2", "stop2", "name", "score", "strand1", "strand2"};
3335
boolean col7isNumeric = true; // Until proven otherwise
3436

3537
Map<String, Color> colorCache = new HashMap<>();
3638
List<BedPEFeature> features = new ArrayList<>();
3739
BufferedReader br = null;
3840
br = ParsingUtils.openBufferedReader(locator.getPath());
3941
String nextLine;
42+
boolean firstLine = true;
4043
while ((nextLine = br.readLine()) != null) {
4144

4245
if (nextLine.startsWith("#columns")) {
@@ -57,11 +60,15 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc
5760
} catch (NumberFormatException e) {
5861
log.error("Error parsing #column line.", e);
5962
}
60-
} else if (nextLine.startsWith("#")) {
61-
columns = Globals.tabPattern.split(nextLine);
62-
if (nextLine.trim().equals("#chrom1\tstart1\tstop1\tchrom2\tstart2\tstop2\tname\tqual\tstrand1\tstrand2\tfilters\tinfo")) {
63-
type = DatasetType.TENX;
64-
} else {
63+
} else if (nextLine.trim().equals("#chrom1\tstart1\tstop1\tchrom2\tstart2\tstop2\tname\tqual\tstrand1\tstrand2\tfilters\tinfo")) {
64+
type = DatasetType.TENX;
65+
}
66+
67+
if (nextLine.startsWith("#") || nextLine.startsWith("chr1\tx1\tx2")) {
68+
69+
String[] tokens = Globals.tabPattern.split(nextLine);
70+
if (tokens.length >= 6) {
71+
columns = tokens;
6572
for (int i = 6; i < columns.length; i++) {
6673
if (columns[i].equalsIgnoreCase("color")) {
6774
colorColumn = i;
@@ -70,10 +77,16 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc
7077
}
7178
}
7279
}
73-
7480
} else if (nextLine.startsWith("track") || nextLine.startsWith("##track")) {
7581
TrackProperties trackProperties = new TrackProperties();
7682
ParsingUtils.parseTrackLine(nextLine, trackProperties);
83+
} else if (firstLine && nextLine.startsWith("chromosome1\tx1\tx2") || nextLine.startsWith("chr1\tx1\tx2")) {
84+
columns = Globals.tabPattern.split(nextLine);
85+
for (int i = 6; i < columns.length; i++) {
86+
if (columns[i].equalsIgnoreCase("color")) {
87+
colorColumn = i;
88+
}
89+
}
7790
} else {
7891
String[] tokens = Globals.tabPattern.split(nextLine);
7992

@@ -100,50 +113,68 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc
100113
}
101114

102115
if (tokens.length > 7) {
103-
feature.score = Double.parseDouble(tokens[7]);
116+
feature.scoreString = tokens[7];
117+
try {
118+
feature.score = Double.parseDouble(tokens[7]);
119+
} catch (NumberFormatException e) {
120+
feature.score = 0;
121+
}
104122
}
105123

106-
if (type == DatasetType.TENX) {
124+
if (tokens.length > 8) {
107125
Map<String, String> attributes = new LinkedHashMap<>();
108-
if (!tokens[8].equals(".")) {
109-
attributes.put("filters", tokens[8]);
110-
}
111-
String[] kvPairs = Globals.semicolonPattern.split(tokens[11]);
112-
for (String kvPair : kvPairs) {
113-
String[] kv = Globals.equalPattern.split(kvPair);
114-
attributes.put(kv[0], kv[1]);
126+
127+
for (int i = 8; i < tokens.length; i++) {
128+
129+
String t = tokens[i];
130+
String c = columns != null && columns.length > i ? columns[i] : String.valueOf(i);
131+
132+
if (c.equals("info") && t.contains("=")) {
133+
String[] kvPairs = Globals.semicolonPattern.split(tokens[11]);
134+
for (String kvPair : kvPairs) {
135+
String[] kv = Globals.equalPattern.split(kvPair);
136+
if (kv.length > 1) {
137+
attributes.put(kv[0], kv[1]);
138+
}
139+
}
140+
} else {
141+
attributes.put(c, t);
142+
}
115143
}
116144
feature.attributes = attributes;
117145
feature.type = attributes.get("TYPE");
118-
} else {
119-
if (colorColumn > 0) {
120-
String colorString = tokens[colorColumn];
121-
Color c = colorCache.get(colorString);
122-
if (c == null) {
123-
c = ColorUtilities.stringToColor(colorString);
124-
colorCache.put(colorString, c);
125-
}
126-
feature.color = c;
127-
}
146+
}
128147

129-
if (thicknessColumn > 0) {
130-
feature.thickness = Integer.parseInt(tokens[thicknessColumn]);
148+
if (colorColumn > 0) {
149+
String colorString = tokens[colorColumn];
150+
Color c = colorCache.get(colorString);
151+
if (c == null) {
152+
c = ColorUtilities.stringToColor(colorString);
153+
colorCache.put(colorString, c);
131154
}
155+
feature.color = c;
132156
}
157+
158+
if (thicknessColumn > 0) {
159+
feature.thickness = Integer.parseInt(tokens[thicknessColumn]);
160+
}
161+
133162
// Skipping remaining fields for now
134163

135164
features.add(feature);
136165
}
166+
firstLine = false;
137167
}
138168

139169

140170
// A hack to detect "interaction" bedpe files, which are not spec compliant. Interaction score is column 7
141171
if (col7isNumeric) {
142172
for (BedPEFeature f : features) {
143173
f.score = Double.parseDouble(f.name);
174+
f.scoreString = f.name;
144175
f.name = null;
145176
}
146-
if(type == DatasetType.UNKNOWN) {
177+
if (type == DatasetType.UNKNOWN) {
147178
type = DatasetType.CLUSTER; // A guess
148179
}
149180
}

src/main/java/org/broad/igv/bedpe/InteractionTrack.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class InteractionTrack extends AbstractTrack {
4141
protected static Color axisLineColor = new Color(255, 180, 180);
4242
private JCheckBoxMenuItem autoscaleCB;
4343
private JMenuItem maxScoreItem;
44+
private List<BedPE> wgFeatures;
4445

4546

4647
enum Direction {UP, DOWN}
@@ -101,6 +102,15 @@ public InteractionTrack(ResourceLocator locator, BedPEParser.Dataset dataset, Ge
101102
} else {
102103
direction = UP;
103104
}
105+
106+
String blockString = PreferencesManager.getPreferences().get(Constants.ARC_BLOCKS);
107+
if (blockString != null) {
108+
try {
109+
showBlocks = Boolean.valueOf(blockString);
110+
} catch (IllegalArgumentException e) {
111+
log.error("Illegal arc blocks option: " + blockString, e);
112+
}
113+
}
104114
}
105115

106116
private void init(List<BedPEFeature> featureList, Genome genome) {
@@ -116,10 +126,12 @@ private void init(List<BedPEFeature> featureList, Genome genome) {
116126
newList.add(new BedPEInterFeature(f, 2));
117127
}
118128
}
119-
newList.addAll(createWGFeatures(featureList, genome));
120129

121130
featureCache = new FeatureCache<>(newList, 50);
122131

132+
wgFeatures = createWGFeatures(featureList, genome);
133+
134+
123135
}
124136

125137

@@ -160,7 +172,11 @@ public void load(ReferenceFrame frame) {
160172

161173
private List<BedPE> getFeaturesOverlapping(String chr, double start, double end) {
162174

163-
return featureCache.getFeatures(chr, (int) start, (int) end);
175+
if(chr.equals(Globals.CHR_ALL)) {
176+
return wgFeatures;
177+
} else {
178+
return featureCache.getFeatures(chr, (int) start, (int) end);
179+
}
164180
}
165181

166182
@Override
@@ -281,6 +297,7 @@ public IGVPopupMenu getPopupMenu(TrackClickEvent te) {
281297
showBlocksCB.setSelected(showBlocks);
282298
showBlocksCB.addActionListener(e -> {
283299
showBlocks = showBlocksCB.isSelected();
300+
PreferencesManager.getPreferences().put(Constants.ARC_BLOCKS, String.valueOf(showBlocksCB.isSelected()));
284301
refresh();
285302
});
286303
menu.add(showBlocksCB);

src/main/java/org/broad/igv/bedpe/PEBlockRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
public class PEBlockRenderer implements BedPERenderer {
99

1010
InteractionTrack track;
11-
int rowHeight = 10;
11+
int rowHeight = 6;
1212

1313
public PEBlockRenderer(InteractionTrack track) {
1414
this.track = track;

src/main/java/org/broad/igv/bedpe/ProportionalArcRenderer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ public void render(List<BedPE> features, RenderContext context, Rectangle trackR
4949

5050

5151
if (track.maxScore > 0 && bedPE.getScore() > 0) {
52-
double logMax = Math.log10(track.maxScore);
53-
h = (int) ((Math.log10(bedPE.getScore()) / logMax) * h);
52+
double logMax = Math.log10(track.maxScore + 1);
53+
h = (int) ((Math.log10(bedPE.getScore() + 1) / logMax) * h);
5454
}
5555

5656
if (bedPE.isSameChr()) {

0 commit comments

Comments
 (0)