From eb77c434f67a29e92ab648f523757b46cffee5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilhem=20M=2E=20An=C3=A9?= Date: Wed, 21 Jul 2021 17:53:33 -0500 Subject: [PATCH] New icy tree style and added documentation (#16) - the new icytree style is now the default for R-based plots (breaking change) because the old style is bad for networks that are not tree-child. - the icytree style option (:fulltree) is only supported with RCall. The old style (:majortree) is the default for the Gadfly-based plot. - new manual - cladewiseorder! is no longer used. It could be deleted from PhyloNetworks or un-exported. --- Project.toml | 2 +- docs/Project.toml | 4 +- docs/make.jl | 10 ++- docs/readme.md | 71 ++++++++++++++++ docs/src/index.md | 15 ++++ docs/src/man/adding_data.md | 108 ++++++++++++++++++++++++ docs/src/man/better_edges.md | 102 +++++++++++++++++++++++ docs/src/man/getting_started.md | 40 +++++++++ docs/src/man/installation.md | 22 +++++ docs/src/man/untangling_edges.md | 45 ++++++++++ src/PhyloPlots.jl | 2 + src/phylonetworksPlots.jl | 138 +++++++++++++++++++++++-------- src/plotGadfly.jl | 6 +- src/plotRCall.jl | 77 ++++++++++++----- test/test_phylonetworkPlots.jl | 97 ++++++++++++---------- test/test_plotRCall.jl | 8 ++ 16 files changed, 647 insertions(+), 100 deletions(-) create mode 100644 docs/src/man/adding_data.md create mode 100644 docs/src/man/better_edges.md create mode 100644 docs/src/man/getting_started.md create mode 100644 docs/src/man/installation.md create mode 100644 docs/src/man/untangling_edges.md diff --git a/Project.toml b/Project.toml index e55f846..306d7ec 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PhyloPlots" uuid = "c0d5b6db-e3fc-52bc-a87d-1d050989ed3b" license = "MIT" -version = "0.2.4" +version = "0.3.0" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" diff --git a/docs/Project.toml b/docs/Project.toml index bbc8999..e04961c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,10 +1,12 @@ [deps] +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterMarkdown = "997ab1e6-3595-5248-9280-8efb232c3433" # will be added by the CI platform as being developed: # PhyloPlots = "c0d5b6db-e3fc-52bc-a87d-1d050989ed3b" # will be added to track master version in make.jl: # PhyloNetworks = "33ad39ac-ed31-50eb-9b15-43d0656eaa72" +RCall = "6f49c342-dc21-5d91-9882-a32aef131414" [compat] -Documenter = "~0.26" +Documenter = "~0.27" diff --git a/docs/make.jl b/docs/make.jl index 37e0646..f2dd72d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -3,16 +3,24 @@ using Documenter, DocumenterMarkdown using Pkg Pkg.add(PackageSpec(name="PhyloNetworks", rev="master")) +using PhyloNetworks using PhyloPlots DocMeta.setdocmeta!(PhyloPlots, :DocTestSetup, :(using PhyloPlots); recursive=true) makedocs( sitename = "PhyloPlots.jl", - authors = "Cécile Ané", + authors = "Cécile Ané and Guilhem Ané", modules = [PhyloPlots], # to list plot() methods from PhyloPlots only, not from Gadfly etc. format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"), # easier local build pages = [ "home" => "index.md", + "manual" => [ + "installation" => "man/installation.md", + "getting started" => "man/getting_started.md", + "untangling edges" => "man/untangling_edges.md", + "better edges" => "man/better_edges.md", + "adding data" => "man/adding_data.md", + ], "library" => [ "public" => "lib/public.md", "internals" => "lib/internals.md", diff --git a/docs/readme.md b/docs/readme.md index 4ea56de..2fb55be 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -22,6 +22,66 @@ version of Documenter was released. If so, up the dependency, check locally that it works, make any updates as needed. - update Julia version in `.github/workflows/ci.yml`, `matrix.version` +## The "Documenter md" format + +### Note on the format + +The documentation pages are all written in this format. It is a regular md, but +with extra blocks of codes (as `@example` and `@setup`) that contain Julia +commands. These lines will be executed during the `makedoc()` process. See the +`Documenter` [doc](https://juliadocs.github.io/Documenter.jl/stable/man/syntax/) +for more details on the syntax. For instance, @example blocks with the same "name" +are run in the same session. Otherwise, an @example blocks with no name +is run in its own anonymous Modules. + +### Setting up the plot environment + +Some of these blocs may contain plots, which are going to be drawn during the +process, requiring the use of `PhyloPlots` along with `RCall`. Hence, +before the doc is built, `.github/workflows/ci.yml` installs `R` on the server, +sets up the julia environment with dependencies like `PhyloPlots` before +starting the build in itself. + +### Directory of the plots + +We chose to group all the output plots in the directory `assets/figures`. +Hence, the typical setup in a documentation page containing plots is: + + ```@setup name + using PhyloNetworks, PhyloPlots, RCall + mkpath("../assets/figures") + figname(x) = joinpath("..", "assets", "figures", x) + ``` + +The `mkpath` command is there to ensure that the target directory does indeed +exist. In theory, it only needs to be called once (by the first documentation +page being built). However, as this order might be subject to change over time, +it could be safer to include it on every such page. + +### Format of the plots + +After trial and error and discussions, we chose to use only the *SVG* format +for the plot. This format should ensure that when a plot is drawn again, +identical, in a new build, then Git will recognize that it has not change, and +hence not save a new version of it. This should ensure that the repository does +not grow unreasonably at each build of the doc, i.e. after each push to +master. The typical commands to save and display a plot should hence be: + + ```@example name + R"svg"(figname("my_useful_name.svg"), width=4, height=3); # hide + plot(net, :R); + R"dev.off()" # hide + nothing # hide + ``` + ![my_useful_name](../assets/figures/my_useful_name.svg) + +I like to add `R"par"(mar=[.1,.1,.1,.1]) # hide` since the default margins are too wide for my preference. + +**Warning**: this is not like an interactive session. If the same file name +is re-used by some other documentation page for some other plot, only the +final version of the plot will be committed by git, with possible unintended +consequences. Make sure to use different file names for plots that are supposed +to look different (across the whole site). ## to make a local version of the website @@ -42,3 +102,14 @@ julia> include("make.jl") it will: - tests the `jldoctest` blocks of examples in the docstrings - creates or updates a `build/` directory with html files + +## references + +big difference: + + [blabla](@ref) + [`blabla`](@ref) + +The first version will look for a *section* header "blabla", to link to that section. +The secon version will look for a *function* named "blabla", +to link to the documentation for that function. diff --git a/docs/src/index.md b/docs/src/index.md index ce01991..c276df2 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -9,6 +9,21 @@ objects can be displayed via [Gadfly](http://gadflyjl.org/stable/), and through [R](https://www.r-project.org) via [RCall](https://github.com/JuliaInterop/RCall.jl). +## manual outline + +```@contents +Pages = [ + "man/installation.md", + "man/getting_started.md", + "man/untangling_edges.md", + "man/better_edges.md", + "man/adding_data.md" +] +Depth = 3 +``` + +## library outline + ```@contents Pages = ["lib/public.md", "lib/internals.md"] Depth = 1 diff --git a/docs/src/man/adding_data.md b/docs/src/man/adding_data.md new file mode 100644 index 0000000..055f979 --- /dev/null +++ b/docs/src/man/adding_data.md @@ -0,0 +1,108 @@ +```@setup adding_data +using PhyloNetworks, PhyloPlots, RCall, DataFrames +mkpath("../assets/figures") +figname(x) = joinpath("..", "assets", "figures", x) +``` + +# Adding data + +In this section, we will look over ways of adding extra information or data to a plot. + +## Adding labels + +!!! note + For demonstration purposes, I will walk through the process of adding labels to edges, + with notes on how to do the same for nodes in parentheses. + +To add labels on edges (or nodes), we need to know their numbers. We can use the +`showEdgeNumbers = true` option for this. (Use `showNodeNumbers = true` to see node numbers). + +```@example adding_data +R"svg"(figname("adding_data1.svg"), width=3, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +net = readTopology("(A,((B,#H1),((C)#H1, D)));") # hide +plot(net, :R, showEdgeNumber=true); +R"dev.off()" # hide +nothing # hide +``` +![example1](../assets/figures/adding_data1.svg) + +We will need to define a DataFrame with two columns of information: the number of the edge (or +node), and the label that goes on it, like this: + +| Number | Label | +|--------|------------------| +| 1 | "My first edge" | +| 2 | "My second edge" | + +After including the DataFrames package, we can define it as so: +```@repl +using DataFrames +DataFrame(Number=[1, 2], Label=["My first edge", "My second edge"]) +``` +Using this dataframe as input to the `edgeLabel` (`nodeLabel` for nodes) option puts the text on the correct edges: +```@example adding_data +R"svg"(figname("edge_labels_example.svg"), width=4, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +net = readTopology("(A,((B,#H1),(C,(D)#H1)));") # hide +plot(net, :R, edgeLabel=DataFrame(Number=[1, 2], Label=["My first edge", "My second edge"])); +R"dev.off()" # hide +nothing # hide +``` +![example2](../assets/figures/edge_labels_example.svg) + +## Adding other data using R + +We can use the return values of [`plot`](@ref) to get some information on the coordinates of +different elements of the plot. Using this, we can add any other information we want. + +The [`plot`](@ref) function returns the following tuple: +``` +(xmin, xmax, ymin, ymax, node_x, node_y, node_yB, node_yE, +edge_xB, edge_xE, edge_yB, edge_yE, ndf, edf) +``` +See the documentation for descriptions on every element: [`plot`](@ref) + +## Side clade bars example + +Here's some example code that adds bars to denote clades in the margin: + +```@example adding_data +R"svg"(figname("side_bars.svg"), width=4, height=4) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +net = readTopology("(((((((1,2),3),4),5),(6,7)),(8,9)),10);"); +plot(net, :R, xlim=(1, 10)) +R"segments"([9, 9, 9], [0.8, 7.8, 9.8], [9, 9, 9], [7.2, 9.2, 10.2]) +R"text"([9.5, 9.5, 9.5], [4, 8.5, 10], ["C", "B", "A"]) +R"dev.off()" # hide +nothing # hide +``` +![example3](../assets/figures/side_bars.svg) + +Let's break this down step by step. +First, we read the topology, and plot the graph normally. `plot` actually returns +a value, from which we can get useful information. +Below, we store the plot output in `res`, then check its first two values +because they contain the default range of the x axis; `xmin` and `xmax`. + +```@example adding_data +res = plot(net, :R); +res[1:2] +``` + +Looking at `xmin` and `xmax` returned by default, we can see that the x +range is about `(0.3, 9)`. To give us extra space to work with, we can +set `xlim` to `(1,10)`, forcing the range to be wider. + +```julia +plot(net, :R, xlim=(1, 10)); +``` + +Knowing the coordinates, we can now add more information to the plot through +`RCall`. For this, I use the `segments` and `text` functions to add side bars with +text on them. + +``` +R"segments"([9, 9, 9], [0.8, 7.8, 9.8], [9, 9, 9], [7.2, 9.2, 10.2]) +R"text"([9.5, 9.5, 9.5], [4, 8.5, 10], ["C", "B", "A"]) +``` diff --git a/docs/src/man/better_edges.md b/docs/src/man/better_edges.md new file mode 100644 index 0000000..42fac68 --- /dev/null +++ b/docs/src/man/better_edges.md @@ -0,0 +1,102 @@ +```@setup better_edges +using PhyloNetworks, PhyloPlots, RCall, DataFrames +mkpath("../assets/figures") +figname(x) = joinpath("..", "assets", "figures", x) +``` + +# Better edges + +## Different hybrid edge styles + +We can use the `style` option to visualize minor hybrid edges as simple lines, +unlike the [icytree](https://icytree.org/) style visualization. `style` is by default `:fulltree`, +but by switching it to `:majortree`, we can draw minor hybrid edges as diagonal lines. + +```@example better_edges +R"svg"(figname("style_example.svg"), width=3, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +net = readTopology("(A,((B,#H1),(C,(D)#H1)));") # hide +plot(net, :R, style=:majortree); +R"dev.off()" # hide +nothing # hide +``` +![example1](../assets/figures/style_example.svg) + +## Using edge lengths + +We can use `useEdgeLength=true` to draw a plot that uses the network's edge lengths to determine the lengths of the +lines. For this, we'll use a network that has branch lengths: + +```@example better_edges +R"svg"(figname("edge_len_example.svg"), width=6, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +R"layout"([1 2]) # hide +net = readTopology("(A:3.3,((B:1.5,#H1:0.5):1.5,((C:1)#H1:1.8,D:1.1):.2):0.3);") +df = DataFrame(Number=[-3, 3], Label=["N", "H1"]); # hide +plot(net, :R, useEdgeLength=true, ylim = [-1, 5.5], nodeLabel = df); # hide +R"text"([3], [0], ["useEdgeLength=true"]) # hide +plot(net, :R, useEdgeLength=false, ylim = [-1, 5.5], nodeLabel = df); # hide +R"text"([3], [0], ["useEdgeLength=false"]) # hide +R"dev.off()" # hide +nothing # hide +``` +![example2](../assets/figures/edge_len_example.svg) + +!!! note + I used a DataFrame to add labels to the plot. For more on this, + see the [Adding labels](@ref) section. + +If branch lengths represent time, D could represent a fossil, or a virus strain sequenced +a year before the others. Seeing this visually is the advantage of `useEdgeLengths=true` + +This network happens to be time consistent, because the distance +along the time (x) axis from node `N` to the hybrid node `H1` is +the same both ways. + +!!! note "Time consistency" + A network is time-consistent if all the paths between 2 given nodes all + have the same length. + Time inconsistency can occur when branch lengths are not measured in + calendar time, such as if branch lengths are in substitutions per site + (some paths might evolve with more substitutions than others), or in + number of generations (some lineages might have 1 generation per year, + others more or fewer generations per year), or in coalescent units + (number of generations / effective population size). + + A time-consistent network may be ultrametric (the distance + between the root and the tips is the same across all tips), + or not like the network above. + +Time inconsistent networks like these ones below might cause confusion: + +```@example better_edges +R"svg"(figname("edge_len_example2.svg"), width=6, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +R"layout"([1 2]) # hide +net1 = readTopology("(A:3.3,((B:1.5,#H1:1.2):1.5,((C:1.8)#H1:1,D:1.1):.2):0.3);"); +net2 = readTopology("(A:3.3,((B:1.5,#H1:0.2):1.5,((C:1)#H1:1.8,D:1.1):.2):0.3);"); +plot(net1, :R, useEdgeLength=true); # hide +plot(net2, :R, useEdgeLength=true); # hide +R"dev.off()" # hide +nothing # hide +``` +![example3](../assets/figures/edge_len_example2.svg) + +It may be useful to consider using `style=:majortree` if it causes +too much confusion, since the `:majortree` style doesn't visually represent +minor edge lengths. Because of this, I used the `showEdgeLength=true` option to +see the information anyway. + +```@example better_edges +R"svg"(figname("edge_len_example3.svg"), width=6, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +R"layout"([1 2]) +plot(net1, :R, useEdgeLength=true, style = :majortree, showEdgeLength=true, arrowlen=0.1); +plot(net2, :R, useEdgeLength=true, style = :majortree, showEdgeLength=true, arrowlen=0.1); +R"dev.off()" # hide +nothing # hide +``` +![example4](../assets/figures/edge_len_example3.svg) + +I also used the `arrowlen=0.1` option to show the arrow tips to show the direction of minor edges, +which are hidden by default when using the `style=:majortree` option. diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md new file mode 100644 index 0000000..0033273 --- /dev/null +++ b/docs/src/man/getting_started.md @@ -0,0 +1,40 @@ +```@setup getting_started +using PhyloNetworks, PhyloPlots, RCall, DataFrames +mkpath("../assets/figures") +figname(x) = joinpath("..", "assets", "figures", x) +``` + +# Getting started + +To demonstrate, we will plot the simple network: `(A,((B,#H1),(C,(D)#H1)));` + +To start plotting, use the packages: + +```@repl getting_started +using PhyloNetworks +using PhyloPlots +``` +Then read the topology +```@repl getting_started +net = readTopology("(A,((B,#H1),(C,(D)#H1)));") +``` +and call `plot`, using :R for full funtionality: + +!!! note "About the two versions of plot()" + One is made using Gadfly (`plot(net)`), the other using RCall (`plot(net, :R)`). + Since only the RCall version supports the option `style=:fulltree` for the same + style used by [icytree](https://icytree.org), we will only use RCall. + +For the function's full documentation, see here: [`plot`](@ref) + +This will draw the following plot. + +```@example getting_started +R"svg"(figname("gettingstarted.svg"), width=3, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +net = readTopology("(A,((B,#H1),(C,(D)#H1)));") # hide +plot(net, :R); +R"dev.off()" # hide +nothing # hide +``` +![example1](../assets/figures/gettingstarted.svg) diff --git a/docs/src/man/installation.md b/docs/src/man/installation.md new file mode 100644 index 0000000..df62faa --- /dev/null +++ b/docs/src/man/installation.md @@ -0,0 +1,22 @@ + +# Installation + +This will assume you have installed Julia and PhyloNetworks. For information on how to +install them, see +[here](https://crsl4.github.io/PhyloNetworks.jl/dev/man/installation/#Installation) + +PhyloPlots depends on PhyloNetworks, and has further dependencies +like [Gadfly](http://gadflyjl.org/stable/) and +[RCall](https://github.com/JuliaInterop/RCall.jl) + +To install in the Julia REPL, enter package mode with `]`, and: + +``` +add PhyloPlots +``` +Or in julian mode: + +```julia +using Pkg +Pkg.add("PhyloPlots") +``` diff --git a/docs/src/man/untangling_edges.md b/docs/src/man/untangling_edges.md new file mode 100644 index 0000000..ca2c483 --- /dev/null +++ b/docs/src/man/untangling_edges.md @@ -0,0 +1,45 @@ +```@setup untangling +using PhyloNetworks, PhyloPlots, RCall, DataFrames +mkpath("../assets/figures") +figname(x) = joinpath("..", "assets", "figures", x) +``` + +# Untangling the network + +This plot may not be the easiest to read, as the hybrid edge crosses over C's +edge: + +![example1](../assets/figures/gettingstarted.svg) + +To fix this, we can to rotate C and D's edges around their parent node. + +First we need to know the number of this parent node. By showing node numbers +with the `showNodeNumbers = true` option, we can find the number of the node +whose child edges we should rotate. + +```@example untangling +R"svg"(figname("untangling1.svg"), width=3, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +net = readTopology("(A,((B,#H1),(C,(D)#H1)));") # hide +plot(net, :R, showNodeNumber=true); +R"dev.off()" # hide +nothing # hide +``` +![example2](../assets/figures/untangling1.svg) + +As we can see, rotating edges around node `-5` will make for a prettier network. + +```@example untangling +R"svg"(figname("untangling2.svg"), width=3, height=3) # hide +R"par"(mar=[.1,.1,.1,.1]) # hide +net = readTopology("(A,((B,#H1),(C,(D)#H1)));") # hide +rotate!(net, -5) +plot(net, :R) +R"dev.off()" # hide +nothing # hide +``` +![example3](../assets/figures/untangling2.svg) + + +This may seem unnecesary for a small network as shown, but it is a useful tool for plotting +large networks. diff --git a/src/PhyloPlots.jl b/src/PhyloPlots.jl index 514f5aa..7d2cad0 100644 --- a/src/PhyloPlots.jl +++ b/src/PhyloPlots.jl @@ -3,6 +3,8 @@ __precompile__() module PhyloPlots # standard libraries +using DataFrames: _broadcast_unalias_helper, LatexTableFormat +using PhyloNetworks: getMinorParent, getParent, getChildren, getMajorParentEdge, getChild, Edge, getMinorParentEdge using Markdown using Printf: @printf, @sprintf diff --git a/src/phylonetworksPlots.jl b/src/phylonetworksPlots.jl index 746293d..7006264 100644 --- a/src/phylonetworksPlots.jl +++ b/src/phylonetworksPlots.jl @@ -9,7 +9,7 @@ Calculate coordinates for plotting later with Gadfly or RCall. Actually modifies some (minor) attributes of the network, as it calls `directEdges!`, `preorder!` and `cladewiseorder!`. """ -function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool) +function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool, useSimpleHybridLines::Bool) try directEdges!(net) # to update isChild1 catch e @@ -19,23 +19,50 @@ function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool) rethrow(e) end preorder!(net) # to update net.nodes_changed: true pre-ordering - cladewiseorder!(net) # to update cladewiseorder_nodeIndex: cladewise on major tree # determine y for each node = y of its parent edge: post-order traversal # also [yB,yE] for each internal node: range of y's of all children nodes - ymin = 1.0; ymax = Float64(net.numTaxa); + # y max is the numTaxa + number of minor edges + ymin = 1.0; + ymax = net.numTaxa + if !useSimpleHybridLines + ymax += sum(!e.isMajor for e in net.edge) + end + node_y = zeros(Float64, net.numNodes) # order: in net.nodes, *!not in nodes_changed!* node_yB = zeros(Float64,net.numNodes) # min (B=begin) and max (E=end) node_yE = zeros(Float64,net.numNodes) # of at children's nodes + edge_yB = zeros(Float64,net.numEdges) # yE of edge = y of child node # set node_y of leaves: follow cladewise order + # also sets edge_yB of minor hybrid edges nexty = ymax # first tips at the top, last at bottom - for i=length(net.node):-1:1 - ni = net.cladewiseorder_nodeIndex[i] - if net.node[ni].leaf - node_y[ni] = nexty - nexty -= 1.0 + cladewise_queue = copy(net.node[net.root].edge) # the child edges of root + # print("queued the root's children's indices: "); @show queue + while !isempty(cladewise_queue) + cur_edge = pop!(cladewise_queue); # deliberate choice over shift! for cladewise order + # increment spacing and add to node_y if leaf + if getChild(cur_edge).leaf + node_y[findfirst(x->x===getChild(cur_edge), net.node)] = nexty + nexty -= 1 + end + + # only for new hybrid lines: + # increment spacing and add to edge_yB if parent edge is minor + if !cur_edge.isMajor && !useSimpleHybridLines + edge_yB[findfirst(x->x===cur_edge, net.edge)] = nexty + nexty -= 1 + end + + # push children edges if this is a major edge: + if cur_edge.isMajor + for e in getChild(cur_edge).edge + if getParent(e) === getChild(cur_edge)# don't go backwards + push!(cladewise_queue, e) + end + end end end + # set node_y of internal nodes: follow post-order for i=length(net.node):-1:1 nn = net.nodes_changed[i] @@ -43,21 +70,35 @@ function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool) ni = findfirst(x -> x===nn, net.node) node_yB[ni]=ymax; node_yE[ni]=ymin; minor_yB = ymax; minor_yE = ymin; - nomajorchild=true + nomajorchild=useSimpleHybridLines # only use this var if using simple hybrid lines for e in nn.edge if nn == PhyloNetworks.getParent(e) # if e = child of node - if e.isMajor || nomajorchild - cc = PhyloNetworks.getChild(e) - yy = node_y[findfirst(x -> x===cc, net.node)] - yy!==nothing || error("oops, child $(cc.number) has not been visited before node $(nn.number).") - end - if e.isMajor - nomajorchild = false # we found a child edge that is a major edge - node_yB[ni] = min(node_yB[ni], yy) - node_yE[ni] = max(node_yE[ni], yy) - elseif nomajorchild # e is minor edge, and no major found so far - minor_yB = min(minor_yB, yy) - minor_yE = max(minor_yE, yy) + if useSimpleHybridLines + # old simple hybrid lines + if e.isMajor || nomajorchild + cc = PhyloNetworks.getChild(e) + yy = node_y[findfirst(x -> x===cc, net.node)] + yy!==nothing || error("oops, child $(cc.number) has not been visited before node $(nn.number).") + end + if e.isMajor + nomajorchild = false # we found a child edge that is a major edge + node_yB[ni] = min(node_yB[ni], yy) + node_yE[ni] = max(node_yE[ni], yy) + elseif nomajorchild # e is minor edge, and no major found so far + minor_yB = min(minor_yB, yy) + minor_yE = max(minor_yE, yy) + end + else + # new pretty hybrid lines + if e.isMajor + cc = PhyloNetworks.getChild(e) + child_y = node_y[findfirst(x -> x===cc, net.node)] + child_y!==nothing || error("oops, child $(cc.number) has not been visited before node $(nn.number).") + else + child_y = edge_yB[findfirst(x->x===e, net.edge)] + end + node_yB[ni] = min(node_yB[ni], child_y) + node_yE[ni] = max(node_yE[ni], child_y) end end end @@ -70,6 +111,10 @@ function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool) node_yE[ni] = minor_yE end node_y[ni] = (node_yB[ni]+node_yE[ni])/2 + if nomajorchild #since the minor edges are leaving from the center of the node's y pos. + node_yB[ni] = node_y[ni] + node_yE[ni] = node_y[ni] + end end # setting branch lengths for plotting @@ -117,7 +162,6 @@ function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool) node_x = zeros(Float64,net.numNodes) # order: in net.nodes, *!not in nodes_changed!* edge_xB = zeros(Float64,net.numEdges) # min (B=begin) and max (E=end) edge_xE = zeros(Float64,net.numEdges) # xE-xB = edge length - edge_yE = zeros(Float64,net.numEdges) # yE of edge = y of child node node_x[net.root] = xmin # root node: x=xmin=0 for i=2:length(net.node) # true pre-order, skipping the root (i=1) ni = findfirst(x -> x===net.nodes_changed[i], net.node) @@ -129,7 +173,7 @@ function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool) end end ei !== nothing || error("oops, could not find major parent edge of node number $ni.") - edge_yE[ei] = node_y[ni] + edge_yB[ei] = node_y[ni] pni = findfirst(x -> x===PhyloNetworks.getParent(net.edge[ei]), net.node) # parent node index edge_xB[ei] = node_x[pni] if elenCalculate @@ -137,26 +181,44 @@ function getEdgeNodeCoordinates(net::HybridNetwork, useEdgeLength::Bool) end edge_xE[ei] = edge_xB[ei] + elen[ei] node_x[ni] = edge_xE[ei] - xmax = max(xmax, edge_xE[ei]) end - edge_yB = copy(edge_yE) # true for tree and major edges + edge_yE = copy(edge_yB) # true for tree and major edges + + # coordinates of the diagonal lines that connect hybrid edges with their targets + minoredge_xB = Float64[] + minoredge_xE = Float64[] + minoredge_yB = Float64[] + minoredge_yE = Float64[] + for i=1:net.numEdges if (!net.edge[i].isMajor) # minor hybrid edges + # indices of child and parent nodes cni = findfirst(x -> x===PhyloNetworks.getChild( net.edge[i]), net.node) pni = findfirst(x -> x===PhyloNetworks.getParent(net.edge[i]), net.node) - # indices of child and parent nodes + edge_xB[i] = node_x[pni] - edge_xE[i] = node_x[cni] - edge_yB[i] = node_y[pni] - edge_yE[i] = node_y[cni] + edge_xE[i] = useSimpleHybridLines ? edge_xB[i] : (useEdgeLength ? edge_xB[i] + elen[i] : node_x[cni]) + + if useSimpleHybridLines + edge_yB[i] = node_y[pni] + end + edge_yE[i] = edge_yB[i] + + push!(minoredge_xB, edge_xE[i]) + push!(minoredge_yB, edge_yE[i]) + push!(minoredge_xE, node_x[cni]) + push!(minoredge_yE, node_y[cni]) #@show i; @show net.edge[i]; @show pni; @show net.node[pni]; @show cni; @show net.node[cni] end end + xmax = max(xmax, edge_xE...) + #@show node_x; @show node_yB; @show node_y; @show node_yE #@show edge_xB; @show edge_xE; @show edge_yB; @show edge_yE return edge_xB, edge_xE, edge_yB, edge_yE, node_x, node_y, node_yB, node_yE, + minoredge_xB, minoredge_xE, minoredge_yB, minoredge_yE, xmin, xmax, ymin, ymax end @@ -238,7 +300,8 @@ end """ prepareEdgeDataFrame(net, edgeLabel::DataFrame, mainTree::Bool, - edge_xB, edge_xE, edge_yB, edge_yE) + edge_xB, edge_xE, edge_yB, edge_yE, + minoredge_xB, minoredge_xE, minoredge_yB, minoredge_yE) Check data frame for edge annotation. `edge_*`: Float64 vectors giving the coordinates for the beginning and end of edges. @@ -253,7 +316,9 @@ Return data frame with columns """ function prepareEdgeDataFrame(net::HybridNetwork, edgeLabel::DataFrame, mainTree::Bool, edge_xB::Array{Float64,1}, edge_xE::Array{Float64,1}, - edge_yB::Array{Float64,1}, edge_yE::Array{Float64,1}) + edge_yB::Array{Float64,1}, edge_yE::Array{Float64,1}, + minoredge_xB::Array{Float64,1}, minoredge_xE::Array{Float64,1}, + minoredge_yB::Array{Float64,1}, minoredge_yE::Array{Float64,1}) nrows = net.numEdges - (mainTree ? net.numHybrids : 0) edf = DataFrame(:len => Vector{String}(undef,nrows), :gam => Vector{String}(undef,nrows), :num => Vector{String}(undef,nrows), @@ -293,8 +358,15 @@ function prepareEdgeDataFrame(net::HybridNetwork, edgeLabel::DataFrame, mainTree end edf[j,:hyb] = net.edge[i].hybrid edf[j,:min] = !net.edge[i].isMajor - edf[j,:y] = (edge_yB[i] + edge_yE[i])/2 - edf[j,:x] = (edge_xB[i] + edge_xE[i])/2 + minorIndex = 1; + if net.edge[i].isMajor + edf[j,:y] = (edge_yB[i] + edge_yE[i])/2 + edf[j,:x] = (edge_xB[i] + edge_xE[i])/2 + else + edf[j,:y] = (minoredge_yB[minorIndex] + minoredge_yE[minorIndex])/2 + edf[j,:x] = (minoredge_xB[minorIndex] + minoredge_xE[minorIndex])/2 + minorIndex += 1 + end j += 1 end end diff --git a/src/plotGadfly.jl b/src/plotGadfly.jl index 25ad978..7b558eb 100644 --- a/src/plotGadfly.jl +++ b/src/plotGadfly.jl @@ -41,7 +41,8 @@ function Gadfly.plot(net::HybridNetwork; useEdgeLength=false::Bool, edgeLabel=DataFrame()::DataFrame, nodeLabel=DataFrame()::DataFrame) (edge_xB, edge_xE, edge_yB, edge_yE, node_x, node_y, node_yB, node_yE, - xmin, xmax, ymin, ymax) = getEdgeNodeCoordinates(net, useEdgeLength) + hybridedge_xB, hybridedge_xE, hybridedge_yB, hybridedge_yE, + xmin, xmax, ymin, ymax) = getEdgeNodeCoordinates(net, useEdgeLength, true) # true for simple hybrid lines / style of v0.2.4 and earlier !net.node[net.root].leaf || @warn "the root is leaf $(net.node[net.root].name): the plot will look weird..." @@ -99,7 +100,8 @@ function Gadfly.plot(net::HybridNetwork; useEdgeLength=false::Bool, end # data frame for edge annotations. labeledges, edf = prepareEdgeDataFrame(net, edgeLabel, mainTree, - edge_xB, edge_xE, edge_yB, edge_yE) + edge_xB, edge_xE, edge_yB, edge_yE, + hybridedge_xB, hybridedge_xE, hybridedge_yB, hybridedge_yE) if labeledges push!(mylayers, layer(edf[!,[:x,:y,:lab]], y="y", x="x", label="lab", Geom.label(position=:above ;hide_overlaps=false))[1]) diff --git a/src/plotRCall.jl b/src/plotRCall.jl index 1f65774..60e633b 100755 --- a/src/plotRCall.jl +++ b/src/plotRCall.jl @@ -5,30 +5,56 @@ Plot a network using R graphics. `method` should be `:R` (actually, any symbol would do, for now!). optional arguments, shared with the Gadfly-based plot function: -- useEdgeLength: if true, the tree edges and major hybrid edges are +- `useEdgeLength = false` : if true, the tree edges and major hybrid edges are drawn proportionally to their length. Minor hybrid edges are not, however. Note that edge lengths in coalescent units may scale very poorly with time. -- showTipLabel: if true, taxon labels are shown. You may need to zoom out to see them. -- showNodeNumber: if true, nodes are labelled with the number used internally. -- showEdgeLength: if true, edges are labelled with their length (above) -- showGamma: if true, hybrid edges are labelled with their heritability (below) -- edgeColor: color for tree edges. black by default. -- majorHybridEdgeColor: color for major hybrid edges -- minorHybridEdgeColor: color for minor hybrid edges -- showEdgeNumber: if true, edges are labelled with the number used internally. -- showIntNodeLabel: if true, internal nodes are labelled with their names. +- `showTipLabel = true` : if true, taxon labels are shown. You may need to zoom out to see them. +- `showNodeNumber = false` : if true, nodes are labelled with the number used internally. +- `showEdgeLength = false` : if true, edges are labelled with their length (above) +- `showGamma = false` : if true, hybrid edges are labelled with their heritability (below) +- `edgeColor = "black"` : color for tree edges. +- `majorHybridEdgeColor = "deepskyblue4"` : color for major hybrid edges +- `minorHybridEdgeColor = "deepskyblue"` : color for minor hybrid edges +- `showEdgeNumber = false` : if true, edges are labelled with the number used internally. +- `showIntNodeLabel = false` : if true, internal nodes are labelled with their names. Useful for hybrid nodes, which do have tags like 'H1'. -- edgeLabel: dataframe with two columns: the first with edge numbers, the second with labels +- `edgeLabel = DataFrame()` : dataframe with two columns: the first with edge numbers, the second with labels (like bootstrap values) to annotate edges. empty by default. -- nodeLabel: dataframe with two columns: the first with node numbers, the second with labels +- `nodeLabel = DataFrame()` : dataframe with two columns: the first with node numbers, the second with labels (like bootstrap values for hybrid relationships) to annotate nodes. empty by default. +- `style = :fulltree` : symbol indicating the style of the diagram + * `:majortree` will simply draw minor edges onto the major tree. + * `:fulltree` will draw minor edges as their own branches in the tree (like in icytree.org), + useful for overlapping or confusing networks. +- `arrowlen` : the length of the arrow tips in the full tree style. if `style = :fulltree`, then + `arrowlen = 0.2`. otherwise, `arrowlen = 0`, which makes the arrows appear as segments. optional arguments specific to this function: -- xlim, ylim: array of 2 values -- tipOffset: to offset tip labels +- `xlim`, `ylim` : array of 2 values +- `tipOffset = 0.0` : to offset tip labels + +plot() returns the following tuple: +`(xmin, xmax, ymin, ymax, node_x, node_y, node_yB, node_yE, +edge_xB, edge_xE, edge_yB, edge_yE, ndf, edf)` + +1. `xmin` : the minimum x value of the plot +2. `xmax` : the maximum x value of the plot +3. `ymin` : the minimum y value of the plot +4. `ymax` : the maximum y value of the plot +5. `node_x` : the x values of the nodes in net.node in their respective order +6. `node_y` : the y values of the nodes +7. `node_yB` : the y value of the beginning of the verticle bar +8. `node_yE` : the y value of the end of the verticle bar +9. `node_yE` : the y value of the end of the verticle bar +10. `edge_xB` : the x value of the beginning of the edges in net.edge in their respective order +11. `edge_xE` : the x value of the end of the edges +12. `edge_yB` : the y value of the beginning of the edges +13. `edge_yE` : the y value of the end of the edges +14. `ndf` : the node data frame: see section [Adding labels](@ref) for more +15. `edf` : the edge data frame Note that `plot` actually modifies some (minor) attributes of the network, -as it calls `directEdges!`, `preorder!` and `cladewiseorder!`. +as it calls `directEdges!` and `preorder!`. If hybrid edges cross tree and major edges, you may choose to rotate some tree edges to eliminate crossing edges, using `rotate!` @@ -37,7 +63,7 @@ edges to eliminate crossing edges, using `rotate!` **Alternative**: a tree or network can be exported with [`sexp`](@ref) and then displayed with R's "plot" and all its options. """ -function plot(net::HybridNetwork, method::Symbol; useEdgeLength=false::Bool, +function plot(net::HybridNetwork, ::Symbol; useEdgeLength=false::Bool, mainTree=false::Bool, showTipLabel=true::Bool, showNodeNumber=false::Bool, showEdgeLength=false::Bool, showGamma=false::Bool, edgeColor="black"::String, @@ -46,10 +72,12 @@ function plot(net::HybridNetwork, method::Symbol; useEdgeLength=false::Bool, showEdgeNumber=false::Bool, showIntNodeLabel=false::Bool, edgeLabel=DataFrame()::DataFrame, nodeLabel=DataFrame()::DataFrame, xlim=Float64[]::Array{Float64,1}, ylim=Float64[]::Array{Float64,1}, - tipOffset=0.0::Float64, tipcex=1.0::Float64) + tipOffset=0.0::Float64, tipcex=1.0::Float64, + style=:fulltree::Symbol, arrowlen=(style==:majortree ? 0 : 0.1)::Real) (edge_xB, edge_xE, edge_yB, edge_yE, node_x, node_y, node_yB, node_yE, - xmin, xmax, ymin, ymax) = getEdgeNodeCoordinates(net, useEdgeLength) + hybridedge_xB, hybridedge_xE, hybridedge_yB, hybridedge_yE, + xmin, xmax, ymin, ymax) = getEdgeNodeCoordinates(net, useEdgeLength, style==:majortree) labelnodes, nodeLabel = checkNodeDataFrame(net, nodeLabel) ndf = prepareNodeDataFrame(net, nodeLabel, showNodeNumber, showIntNodeLabel, labelnodes, node_x, node_y) @@ -73,13 +101,21 @@ function plot(net::HybridNetwork, method::Symbol; useEdgeLength=false::Bool, eCol[ [ e.hybrid for e in net.edge] ] .= majorHybridEdgeColor eCol[ [!e.isMajor for e in net.edge] ] .= minorHybridEdgeColor + # this makes the arrows dashed if :fulltree is used + arrowstyle = style==:majortree ? "solid" : "longdash" + + if !(style in [:fulltree, :majortree]) + @warn "Style $style is unknown. Defaulted to :fulltree." + style = :fulltree + end R""" plot($(node_x[leaves]), $(node_y[leaves]), type='n', xlim=c($xmin,$xmax), ylim=c($ymin,$ymax), axes=FALSE, xlab='', ylab='') segments($edge_xB, $edge_yB, $edge_xE, $edge_yE, col=$eCol) - segments($node_x, $node_yB, $node_x, $node_yE, col=$edgeColor) + arrows($hybridedge_xB, $hybridedge_yB, $hybridedge_xE, $hybridedge_yE, length=$arrowlen, angle = 20, col=$minorHybridEdgeColor, lty=$arrowstyle) + segments($node_x, $node_yB, $node_x, $node_yE, col=$edgeColor,) """ if showTipLabel R"text"(node_x[leaves] .+ tipOffset, node_y[leaves], @@ -96,7 +132,8 @@ function plot(net::HybridNetwork, method::Symbol; useEdgeLength=false::Bool, R"text"(ndf[!,:x], ndf[!,:y], ndf[!,:lab], adj=1) end labeledges, edf = prepareEdgeDataFrame(net, edgeLabel, mainTree, - edge_xB, edge_xE, edge_yB, edge_yE) + edge_xB, edge_xE, edge_yB, edge_yE, + hybridedge_xB, hybridedge_xE, hybridedge_yB, hybridedge_yE) if labeledges R"text"(edf[!,:x], edf[!,:y], edf[!,:lab], adj=[.5,0]) end diff --git a/test/test_phylonetworkPlots.jl b/test/test_phylonetworkPlots.jl index a7b2db5..8381b51 100644 --- a/test/test_phylonetworkPlots.jl +++ b/test/test_phylonetworkPlots.jl @@ -1,30 +1,41 @@ @testset "Test setup for plotting PhyloNetworks objects" begin net = readTopology("(A:2.5,((B:1,#H1:0.5::0.1):1,(C:1,(D:0.5)#H1:0.5::0.9):1):0.5);") - @test PhyloPlots.getEdgeNodeCoordinates(net, true) == ( - [1., 2.5,2.5,1.5,2.5,3., 2.5,1.5, 1.], - [3.5,3.5,3., 2.5,3.5,3.5,3., 2.5, 1.5], - [4., 3., 3., 3., 2., 1., 1., 1.5, 2.25], - [4., 3., 1., 3., 2., 1., 1., 1.5, 2.25], - [3.5,3.5,2.5,3.5,3.5,3., 2.5,1.5, 1.], - [4., 3., 3., 2., 1., 1., 1.5,2.25,3.125], - [0., 0., 3., 0., 0., 1., 1., 1.5, 2.25], - [0., 0., 3., 0., 0., 1., 2., 3., 4.], - 1., 3.5, 1., 4.) # in this order: # edge_xB, edge_xE, edge_yB, edge_yE, # node_x, node_y, node_yB, node_yE, + # minoredge_xB, minoredge_xE, minoredge_yB, minoredge_yE, # xmin, xmax, ymin, ymax - @test PhyloPlots.getEdgeNodeCoordinates(net, false) == ( - [1.,3.,3.,2.,3.,4.,3.,2., 1.], - [5.,5.,4.,3.,5.,5.,4.,3., 2.], - [4.,3.,3.,3.,2.,1.,1.,1.5,2.25], - [4.,3.,1.,3.,2.,1.,1.,1.5,2.25], - [5.,5.,3.,5.,5.,4.,3., 2., 1.], - [4.,3.,3.,2.,1.,1.,1.5,2.25,3.125], - [0.,0.,3.,0.,0.,1.,1., 1.5, 2.25], - [0.,0.,3.,0.,0.,1.,2., 3., 4.], - 1., 5.,1., 4.) + @test PhyloPlots.getEdgeNodeCoordinates(net, true, false) == ( + [1.0, 2.5, 2.5, 1.5, 2.5, 3.0, 2.5, 1.5, 1.0], + [3.5, 3.5, 3.0, 2.5, 3.5, 3.5, 3.0, 2.5, 1.5], + [1.0, 2.0, 3.0, 2.5, 4.0, 5.0, 5.0, 4.5, 3.5], + [1.0, 2.0, 3.0, 2.5, 4.0, 5.0, 5.0, 4.5, 3.5], + [3.5, 3.5, 2.5, 3.5, 3.5, 3.0, 2.5, 1.5, 1.0], + [1.0, 2.0, 2.5, 4.0, 5.0, 5.0, 4.5, 3.5, 2.25], + [0.0, 0.0, 2.0, 0.0, 0.0, 5.0, 4.0, 2.5, 1.0], + [0.0, 0.0, 3.0, 0.0, 0.0, 5.0, 5.0, 4.5, 3.5], + [3.0], [3.0], [3.0], [5.0], 1.0, 3.5, 1.0, 5.0) + @test PhyloPlots.getEdgeNodeCoordinates(net, true, true) == ( + [1.0, 2.5, 2.5, 1.5, 2.5, 3.0, 2.5, 1.5, 1.0], + [3.5, 3.5, 2.5, 2.5, 3.5, 3.5, 3.0, 2.5, 1.5], + [1.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0, 3.5, 2.75], + [1.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0, 3.5, 2.75], + [3.5, 3.5, 2.5, 3.5, 3.5, 3.0, 2.5, 1.5, 1.0], + [1.0, 2.0, 2.0, 3.0, 4.0, 4.0, 3.5, 2.75, 1.875], + [0.0, 0.0, 2.0, 0.0, 0.0, 4.0, 3.0, 2.0, 1.0], + [0.0, 0.0, 2.0, 0.0, 0.0, 4.0, 4.0, 3.5, 2.75], + [2.5], [3.0], [2.0], [4.0], 1.0, 3.5, 1.0, 4) + @test PhyloPlots.getEdgeNodeCoordinates(net, false, true) == ( + [1.0, 3.0, 3.0, 2.0, 3.0, 4.0, 3.0, 2.0, 1.0], + [5.0, 5.0, 3.0, 3.0, 5.0, 5.0, 4.0, 3.0, 2.0], + [1.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0, 3.5, 2.75], + [1.0, 2.0, 2.0, 2.0, 3.0, 4.0, 4.0, 3.5, 2.75], + [5.0, 5.0, 3.0, 5.0, 5.0, 4.0, 3.0, 2.0, 1.0], + [1.0, 2.0, 2.0, 3.0, 4.0, 4.0, 3.5, 2.75, 1.875], + [0.0, 0.0, 2.0, 0.0, 0.0, 4.0, 3.0, 2.0, 1.0], + [0.0, 0.0, 2.0, 0.0, 0.0, 4.0, 4.0, 3.5, 2.75], + [3.0], [4.0], [2.0], [4.0], 1.0, 5.0, 1.0, 4) dat = DataFrame(node=[-5,-3,-4,5,100],bs=["90","95","99","mytip","bogus"],edge=[8,9,4,6,200]); @test_logs (:warn, "Some node numbers in the nodeLabel data frame are not found in the network:\n 100") PhyloPlots.checkNodeDataFrame(net, dat); @test_logs (:warn, "nodeLabel should have 2+ columns, the first one giving the node numbers (Integer)") PhyloPlots.checkNodeDataFrame(net, dat[!,2:3]) @@ -41,10 +52,10 @@ x=collect(1.:9), y=collect(10.:18)) dat = DataFrame(edge=[8,9,4,6,200],bs=["90","95","99","mytips","bogus"]); @test_logs (:warn, "Some edge numbers in the edgeLabel data frame are not found in the network:\n 200") PhyloPlots.prepareEdgeDataFrame( - net,dat,true,collect(1.:9),collect(10.:18),collect(19.:27),collect(28.:36)); + net,dat,true,collect(1.:9),collect(10.:18),collect(19.:27),collect(28.:36),[6.5],[8.5],[24.5],[26.5]); dat = DataFrame(edge=[8,9,4,6],bs=[missing,"95","99","mytips"]); @test PhyloPlots.prepareEdgeDataFrame(net,dat,false,collect(1.:9),collect(11.:19), - collect(21.:29),collect(31.:39)) == (true, DataFrame( + collect(21.:29),collect(31.:39),[7.],[9.],[27.],[29.]) == (true, DataFrame( len=["2.5","1","0.5","1","1","0.5","0.5","1","0.5"], gam=["1","1","0.1","1","1","1","0.9","1","1"], num=["1","2","3","4","5","6","7","8","9"], @@ -58,26 +69,28 @@ # no major child edge to follow to set coordinates net = readTopology("((((B)#H1:::0.2)#H2,((D,C,#H2:::0.8)S1,(#H1,A)S2)S3)S4);") @test_logs plot(net, :R, showNodeNumber=true, showGamma=true); - @test PhyloPlots.getEdgeNodeCoordinates(net, false) == ( - [5.,4.,1.,3.,3.,3.,2.,4.,4.,2.,1.], - [6.,5.,4.,6.,6.,4.,3.,5.,6.,4.,2.], - [2.,2.1,2.275,4.,3.,2.1,3.05,2.,1.,1.5,2.275], - [2.,2. ,2.1, 4.,3.,2.1,3.05,2.,1.,1.5,2.275], - [6.,5.,4., 6.,6.,3., 6.,4., 2., 1.], - [2.,2.,2.1,4.,3.,3.05,1.,1.5,2.275,2.275], - [0.,2.,2.1,0.,0.,2.1,0.,1.,1.5, 2.275], - [0.,2.,2.1,0.,0.,4. ,0.,2.,3.05,2.275], - 1.,6.,1.,4.) + @test PhyloPlots.getEdgeNodeCoordinates(net, false, false) == ( + [5.0, 4.0, 1.0, 3.0, 3.0, 3.0, 2.0, 4.0, 4.0, 2.0, 1.0], + [6.0, 5.0, 4.0, 6.0, 6.0, 4.0, 3.0, 5.0, 6.0, 4.0, 2.0], + [5.0, 4.0, 1.0, 2.0, 3.0, 4.0, 3.0, 5.0, 6.0, 5.5, 4.25], + [5.0, 4.0, 1.0, 2.0, 3.0, 4.0, 3.0, 5.0, 6.0, 5.5, 4.25], + [6.0, 5.0, 4.0, 6.0, 6.0, 3.0, 6.0, 4.0, 2.0, 1.0], + [5.0, 5.0, 4.0, 2.0, 3.0, 3.0, 6.0, 5.5, 4.25, 2.625], + [0.0, 5.0, 4.0, 0.0, 0.0, 2.0, 0.0, 5.0, 3.0, 1.0], + [0.0, 5.0, 4.0, 0.0, 0.0, 4.0, 0.0, 6.0, 5.5, 4.25], + [5.0, 4.0], [5.0, 4.0], [4.0, 1.0], [5.0, 4.0], + 1.0, 6.0, 1.0, 6.0) net = readTopology("((((B)#H1:::0.2)#H2,((D,C,#H2)S1,(#H1,A)S2)S3)S4);") @test_logs plot(net, :R, showNodeNumber=true, showGamma=true); - @test PhyloPlots.getEdgeNodeCoordinates(net, false) == ( - [5.,4.,1.,3.,3.,3.,2.,4.,4.,2.,1.], - [6.,5.,4.,6.,6.,4.,3.,5.,6.,4.,2.], - [2.,2.1,2.1,4.,3.,3.5,3.5,2.,1.,1.5,2.5], - [2.,2. ,2.1,4.,3.,2.1,3.5,2.,1.,1.5,2.5], - [6.,5.,4., 6.,6.,3., 6.,4., 2., 1.], - [2.,2.,2.1,4.,3.,3.5,1.,1.5,2.5,2.3], - [0.,2.,2.1,0.,0.,3.,0.,1.,1.5,2.1], - [0.,2.,2.1,0.,0.,4.,0.,2.,3.5,2.5], - 1.,6.,1.,4.0) + @test PhyloPlots.getEdgeNodeCoordinates(net, false, false) == ( + [5.0, 4.0, 1.0, 3.0, 3.0, 3.0, 2.0, 4.0, 4.0, 2.0, 1.0], + [6.0, 5.0, 4.0, 6.0, 6.0, 4.0, 3.0, 5.0, 6.0, 4.0, 2.0], + [5.0, 1.0, 1.0, 2.0, 3.0, 4.0, 3.0, 5.0, 6.0, 5.5, 4.25], + [5.0, 1.0, 1.0, 2.0, 3.0, 4.0, 3.0, 5.0, 6.0, 5.5, 4.25], + [6.0, 5.0, 4.0, 6.0, 6.0, 3.0, 6.0, 4.0, 2.0, 1.0], + [5.0, 5.0, 1.0, 2.0, 3.0, 3.0, 6.0, 5.5, 4.25, 2.625], + [0.0, 5.0, 1.0, 0.0, 0.0, 2.0, 0.0, 5.0, 3.0, 1.0], + [0.0, 5.0, 1.0, 0.0, 0.0, 4.0, 0.0, 6.0, 5.5, 4.25], + [5.0, 4.0], [5.0, 4.0], [1.0, 4.0], [5.0, 1.0], + 1.0, 6.0, 1.0, 6.0) end diff --git a/test/test_plotRCall.jl b/test/test_plotRCall.jl index d7ee4fe..5c26861 100644 --- a/test/test_plotRCall.jl +++ b/test/test_plotRCall.jl @@ -15,6 +15,14 @@ @test_logs (:warn, "Some node numbers in the nodeLabel data frame are not found in the network:\n -1") plot(net,:RCall, nodeLabel=dat); @test_logs plot(net,:RCall, edgeLabel=dat[!,[:edge,:bs]]); + @test_logs plot(net,:RCall, style=:majortree, arrowlen=0.1); + @test_logs (:warn, "Style bogus is unknown. Defaulted to :fulltree.") plot(net,:RCall, style=:bogus); + + + # coverage for the nomajorchild + net2 = readTopology("((((B)#H1)#H2,((D,C,#H2)S1,(#H1:::.8,A)S2)S3)S4);") + @test_logs plot(net2, :R, style=:majortree); + # plot based on RCall and ape: tre = readTopology("(((((((1,2),3),4),5),(6,7)),(8,9)),10);"); # fixit: plot(tre, :ape)