2 Module : Gargantext.Viz.Phylo.Tools
3 Description : Phylomemy Tools to build/manage it
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
13 {-# LANGUAGE NoImplicitPrelude #-}
14 {-# LANGUAGE FlexibleContexts #-}
15 {-# LANGUAGE OverloadedStrings #-}
17 module Gargantext.Viz.Phylo.View.Export
20 import Control.Lens hiding (Level)
22 import Data.GraphViz hiding (DotGraph)
23 import Data.GraphViz.Attributes.Complete hiding (EdgeType)
24 import Data.GraphViz.Types
25 import Data.GraphViz.Types.Generalised (DotGraph)
26 import Data.GraphViz.Types.Monadic
27 import Data.List ((++),unwords,concat,sortOn,nub,nubBy)
28 import Data.Map (Map,mapWithKey,elems,toList)
29 import Data.Maybe (isJust,isNothing,fromJust)
30 import Data.Text (Text)
31 import Data.Text.Lazy (Text, fromStrict, pack)
32 import GHC.TypeLits (KnownNat)
34 import qualified Data.Text as T
35 import qualified Data.Text.Lazy as T'
36 import qualified Data.GraphViz.Attributes.HTML as H
38 import Gargantext.Prelude
39 import Gargantext.Viz.Phylo
40 import Gargantext.Viz.Phylo.Tools
44 --------------------------
45 -- | PhyloView to SVG | --
46 --------------------------
49 viewToSvg v = undefined
52 --------------------------
53 -- | PhyloView to DOT | --
54 --------------------------
56 -- | From http://haroldcarr.com/posts/2014-02-28-using-graphviz-via-haskell.html & https://hackage.haskell.org/package/graphviz
59 -- | To create a custom Graphviz's Attribute
60 setAttr :: AttributeName -> T'.Text -> CustomAttribute
61 setAttr k v = customAttribute k v
64 -- | To create customs Graphviz's Attributes out of some Metrics
65 setAttrFromMetrics :: Map T.Text [Double] -> [CustomAttribute]
66 setAttrFromMetrics attrs = map (\(k,v) -> setAttr (fromStrict k)
68 $ map show v) $ toList attrs
71 -- | To transform a PhyloBranchId into a DotId
72 toBranchDotId :: PhyloBranchId -> DotId
73 toBranchDotId (lvl,idx) = fromStrict $ T.pack $ (show lvl) ++ (show idx)
76 -- | To transform a PhyloGroupId into a DotId
77 toNodeDotId :: PhyloGroupId -> DotId
78 toNodeDotId (((d,d'),lvl),idx) = fromStrict $ T.pack $ (show d) ++ (show d') ++ (show lvl) ++ (show idx)
81 -- | To transform a PhyloPeriodId into a DotId
82 toPeriodDotId :: PhyloPeriodId -> DotId
83 toPeriodDotId (d,d') = fromStrict $ T.pack $ (show d) ++ (show d')
86 -- | To transform a PhyloPeriodId into a Graphviz's label
87 toPeriodDotLabel ::PhyloPeriodId -> Label
88 toPeriodDotLabel (d,d') = toDotLabel $ T.pack $ (show d) ++ " " ++ (show d')
91 -- | To get all the Phyloperiods covered by a PhyloView
92 getViewPeriods :: PhyloView -> [PhyloPeriodId]
93 getViewPeriods pv = sortOn fst $ nub $ map (\pn -> (fst . fst) $ pn ^. pn_id) $ pv ^. pv_nodes
96 -- | To get for each PhyloBranch, their corresponding oldest PhyloNodes
97 getFirstNodes :: Level -> PhyloView -> [(PhyloBranchId,PhyloGroupId)]
98 getFirstNodes lvl pv = concat
99 $ map (\bId -> map (\pn -> (bId,pn ^. pn_id))
100 $ filterNodesByFirstPeriod
101 $ filterNodesByBranch bId
102 $ filterNodesByLevel lvl
103 $ pv ^. pv_nodes) bIds
105 --------------------------------------
106 bIds :: [PhyloBranchId]
107 bIds = map getBranchId $ filterBranchesByLevel lvl pv
108 --------------------------------------
111 -- | To transform a Text into a Graphviz's Label
112 toDotLabel :: T.Text -> Label
113 toDotLabel lbl = StrLabel $ fromStrict lbl
116 -- | To set a Peak Node
117 setPeakDotNode :: PhyloBranch -> Dot DotId
118 setPeakDotNode pb = node (toBranchDotId $ pb ^. pb_id)
119 ([FillColor [toWColor CornSilk], FontName "Arial", FontSize 40, Shape Egg, Style [SItem Bold []], Label (toDotLabel $ pb ^. pb_label)]
120 <> (setAttrFromMetrics $ pb ^. pb_metrics))
123 -- | To set a Peak Edge
124 setPeakDotEdge :: DotId -> DotId -> Dot DotId
125 setPeakDotEdge bId nId = edge bId nId [Width 3, Color [toWColor Black], ArrowHead (AType [(ArrMod FilledArrow RightSide,DotArrow)])]
128 -- | To set an HTML table
129 setHtmlTable :: PhyloNode -> H.Label
130 setHtmlTable pn = H.Table H.HTable
131 { H.tableFontAttrs = Just [H.PointSize 14, H.Align H.HLeft]
132 , H.tableAttrs = [H.Border 0, H.CellBorder 0, H.BGColor (toColor White)]
133 , H.tableRows = [header] <> (if isNothing $ pn ^. pn_ngrams
135 else map ngramsToRow $ splitEvery 4 $ fromJust $ pn ^. pn_ngrams) }
137 --------------------------------------
138 ngramsToRow :: [Ngrams] -> H.Row
139 ngramsToRow ns = H.Cells $ map (\n -> H.LabelCell [H.BAlign H.HLeft] $ H.Text [H.Str $ fromStrict n]) ns
140 --------------------------------------
142 header = H.Cells [H.LabelCell [H.Color (toColor Black), H.BGColor (toColor Chartreuse2)]
143 $ H.Text [H.Str $ (fromStrict . T.toUpper) $ pn ^. pn_label]]
144 --------------------------------------
148 setDotNode :: PhyloNode -> Dot DotId
149 setDotNode pn = node (toNodeDotId $ pn ^. pn_id)
150 ([FontName "Arial", Shape Square, toLabel (setHtmlTable pn)])
154 setDotEdge :: PhyloEdge -> Dot DotId
155 setDotEdge pe = edge (toNodeDotId $ pe ^. pe_source) (toNodeDotId $ pe ^. pe_target) [Width 2, Color [toWColor Black]]
158 -- | To set a Period Edge
159 setDotPeriodEdge :: (PhyloPeriodId,PhyloPeriodId) -> Dot DotId
160 setDotPeriodEdge (prd,prd') = edge (toPeriodDotId prd) (toPeriodDotId prd') [Width 5, Color [toWColor Black]]
163 -- | To transform a given PhyloView into the corresponding GraphViz Graph (ie: Dot format)
164 viewToDot :: PhyloView -> DotGraph DotId
165 viewToDot pv = digraph ((Str . fromStrict) $ pv ^. pv_title)
169 -- set the global graph attributes
171 graphAttrs ( [Label (toDotLabel $ pv ^. pv_title)]
172 <> [setAttr "description" $ fromStrict $ pv ^. pv_description]
173 <> [setAttr "filiation" $ (pack . show) $ pv ^. pv_filiation]
174 <> (setAttrFromMetrics $ pv ^. pv_metrics)
175 <> [FontSize (fromIntegral 30), LabelLoc VTop, Splines SplineEdges, Overlap ScaleOverlaps,
176 Ratio AutoRatio, Style [SItem Filled []],Color [toWColor White]])
180 subgraph (Str "Peaks") $ do
182 graphAttrs [Rank SameRank]
184 mapM setPeakDotNode $ filterBranchesByLevel (pv ^. pv_level) pv
186 -- set the nodes, period by period
189 subgraph (Str $ fromStrict $ T.pack $ "subGraph " ++ (show $ (fst prd)) ++ (show $ (snd prd)))
193 graphAttrs [Rank SameRank]
195 -- set the period label
197 node (toPeriodDotId prd) [Shape Square, FontSize 50, Label (toPeriodDotLabel prd)]
199 mapM setDotNode $ filterNodesByPeriod prd $ filterNodesByLevel (pv ^. pv_level) (pv ^.pv_nodes)
201 ) $ getViewPeriods pv
203 -- set the edges : from peaks to nodes, from nodes to nodes, from periods to periods
205 mapM (\(bId,nId) -> setPeakDotEdge (toBranchDotId bId) (toNodeDotId nId)) $ getFirstNodes (pv ^. pv_level) pv
207 mapM setDotEdge $ filterEdgesByLevel (pv ^. pv_level) $ filterEdgesByType PeriodEdge (pv ^. pv_edges)
209 mapM setDotPeriodEdge $ listToSequentialCombi $ getViewPeriods pv