diff --git a/.idea/dictionaries/awilkey.xml b/.idea/dictionaries/awilkey.xml new file mode 100644 index 00000000..6912849b --- /dev/null +++ b/.idea/dictionaries/awilkey.xml @@ -0,0 +1,80 @@ + + + + assistive + attrs + autocomplete + bioconfig + biomap + biomaps + bmaps + child.data.draw + circos + cmap + cmax + cmin + colorselector + colorspace + composited + coor + coord + correspondenc + datasource + disp + dropdown + dropdowns + eslint + fieldset + flexbox + fscale + hammerjs + hamsterjs + hardcode + hbox + hitmap + horizonta + iterable + lensed + linkout + linkouts + mapbackbone + mithril + mixwith + mozilla + nunito + offscreen + onbeforeremove + onbeforeupdate + oncreate + oninit + oninput + onload + onremove + onupdate + panend + panmove + panstart + papaparse + pinchend + pinchmove + propegation + pubsub + rbush + rtrees + scenegraph + subnodes + subpixel + subtrack + tbody + textarea + thead + tuples + uniquified + urlpage + vbox + vdom + visc + vnode + + + \ No newline at end of file diff --git a/Configuration.md b/Configuration.md new file mode 100644 index 00000000..be138a03 --- /dev/null +++ b/Configuration.md @@ -0,0 +1,322 @@ +#cmap-js Start Guide +## Table of Contents ++ [Setup](#Setup) ++ [cmap.json](#cmap.json) ++ [Configuration](#Configuration) + +## Setup + +Prerequisite: `npm` is required so install [NodeJs](https://nodejs.org) if you +do not have it. `npm` is used as javascript package manager and task runner +here. + +- Clone the project and initialize git submodule(s): + ``` + git clone https://github.com/ncgr/cmap-js.git + cd cmap-js + git submodule init + git submodule update + ``` +- Install the required javascript packages listed in `package.json` + ``` + npm install + ``` +- cmap-js may be run locally using the command + ``` + npm run watch + ``` + This starts the development server, and assuiming there are no issues + with the build process, this should start a cmap-js server that can be + reached at `localhost:8888` + +##cmap.json +The cmap.json file is the core of configuring cmap.js an example file looks like: +``` + "#": "cmap-js example configuration file (json format)", + "header" : ": soybean and common bean genomes and genetic maps", + "attribution" : "data attribution: soybase, LIS", + "initialView" : [ { + "source" : "pv_consensus", + "map" : "Pv02", + "tracks":[ + { + "type: 'qtl', + "position" : 1, + "filters":["QTL_seed","QTL_other-seed"], + "fillColor": "orange". + } + ] + }, { + "source" : "pv_genome", + "map" : "phavu.Chr02", + "tracks" : [ + { + "type": "manhattan", + "dataId" : "manhattan_test", + "displayWidth" : 50, + "maxValue" : 6, + "width" : 2, + "rulerMajorMark" : 10, + "rulerMinorMark" : 2, + "posField" :"Pos", + "targetField" : "Chr", + "pField" : "p", + "lines" : [ + { + "value": 5, + "lineWeight": 3, + "lineColor": "red" + }, + { + "value": 2, + "lineWeight": 3, + "lineColor": "blue" + } + ] + } + ] + } + ], + "sources" : [ { + "id" : "pv_consensus", + "method": "GET", + "url": "data/PvConsensus_GaleanoFernandez2011_a.cmap", + "filters" : [], + "config": { + "url" : "config/phavu_genes.json", + "method":"GET", + "data":{} + }, + "linkouts" : [ + { + "featuretypePattern" : "QTL", + "url" : "https://legumeinfo.org/feature/Phaseolus/vulgaris/QTL/phavu.${item.id}", + "text" : "View QTL info at LIS" + }, + { + "featuretypePattern" : "^(SSR|SNP|AFLP|STS|RAPD)$", + "url" : "https://legumeinfo.org/feature/Phaseolus/vulgaris/genetic_marker/${item.id}", + "text" : "View marker at LIS" + } + ] + }, { + "id" : "pv_genome", + "method": "GET", + "url": "data/phavu.collinearity+genes+markers.cmap", + "filters" : [], + "config": { + "url" : "config/phavu_genes.json", + "method":"GET", + "data":{} + }, + "linkouts" : [ + { + "featuretype" : "gene", + "url" : "https://legumeinfo.org/gene_links/${item.id}/json", + "isLinkingService" : true + } + ] + }, + { + "id" : "manhattan_test", + "method": "GET", + "url" : "data/sample2.txt", + "filters" : [] + } + ] +} +``` + +The sub-components of the format are: +`#`: comment +`header` : Header line to display +`attribution` : Data attribution footer +`initialView` : One or more maps and their initial configuration to display as the starting view +`sources` : Array of data sources to grab map information from + +##Initial View +Initial View has the following additional settings: + +| Setting | Description | +|---------|-------------| +|`source` * | Which of the sources to usefor this view's data | +|`map`*| Which map to display from the data source above | +|`tracks`| An array of data tracks to display | +Settings marked with a \* are **REQUIRED** + +All `tracks` currently have the following shared options: + +|Setting | Description | +|---------|-------------| +|`type` * | Track type: currently `qtl` or `manhattan` | +|`position`| Which side of the backbone to display map on `-1` LHS, `1` RHS | +|`title`| Title the for the track | + +##### Qtl + +`qtl` style tracks require the following options: + +| Setting | Description | +|---------|-------------| +|`filters` \*\* | Array of tags to populate qtl track with | +|`fillColor` \*\* | Array of colors to draw filtered qtl elements | + +The default title for a `qtl` track is the first entry in the filters array + +##### Manhattan +`manhattan` style tracks require the following options: + +| Setting | Description | +|---------|-------------| +|`dataId` | Source file for gwas data | +|`posField` | field in source file to base starting position | +|`pField` | field in source file to get pvalue from | +|`targetField` | field in source file to get target map from | +|`lines` | array of line objects to draw reference lines | +|`lines.value` | -log10(p) value to draw constant value line | + +The default title for a `manhattan` track is 'manhattan' + +Both track types also may be passed in any of the configuration elements supported by the track style. + + +####Sources +--- +Sources are the data models for displaying data. Currently only CMAP format data is officially supported + +| Setting | Description | +|---------|-------------| +|`id` * | Tag to identify source when setting up a view | +|`method` *| REST Call to get data, should always be `GET` | +| `url` * | location of file, may be a local or remote URL | +|`filters`| Advanced filtering | +|`config`| File to find source's custom configuration settings, similar requriements for configuration| +|`linkouts`| Custom linkouts to attach to the data model for each map | + +Settings marked with a \* are **REQUIRED** + +## Configuration +A configuration file has the following format: +``` +{ + "default":{ + "ruler" : { + "width" : 10, + "fillColor" : "aqua" + }, + "backbone":{ + "width":20 + } + }, + "Pv02":{ + "backbone":{ + "width" : 60 + }, + "invert":true + } +} +``` +`default` is the fallback configuration to use for all maps of the associated data model, otherwise you can tweak the defaults on a per-map basis by the map name. + +The default configuration object is: + +``` +{ + 'backbone' : { + 'width' : 20, + 'fillColor' : '#fff6e8', + 'lineWeight' : 0, + 'lineColor' : 'black' + }, + 'ruler' : { + 'width' : 10, + 'padding' : 5, + 'fillColor' : 'aqua', + 'lineWeight' : 0, + 'lineColor' : 'black', + 'labelFace' : 'Nunito', + 'labelSize' : 12, + 'labelColor' : 'black', + 'innerLineWeight' : 1.0, + 'innerLineColor' : 'black', + 'precision' : 2, + 'steps' : 100 + }, + 'track' : { + 'width' : 5, + 'padding' : 5, + 'fillColor' : '#636081', + 'lineWeight' : 0, + 'lineColor' : 'black', + 'labelFace' : 'Nunito', + 'labelSize' : 12, + 'labelColor' : 'black', + 'internalPadding' : '5' + }, + 'marker':{ + 'lineWeight' : 1, + 'lineColor' : 'black', + 'labelFace' : 'Nunito', + 'labelSize' : 12, + 'labelColor' : 'black' + }, + + 'manhattan' :{ + 'width' : 2, + 'fillColor':'green', + 'lineWeight':1, + 'lineColor':'black', + 'labelFace' : 'Nunito', + 'labelSize' : 10, + 'labelColor' : 'black', + 'displayWidth' : 50, + 'featureLineWeight' : 3, + 'featureLineColor' : 'red', + 'rulerWeight' : 2, + 'rulerColor' : 'black', + 'rulerMajorMark':10, + 'rulerMinorMark':2, + 'position; : 1, + 'type': 'manhattan' + }, + 'qtl':{ + 'width': 5, + 'fillColor': 'green', + 'labelSize': 12, + 'labelFace': 'Nunito', + 'labelColor': 'black', + 'trackMinWidth' : 50, + 'internalPadding': 5, + 'position' : 1, + 'type' : 'qtl' + }, + 'invert': false, +} +``` + +Options are defined as: + +| Option | Modifies| +|--------|---------| +| width | Width of an individual feature of this type| +| fillColor | Fill color for track, same as canvas `fillStyle` | +| lineWeight | Width of border line in px | +| lineColor | `fillStyle` of border line | +| labelFace | Font to use for labels | +| labelColor | color of label | +| labelSize | fontsize of label | +| padding | left/right spacing of whole track | +| innerLineWeight | weight of the inner ruler line | +| innerLineColor | color of the inner ruler line | +| preision | number of decimal places to display on ruler position labels | +| steps | number of zoom steps ruler has when using mouse wheel zoom | +| featureLineWeight | weight of solid line for manhattan track | +| featureLineColor | color of mahnattan line | +| rulerWeight | weight of solid lines for manhattan ruler | +| rulerColor | color of solid lines for manhattan ruler | +| rulerMajorMark | interval which to add line with label | +| rulerMinorMark | interval which to add line | +| trackMinWidth | minimum width for QTL track | +| position | display on right (1) or left (-1 ) of backbone| +| internalPadding | spacing between elements within track | +| invert | flip upper and lower values of track for display | diff --git a/README.md b/README.md index ac21612a..64f8e61d 100644 --- a/README.md +++ b/README.md @@ -80,4 +80,9 @@ Using the white boxes with arrows at the top of a map swaps map position with its neighbor. Maps may be added and removed from display using the as labeled buttons. +## Known Issues + ++ Manhattan style plots can **not** be added through the configure/add track menu. ++ Configuration menu will not display configuration of current tracks, however tracks +may still be configured in the same way as in the `cmap.json`, overwriting existing tracks. diff --git a/cmap.json b/cmap.json index 176b1d39..84624fc8 100644 --- a/cmap.json +++ b/cmap.json @@ -5,15 +5,52 @@ "initialView" : [ { "source" : "pv_consensus", "map" : "Pv02", - "qtl" : [{ - "filters":["QTL_seed","QTL_other-seed"], - "trackColor": "orange" - } + "tracks" :[ + { + "type": "qtl", + "title": "seed 1", + "filters": [ + "QTL_root", + "QTL_seed", + "QTL_other-seed" + ], + "fillColor": ["yellow"] + },{ + "type": "qtl", + "filters": [ + "QTL_seed", + "QTL_root" + ], + "fillColor": ["green","red"] + } ] - }, { - "source" : "pv_genome", - "map" : "phavu.Chr02" - + },{ + "source" : "pv_genome", + "map" : "phavu.Chr02", + "tracks" : [{ + "type" : "manhattan", + "dataId" : "manhattan_test", + "displayWidth" : 50, + "maxValue" : 6, + "width" : 2, + "rulerMajorMark" : 10, + "rulerMinorMark" : 2, + "posField" :"Pos", + "targetField" : "Chr", + "pField" : "p", + "lines" : [ + { + "value": 5, + "lineWeight": 3, + "lineColor": "red" + }, + { + "value": 2, + "lineWeight": 3, + "lineColor": "blue" + } + ] + }] }, { "source" : "gm_genome", "map" : "glyma.Chr01" @@ -21,11 +58,18 @@ }, { "source" : "gm_composite", "map" : "D1a", - "qtl" : [{ - "filters":"QTL_other-seed", - "trackColor": "orange" + "tracks" : [{ + "type" : "qtl", + "filters":["QTL_other-seed","QTL_root"], + "fillColor": ["orange","blue"], + "position":-1 + },{ + "type" : "qtl", + "filters":["QTL_root"], + "fillColor": ["red"], + "position":1 } - ] + ] } ], "sources" : [ { @@ -105,6 +149,11 @@ "text" : "View Marker info at SoyBase" } ] + }, { + "id" : "manhattan_test", + "method": "GET", + "url" : "data/sample2.txt", + "filters" : [] } ] } diff --git a/config/phavu_genes.json b/config/phavu_genes.json index ba370515..5a86314c 100644 --- a/config/phavu_genes.json +++ b/config/phavu_genes.json @@ -1,10 +1,20 @@ { "default":{ - "rulerWidth":10, - "rulerColor":"aqua", - "backboneWidth":10 + "ruler" : { + "width" : 10, + "fillColor" : "aqua" + }, + "backbone":{ + "width":20 + }, + "qtl" : { + "fillColor" : ["red"] + } }, "Pv02":{ - "backboneWidth":60 + "backbone":{ + "width" : 60 + }, + "invert":true } } diff --git a/data/Tassel_output_sample.txt b/data/Tassel_output_sample.txt new file mode 100644 index 00000000..798fe746 --- /dev/null +++ b/data/Tassel_output_sample.txt @@ -0,0 +1,6 @@ +Trait Marker Chr Pos marker_F p marker_Rsq add_F add_p dom_F dom_p marker_df marker_MS error_df error_MS model_df model_MS minorObs +CotColor ss715579607 phavu.Chr02 47559286 7.89349 3.9441E-4 0.01094 15.28389 9.8076E-5 0.5098 0.47538 2 12316.32743 1115 1560.31546 8 64025.55604 22 +CotColor ss715579608 phavu.Chr02 47574690 0.0054 0.94146 3.7698E-6 0.0054 0.94146 NaN NaN 1 8.51277 1119 1577.79889 7 70373.20091 15 +CotColor ss715579610 phavu.Chr02 47607800 7.81446 4.2635E-4 0.01081 14.55035 1.4393E-4 1.07755 0.29947 2 12189.5289 1116 1559.86931 8 64178.30124 20 +CotColor ss715579613 phavu.Chr02 47641758 4.93237 0.00737 0.00684 9.70526 0.00188 0.16672 0.68312 2 7715.275 1116 1564.21295 8 63635.39194 544 +CotColor ss715579614 phavu.Chr02 47643080 9.01058 1.3122E-4 0.01241 17.88483 2.5386E-5 0.14993 0.69868 2 13997.86913 1117 1553.49263 8 65180.57291 31546 \ No newline at end of file diff --git a/data/sample2.txt b/data/sample2.txt new file mode 100644 index 00000000..b33f42bc --- /dev/null +++ b/data/sample2.txt @@ -0,0 +1,998 @@ +samTrait Marker Chr Pos marker_F p marker_Rsq add_F add_p dom_F dom_p marker_df marker_MS error_df error_MS model_df model_MS minorObs +CotColor ss715578788 Phavu.Chr02phavu.chr02 24952 2.24736 0.10616 0.00314 4.41457 0.03586 0.08378 0.77229 2 3533.26958 1115 1572.18343 8 62374.88191 258 +CotColor ss715578818 phavu.Chr02 26003 3.10145 0.04538 0.00439 4.10897 0.0429 2.08987 0.14856 2 4875.522 1100 1572.01277 8 61731.81445 244 +CotColor ss715578923 phavu.Chr02 29671 2.52352 0.08064 0.00354 2.68448 0.10161 2.35928 0.12482 2 3978.73472 1112 1576.6587 8 61754.20401 252 +CotColor ss715578960 phavu.Chr02 30712 1.51236 0.22084 0.00212 1.80699 0.17914 1.21737 0.27012 2 2382.15939 1114 1575.12973 8 61818.21666 69 +CotColor ss715579193 phavu.Chr02 37018 0.63828 0.52839 8.9209E-4 0.05784 0.80998 1.21871 0.26985 2 1005.47412 1116 1575.27882 8 62023.42667 164 +CotColor ss715579265 phavu.Chr02 38482 0.79698 0.45094 0.00111 1.52579 0.217 0.06945 0.79219 2 1255.74799 1119 1575.62387 8 62141.32978 69 +CotColor ss715579576 phavu.Chr02 46373 2.35658 0.09522 0.00329 3.74993 0.05306 0.96336 0.32656 2 3699.81738 1113 1569.99317 8 62566.17193 266 +CotColor ss715579942 phavu.Chr02 49238 0.53158 0.58783 7.4515E-4 0.0159 0.89969 1.04725 0.30636 2 838.73369 1114 1577.82642 8 61685.98582 165 +CotColor ss715580755 phavu.Chr02 61191 2.33898 0.0969 0.00331 3.95482 0.04698 0.72412 0.39498 2 3685.08268 1102 1575.51046 8 61391.60763 273 +CotColor ss715580822 phavu.Chr02 81844 1.5426 0.21428 0.00216 2.97823 0.08467 0.10935 0.74095 2 2427.58178 1114 1573.69358 8 62154.79915 269 +CotColor ss715580828 phavu.Chr02 83156 2.64651 0.07134 0.00369 4.37045 0.03679 0.92287 0.33693 2 4158.44614 1117 1571.29585 8 62670.32449 268 +CotColor ss715580853 phavu.Chr02 90280 2.40892 0.09038 0.00336 3.73022 0.05369 1.08733 0.29729 2 3789.8565 1117 1573.25943 8 62357.33043 268 +CotColor ss715580861 phavu.Chr02 92363 2.30673 0.10006 0.00321 3.60888 0.05773 1.00457 0.31642 2 3624.77577 1119 1571.38968 8 62733.58672 271 +CotColor ss715580878 phavu.Chr02 96802 3.13361 0.04394 0.00436 3.8671 0.04949 2.39529 0.12198 2 4923.55548 1117 1571.21014 8 62702.91052 265 +CotColor ss715580885 phavu.Chr02 98213 1.92353 0.14657 0.00269 3.48131 0.06233 0.36774 0.54436 2 3025.86109 1115 1573.07346 8 62277.53472 265 +CotColor ss715578496 phavu.Chr02 138690 0.58368 0.55801 8.1834E-4 0.0732 0.78678 1.09414 0.29578 2 920.54155 1114 1577.14047 8 61606.04029 522 +CotColor ss715578522 phavu.Chr02 148336 2.27251 0.10353 0.00317 3.43357 0.06415 1.11111 0.29207 2 3574.0584 1116 1572.7347 8 62445.61226 267 +CotColor ss715578587 phavu.Chr02 166728 1.11134 0.32948 0.00157 1.17129 0.27937 1.05135 0.30542 2 1759.03003 1110 1582.79668 8 60764.01915 188 +CotColor ss715578628 phavu.Chr02 183377 2.11115 0.12158 0.00296 4.19216 0.04085 0.03379 0.8542 2 3324.34375 1113 1574.65741 8 61954.99128 255 +CotColor ss715578633 phavu.Chr02 185211 0.63926 0.52788 8.9378E-4 1.01829 0.31314 0.2609 0.6096 2 1005.67932 1114 1573.1913 8 62232.52746 200 +CotColor ss715578635 phavu.Chr02 186170 2.1337 0.11888 0.00297 4.21295 0.04035 0.058 0.80973 2 3353.91108 1119 1571.8738 8 62665.87055 259 +CotColor ss715578726 phavu.Chr02 225505 1.54066 0.2147 0.00215 3.08219 0.07943 0.00188 0.96542 2 2426.28733 1115 1574.83997 8 61989.88581 267 +CotColor ss715578743 phavu.Chr02 234135 2.17047 0.11461 0.00303 4.34257 0.0374 0.00226 0.96208 2 3416.82946 1116 1574.23204 8 62190.83738 262 +CotColor ss715578754 phavu.Chr02 237230 2.36912 0.09403 0.0033 3.91925 0.04798 0.81963 0.36548 2 3725.64883 1118 1572.58744 8 62510.25002 272 +CotColor ss715578773 phavu.Chr02 242879 0.67769 0.508 9.5131E-4 0.0697 0.79182 1.28565 0.2571 2 1065.74833 1110 1572.62533 8 61871.81182 332 +CotColor ss715578777 phavu.Chr02 244125 3.95133 0.0195 0.00549 5.27775 0.02178 2.61728 0.10599 2 6196.44474 1118 1568.19216 8 63142.13074 102 +CotColor ss715578780 phavu.Chr02 245030 1.18091 0.30739 0.00166 2.35012 0.12556 0.01378 0.90657 2 1862.40134 1111 1577.09563 8 61485.28165 247 +CotColor ss715578887 phavu.Chr02 281645 20.18024 7.78E-6 0.01384 20.18024 7.78E-6 NaN NaN 1 31190.89372 1117 1545.61582 7 75270.10976 251 +CotColor ss715578894 phavu.Chr02 283533 7.07794 8.818E-4 0.00976 14.12078 1.8028E-4 0.04711 0.82822 2 11028.5393 1119 1558.15686 8 64584.52761 169 +CotColor ss715579048 phavu.Chr02 334437 5.11469 0.00615 0.00707 10.23552 0.00142 0.00298 0.95646 2 7984.80345 1118 1561.15067 8 64077.72047 130 +CotColor ss715579052 phavu.Chr02 335952 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715579185 phavu.Chr02 368054 0.60509 0.5462 8.4646E-4 1.21124 0.27132 2.653E-5 0.99589 2 952.23221 1114 1573.7032 8 62101.71987 2 +CotColor ss715579226 phavu.Chr02 377146 3.95774 0.01937 0.00549 7.85935 0.00514 0.06271 0.80231 2 6200.93422 1119 1566.78529 8 63377.62634 117 +CotColor ss715579235 phavu.Chr02 378633 2.9413 0.05321 0.00412 5.00879 0.02542 0.87437 0.34995 2 4606.83089 1107 1566.25767 8 62875.11804 314 +CotColor ss715579252 phavu.Chr02 382080 3.14732 0.04335 0.00438 5.70014 0.01713 0.59655 0.44006 2 4942.03856 1118 1570.23773 8 62799.15513 326 +CotColor ss715579281 phavu.Chr02 388640 3.26215 0.03867 0.00453 5.13488 0.02364 1.38763 0.23906 2 5113.87956 1118 1567.6428 8 63177.49257 34 +CotColor ss715579337 phavu.Chr02 401923 5.13242 0.00604 0.00713 10.27084 0.00139 0.00315 0.95525 2 8037.63947 1117 1566.05368 8 63348.62 346 +CotColor ss715579366 phavu.Chr02 409034 3.21529 0.04052 0.00448 6.09565 0.0137 0.33854 0.56079 2 5047.01741 1115 1569.69336 8 62684.42945 318 +CotColor ss715579418 phavu.Chr02 423213 4.64863 0.00976 0.00643 9.28647 0.00236 0.01893 0.89059 2 7264.09149 1118 1562.62976 8 63918.45288 123 +CotColor ss715579444 phavu.Chr02 429656 3.60254 0.02757 0.00502 6.69806 0.00978 0.50996 0.4753 2 5653.95046 1115 1569.43383 8 62824.11915 311 +CotColor ss715579457 phavu.Chr02 432637 4.48411 0.01149 0.00627 8.16611 0.00435 0.80355 0.37023 2 7027.17899 1111 1567.12878 8 62770.53181 316 +CotColor ss715579484 phavu.Chr02 439444 2.37819 0.09319 0.00331 4.4213 0.03572 0.33769 0.56128 2 3731.03349 1117 1568.85703 8 62897.81076 320 +CotColor ss715579534 phavu.Chr02 452504 2.74196 0.06488 0.00384 5.48865 0.01932 1.9145E-4 0.98896 2 4312.00408 1112 1572.60102 8 62163.47214 300 +CotColor ss715579556 phavu.Chr02 458739 2.65349 0.07085 0.0037 5.07451 0.02447 0.23594 0.62725 2 4162.83811 1114 1568.81612 8 62925.44535 107 +CotColor ss715579584 phavu.Chr02 465259 2.3759 0.0934 0.00332 0.58998 0.44259 4.16015 0.04162 2 3733.61377 1115 1571.45225 8 62457.41334 381 +CotColor ss715579660 phavu.Chr02 470982 7.16584 8.0875E-4 0.00996 14.33356 1.6129E-4 0.01087 0.91697 2 11180.33197 1111 1560.22547 8 63815.65498 384 +CotColor ss715579674 phavu.Chr02 471895 4.60303 0.01021 0.00639 7.74854 0.00547 1.45437 0.22808 2 7209.1778 1118 1566.18202 8 63365.93994 272 +CotColor ss715579733 phavu.Chr02 477326 4.2089 0.0151 0.00584 8.40485 0.00382 0.02032 0.88668 2 6595.30969 1118 1566.98965 8 63333.88716 343 +CotColor ss715580685 phavu.Chr02 554257 3.9405 0.01971 0.00547 4.73666 0.02973 3.1353 0.07689 2 6179.12432 1118 1568.10625 8 63108.67527 302 +CotColor ss715580737 phavu.Chr02 566240 0.92152 0.39822 0.00129 1.29368 0.25561 0.54988 0.45852 2 1450.22311 1117 1573.72608 8 62273.30725 423 +CotColor ss715580739 phavu.Chr02 571427 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715580748 phavu.Chr02 595508 6.49639 0.00157 0.00904 9.77164 0.00182 3.20179 0.07383 2 10141.97914 1111 1561.17141 8 63719.50586 324 +CotColor ss715580760 phavu.Chr02 628950 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715580775 phavu.Chr02 683307 4.27549 0.01413 0.00594 8.55464 0.00352 0.00395 0.94988 2 6700.8127 1118 1567.26248 8 63197.23457 60 +CotColor ss715580778 phavu.Chr02 688904 5.80005 0.00312 0.00804 11.57924 6.9056E-4 0.0309 0.8605 2 9061.97731 1116 1562.39755 8 63757.87372 303 +CotColor ss715580782 phavu.Chr02 700521 11.42546 1.2253E-5 0.01573 20.67761 6.0293E-6 2.15192 0.14267 2 17678.12652 1113 1547.25784 8 65786.96796 265 +CotColor ss715580815 phavu.Chr02 801884 0.50239 0.60522 7.008E-4 0.95107 0.32966 0.05451 0.81544 2 791.98881 1119 1576.45275 8 62025.38998 44 +CotColor ss715580817 phavu.Chr02 807882 6.11091 0.00229 0.00852 11.35854 7.7686E-4 0.86466 0.35264 2 9534.82648 1109 1560.29681 8 63588.47639 397 +CotColor ss715580823 phavu.Chr02 823278 0.81678 0.44212 0.00116 0.57087 0.45007 1.06265 0.30284 2 1288.6531 1102 1577.73298 8 60844.98413 511 +CotColor ss715580827 phavu.Chr02 832468 0.29215 0.58895 2.0371E-4 0.29215 0.58895 NaN NaN 1 460.43961 1120 1576.04836 7 70725.65455 32 +CotColor ss715580833 phavu.Chr02 850106 2.72491 0.09908 0.0019 2.72491 0.09908 NaN NaN 1 4282.01883 1116 1571.43451 7 71282.14889 42 +CotColor ss715580837 phavu.Chr02 862022 2.9919 0.08396 0.00208 2.9919 0.08396 NaN NaN 1 4704.04431 1120 1572.25943 7 71331.88379 25 +CotColor ss715580841 phavu.Chr02 875209 7.07908 8.8105E-4 0.00983 12.68114 3.8497E-4 1.47165 0.22534 2 11033.91398 1112 1558.66492 8 64093.25392 344 +CotColor ss715580851 phavu.Chr02 901990 0.16572 0.8473 2.3229E-4 0.09613 0.75658 0.23538 0.62766 2 261.55164 1115 1578.27729 8 61519.75826 503 +CotColor ss715580859 phavu.Chr02 921362 0.60431 0.54663 8.4361E-4 1.10172 0.29412 0.10778 0.74274 2 952.43344 1118 1576.05763 8 61993.86892 156 +CotColor ss715580862 phavu.Chr02 925792 6.69007 0.00982 0.00464 6.69007 0.00982 NaN NaN 1 10483.99417 1120 1567.09876 7 72157.59092 38 +CotColor ss715580871 phavu.Chr02 955846 9.67055 6.8566E-5 0.0133 14.71629 1.3196E-4 4.57768 0.03261 2 14991.63563 1116 1550.23589 8 65575.28558 454 +CotColor ss715580875 phavu.Chr02 964146 5.1417 0.00599 0.0073 9.95893 0.00164 0.33057 0.56544 2 8048.91106 1093 1565.41794 8 61662.6787 502 +CotColor ss715580881 phavu.Chr02 974080 5.57685 0.00389 0.00774 10.45032 0.00126 0.70613 0.40091 2 8708.8122 1115 1561.60108 8 63779.42728 82 +CotColor ss715580883 phavu.Chr02 979924 1.19894 0.3019 0.00168 1.65433 0.19864 0.74393 0.38859 2 1888.30533 1114 1574.97688 8 61982.70339 125 +CotColor ss715578403 phavu.Chr02 1006995 4.41823 0.01227 0.00615 8.74779 0.00317 0.09576 0.75703 2 6917.32627 1114 1565.63321 8 63356.92939 542 +CotColor ss715578406 phavu.Chr02 1011973 0.17726 0.83758 2.4995E-4 0.32407 0.56929 0.03074 0.86086 2 280.64558 1111 1583.23376 8 60830.85265 418 +CotColor ss715578407 phavu.Chr02 1013994 6.4557 0.00163 0.00892 9.30735 0.00234 3.58252 0.05865 2 10043.42392 1115 1555.74461 8 64646.63495 555 +CotColor ss715578418 phavu.Chr02 1059707 6.31611 0.00187 0.00873 11.85513 5.9635E-4 0.77943 0.37751 2 9833.55315 1115 1556.90086 8 64506.52063 367 +CotColor ss715578431 phavu.Chr02 1097741 7.01403 9.3936E-4 0.0097 13.15509 2.9968E-4 0.87445 0.34993 2 10924.49944 1116 1557.52041 8 64376.14263 264 +CotColor ss715578435 phavu.Chr02 1107228 15.42764 2.4593E-7 0.021 28.99693 8.8334E-8 1.83663 0.17562 2 23658.82014 1116 1533.53489 8 67707.38174 203 +CotColor ss715578454 phavu.Chr02 1234293 19.71419 3.8526E-9 0.02661 39.37412 4.993E-10 0.08641 0.76885 2 30042.03337 1118 1523.87861 8 69333.00536 151 +CotColor ss715578456 phavu.Chr02 1237296 22.55742 2.4885E-10 0.03029 45.05632 3.0366E-11 0.09495 0.75803 2 34206.70702 1118 1516.42839 8 70374.17377 159 +CotColor ss715578459 phavu.Chr02 1247422 0.30258 0.73897 4.2701E-4 5.8866E-4 0.98065 0.60457 0.43701 2 474.78244 1100 1569.10744 8 62214.81004 114 +CotColor ss715578461 phavu.Chr02 1270186 7.2762 0.00709 0.00506 7.2762 0.00709 NaN NaN 1 11399.7604 1117 1566.71825 7 72061.7064 199 +CotColor ss715578462 phavu.Chr02 1271143 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578463 phavu.Chr02 1273914 5.72262 0.00337 0.00793 3.53757 0.06025 7.88588 0.00507 2 8949.01003 1117 1563.79571 8 63646.52715 430 +CotColor ss715578465 phavu.Chr02 1276908 29.78129 2.504E-13 0.03948 54.28431 3.3589E-13 5.0805 0.02439 2 44616.11178 1119 1498.12545 8 72981.42073 382 +CotColor ss715578470 phavu.Chr02 1301020 0.213 0.80819 2.985E-4 0.18083 0.67075 0.24529 0.62051 2 336.47221 1116 1579.71742 8 61428.46198 122 +CotColor ss715578472 phavu.Chr02 1302531 1.40879 0.24487 0.00197 1.89858 0.16851 0.91913 0.33791 2 2215.60635 1116 1572.7028 8 62332.37967 117 +CotColor ss715578473 phavu.Chr02 1308879 1.82195 0.16219 0.00254 0.18992 0.66306 3.45357 0.06338 2 2866.04104 1118 1573.05908 8 62487.92613 130 +CotColor ss715578474 phavu.Chr02 1309778 2.72 0.09938 0.00189 2.72 0.09938 NaN NaN 1 4278.42898 1119 1572.95238 7 71207.10799 119 +CotColor ss715578476 phavu.Chr02 1314722 0.96662 0.38069 0.00136 0.57836 0.44712 1.35471 0.24471 2 1524.92062 1107 1577.57464 8 61085.25328 214 +CotColor ss715578482 phavu.Chr02 1337697 13.07077 3.1329E-4 0.00901 13.07077 3.1329E-4 NaN NaN 1 20345.0689 1119 1556.53156 7 73746.64765 242 +CotColor ss715578484 phavu.Chr02 1339744 3.32137 0.03646 0.00462 6.58773 0.0104 0.06055 0.80568 2 5204.88726 1116 1567.0922 8 63108.84674 280 +CotColor ss715578485 phavu.Chr02 1344360 9.97922 5.0675E-5 0.01387 19.97264 8.6645E-6 0.00381 0.95081 2 15479.33951 1104 1551.15749 8 64985.57851 270 +CotColor ss715578487 phavu.Chr02 1350243 1.95068 0.14266 0.00272 3.75336 0.05295 0.15084 0.6978 2 3070.41942 1117 1574.02841 8 62342.49019 150 +CotColor ss715578492 phavu.Chr02 1373443 1.23029 0.2926 0.00172 2.34338 0.1261 0.11904 0.73015 2 1937.35828 1118 1574.72041 8 62255.75544 142 +CotColor ss715578494 phavu.Chr02 1379494 1.55294 0.21296 0.00108 1.55294 0.21296 NaN NaN 1 2446.74336 1119 1575.56032 7 70758.61177 140 +CotColor ss715578497 phavu.Chr02 1384749 0.81797 0.44159 0.00114 0.75449 0.38525 0.88152 0.34799 2 1286.08952 1117 1572.30214 8 62485.62628 151 +CotColor ss715578499 phavu.Chr02 1397232 8.95282 1.389E-4 0.01233 17.30462 3.4282E-5 0.6071 0.43605 2 13895.8125 1116 1552.11616 8 65240.67235 302 +CotColor ss715578503 phavu.Chr02 1402999 1.16045 0.31373 0.00164 2.17862 0.14022 0.14397 0.70444 2 1823.55301 1104 1571.41609 8 61849.96771 117 +CotColor ss715578507 phavu.Chr02 1419229 1.56668 0.2092 0.00223 3.03376 0.08183 0.10208 0.74941 2 2472.15435 1097 1577.9615 8 61005.08689 360 +CotColor ss715578509 phavu.Chr02 1423756 15.71554 1.8592E-7 0.02144 31.44913 2.5804E-8 0.00988 0.92083 2 24122.96069 1114 1534.97457 8 67477.87549 400 +CotColor ss715578520 phavu.Chr02 1475349 1.32828 0.26535 0.00186 0.51561 0.47287 2.14042 0.14374 2 2093.3664 1112 1575.99382 8 61723.36766 164 +CotColor ss715578523 phavu.Chr02 1482285 0.37512 0.68729 5.2338E-4 0.64869 0.42075 0.10207 0.74942 2 591.49047 1119 1576.8111 8 61975.2654 202 +CotColor ss715578525 phavu.Chr02 1483918 0.2768 0.75826 3.8736E-4 0.51283 0.47406 0.04121 0.83917 2 436.71018 1116 1577.69629 8 61758.21338 209 +CotColor ss715578527 phavu.Chr02 1486712 19.74773 3.7287E-9 0.02663 33.78931 8.0012E-9 5.56832 0.01846 2 30097.02613 1119 1524.07556 8 69351.64931 506 +CotColor ss715578529 phavu.Chr02 1490607 0.31696 0.72843 4.4256E-4 0.61513 0.43303 0.01933 0.88946 2 499.28872 1117 1575.2474 8 62101.93199 209 +CotColor ss715578533 phavu.Chr02 1498823 14.01782 9.7129E-7 0.01917 26.61456 2.937E-7 1.41128 0.2351 2 21596.5781 1116 1540.65174 8 66796.40243 559 +CotColor ss715578536 phavu.Chr02 1508766 2.80998 0.06063 0.0039 5.62332 0.01789 0.00165 0.96765 2 4411.61421 1119 1569.98336 8 62930.29633 149 +CotColor ss715578538 phavu.Chr02 1513766 6.00268 0.00255 0.00829 11.56326 6.9639E-4 0.4478 0.50352 2 9370.90419 1119 1561.11957 8 64170.11883 54 +CotColor ss715578540 phavu.Chr02 1516846 18.5012 1.2459E-8 0.02507 31.27106 2.8206E-8 5.60237 0.01811 2 28222.75119 1115 1525.45554 8 68872.83522 284 +CotColor ss715578543 phavu.Chr02 1521991 0.60396 0.54682 8.4234E-4 1.16952 0.27973 0.03941 0.84268 2 951.94561 1119 1576.16686 8 62065.37918 331 +CotColor ss715578545 phavu.Chr02 1528791 1.12518 0.32496 0.00158 0.65009 0.42025 1.59992 0.20618 2 1771.74762 1112 1574.63326 8 61968.91379 81 +CotColor ss715578547 phavu.Chr02 1531899 0.08535 0.91819 1.1919E-4 0.14552 0.70293 0.02531 0.87362 2 134.5863 1118 1576.83105 8 61939.06814 12 +CotColor ss715578550 phavu.Chr02 1533882 10.25392 3.8648E-5 0.01408 20.45532 6.7544E-6 0.06954 0.79206 2 15900.09101 1118 1550.63477 8 65538.66825 548 +CotColor ss715578552 phavu.Chr02 1541093 0.23068 0.79403 3.2564E-4 0.02908 0.86462 0.43229 0.511 2 364.05052 1106 1578.16836 8 61306.98787 180 +CotColor ss715578554 phavu.Chr02 1549921 0.82858 0.43694 0.00116 0.01594 0.89954 1.64121 0.20043 2 1306.89485 1116 1577.2668 8 61833.90964 179 +CotColor ss715578556 phavu.Chr02 1553130 0.64794 0.52332 9.051E-4 6.9293E-4 0.979 1.2952 0.25534 2 1021.08749 1117 1575.88763 8 62004.35154 190 +CotColor ss715578558 phavu.Chr02 1556076 3.00541 0.04992 0.00417 4.71935 0.03003 1.29025 0.25624 2 4716.80723 1119 1569.43788 8 63006.59459 514 +CotColor ss715578563 phavu.Chr02 1580127 2.61598 0.07354 0.00365 4.76706 0.02922 0.46717 0.49443 2 4115.01947 1116 1573.0333 8 62400.68805 115 +CotColor ss715578568 phavu.Chr02 1600224 1.60153 0.20205 0.00223 2.34247 0.12617 0.86088 0.35369 2 2521.85049 1118 1574.64878 8 62194.35681 142 +CotColor ss715578571 phavu.Chr02 1608091 1.41868 0.24247 0.00199 2.59048 0.10779 0.24863 0.61814 2 2233.11453 1113 1574.07542 8 62089.99573 56 +CotColor ss715578574 phavu.Chr02 1616784 1.40606 0.24554 0.00196 2.7729 0.09615 0.04159 0.83844 2 2213.0121 1119 1573.91294 8 62380.64581 55 +CotColor ss715578575 phavu.Chr02 1619075 1.36102 0.25682 0.0019 2.41993 0.12008 0.30363 0.58173 2 2142.73378 1118 1574.35301 8 62307.09932 55 +CotColor ss715578578 phavu.Chr02 1624141 0.42838 0.65167 5.9764E-4 0.81164 0.36783 0.04581 0.83055 2 675.40879 1119 1576.66112 8 61996.24498 1 +CotColor ss715578581 phavu.Chr02 1641539 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1109 1572.23946 6 81796.55318 0 +CotColor ss715578584 phavu.Chr02 1653600 1.01454 0.3629 0.00142 0.34324 0.55808 1.68563 0.19445 2 1598.42723 1118 1575.52368 8 62065.26997 209 +CotColor ss715578589 phavu.Chr02 1675532 0.62012 0.53806 8.6485E-4 1.18297 0.27699 0.05827 0.8093 2 977.38793 1119 1576.12139 8 62071.73976 61 +CotColor ss715578593 phavu.Chr02 1696769 16.85974 6.1116E-8 0.02289 33.34605 9.9848E-9 0.39155 0.53161 2 25840.95389 1118 1532.70205 8 68084.56443 538 +CotColor ss715578603 phavu.Chr02 1722193 0.83608 0.43368 0.00117 0.45435 0.50041 1.21772 0.27005 2 1317.41409 1117 1575.70202 8 62046.90727 408 +CotColor ss715578610 phavu.Chr02 1745945 6.25455 0.00199 0.00864 10.16903 0.00147 2.32801 0.12735 2 9751.88099 1118 1559.16507 8 64374.9853 557 +CotColor ss715578613 phavu.Chr02 1756948 3.2401 0.03953 0.0045 5.33669 0.02106 1.14282 0.28529 2 5082.64362 1118 1568.66906 8 63026.42146 456 +CotColor ss715578619 phavu.Chr02 1786647 0.44679 0.63979 6.2331E-4 0.81879 0.36573 0.07547 0.78358 2 704.41718 1119 1576.60927 8 62003.49708 53 +CotColor ss715578621 phavu.Chr02 1796956 4.6447 0.0098 0.00649 6.04676 0.01408 3.23051 0.07255 2 7278.16971 1110 1566.98293 8 62836.37373 99 +CotColor ss715578622 phavu.Chr02 1804463 0.62264 0.43024 4.3826E-4 0.62264 0.43024 NaN NaN 1 979.8691 1108 1573.72853 7 70300.86921 2 +CotColor ss715578636 phavu.Chr02 1866808 1.1477 0.31774 0.0016 2.26983 0.1322 0.02755 0.8682 2 1808.3089 1117 1575.58891 8 62037.15621 295 +CotColor ss715578639 phavu.Chr02 1878694 0.87899 0.41549 0.00123 1.07278 0.30054 0.68549 0.40788 2 1383.81692 1115 1574.33414 8 62080.44553 418 +CotColor ss715578640 phavu.Chr02 1881454 0.23244 0.79264 3.2543E-4 0.00191 0.96512 0.46297 0.49638 2 366.38398 1115 1576.24787 8 61768.46225 203 +CotColor ss715578642 phavu.Chr02 1887609 1.65321 0.19191 0.00233 2.70201 0.1005 0.60536 0.43671 2 2606.02909 1109 1576.34785 8 61547.71473 490 +CotColor ss715578643 phavu.Chr02 1889374 6.81024 0.00115 0.00943 13.46883 2.5401E-4 0.16177 0.68761 2 10636.39604 1116 1561.82316 8 64020.10203 266 +CotColor ss715578647 phavu.Chr02 1902673 0.23081 0.79393 3.2229E-4 0.33474 0.563 0.12715 0.72148 2 363.91932 1118 1576.68582 8 61945.97069 353 +CotColor ss715578648 phavu.Chr02 1905368 1.63409 0.2014 0.00114 1.63409 0.2014 NaN NaN 1 2572.32501 1120 1574.16275 7 71027.35246 79 +CotColor ss715578656 phavu.Chr02 1936927 0.9257 0.39656 0.0013 1.18707 0.27616 0.66469 0.41508 2 1460.97185 1116 1578.231 8 61662.17702 144 +CotColor ss715578662 phavu.Chr02 1976480 1.88274 0.1703 0.00131 1.88274 0.1703 NaN NaN 1 2963.07608 1120 1573.81387 7 71083.17405 2 +CotColor ss715578665 phavu.Chr02 1990687 3.04584 0.04795 0.00424 2.34077 0.12631 3.74514 0.05321 2 4772.09678 1115 1566.76 8 63180.5152 73 +CotColor ss715578669 phavu.Chr02 2010584 1.18618 0.30577 0.00165 0.72934 0.39328 1.64261 0.20023 2 1867.68282 1119 1574.53015 8 62294.31348 210 +CotColor ss715578673 phavu.Chr02 2038467 1.65358 0.19183 0.00232 2.61471 0.10616 0.69317 0.40527 2 2603.19258 1113 1574.27775 8 62017.1904 219 +CotColor ss715578674 phavu.Chr02 2039986 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578675 phavu.Chr02 2042156 1.52064 0.21902 0.00212 1.90131 0.16821 1.13973 0.28594 2 2395.58758 1117 1575.38041 8 62001.51657 208 +CotColor ss715578682 phavu.Chr02 2059401 0.47559 0.62164 6.6369E-4 0.27103 0.60274 0.68023 0.40968 2 749.29709 1118 1575.50342 8 62071.64614 125 +CotColor ss715578683 phavu.Chr02 2065014 3.09976 0.04545 0.00433 5.96441 0.01475 0.23917 0.6249 2 4869.64733 1114 1570.97714 8 62493.7671 528 +CotColor ss715578689 phavu.Chr02 2102513 1.66225 0.19018 0.00233 0.16822 0.68178 3.15595 0.07592 2 2621.84531 1113 1577.29106 8 61633.29865 475 +CotColor ss715578694 phavu.Chr02 2126801 0.95942 0.38344 0.00137 1.13805 0.2863 0.781 0.37703 2 1520.29221 1100 1584.60287 8 59867.7549 338 +CotColor ss715578698 phavu.Chr02 2136619 0.78479 0.45647 0.0011 1.26638 0.26069 0.30399 0.5815 2 1235.49519 1115 1574.29512 8 62063.21145 87 +CotColor ss7155787phavu.Chr02 01 2183554 9.36331 9.2793E-5 0.01302 18.29648 2.0533E-5 0.43939 0.50756 2 14573.82871 1109 1556.48187 8 64081.53112 133 +CotColor ss715578703 phavu.Chr02 2187616 2.82041 0.09335 0.00196 2.82041 0.09335 NaN NaN 1 4433.58879 1119 1571.96515 7 71324.78795 64 +CotColor ss715578706 phavu.Chr02 2194371 0.68517 0.50421 9.5831E-4 0.66192 0.41606 0.7086 0.40009 2 1079.69673 1116 1575.80069 8 61843.09405 307 +CotColor ss715578707 phavu.Chr02 2195303 1.89518 0.15078 0.00267 0.29083 0.58979 3.49886 0.06168 2 2978.90685 1104 1571.83541 8 62002.32802 129 +CotColor ss715578709 phavu.Chr02 2205418 2.56214 0.07759 0.00357 4.73256 0.02981 0.3943 0.53018 2 4017.21367 1113 1567.91151 8 62808.19395 65 +CotColor ss715578711 phavu.Chr02 2211215 2.36106 0.09479 0.00328 4.27048 0.03901 0.45374 0.5007 2 3704.13571 1118 1568.84141 8 62965.14298 165 +CotColor ss715578714 phavu.Chr02 2218064 3.73207 0.02424 0.00518 7.42975 0.00652 0.04076 0.84004 2 5849.70139 1119 1567.41305 8 63289.81813 68 +CotColor ss715578716 phavu.Chr02 2223451 7.4052 6.3835E-4 0.0102 13.91341 2.0102E-4 0.89826 0.34345 2 11531.80561 1119 1557.25737 8 64710.34418 109 +CotColor ss715578717 phavu.Chr02 2230781 0.16386 0.84888 2.3E-4 0.2958 0.58664 0.03218 0.85767 2 259.00448 1115 1580.64473 8 61218.8624 302 +CotColor ss715578722 phavu.Chr02 2246694 3.17767 0.04206 0.00441 4.89644 0.02711 1.4569 0.22768 2 4985.63389 1119 1568.95741 8 63073.80125 121 +CotColor ss715578728 phavu.Chr02 2274638 4.34234 0.01323 0.00603 6.49657 0.01094 2.18125 0.13998 2 6799.98647 1116 1565.97114 8 63367.50917 547 +CotColor ss715578729 phavu.Chr02 2275959 14.66787 1.3532E-4 0.0101 14.66787 1.3532E-4 NaN NaN 1 22824.37997 1120 1556.08056 7 73920.50317 20 +CotColor ss715578730 phavu.Chr02 2279828 2.19185 0.11219 0.00306 4.24692 0.03955 0.14005 0.70831 2 3448.66776 1117 1573.40636 8 62350.06759 495 +CotColor ss715578731 phavu.Chr02 2284699 5.34593 0.00489 0.00745 10.22263 0.00143 0.47405 0.49127 2 8382.72194 1115 1568.05679 8 62930.7181 74 +CotColor ss715578733 phavu.Chr02 2287938 2.07657 0.12584 0.0029 2.86247 0.09095 1.28993 0.25631 2 3267.8026 1115 1573.65383 8 62276.7362 396 +CotColor ss715578734 phavu.Chr02 2296433 7.34947 6.7485E-4 0.01029 10.71331 0.0011 3.957 0.04692 2 11492.74499 1105 1563.75076 8 63103.34568 464 +CotColor ss715578746 phavu.Chr02 2355288 2.41012 0.09027 0.00336 2.39227 0.12222 2.42492 0.1197 2 3785.58607 1116 1570.7043 8 62702.85442 100 +CotColor ss715578750 phavu.Chr02 2364655 0.65649 0.51887 9.1551E-4 0.8736 0.35016 0.43982 0.50735 2 1034.6445 1119 1576.01905 8 62086.0539 57 +CotColor ss715578752 phavu.Chr02 2378119 0.77136 0.46263 0.00108 1.43085 0.23188 0.11302 0.7368 2 1214.89104 1116 1574.98993 8 62095.30308 139 +CotColor ss715578755 phavu.Chr02 2381212 1.55221 0.21224 0.00216 2.63175 0.10503 0.47389 0.49134 2 2442.40037 1119 1573.50295 8 62437.99287 3 +CotColor ss715578757 phavu.Chr02 2384412 0.889 0.34595 6.2154E-4 0.889 0.34595 NaN NaN 1 1399.92468 1116 1574.71218 7 70712.49907 1 +CotColor ss715578763 phavu.Chr02 2396176 1.864 0.17244 0.0013 1.864 0.17244 NaN NaN 1 2932.17278 1118 1573.05752 7 71010.67522 2 +CotColor ss715578768 phavu.Chr02 2421404 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578770 phavu.Chr02 2426934 3.0572 0.08065 0.00213 3.0572 0.08065 NaN NaN 1 4810.51325 1119 1573.50295 7 71104.37711 3 +CotColor ss715578771 phavu.Chr02 2431330 1.18096 0.2774 8.2399E-4 1.18096 0.2774 NaN NaN 1 1859.13536 1118 1574.26078 7 70892.72613 1 +CotColor ss715578772 phavu.Chr02 2436476 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578784 phavu.Chr02 2481744 0.99841 0.36879 0.00139 1.89323 0.16911 0.10512 0.74584 2 1572.56051 1119 1575.05763 8 62220.53291 1 +CotColor ss715578787 phavu.Chr02 2489455 7.65585 0.00575 0.0053 7.65585 0.00575 NaN NaN 1 11987.19438 1120 1565.75662 7 72372.3338 25 +CotColor ss715578790 phavu.Chr02 2506568 8.54717 0.00353 0.00592 8.54717 0.00353 NaN NaN 1 13374.36084 1119 1564.77057 7 72489.83499 26 +CotColor ss715578792 phavu.Chr02 2510357 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578794 phavu.Chr02 2523130 3.24662 0.03928 0.00471 5.15574 0.02336 1.33591 0.24801 2 5171.03341 1088 1592.74507 8 57899.15098 28 +CotColor ss715578795 phavu.Chr02 2525149 0.85112 0.42721 0.00119 0.72936 0.39327 0.9729 0.32417 2 1340.91254 1119 1575.47165 8 62162.62092 74 +CotColor ss715578799 phavu.Chr02 2555747 0.19781 0.82055 2.7796E-4 0.31726 0.57337 0.07863 0.77921 2 312.78537 1114 1581.20998 8 61135.73155 509 +CotColor ss7155788phavu.Chr02 01 2560021 2.3083 0.0999 0.00321 3.36948 0.06668 1.24637 0.26449 2 3627.22273 1119 1571.38531 8 62734.19846 19 +CotColor ss715578803 phavu.Chr02 2565976 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578805 phavu.Chr02 2580590 2.1755 0.11403 0.00305 0.90551 0.34152 3.4435 0.06377 2 3420.77512 1112 1572.41115 8 62235.80741 540 +CotColor ss715578810 phavu.Chr02 2601764 0.19532 0.8226 2.7451E-4 0.19607 0.658 0.19472 0.6591 2 308.74237 1113 1580.67176 8 61265.07087 509 +CotColor ss715578812 phavu.Chr02 2605265 1.21761 0.29633 0.0017 1.23124 0.2674 1.20375 0.27281 2 1917.4972 1117 1574.80611 8 62135.99349 319 +CotColor ss715578815 phavu.Chr02 2610178 3.61733 0.02717 0.00502 6.56316 0.01054 0.67342 0.41204 2 5669.00567 1118 1567.1788 8 63245.59978 290 +CotColor ss715578816 phavu.Chr02 2611571 2.00389 0.13529 0.0028 1.97138 0.16058 2.03458 0.15404 2 3151.91205 1116 1572.89317 8 62380.9446 261 +CotColor ss715578817 phavu.Chr02 2614460 3.75872 0.02361 0.00523 7.3706 0.00673 0.15242 0.6963 2 5899.48937 1116 1569.54845 8 62785.44771 278 +CotColor ss715578820 phavu.Chr02 2617317 1.50615 0.22221 0.0021 3.01003 0.08303 0.00496 0.94388 2 2371.16246 1116 1574.31777 8 62099.15889 275 +CotColor ss715578822 phavu.Chr02 2626192 5.75295 0.00327 0.00798 11.49677 7.2164E-4 0.01925 0.88968 2 8958.72432 1112 1557.24092 8 64193.61637 378 +CotColor ss715578826 phavu.Chr02 2635762 0.78339 0.45711 0.00109 0.96828 0.32532 0.59884 0.43918 2 1232.64503 1117 1573.48313 8 62294.18642 257 +CotColor ss715578827 phavu.Chr02 2637003 5.38139 0.02053 0.00374 5.38139 0.02053 NaN NaN 1 8450.34544 1119 1570.28929 7 71583.00685 26 +CotColor ss715578832 phavu.Chr02 2654324 0.43134 0.64974 6.0177E-4 0.83483 0.36108 0.02858 0.86579 2 680.07703 1119 1576.65277 8 61997.41204 10 +CotColor ss715578838 phavu.Chr02 2691624 8.05732 3.3567E-4 0.01117 14.22164 1.7105E-4 1.88171 0.17042 2 12527.3738 1110 1554.78156 8 64577.74275 548 +CotColor ss715578842 phavu.Chr02 2705392 7.25521 7.404E-4 0.01004 11.97589 5.5934E-4 2.5182 0.11282 2 11294.40495 1113 1556.73125 8 64605.33043 556 +CotColor ss715578845 phavu.Chr02 2715338 1.82158 0.1774 0.00127 1.82158 0.1774 NaN NaN 1 2867.29487 1119 1574.06959 7 70991.76955 2 +CotColor ss715578848 phavu.Chr02 2724688 2.57543 0.07657 0.00358 5.11712 0.02388 0.03813 0.84521 2 4044.12873 1117 1570.2761 8 62780.89985 457 +CotColor ss715578856 phavu.Chr02 2753216 5.18148 0.00576 0.00716 8.87297 0.00296 1.48614 0.22307 2 8088.12018 1118 1560.96584 8 64103.54965 122 +CotColor ss715578858 phavu.Chr02 2755143 4.91277 0.00751 0.00681 9.82605 0.00177 0.00821 0.92782 2 7677.36531 1117 1562.73536 8 63795.58785 124 +CotColor ss715578860 phavu.Chr02 2763102 1.53217 0.21652 0.00216 1.37288 0.24157 1.69061 0.19379 2 2417.07473 1106 1577.54813 8 61373.00199 440 +CotColor ss715578862 phavu.Chr02 2769018 5.00184 0.00688 0.00693 9.97818 0.00163 0.03411 0.8535 2 7814.71117 1116 1562.36868 8 63910.35427 136 +CotColor ss715578865 phavu.Chr02 2778279 1.30475 0.27165 0.00182 0.00474 0.94514 2.60475 0.10683 2 2054.71872 1118 1574.80241 8 62264.08027 233 +CotColor ss715578870 phavu.Chr02 2790162 0.06557 0.93653 9.1603E-5 0.09546 0.7574 0.03576 0.85004 2 103.25526 1116 1574.67528 8 62132.93475 289 +CotColor ss715578872 phavu.Chr02 2791752 1.29598 0.27404 0.00182 2.57244 0.10902 0.02177 0.88273 2 2045.83032 1113 1578.60242 8 61396.14654 187 +CotColor ss715578874 phavu.Chr02 2796266 1.91025 0.14853 0.00267 3.73682 0.05348 0.08674 0.76842 2 3004.41361 1116 1572.78414 8 62303.55111 182 +CotColor ss715578876 phavu.Chr02 2801896 4.7008 0.00927 0.00658 8.67325 0.0033 0.73045 0.39292 2 7364.45906 1108 1566.64064 8 62780.24942 103 +CotColor ss715578879 phavu.Chr02 2812412 6.61874 0.00139 0.00925 11.19179 8.4907E-4 2.03526 0.15397 2 10366.33333 1110 1566.20992 8 62912.77816 131 +CotColor ss715578882 phavu.Chr02 2825569 2.98687 0.05085 0.00418 2.03364 0.15413 3.93473 0.04754 2 4691.21733 1111 1570.61378 8 62535.29218 456 +CotColor ss715578885 phavu.Chr02 2829751 2.13846 0.14393 0.00149 2.13846 0.14393 NaN NaN 1 3351.4646 1113 1567.22911 7 71631.19686 438 +CotColor ss715578902 phavu.Chr02 2892575 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1117 1575.78062 6 82193.88229 0 +CotColor ss715578907 phavu.Chr02 2915019 0.00305 0.95593 2.1321E-6 0.00305 0.95593 NaN NaN 1 4.81412 1119 1575.84886 7 70657.20223 61 +CotColor ss715578913 phavu.Chr02 2944680 4.10695 0.01671 0.0058 8.15089 0.00438 0.06987 0.79158 2 6471.77232 1104 1575.80936 8 61311.82748 466 +CotColor ss715578941 phavu.Chr02 3031424 3.93618 0.0198 0.00548 3.25351 0.07154 4.60831 0.03203 2 6160.1388 1112 1565.00326 8 63349.61025 372 +CotColor ss715578942 phavu.Chr02 3033126 1.97577 0.13914 0.00276 3.27563 0.07059 0.67686 0.41085 2 3094.46045 1112 1566.20514 8 62992.88474 373 +CotColor ss715578946 phavu.Chr02 3038334 0.49927 0.60711 6.9821E-4 0.11261 0.73726 0.88595 0.34678 2 786.66931 1116 1575.63371 8 61874.30972 522 +CotColor ss715578951 phavu.Chr02 3051719 4.7029 0.00925 0.00656 3.01591 0.08273 6.37529 0.01171 2 7354.93947 1110 1563.91662 8 63097.16024 405 +CotColor ss715578954 phavu.Chr02 3071924 19.57774 4.398E-9 0.02647 37.73342 1.126E-9 1.40827 0.2356 2 29850.05902 1116 1524.69376 8 69188.16342 100 +CotColor ss715578957 phavu.Chr02 3080035 2.10919 0.12182 0.00294 4.21324 0.04034 0.00888 0.92494 2 3311.37739 1117 1569.97336 8 62772.86761 310 +CotColor ss715578964 phavu.Chr02 3099830 19.82447 3.4654E-9 0.02683 39.62428 4.4147E-10 0.05808 0.8096 2 30236.78308 1116 1525.22492 8 68996.29087 73 +CotColor ss715578972 phavu.Chr02 3128484 7.61475 5.1925E-4 0.01057 14.7213 1.3163E-4 0.51461 0.4733 2 11886.88108 1113 1561.03436 8 63871.10827 331 +CotColor ss715578975 phavu.Chr02 3137444 2.11888 0.12065 0.00297 1.18728 0.27612 3.04829 0.0811 2 3333.50462 1110 1573.23913 8 62097.62022 556 +CotColor ss715578982 phavu.Chr02 3160446 0.70702 0.49333 9.8997E-4 0.6417 0.42327 0.77247 0.37964 2 1115.16215 1115 1577.27415 8 61783.90309 512 +CotColor ss715578983 phavu.Chr02 3162076 4.79674 0.00843 0.00665 9.49402 0.00211 0.10706 0.74358 2 7488.244 1115 1561.11034 8 63932.04563 310 +CotColor ss715578986 phavu.Chr02 3176824 47.97732 7.265E-12 0.03217 47.97732 7.265E-12 NaN NaN 1 72507.80024 1117 1511.29321 7 80787.00296 +CotColor ss715578987 phavu.Chr02 3178447 10.78966 2.2845E-5 0.01483 21.47292 4.0105E-6 0.12323 0.72562 2 16726.8347 1117 1550.26555 8 65573.43791 287 +CotColor ss7155790phavu.Chr02 01 3237203 0.00108 0.97375 7.676E-7 0.00108 0.97375 NaN NaN 1 1.72049 1111 1587.67343 7 68211.8557 50 +CotColor ss715579006 phavu.Chr02 3251994 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715579phavu.Chr022 01 3276003 0.44247 0.64256 6.2004E-4 0.54751 0.45949 0.33776 0.56124 2 694.84312 1110 1570.36611 8 62272.02684 8 +CotColor ss715579phavu.Chr023 01 3281308 0.11971 0.88719 1.6726E-4 0.14576 0.7027 0.09378 0.75948 2 188.85246 1118 1577.60024 8 61800.31524 91 +CotColor ss715579phavu.Chr028 01 3290574 20.07729 2.7259E-9 0.02747 40.06858 3.5592E-10 0.11802 0.73126 2 30601.75799 1103 1524.19747 8 68402.29613 545 +CotColor ss715579021 phavu.Chr02 3296361 20.00423 2.9132E-9 0.02702 39.86229 3.9246E-10 0.17559 0.67527 2 30456.94947 1116 1522.52543 8 69412.56543 545 +CotColor ss715579023 phavu.Chr02 3300420 20.40236 1.9864E-9 0.02771 39.15936 5.5631E-10 1.62343 0.20288 2 31114.2876 1112 1525.03354 8 68712.22158 554 +CotColor ss715579027 phavu.Chr02 3312803 2.16607 0.11511 0.00303 4.16915 0.0414 0.16612 0.68367 2 3401.38488 1111 1570.30153 8 62579.10683 449 +CotColor ss715579028 phavu.Chr02 3313958 0.94736 0.38808 0.00135 1.03302 0.30967 0.86184 0.35343 2 1492.56416 1099 1575.49251 8 60974.70226 515 +CotColor ss715579029 phavu.Chr02 3315586 0.75872 0.46851 0.00107 1.33647 0.24791 0.18195 0.66978 2 1198.96872 1111 1580.25407 8 61161.23974 516 +CotColor ss715579032 phavu.Chr02 3320292 1.18134 0.30725 0.00165 2.15115 0.14274 0.21303 0.64449 2 1860.30727 1117 1574.74879 8 62164.40476 501 +CotColor ss715579033 phavu.Chr02 3323142 2.65275 0.0709 0.00371 4.61505 0.03191 0.69173 0.40576 2 4164.66422 1112 1569.9425 8 62560.36976 422 +CotColor ss715579035 phavu.Chr02 3328987 1.88274 0.1703 0.00131 1.88274 0.1703 NaN NaN 1 2963.07608 1120 1573.81387 7 71083.17405 2 +CotColor ss715579037 phavu.Chr02 3333585 1.68677 0.18559 0.00236 0.41786 0.51814 2.95495 0.08589 2 2658.02316 1117 1575.80583 8 62027.19171 50 +CotColor ss715579038 phavu.Chr02 3336772 0.58734 0.44361 4.108E-4 0.58734 0.44361 NaN NaN 1 926.02278 1117 1576.64285 7 70438.46577 540 +CotColor ss715579040 phavu.Chr02 3342559 0.51493 0.59768 7.1903E-4 0.75411 0.38536 0.27623 0.59928 2 812.0559 1118 1577.02542 8 61953.41456 542 +CotColor ss715579042 phavu.Chr02 3345138 0.61328 0.54175 8.6075E-4 0.06476 0.79918 1.1618 0.28133 2 969.54048 1115 1580.90546 8 61260.3443 501 +CotColor ss715579044 phavu.Chr02 3355052 1.83669 0.15983 0.00257 3.12213 0.07751 0.5525 0.45746 2 2894.85399 1114 1576.12935 8 61913.51912 49 +CotColor ss715579053 phavu.Chr02 3375794 11.76627 8.776E-6 0.01622 21.56019 3.8373E-6 1.95386 0.16245 2 18227.16213 1112 1549.1032 8 65640.95779 551 +CotColor ss715579054 phavu.Chr02 3378315 11.27432 1.42E-5 0.01543 20.47842 6.6742E-6 2.05101 0.15238 2 17438.01044 1119 1546.70115 8 66186.89539 559 +CotColor ss715579055 phavu.Chr02 3382442 11.07853 1.7216E-5 0.01526 20.28909 7.3591E-6 1.85244 0.17378 2 17160.24754 1113 1548.96422 8 65651.59501 547 +CotColor ss715579057 phavu.Chr02 3387651 10.90736 2.036E-5 0.01497 21.83425 3.3349E-6 5.3802E-5 0.99415 2 16842.44636 1114 1544.13554 8 66194.11358 551 +CotColor ss715579060 phavu.Chr02 3404910 16.52022 8.4983E-8 0.0224 32.38719 1.6122E-8 0.66299 0.41568 2 25291.13829 1118 1530.9205 8 68328.455240 +CotColor ss715579065 phavu.Chr02 3415315 14.86614 4.2484E-7 0.02027 29.74114 6.0761E-8 0.0173 0.89538 2 22859.90174 1116 1537.71645 8 67402.327290 +CotColor ss715579069 phavu.Chr02 3427220 15.45226 2.4029E-7 0.02114 30.9252 3.3565E-8 0.0069 0.93379 2 23757.29515 1112 1537.46457 8 67246.54082 400 +CotColor ss715579081 phavu.Chr02 3458900 21.55716 6.521E-10 0.02906 42.50823 1.0641E-10 0.62054 0.43101 2 32717.90818 1115 1517.72831 8 69951.88952 549 +CotColor ss715579089 phavu.Chr02 3483188 1.00158 0.36763 0.0014 1.73777 0.18769 0.26653 0.60577 2 1581.4048 1116 1578.91018 8 61464.59902 107 +CotColor ss715579100 phavu.Chr02 3518783 2.03041 0.13177 0.00283 3.72673 0.0538 0.33631 0.56209 2 3183.85175 1114 1568.08501 8 62880.23032 71 +CotColor ss715579102 phavu.Chr02 3524524 3.20123 0.04109 0.00445 6.31282 0.01213 0.09476 0.75827 2 5020.10142 1117 1568.17877 8 63058.27186 419 +CotColor ss715579104 phavu.Chr02 3530881 0.76252 0.46673 0.00107 1.04099 0.30781 0.48453 0.48652 2 1204.27058 1111 1579.33387 8 61287.20634 471 +CotColor ss715579107 phavu.Chr02 3536177 1.62772 0.19684 0.00227 3.00362 0.08335 0.25382 0.6145 2 2565.20161 1117 1575.95182 8 61956.40612 67 +CotColor ss715579112 phavu.Chr02 3548683 1.17902 0.30796 0.00164 2.29325 0.13022 0.06669 0.79626 2 1856.42346 1119 1574.55028 8 62291.49864 557 +CotColor ss715579118 phavu.Chr02 3562237 1.95948 0.14142 0.00274 2.36096 0.12469 1.55683 0.21239 2 3084.31916 1116 1574.04994 8 62221.41284 59 +CotColor ss715579123 phavu.Chr02 3571972 2.2124 0.10992 0.00308 4.3385 0.03749 0.08983 0.76445 2 3480.44447 1117 1573.15545 8 62411.30627 89 +CotColor ss715579125 phavu.Chr02 3574425 1.25549 0.28534 0.00176 2.45752 0.11725 0.05554 0.81374 2 1980.02113 1117 1577.08992 8 61768.11575 63 +CotColor ss715579127 phavu.Chr02 3579099 1.85405 0.15708 0.0026 2.06048 0.15144 1.64642 0.19971 2 2922.00506 1114 1576.01379 8 61962.52361 547 +CotColor ss715579129 phavu.Chr02 3581262 2.44833 0.0869 0.00342 2.9087 0.08838 1.98538 0.1591 2 3847.15845 1113 1571.34066 8 62326.20965 553 +CotColor ss715579131 phavu.Chr02 3582745 1.66879 0.18895 0.00233 2.17514 0.14054 1.16213 0.28126 2 2624.51696 1115 1572.70564 8 62234.04612 551 +CotColor ss715579132 phavu.Chr02 3585591 2.07589 0.12593 0.0029 1.86719 0.17207 2.28244 0.13113 2 3264.78197 1116 1572.71699 8 62506.21966 553 +CotColor ss715579135 phavu.Chr02 3589812 1.61265 0.19982 0.00225 2.43944 0.1186 0.78634 0.3754 2 2537.60003 1116 1573.55421 8 62340.02129 554 +CotColor ss715579138 phavu.Chr02 3593529 1.86914 0.15474 0.0026 1.81828 0.17779 1.91851 0.1663 2 2940.56393 1118 1573.21772 8 62485.54157 554 +CotColor ss715579139 phavu.Chr02 3595146 12.05485 6.6144E-6 0.01659 23.875 1.1783E-6 0.25075 0.61665 2 18660.83582 1114 1547.99368 8 65693.70398 399 +CotColor ss715579141 phavu.Chr02 3597388 1.86475 0.15543 0.00264 2.40171 0.12149 1.32707 0.24958 2 2922.29977 1095 1567.13036 8 62139.92074 527 +CotColor ss715579145 phavu.Chr02 3605511 3.14293 0.04354 0.00436 5.72183 0.01692 0.56626 0.45191 2 4931.43464 1119 1569.05428 8 63060.25144 415 +CotColor ss715579147 phavu.Chr02 3608479 5.58226 0.00387 0.00773 9.86156 0.00173 1.3003 0.2544 2 8702.99224 1115 1559.04502 8 64240.72324 464 +CotColor ss715579153 phavu.Chr02 3621743 16.89123 5.9355E-8 0.02308 30.06823 5.1594E-8 3.64283 0.05657 2 25941.58954 1112 1535.80199 8 67520.221321 +CotColor ss715579156 phavu.Chr02 3629512 8.04582 3.3936E-4 0.01109 12.04716 5.3849E-4 4.01204 0.04542 2 12519.1885 1118 1555.98744 8 64795.46029 218 +CotColor ss715579157 phavu.Chr02 3632583 4.16463 0.01578 0.00578 7.83105 0.00522 0.50172 0.47889 2 6504.9995 1114 1561.96163 8 63737.73391 493 +CotColor ss715579164 phavu.Chr02 3649944 3.9922 0.01872 0.00555 5.05692 0.02472 2.91877 0.08783 2 6253.07271 1115 1566.32414 8 63331.37054 554 +CotColor ss715579168 phavu.Chr02 3655869 2.33343 0.09744 0.0033 4.50819 0.03395 0.16209 0.68732 2 3680.96443 1106 1577.48944 8 61181.04592 424 +CotColor ss715579174 phavu.Chr02 3685509 1.63877 0.19469 0.00231 0.43334 0.51049 2.84349 0.09203 2 2586.7816 1107 1578.48608 8 61348.3741 449 +CotColor ss715579176 phavu.Chr02 3687367 1.63622 0.19518 0.00228 0.13763 0.71072 3.13455 0.07692 2 2574.21379 1119 1573.26736 8 62470.94623 358 +CotColor ss715579177 phavu.Chr02 3689171 0.19161 0.82566 2.6837E-4 0.19343 0.66016 0.18992 0.66306 2 302.74693 1117 1580.03588 8 61413.29265 350 +CotColor ss715579181 phavu.Chr02 3694704 2.53504 0.07971 0.00353 2.68381 0.10165 2.38295 0.12295 2 3970.57773 1115 1566.27532 8 63107.05046 292 +CotColor ss715579183 phavu.Chr02 3698664 1.08397 0.33861 0.00151 0.41252 0.52083 1.75514 0.1855 2 1705.07685 1117 1572.99808 8 62401.27537 454 +CotColor ss715579198 phavu.Chr02 3734020 2.29669 0.10107 0.00321 4.46364 0.03485 0.1332 0.71521 2 3609.41047 1113 1571.57398 8 62460.45616 471 +CotColor ss715579200 phavu.Chr02 3735682 2.10512 0.12232 0.00294 3.96596 0.04667 0.24696 0.61932 2 3303.79124 1113 1569.41017 8 62719.952 472 +CotColor ss7155792phavu.Chr02 01 3737074 2.02498 0.13248 0.00283 3.68582 0.05513 0.36622 0.5452 2 3187.45057 1117 1574.06839 8 62178.46126 89 +CotColor ss715579213 phavu.Chr02 3764966 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1120 1575.31485 6 82342.41402 0 +CotColor ss715579214 phavu.Chr02 3766042 0.78073 0.45832 0.00112 1.24562 0.26463 0.31663 0.57376 2 1244.24335 1102 1593.68227 8 58707.01985 392 +CotColor ss715579216 phavu.Chr02 3767728 0.51413 0.59817 7.2164E-4 1.01363 0.31425 0.01552 0.90086 2 809.42503 1110 1574.36961 8 61967.3671 440 +CotColor ss715579221 phavu.Chr02 3783569 1.76955 0.17089 0.00248 1.19393 0.27477 2.34373 0.12607 2 2788.17258 1115 1575.64044 8 62001.31618 543 +CotColor ss715579222 phavu.Chr02 3788872 0.0364 0.96426 5.0998E-5 0.07165 0.789 0.00121 0.97222 2 57.38074 1114 1576.37139 8 61781.36072 443 +CotColor ss715579224 phavu.Chr02 3791530 0.98178 0.37497 0.00137 1.41933 0.23377 0.54481 0.4606 2 1546.02055 1115 1574.7111 8 62093.57084 544 +CotColor ss715579228 phavu.Chr02 3796366 2.75715 0.0639 0.00385 4.73429 0.02978 0.78095 0.37704 2 4336.22189 1115 1572.7163 8 62330.31198 87 +CotColor ss715579230 phavu.Chr02 3799569 11.18444 1.551E-5 0.01531 21.64232 3.6773E-6 0.73176 0.3925 2 17290.21629 1118 1545.91643 8 66205.73651 69 +CotColor ss715579231 phavu.Chr02 3801245 0.98898 0.37228 0.00138 1.61368 0.20424 0.36518 0.54576 2 1555.7461 1114 1573.0889 8 62230.3574 368 +CotColor ss715579232 phavu.Chr02 3802899 12.35207 4.9428E-6 0.01689 24.01184 1.0985E-6 0.69878 0.40337 2 19077.91394 1118 1544.51102 8 66489.68968 67 +CotColor ss715579233 phavu.Chr02 3804490 2.1572 0.11613 0.00301 1.04088 0.30784 3.27141 0.07077 2 3392.82799 1114 1572.79093 8 62390.36763 545 +CotColor ss715579239 phavu.Chr02 3815743 2.54899 0.07863 0.00362 1.79324 0.18081 3.30092 0.06952 2 3941.24891 1078 1546.19872 8 63986.43576 495 +CotColor ss715579244 phavu.Chr02 3828570 2.55131 0.07843 0.00355 4.55146 0.03311 0.55298 0.45726 2 4007.87064 1118 1570.90568 8 62754.50807 138 +CotColor ss715579246 phavu.Chr02 3829800 1.21842 0.29609 0.00171 2.10687 0.14692 0.33124 0.56505 2 1922.88957 1115 1578.18177 8 61565.66303 539 +CotColor ss715579249 phavu.Chr02 3836502 2.52534 0.08049 0.00351 1.79555 0.18052 3.25152 0.07163 2 3962.83525 1117 1569.22722 8 62901.46028 545 +CotColor ss715579255 phavu.Chr02 3853942 2.05933 0.12802 0.00287 2.21301 0.13713 1.90386 0.16792 2 3234.69737 1114 1570.75128 8 62580.75622 541 +CotColor ss715579256 phavu.Chr02 3859993 1.47963 0.22817 0.00206 2.92406 0.08755 0.03772 0.84605 2 2322.25766 1116 1569.48807 8 62776.11376 532 +CotColor ss715579258 phavu.Chr02 3861711 1.69635 0.18383 0.00236 2.22967 0.13567 1.16269 0.28114 2 2659.93423 1115 1568.03792 8 62907.63768 534 +CotColor ss715579260 phavu.Chr02 3863726 4.13009 0.01633 0.00579 2.76188 0.09682 5.48715 0.01933 2 6490.95209 1110 1571.62348 8 62261.77024 516 +CotColor ss715579268 phavu.Chr02 3871133 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1120 1575.31485 6 82342.41402 0 +CotColor ss715579270 phavu.Chr02 3875155 5.75142 0.00327 0.00799 11.2581 8.1944E-4 0.25229 0.61557 2 9002.03832 1115 1565.18447 8 63515.20129 315 +CotColor ss715579284 phavu.Chr02 3913764 3.29194 0.03754 0.0046 6.58217 0.01043 0.00758 0.93066 2 5171.95214 1113 1571.0951 8 62530.82612 103 +CotColor ss715579286 phavu.Chr02 3917724 3.46387 0.03165 0.00482 6.86484 0.00891 0.06864 0.79338 2 5423.80861 1115 1565.82152 8 63233.87701 99 +CotColor ss715579291 phavu.Chr02 3926357 0.99951 0.36839 0.0014 1.97555 0.16014 0.02521 0.87388 2 1575.63808 1113 1576.40461 8 61818.22113 372 +CotColor ss715579294 phavu.Chr02 3932050 5.63157 0.00369 0.0078 11.24887 8.2341E-4 0.02408 0.87671 2 8804.2834 1118 1563.3804 8 63805.79181 102 +CotColor ss715579295 phavu.Chr02 3937221 1.94632 0.16326 0.00136 1.94632 0.16326 NaN NaN 1 3062.58514 1119 1573.52553 7 71112.87005 5 +CotColor ss715579299 phavu.Chr02 3947231 1.05461 0.34867 0.00147 0.57041 0.45025 1.53854 0.2151 2 1662.39039 1118 1576.30389 8 62008.01654 131 +CotColor ss715579303 phavu.Chr02 3956145 0.82419 0.43886 0.00115 1.32189 0.2505 0.32729 0.56737 2 1299.51066 1116 1576.71061 8 61867.21954 129 +CotColor ss715579313 phavu.Chr02 3986233 3.06518 0.04704 0.0043 4.53783 0.03337 1.59012 0.20757 2 4827.09199 1111 1574.81665 8 61931.52164 335 +CotColor ss715579316 phavu.Chr02 3994984 0.45205 0.63644 6.3507E-4 0.70075 0.40271 0.20384 0.65172 2 714.24411 1114 1580.01653 8 61150.20308 5 +CotColor ss715579324 phavu.Chr02 4015639 2.23998 0.10694 0.00312 3.82095 0.05086 0.66017 0.41667 2 3516.96623 1117 1570.08903 8 62794.11099 200 +CotColor ss715579327 phavu.Chr02 4023442 3.91756 0.02017 0.00548 6.3529 0.01186 1.47947 0.22412 2 6123.45349 1107 1563.07857 8 63284.29973 214 +CotColor ss715579330 phavu.Chr02 4030428 2.90345 0.05525 0.00406 5.69461 0.01718 0.11681 0.73259 2 4567.63446 1114 1573.1747 8 62339.68152 218 +CotColor ss715579332 phavu.Chr02 4034488 3.88442 0.02084 0.00541 5.39133 0.02042 2.3709 0.1239 2 6101.52096 1116 1570.76615 8 62627.40203 223 +CotColor ss715579333 phavu.Chr02 4036276 2.22339 0.10872 0.00311 4.02856 0.04498 0.42033 0.51691 2 3488.58671 1112 1569.03606 8 62541.69869 207 +CotColor ss715579335 phavu.Chr02 4040493 3.08632 0.04606 0.0043 5.98048 0.01462 0.19647 0.65767 2 4842.98722 1116 1569.17741 8 62888.44868 221 +CotColor ss715579338 phavu.Chr02 4042298 0.86 0.42344 0.0012 0.67993 0.40979 1.04004 0.30803 2 1355.11202 1115 1575.72053 8 61953.12573 126 +CotColor ss715579343 phavu.Chr02 4062705 0.67111 0.51135 9.4086E-4 1.22054 0.26949 0.12263 0.72627 2 1059.87996 1115 1579.30543 8 61509.11451 104 +CotColor ss715579345 phavu.Chr02 4067425 0.58887 0.55512 8.2131E-4 0.68685 0.40742 0.49121 0.48353 2 928.18795 1119 1576.20932 8 62059.43977 102 +CotColor ss715579347 phavu.Chr02 4070365 0.86744 0.35187 6.0847E-4 0.86744 0.35187 NaN NaN 1 1370.30264 1116 1579.71245 7 69870.35353 106 +CotColor ss715579351 phavu.Chr02 4075049 1.26937 0.28141 0.00178 0.76549 0.3818 1.77272 0.18332 2 2001.24684 1115 1576.56758 8 61801.14332 102 +CotColor ss715579353 phavu.Chr02 4078329 0.69173 0.50093 9.6878E-4 0.27802 0.5981 1.1054 0.29331 2 1088.71297 1113 1573.90831 8 61979.56404 234 +CotColor ss715579357 phavu.Chr02 4093842 1.19561 0.30291 0.00167 2.26022 0.13302 0.13275 0.71566 2 1881.07731 1117 1573.32306 8 62339.22906 103 +CotColor ss715579360 phavu.Chr02 4101961 0.34422 0.70884 4.816E-4 0.37509 0.54037 0.31359 0.5756 2 543.39458 1117 1578.60321 8 61667.93042 73 +CotColor ss715579362 phavu.Chr02 4105666 0.22199 0.80095 3.1146E-4 0.42607 0.51406 0.0183 0.89243 2 350.30164 1114 1577.9724 8 61444.28467 72 +CotColor ss715579364 phavu.Chr02 4111294 8.49562 2.1785E-4 0.0117 14.54963 1.4398E-4 2.42311 0.11984 2 13212.71113 1118 1555.23828 8 64914.12065 182 +CotColor ss715579365 phavu.Chr02 4112838 7.42516 6.259E-4 0.01023 14.33591 1.6103E-4 0.52054 0.47076 2 11562.47138 1119 1557.20256 8 64718.01063 182 +CotColor ss715579368 phavu.Chr02 4117729 0.27821 0.75719 3.8955E-4 0.30068 0.58357 0.25594 0.61303 2 439.38336 1117 1579.3463 8 61467.06094 72 +CotColor ss715579372 phavu.Chr02 4129901 2.24473 0.10643 0.00313 1.51317 0.21891 2.97361 0.08491 2 3528.09276 1117 1571.72555 8 62601.28442 289 +CotColor ss715579375 phavu.Chr02 4136807 8.51777 2.1313E-4 0.01171 15.11426 1.0714E-4 1.90902 0.16735 2 13238.38614 1119 1554.20718 8 65136.98932 216 +CotColor ss715579377 phavu.Chr02 4140959 16.20472 6.0679E-5 0.01115 16.20472 6.0679E-5 NaN NaN 1 25177.70439 1119 1553.72662 7 74251.54625 208 +CotColor ss715579379 phavu.Chr02 4142416 11.97387 7.1569E-6 0.01639 23.8895 1.169E-6 0.07792 0.78019 2 18511.02671 1118 1545.95217 8 66224.69313 252 +CotColor ss715579385 phavu.Chr02 4164360 10.0078 4.926E-5 0.01389 9.98412 0.00162 9.95082 0.00165 2 15527.33621 1107 1551.52394 8 64732.69148 545 +CotColor ss715579386 phavu.Chr02 4166554 6.53026 0.00152 0.00909 11.93468 5.7181E-4 1.1245 0.28918 2 10180.91195 1108 1559.03554 8 64016.1381 471 +CotColor ss715579400 phavu.Chr02 4208905 0.13845 0.87072 1.9326E-4 0.27516 0.59999 0.00199 0.96444 2 218.4054 1119 1577.47792 8 61881.99413 59 +CotColor ss715579403 phavu.Chr02 4217710 10.41115 3.3139E-5 0.01438 17.52059 3.0664E-5 3.26602 0.071 2 16130.9842 1111 1549.39436 8 65298.257284 +CotColor ss715579408 phavu.Chr02 4230585 7.46319 6.03E-4 0.01037 14.74024 1.3033E-4 0.19677 0.65743 2 11654.86465 1113 1561.64661 8 63711.13243 265 +CotColor ss715579410 phavu.Chr02 4235320 9.25636 1.0304E-4 0.01277 18.31487 2.0331E-5 0.2108 0.64623 2 14379.53348 1115 1553.47629 8 65003.64914 282 +CotColor ss715579412 phavu.Chr02 4241138 1.66887 0.18893 0.00234 0.23666 0.62673 3.10064 0.07854 2 2632.25226 1116 1577.26672 8 61795.15952 415 +CotColor ss715579428 phavu.Chr02 4286525 4.29052 0.01392 0.00596 1.6779 0.19547 6.89427 0.00877 2 6709.21711 1114 1563.73072 8 63498.00163 459 +CotColor ss715579430 phavu.Chr02 4289439 0.18336 0.8325 2.5672E-4 0.0139 0.90616 0.35282 0.55264 2 289.24627 1116 1577.51478 8 61606.27039 428 +CotColor ss715579432 phavu.Chr02 4297976 4.90427 0.00758 0.0068 9.81605 0.00177 0.00125 0.97183 2 7677.71777 1118 1565.51755 8 63498.26726 455 +CotColor ss715579436 phavu.Chr02 4306329 1.62248 0.19788 0.00229 1.49371 0.2219 1.75023 0.18612 2 2560.2531 1108 1577.9893 8 60940.09146 175 +CotColor ss715579441 phavu.Chr02 4312808 1.18016 0.30762 0.00169 2.31534 0.12839 0.04699 0.82843 2 1861.88414 1094 1577.66019 8 60332.52789 182 +CotColor ss715579451 phavu.Chr02 4336306 2.19462 0.11188 0.00308 3.65664 0.0561 0.73348 0.39194 2 3446.39209 1109 1570.37947 8 62280.40311 166 +CotColor ss715579455 phavu.Chr02 4346447 0.31484 0.72997 4.412E-4 0.28189 0.59557 0.34795 0.55539 2 497.19615 1116 1579.21428 8 61425.92676 82 +CotColor ss715579461 phavu.Chr02 4360884 8.69359 1.794E-4 0.01212 15.76165 7.6482E-5 1.61673 0.20382 2 13530.31489 1105 1556.35575 8 64041.19256 133 +CotColor ss715579464 phavu.Chr02 4366543 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715579468 phavu.Chr02 4374694 0.58318 0.55829 8.1713E-4 1.16738 0.28017 3.5184E-5 0.99527 2 919.11635 1113 1576.03738 8 61934.70998 293 +CotColor ss715579472 phavu.Chr02 4387206 1.94521 0.14348 0.0029 1.11525 0.29119 2.77327 0.09615 2 3047.73012 1036 1566.78708 8 59408.10929 298 +CotColor ss715579475 phavu.Chr02 4394511 0.6271 0.53433 8.7669E-4 0.0542 0.81596 1.2 0.27356 2 985.43589 1113 1571.40793 8 62388.21929 303 +CotColor ss715579481 phavu.Chr02 4408827 0.62235 0.53687 8.7329E-4 0.74613 0.38789 0.49891 0.48013 2 981.73104 1113 1577.44753 8 61581.91261 320 +CotColor ss715579489 phavu.Chr02 4432428 0.54147 0.58204 7.5832E-4 0.4078 0.52322 0.67527 0.4114 2 853.83288 1115 1576.86774 8 61714.05374 88 +CotColor ss715579491 phavu.Chr02 4439120 1.16592 0.31202 0.00162 1.76106 0.18476 0.57145 0.44984 2 1835.83627 1119 1574.58707 8 62286.35185 12 +CotColor ss715579493 phavu.Chr02 4442795 1.13984 0.32024 0.00159 1.16616 0.28042 1.1134 0.29157 2 1796.70198 1117 1576.27526 8 61982.38686 9 +CotColor ss715579499 phavu.Chr02 4459620 2.99332 0.05053 0.00421 2.19103 0.1391 3.79009 0.05181 2 4698.20923 1105 1569.56297 8 62339.46067 233 +CotColor ss7155795phavu.Chr02 01 4464958 1.03812 0.35446 0.00145 2.01696 0.15583 0.06096 0.80503 2 1634.9763 1119 1574.94607 8 62236.13686 230 +CotColor ss715579503 phavu.Chr02 4467252 1.08254 0.33909 0.00151 2.13761 0.14401 0.02934 0.86403 2 1700.63227 1117 1570.95861 8 62613.01495 236 +CotColor ss715579505 phavu.Chr02 4473179 1.25793 0.28464 0.00175 2.08754 0.14878 0.42938 0.51243 2 1978.90684 1117 1573.14359 8 62324.12589 229 +CotColor ss715579508 phavu.Chr02 4480272 2.11289 0.12137 0.00294 3.30295 0.06942 0.92307 0.33688 2 3321.32708 1119 1571.93204 8 62657.72455 270 +CotColor ss715579509 phavu.Chr02 4482461 1.40457 0.24591 0.00196 0.80656 0.36933 2.00186 0.15739 2 2210.2889 1117 1573.6388 8 62292.62224 117 +CotColor ss715579515 phavu.Chr02 4496361 10.20381 4.0604E-5 0.01405 20.28878 7.3583E-6 0.13456 0.71382 2 15838.78656 1116 1552.24222 8 65316.99484 459 +CotColor ss715579521 phavu.Chr02 4510338 0.51556 0.59731 7.2046E-4 0.35964 0.54883 0.67158 0.41267 2 813.40231 1118 1577.70504 8 61767.24477 84 +CotColor ss715579526 phavu.Chr02 4536567 0.03479 0.85207 2.4327E-5 0.03479 0.85207 NaN NaN 1 54.89968 1118 1578.01661 7 70353.22488 93 +CotColor ss715579527 phavu.Chr02 4537674 0.00359 0.99642 5.0637E-6 0.0057 0.93986 0.00149 0.96926 2 5.6808 1110 1583.37159 8 60773.26834 364 +CotColor ss715579529 phavu.Chr02 4539492 1.6193 0.1985 0.00226 0.003 0.95635 3.23559 0.07233 2 2542.99889 1114 1570.43484 8 62564.11017 451 +CotColor ss715579530 phavu.Chr02 4540937 0.37942 0.68435 5.3214E-4 0.0156 0.90062 0.74324 0.38881 2 597.72732 1112 1575.37913 8 61833.83629 366 +CotColor ss715579536 phavu.Chr02 4554193 0.67852 0.50758 9.5222E-4 0.70985 0.39968 0.64742 0.42121 2 1068.50737 1111 1574.75809 8 61835.219 100 +CotColor ss715579545 phavu.Chr02 4576595 5.23538 0.00546 0.00725 9.61885 0.00197 0.85318 0.35586 2 8186.32514 1117 1563.65488 8 63809.42916 313 +CotColor ss715579548 phavu.Chr02 4587081 0.31149 0.73242 4.3613E-4 0.40965 0.52228 0.21362 0.64404 2 490.69011 1114 1575.29681 8 61912.15343 3 +CotColor ss715579551 phavu.Chr02 4600480 10.00199 4.9546E-5 0.01387 19.93578 8.8285E-6 0.08469 0.7711 2 15493.80817 1106 1549.07187 8 65120.73448 464 +CotColor ss715579554 phavu.Chr02 4604586 11.00231 1.8553E-5 0.01514 20.78272 5.7136E-6 1.21784 0.27002 2 17008.25885 1112 1545.88023 8 66018.35048 474 +CotColor ss715579559 phavu.Chr02 4617792 7.30709 7.0335E-4 0.01008 14.62686 1.3828E-4 4.1083E-4 0.98383 2 11361.41719 1116 1554.84821 8 64798.22887 554 +CotColor ss715579566 phavu.Chr02 4637369 0.24246 0.78474 3.3849E-4 0.29921 0.58449 0.18593 0.66641 2 382.15398 1118 1576.1602 8 61979.86036 109 +CotColor ss715579567 phavu.Chr02 4639482 1.62848 0.19669 0.00227 2.43872 0.11866 0.81864 0.36577 2 2560.40899 1118 1572.26671 8 62523.28468 464 +CotColor ss715579571 phavu.Chr02 4650267 0.72101 0.48649 0.00101 0.6488 0.42071 0.79333 0.37329 2 1136.18957 1119 1575.83756 8 62111.44017 107 +CotColor ss715579573 phavu.Chr02 4656361 2.05723 0.12829 0.00289 3.65322 0.05622 0.46301 0.49636 2 3245.07826 1113 1577.39982 8 61552.47722 454 +CotColor ss715579575 phavu.Chr02 4657790 1.97125 0.13977 0.00275 3.41432 0.0649 0.52962 0.46692 2 3092.02835 1114 1568.56505 8 62774.595 460 +CotColor ss715579578 phavu.Chr02 4664561 1.51251 0.22081 0.00213 2.97244 0.08497 0.0551 0.81446 2 2383.33271 1110 1575.75128 8 61453.54027 450 +CotColor ss715579585 phavu.Chr02 4676326 4.46299 0.01173 0.00623 7.81965 0.00526 1.10559 0.29327 2 6999.37387 1113 1568.31526 8 62814.15582 259 +CotColor ss715579595 phavu.Chr02 4681959 3.65499 0.02617 0.00507 6.08056 0.01382 1.22818 0.268 2 5729.65982 1119 1567.6276 8 63259.80774 264 +CotColor ss715579600 phavu.Chr02 4684460 4.31331 0.01361 0.00602 6.23919 0.01264 2.37967 0.12321 2 6742.33706 1109 1563.14663 8 63327.40251 367 +CotColor ss715579636 phavu.Chr02 4716654 0.63613 0.52953 8.8773E-4 0.75732 0.38435 0.51528 0.47301 2 1002.50259 1118 1575.92945 8 62085.44027 145 +CotColor ss715579672 phavu.Chr02 4742194 2.7835 0.06226 0.00393 0.00516 0.94273 5.56181 0.01853 2 4372.93356 1102 1571.02254 8 61864.70211 243 +CotColor ss715579689 phavu.Chr02 4748809 0.69037 0.50161 9.701E-4 0.53991 0.46263 0.84091 0.35934 2 1085.41228 1108 1572.21558 8 61963.39799 290 +CotColor ss715579712 phavu.Chr02 4762149 0.02885 0.97157 4.0301E-5 0.02439 0.87594 0.03333 0.85517 2 45.51131 1118 1577.64142 8 61846.19245 288 +CotColor ss715579718 phavu.Chr02 4773298 1.08835 0.33713 0.00152 1.46047 0.22711 0.7166 0.39744 2 1714.41786 1118 1575.24471 8 62201.8361 208 +CotColor ss715579721 phavu.Chr02 4781005 0.99525 0.36996 0.00139 1.95763 0.16204 0.03457 0.85253 2 1568.84414 1118 1576.32394 8 61996.93199 204 +CotColor ss715579727 phavu.Chr02 4789563 12.37301 4.8455E-6 0.01702 24.52455 8.4711E-7 0.23825 0.62557 2 19133.22578 1113 1546.36771 8 65887.07534 259 +CotColor ss715579734 phavu.Chr02 4798023 10.54811 2.8956E-5 0.01451 20.72583 5.8799E-6 0.38185 0.53674 2 16361.87911 1117 1551.16685 8 65402.91658 263 +CotColor ss715579736 phavu.Chr02 4799896 10.92867 1.9933E-5 0.01502 20.52759 6.5091E-6 1.32381 0.25016 2 16939.4578 1117 1550.00119 8 65608.62799 274 +CotColor ss715579784 phavu.Chr02 4830227 2.70274 0.10046 0.00188 2.70274 0.10046 NaN NaN 1 4250.49616 1120 1572.66438 7 71267.0912 3 +CotColor ss715579835 phavu.Chr02 4857737 4.27874 0.01409 0.00594 7.0528 0.00803 1.50151 0.2207 2 6688.97463 1115 1563.30452 8 63590.39794 246 +CotColor ss715579844 phavu.Chr02 4862397 3.7567 0.02366 0.00522 7.46404 0.00639 0.05566 0.81353 2 5892.07616 1118 1568.41821 8 63105.54188 207 +CotColor ss715579846 phavu.Chr02 4863611 3.62344 0.027 0.00503 7.11811 0.00774 0.13428 0.7141 2 5673.55847 1118 1565.7931 8 63396.1743 207 +CotColor ss715579850 phavu.Chr02 4865275 4.75812 0.00876 0.00661 9.06698 0.00266 0.45369 0.50073 2 7451.4761 1117 1566.05516 8 63378.59768 206 +CotColor ss715579855 phavu.Chr02 4868612 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1098 1604.74476 6 74490.31044 0 +CotColor ss715579885 phavu.Chr02 4888243 6.37472 0.00177 0.0088 12.72737 3.7557E-4 0.03306 0.85576 2 9945.15905 1119 1560.0932 8 64313.68254 188 +CotColor ss715579892 phavu.Chr02 4893026 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715579900 phavu.Chr02 4895615 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715579934 phavu.Chr02 4934180 3.75825 0.02362 0.00523 7.38192 0.00669 0.14026 0.70809 2 5894.08406 1116 1568.30678 8 62941.39271 247 +CotColor ss715579935 phavu.Chr02 4937188 5.98623 0.00259 0.00832 11.88342 5.875E-4 0.09866 0.7535 2 9358.39838 1113 1563.3199 8 63554.80665 232 +CotColor ss715579947 phavu.Chr02 4948161 2.11175 0.12151 0.00295 3.80818 0.05125 0.4173 0.51842 2 3318.69677 1115 1571.54206 8 62500.42067 270 +CotColor ss715579960 phavu.Chr02 4955131 6.19261 0.00212 0.00857 12.28838 4.7385E-4 0.10666 0.74405 2 9666.05486 1117 1560.90251 8 64101.24535 236 +CotColor ss715579984 phavu.Chr02 4965066 7.21135 7.7328E-4 0.01005 12.75204 3.7085E-4 1.66305 0.19746 2 11252.46999 1109 1560.38252 8 63680.29669 228 +CotColor ss715579999 phavu.Chr02 4976569 2.49211 0.0832 0.00348 4.55572 0.03303 0.43082 0.51172 2 3914.82111 1115 1570.8866 8 62598.29846 265 +CotColor ss715580002 phavu.Chr02 4978353 2.32076 0.09867 0.00323 4.64548 0.03135 1.9293E-4 0.98892 2 3645.58906 1116 1570.86275 8 62609.4251 265 +CotColor ss715580026 phavu.Chr02 4993000 1.63824 0.19479 0.00229 2.84701 0.09182 0.43091 0.51168 2 2576.76709 1116 1572.89144 8 62413.13689 273 +CotColor ss715580033 phavu.Chr02 4997757 1.87337 0.15409 0.00261 3.74707 0.05315 0.00301 0.95623 2 2948.69671 1118 1574.0028 8 62329.59312 266 +CotColor ss715580049 phavu.Chr02 5013686 37.54334 1.6691E-16 0.0494 68.65834 3.3388E-16 6.11266 0.01357 2 55464.68061 1111 1477.35083 8 75503.94413 192 +CotColor ss71558phavu.Chr0215 01 5066554 6.78911 0.00117 0.0095 10.66165 0.00113 2.89808 0.08897 2 10491.61938 1094 1545.36093 8 64786.57748 237 +CotColor ss71558phavu.Chr0291 01 5132918 8.97971 1.3526E-4 0.01238 17.86056 2.5707E-5 0.11303 0.73679 2 13966.41594 1117 1555.33098 8 64832.79947 232 +CotColor ss71558phavu.Chr0295 01 5137766 11.35623 1.3109E-5 0.01554 22.2587 2.6843E-6 0.46444 0.4957 2 17507.51547 1116 1541.66579 8 66620.42349 412 +CotColor ss715580207 phavu.Chr02 5149033 13.72968 1.2868E-6 0.01882 25.07219 6.4141E-7 2.35669 0.12503 2 21192.00794 1115 1543.51846 8 66433.01372 410 +CotColor ss715580229 phavu.Chr02 5166143 12.98994 2.6494E-6 0.01773 25.93446 4.1425E-7 0.06707 0.7957 2 20006.30315 1117 1540.13811 8 67004.19397 417 +CotColor ss715580248 phavu.Chr02 5181297 13.09138 2.3994E-6 0.0179 25.67815 4.7166E-7 0.51574 0.47282 2 20192.66167 1117 1542.43954 8 66595.07252 417 +CotColor ss715580278 phavu.Chr02 5200744 12.30201 5.1916E-6 0.01684 24.62602 8.0417E-7 1.986E-5 0.99645 2 19005.26869 1117 1544.89192 8 66363.42775 218 +CotColor ss715580285 phavu.Chr02 5203935 11.1357 1.628E-5 0.01531 22.25884 2.6855E-6 0.03193 0.85822 2 17184.75791 1111 1543.21364 8 66342.2424 405 +CotColor ss715580293 phavu.Chr02 5206197 11.83881 8.1697E-6 0.01622 23.61699 1.343E-6 0.08008 0.77725 2 18303.58222 1117 1546.06543 8 66163.80502 216 +CotColor ss715580296 phavu.Chr02 5207243 13.94009 1.0484E-6 0.01914 27.89432 1.5403E-7 0.01066 0.9178 2 21495.58863 1112 1541.99766 8 66395.047208 +CotColor ss715580319 phavu.Chr02 5215085 11.83478 8.2011E-6 0.01621 23.68114 1.2997E-6 0.00939 0.92283 2 18302.61626 1118 1546.51058 8 66154.49188 218 +CotColor ss715580323 phavu.Chr02 5219984 12.77074 3.2824E-6 0.01749 25.38363 5.4757E-7 0.17655 0.67443 2 19735.26555 1117 1545.35006 8 66249.79809 205 +CotColor ss715580324 phavu.Chr02 5222865 11.77481 8.7009E-6 0.01615 23.54047 1.3969E-6 0.02965 0.86332 2 18172.47211 1114 1543.33405 8 66379.250213 +CotColor ss715580327 phavu.Chr02 5224516 11.42628 1.224E-5 0.01568 22.29973 2.6289E-6 0.56159 0.45378 2 17662.5699 1115 1545.78477 8 66090.759221 +CotColor ss715580349 phavu.Chr02 5251363 11.72531 9.1282E-6 0.01603 23.09088 1.7552E-6 0.37268 0.54167 2 18121.23352 1119 1545.48002 8 66357.70116 449 +CotColor ss715580351 phavu.Chr02 5252546 11.72256 9.1548E-6 0.01606 23.41731 1.4867E-6 0.04775 0.82707 2 18108.83119 1117 1544.78493 8 66283.95021 443 +CotColor ss715580360 phavu.Chr02 5261279 11.46831 1.175E-5 0.0158 22.9051 1.9311E-6 0.05105 0.82129 2 17748.68722 1112 1547.62874 8 65748.33277 437 +CotColor ss715580362 phavu.Chr02 5265412 11.60055 1.0315E-5 0.01587 22.67915 2.1652E-6 0.53143 0.46616 2 17932.32886 1119 1545.81765 8 66310.475183 +CotColor ss715580393 phavu.Chr02 5286729 0.66659 0.51366 9.3158E-4 0.79593 0.3725 0.53757 0.4636 2 1049.89747 1116 1575.03779 8 62033.20074 319 +CotColor ss715580423 phavu.Chr02 5313416 10.98815 1.8807E-5 0.01509 21.97638 3.1009E-6 0.01924 0.88971 2 17001.15937 1115 1547.22659 8 65943.54676 443 +CotColor ss715580437 phavu.Chr02 5338663 0.52979 0.58888 7.3991E-4 0.08309 0.77321 0.97649 0.32328 2 834.88922 1117 1575.88971 8 62057.753 4 +CotColor ss715580526 phavu.Chr02 5434257 11.07332 1.7295E-5 0.01518 18.70002 1.6662E-5 3.40639 0.06521 2 17138.50813 1118 1547.73037 8 66039.06722 144 +CotColor ss715580578 phavu.Chr02 5466375 1.49997 0.22359 0.00209 2.91976 0.08778 0.08258 0.77388 2 2358.89179 1118 1572.624 8 62474.04482 557 +CotColor ss715580702 phavu.Chr02 5568956 16.59071 7.9394E-8 0.02254 33.09134 1.1344E-8 0.11626 0.73319 2 25410.70097 1116 1531.62256 8 68131.90019 534 +CotColor ss715580735 phavu.Chr02 5611496 9.34533 9.4397E-5 0.01284 17.79575 2.6583E-5 0.89655 0.34391 2 14485.02227 1117 1549.97441 8 65606.55606 129 +CotColor ss715580736 phavu.Chr02 5647261 8.72915 1.7313E-4 0.01205 17.47287 3.1425E-5 0.00107 0.97385 2 13558.07376 1114 1553.19556 8 65077.73679 125 +CotColor ss715580740 phavu.Chr02 5729824 0.32392 0.72338 4.5366E-4 0.64151 0.42333 0.0069 0.93382 2 511.41778 1116 1578.83436 8 61581.24845 317 +CotColor ss715580742 phavu.Chr02 5790654 8.07537 3.3002E-4 0.01151 15.35294 9.4709E-5 0.8006 0.37111 2 12718.53502 1094 1574.9781 8 60898.11297 238 +CotColor ss715580746 phavu.Chr02 5901905 7.62415 5.1434E-4 0.01051 14.27815 1.66E-4 0.97054 0.32476 2 11869.48514 1118 1556.82651 8 64775.97244 122 +CotColor ss715580747 phavu.Chr02 5949964 3.03513 0.04847 0.00423 5.98576 0.01458 0.08937 0.76504 2 4768.32681 1117 1571.04728 8 62711.48439 290 +CotColor ss715580749 phavu.Chr02 5987459 12.14754 6.0457E-6 0.01678 21.33027 4.3185E-6 2.9277 0.08735 2 18767.20442 1107 1544.93856 8 65744.76286 224 +CotColor ss715580752 phavu.Chr02 6052262 13.69193 1.3375E-6 0.0189 25.55144 5.039E-7 1.81358 0.17836 2 21054.5884 1103 1537.73726 8 66477.25669 215 +CotColor ss715580754 phavu.Chr02 6106278 15.48782 2.3186E-7 0.02108 28.43015 1.1746E-7 2.50721 0.11361 2 23798.33829 1118 1536.58387 8 67498.75065 211 +CotColor ss715580756 phavu.Chr02 6150550 13.85853 1.1351E-6 0.019 26.14226 3.7311E-7 1.56162 0.21169 2 21367.69368 1113 1541.84438 8 66587.386211 +CotColor ss715580757 phavu.Chr02 6193699 9.14376 1.1514E-4 0.01265 18.15884 2.2042E-5 0.14266 0.70572 2 14198.43693 1112 1552.80114 8 64840.29739 109 +CotColor ss715580758 phavu.Chr02 6241916 9.50177 8.0983E-5 0.01322 15.37645 9.3479E-5 3.59119 0.05835 2 14802.37463 1109 1557.855 8 64014.47323 109 +CotColor ss715580759 phavu.Chr02 6285536 12.68396 3.574E-6 0.01736 25.11 6.2922E-7 0.27424 0.6006 2 19556.12924 1115 1541.80056 8 66665.61652 218 +CotColor ss715580763 phavu.Chr02 6389953 9.24448 1.0423E-4 0.01272 18.49934 1.848E-5 0.00606 0.93799 2 14361.54349 1118 1553.52629 8 65165.87258 109 +CotColor ss715580764 phavu.Chr02 6428467 9.48338 8.2402E-5 0.01302 18.95264 1.4626E-5 0.03053 0.86133 2 14714.12338 1119 1551.56958 8 65505.92363 109 +CotColor ss715580766 phavu.Chr02 6512082 13.51026 1.5939E-6 0.01851 25.52844 5.0889E-7 1.48109 0.22386 2 20863.81957 1116 1544.2945 8 66372.23689 214 +CotColor ss715580767 phavu.Chr02 6538586 1.35161 0.25925 0.00188 2.34493 0.12597 0.35962 0.54884 2 2127.51731 1119 1574.06575 8 62359.27211 2 +CotColor ss715580769 phavu.Chr02 6613885 10.47975 3.0987E-5 0.01451 20.83391 5.5672E-6 0.14171 0.70666 2 16252.5685 1109 1550.85409 8 65020.87081 113 +CotColor ss715580770 phavu.Chr02 6637139 9.57861 7.5047E-5 0.01317 18.73475 1.6367E-5 0.432 0.51114 2 14866.10975 1117 1552.01053 8 65433.67537 108 +CotColor ss715580771 phavu.Chr02 6665846 13.55072 1.5326E-6 0.01852 27.04947 2.3583E-7 0.07443 0.78504 2 20826.89604 1114 1536.95845 8 67182.26326 28 +CotColor ss715580772 phavu.Chr02 6708220 6.95119 9.9943E-4 0.0096 13.80049 2.1332E-4 0.11282 0.73702 2 10842.93824 1118 1559.86866 8 64285.69156 182 +CotColor ss715580773 phavu.Chr02 6738583 6.0851 0.01378 0.00423 6.0851 0.01378 NaN NaN 1 9542.94703 1119 1568.24771 7 71959.182 71 +CotColor ss715580774 phavu.Chr02 6767604 1.80316 0.16526 0.00251 2.52422 0.11239 1.08191 0.2985 2 2833.03554 1118 1571.15282 8 62666.58793 196 +CotColor ss715580776 phavu.Chr02 6849198 0.18902 0.82779 2.6382E-4 0.00296 0.9566 0.37509 0.54037 2 298.15387 1119 1577.33539 8 61901.93125 125 +CotColor ss715580779 phavu.Chr02 6917089 0.62093 0.53763 8.6786E-4 0.00568 0.93992 1.23618 0.26645 2 979.1817 1117 1576.95231 8 61886.25424 123 +CotColor ss715580781 phavu.Chr02 6994835 19.82704 9.3263E-6 0.0136 19.82704 9.3263E-6 NaN NaN 1 30718.5163 1119 1549.32406 7 74984.26333 192 +CotColor ss715580784 phavu.Chr02 7071117 8.73419 1.7226E-4 0.01206 11.90219 5.8163E-4 5.518 0.019 2 13576.7112 1115 1554.43274 8 64824.12385 310 +CotColor ss715580785 phavu.Chr02 7097380 4.77524 0.00861 0.00662 8.43488 0.00375 1.11474 0.29128 2 7472.39082 1118 1564.81874 8 63639.51358 66 +CotColor ss715580787 phavu.Chr02 7139371 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715580788 phavu.Chr02 7167156 6.86464 0.00109 0.00949 12.13729 5.1336E-4 1.58562 0.20822 2 10703.90253 1117 1559.2815 8 64351.43682 242 +CotColor ss715580789 phavu.Chr02 7243107 9.36096 9.2944E-5 0.01285 17.33431 3.3752E-5 1.3817 0.24006 2 14527.30942 1119 1551.90347 8 65459.22013 246 +CotColor ss715580790 phavu.Chr02 7320074 4.59058 0.03236 0.00319 4.59058 0.03236 NaN NaN 1 7199.77701 1119 1568.37985 7 71853.29703 76 +CotColor ss715580792 phavu.Chr02 7374320 5.12217 0.02381 0.00356 5.12217 0.02381 NaN NaN 1 8038.13827 1120 1569.28256 7 71808.18293 72 +CotColor ss715580793 phavu.Chr02 7396867 5.0067 0.02545 0.00349 5.0067 0.02545 NaN NaN 1 7863.37322 1118 1570.57048 7 71490.87463 72 +CotColor ss715580795 phavu.Chr02 7439327 6.97492 0.00838 0.00484 6.97492 0.00838 NaN NaN 1 10929.76129 1119 1567.00838 7 72157.29832 83 +CotColor ss715580796 phavu.Chr02 7477081 5.10118 0.0241 0.00355 5.10118 0.0241 NaN NaN 1 8012.15097 1119 1570.6464 7 71516.5778 72 +CotColor ss715580797 phavu.Chr02 7504231 4.32041 0.01352 0.00602 6.57105 0.0105 2.06351 0.15114 2 6766.60719 1114 1566.1955 8 63120.35316 72 +CotColor ss715580798 phavu.Chr02 7536147 0.59948 0.54927 8.4254E-4 0.36708 0.54473 0.83194 0.36191 2 946.25524 1112 1578.45732 8 61368.66675 374 +CotColor ss715580799 phavu.Chr02 7572396 6.5868 0.0104 0.00457 6.5868 0.0104 NaN NaN 1 10325.12384 1119 1567.54872 7 72070.92154 72 +CotColor ss7155808phavu.Chr02 01 7627267 0.8216 0.43999 0.00115 1.00169 0.31712 0.64183 0.42322 2 1294.03755 1118 1575.02193 8 62178.50025 373 +CotColor ss715580802 phavu.Chr02 7657957 0.85798 0.4243 0.0012 1.49066 0.22237 0.22633 0.63435 2 1349.81624 1115 1573.25614 8 62113.06044 372 +CotColor ss715580803 phavu.Chr02 7687334 2.26767 0.10403 0.00316 3.74123 0.05334 0.79479 0.37285 2 3567.92985 1117 1573.39366 8 62359.99652 99 +CotColor ss715580804 phavu.Chr02 7711214 3.66548 0.05581 0.00255 3.66548 0.05581 NaN NaN 1 5761.6448 1119 1571.86757 7 71305.57575 100 +CotColor ss715580807 phavu.Chr02 7803900 7.57186 0.00602 0.00525 7.57186 0.00602 NaN NaN 1 11866.63551 1119 1567.20182 7 72067.21845 71 +CotColor ss715580808 phavu.Chr02 7845924 4.02991 0.04494 0.00281 4.02991 0.04494 NaN NaN 1 6337.79439 1116 1572.68765 7 71120.13561 99 +CotColor ss715580812 phavu.Chr02 7949304 4.14293 0.04204 0.00288 4.14293 0.04204 NaN NaN 1 6507.08775 1120 1570.64957 7 71589.46143 99 +CotColor ss715580813 phavu.Chr02 7985538 7.59853 0.00594 0.00526 7.59853 0.00594 NaN NaN 1 11898.06014 1120 1565.8362 7 72359.60034 71 +CotColor ss715580814 phavu.Chr02 8029741 4.53233 0.03348 0.00315 4.53233 0.03348 NaN NaN 1 7117.1554 1119 1570.30771 7 71587.36751 75 +CotColor ss715580816 phavu.Chr02 8075001 2.70274 0.10046 0.00188 2.70274 0.10046 NaN NaN 1 4250.49616 1120 1572.66438 7 71267.0912 3 +CotColor ss715580819 phavu.Chr02 8145490 2.54486 0.07894 0.00354 4.18662 0.04098 0.90346 0.34206 2 3997.27146 1119 1570.72392 8 62826.71064 100 +CotColor ss715580820 phavu.Chr02 8191754 6.82207 0.00912 0.00473 6.82207 0.00912 NaN NaN 1 10691.69983 1119 1567.22112 7 72123.28954 73 +CotColor ss715580824 phavu.Chr02 8285638 6.96875 0.00841 0.00483 6.96875 0.00841 NaN NaN 1 10918.01567 1120 1566.71124 7 72219.59399 73 +CotColor ss715580829 phavu.Chr02 8387058 0.20472 0.81491 2.8572E-4 0.27046 0.60313 0.13918 0.70917 2 322.89654 1119 1577.29117 8 61908.11692 100 +CotColor ss715580830 phavu.Chr02 8433079 1.23686 0.29069 0.00174 0.53405 0.46506 1.93923 0.16403 2 1953.53848 1115 1579.42894 8 61242.92074 94 +CotColor ss715580832 phavu.Chr02 8533425 2.94487 0.05302 0.0041 5.56167 0.01853 0.3314 0.56495 2 4603.72947 1113 1563.30596 8 63379.03515 365 +CotColor ss715580834 phavu.Chr02 8588978 0.2811 0.75501 3.9298E-4 0.04093 0.83971 0.52129 0.47044 2 443.70537 1118 1578.45735 8 61677.08703 138 +CotColor ss715580836 phavu.Chr02 8648807 0.72463 0.39481 5.0504E-4 0.72463 0.39481 NaN NaN 1 1140.58239 1119 1574.02629 7 71006.33892 103 +CotColor ss715580838 phavu.Chr02 8687918 0.92005 0.3988 0.00129 1.14626 0.28456 0.69415 0.40493 2 1449.87841 1114 1575.86803 8 61844.25701 115 +CotColor ss715580839 phavu.Chr02 8720452 7.21492 7.7076E-4 0.01011 13.194 2.9375E-4 1.23305 0.26706 2 11264.7213 1103 1561.30974 8 63314.75544 376 +CotColor ss715580840 phavu.Chr02 8768272 7.89737 3.9282E-4 0.01087 15.72632 7.7843E-5 0.08132 0.77557 2 12287.56789 1119 1555.90659 8 64899.28475 417 +CotColor ss715580842 phavu.Chr02 8797083 9.68673 6.7479E-5 0.01332 16.46681 5.2951E-5 2.87899 0.09002 2 15025.82574 1117 1551.17579 8 65455.78438 419 +CotColor ss715580843 phavu.Chr02 8839942 9.28553 1.0013E-4 0.01277 15.92143 7.0333E-5 2.62643 0.10538 2 14379.98432 1115 1548.64364 8 65597.34904 422 +CotColor ss715580844 phavu.Chr02 8869123 7.64784 5.0248E-4 0.01056 15.00304 1.1357E-4 0.302 0.58274 2 11916.19616 1117 1558.11326 8 64528.25308 417 +CotColor ss715580845 phavu.Chr02 8892601 1.0701 0.34332 0.0015 2.03859 0.15363 0.10325 0.74802 2 1686.87647 1117 1576.36685 8 61962.91427 103 +CotColor ss715580848 phavu.Chr02 8979855 0.0443 0.83333 3.095E-5 0.0443 0.83333 NaN NaN 1 69.89037 1119 1577.5931 7 70406.48314 99 +CotColor ss715580849 phavu.Chr02 9012496 0.12249 0.72642 8.596E-5 0.12249 0.72642 NaN NaN 1 192.88679 1112 1574.73684 7 70399.86006 97 +CotColor ss715580850 phavu.Chr02 9062511 0.50327 0.60469 7.0203E-4 0.95195 0.32943 0.05539 0.81399 2 793.37805 1119 1576.45027 8 62025.73729 122 +CotColor ss715580852 phavu.Chr02 9088305 0.05051 0.82221 3.5268E-5 0.05051 0.82221 NaN NaN 1 79.64828 1119 1576.73954 7 70568.86249 108 +CotColor ss715580856 phavu.Chr02 9193606 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715580857 phavu.Chr02 9219416 0.20578 0.81404 2.8878E-4 0.08223 0.77435 0.32938 0.56614 2 324.99898 1114 1579.34031 8 61427.16267 92 +CotColor ss715580858 phavu.Chr02 9256932 0.17152 0.67884 1.1961E-4 0.17152 0.67884 NaN NaN 1 270.35504 1120 1576.21808 7 70698.49961 104 +CotColor ss715580860 phavu.Chr02 9284428 0.76437 0.46587 0.00107 0.14197 0.7064 1.38672 0.23921 2 1203.35097 1117 1574.30075 8 62176.08016 105 +CotColor ss715580863 phavu.Chr02 9313902 0.77633 0.37845 5.4086E-4 0.77633 0.37845 NaN NaN 1 1218.76853 1117 1569.90465 7 71402.77304 97 +CotColor ss715580864 phavu.Chr02 9354998 0.89642 0.34395 6.2651E-4 0.89642 0.34395 NaN NaN 1 1413.69063 1118 1577.04461 7 70471.45967 94 +CotColor ss715580865 phavu.Chr02 9410680 0.55604 0.57364 7.7683E-4 0.88744 0.34637 0.22525 0.63516 2 877.22931 1118 1577.64388 8 61834.32314 110 +CotColor ss715580867 phavu.Chr02 9489743 0.81142 0.36789 5.6641E-4 0.81142 0.36789 NaN NaN 1 1279.36113 1119 1576.68996 7 70629.0963 1 +CotColor ss715580868 phavu.Chr02 9532415 0.68902 0.40667 4.827E-4 0.68902 0.40667 NaN NaN 1 1088.07009 1117 1579.14704 7 70030.62132 94 +CotColor ss715580874 phavu.Chr02 9679501 0.62226 0.43038 4.3516E-4 0.62226 0.43038 NaN NaN 1 981.83055 1118 1577.85832 7 70315.00857 103 +CotColor ss715580876 phavu.Chr02 9707810 0.22511 0.63526 1.5698E-4 0.22511 0.63526 NaN NaN 1 354.80818 1120 1576.14268 7 70710.56435 107 +CotColor ss715580877 phavu.Chr02 9736511 0.31541 0.72955 4.4012E-4 0.15822 0.69088 0.47267 0.4919 2 497.39461 1119 1576.97928 8 61951.74143 105 +CotColor ss715580879 phavu.Chr02 9763709 1.11499 0.32828 0.00156 2.02641 0.15486 0.20501 0.65079 2 1758.74478 1117 1577.36267 8 61751.23423 18 +CotColor ss715580882 phavu.Chr02 9822309 0.65722 0.41772 4.5812E-4 0.65722 0.41772 NaN NaN 1 1035.47328 1120 1575.53494 7 70807.80222 111 +CotColor ss715580886 phavu.Chr02 9893154 0.05199 0.81968 3.6257E-5 0.05199 0.81968 NaN NaN 1 81.94994 1120 1576.3863 7 70671.5846 99 +CotColor ss715580888 phavu.Chr02 9976662 1.7657 0.18419 0.00123 1.7657 0.18419 NaN NaN 1 2781.5447 1119 1575.32076 7 70769.34833 113 +CotColor ss715580889 phavu.Chr02 10015821 1.28788 0.27627 0.0018 2.56065 0.10984 0.01736 0.89521 2 2028.81853 1116 1575.32135 8 62007.60776 120 +CotColor ss7155784phavu.Chr02 01 10064358 0.11947 0.8874 1.6881E-4 0.04108 0.83943 0.1979 0.65651 2 188.99107 1109 1581.86448 8 60598.77964 104 +CotColor ss715578402 phavu.Chr02 10100934 0.47771 0.62033 6.6877E-4 0.11485 0.73476 0.84059 0.35943 2 754.42528 1117 1579.25337 8 61515.09006 109 +CotColor ss715578404 phavu.Chr02 10123413 0.15951 0.85258 2.2309E-4 0.02961 0.8634 0.28943 0.59069 2 251.84111 1118 1578.7991 8 61584.99167 104 +CotColor ss715578409 phavu.Chr02 10226830 1.59589 0.20319 0.00225 0.24328 0.62194 2.94806 0.08626 2 2522.06417 1108 1580.35181 8 60970.62242 98 +CotColor ss715578410 phavu.Chr02 10286784 0.27495 0.60013 1.9229E-4 0.27495 0.60013 NaN NaN 1 433.93654 1118 1578.22442 7 70315.72996 109 +CotColor ss715578411 phavu.Chr02 10323008 0.14833 0.70021 1.0344E-4 0.14833 0.70021 NaN NaN 1 233.80795 1120 1576.25071 7 70693.2786 110 +CotColor ss715578413 phavu.Chr02 10475713 0.00753 0.93089 5.5499E-6 0.00753 0.93089 NaN NaN 1 12.02553 1077 1598.0384 7 63671.46321 5 +CotColor ss715578417 phavu.Chr02 10565577 0.16877 0.68129 1.1838E-4 0.16877 0.68129 NaN NaN 1 266.41439 1115 1578.56352 7 70059.94045 108 +CotColor ss715578419 phavu.Chr02 10642573 0.49375 0.61046 6.8876E-4 0.95874 0.32771 0.02959 0.86345 2 778.39034 1119 1576.47706 8 62021.99037 2 +CotColor ss715578420 phavu.Chr02 10689836 0.04148 0.83865 2.901E-5 0.04148 0.83865 NaN NaN 1 65.45861 1118 1578.04692 7 70305.01351 104 +CotColor ss715578422 phavu.Chr02 10741865 0.07743 0.92549 1.082E-4 0.14082 0.70754 0.01417 0.90526 2 122.18128 1118 1577.91437 8 61787.35583 110 +CotColor ss715578423 phavu.Chr02 10781745 0.1482 0.70034 1.0386E-4 0.1482 0.70034 NaN NaN 1 233.94364 1116 1578.61676 7 70114.58173 109 +CotColor ss715578424 phavu.Chr02 10813099 6.39123 0.00174 0.00891 12.7622 3.6887E-4 0.0314 0.85938 2 9978.12285 1108 1561.22183 8 63635.47795 175 +CotColor ss715578426 phavu.Chr02 10839867 0.18997 0.82701 2.6562E-4 0.08153 0.77529 0.29847 0.58495 2 299.90944 1118 1578.68127 8 61650.46406 101 +CotColor ss715578427 phavu.Chr02 10895999 1.801 0.17987 0.00129 1.801 0.17987 NaN NaN 1 2814.45202 1083 1562.71844 7 69768.46927 2 +CotColor ss715578429 phavu.Chr02 10981282 2.91688 0.05451 0.00408 5.31613 0.02131 0.51992 0.47103 2 4593.12419 1115 1574.6682 8 61927.21654 40 +CotColor ss715578432 phavu.Chr02 11088014 2.84406 0.05861 0.00396 4.34346 0.03738 1.34332 0.2467 2 4464.30769 1116 1569.69493 8 62704.3851 114 +CotColor ss715578434 phavu.Chr02 11114270 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578436 phavu.Chr02 11166516 5.15881 0.00589 0.00717 4.38339 0.03652 5.91494 0.01517 2 8084.36178 1116 1567.09928 8 63140.64261 122 +CotColor ss715578437 phavu.Chr02 11202606 2.1619 0.11559 0.00301 3.87104 0.04937 0.45465 0.50027 2 3398.9602 1117 1572.20854 8 62559.02499 122 +CotColor ss715578438 phavu.Chr02 11279779 2.15391 0.11651 0.00301 1.62976 0.202 2.6756 0.10218 2 3385.91863 1115 1571.98977 8 62484.84966 111 +CotColor ss715578439 phavu.Chr02 11302991 0.60303 0.54733 8.4177E-4 0.94624 0.33089 0.26044 0.60992 2 949.62075 1117 1574.75031 8 62155.76879 111 +CotColor ss715578441 phavu.Chr02 11463541 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578443 phavu.Chr02 11615072 6.50004 0.00156 0.00905 8.02395 0.0047 4.94765 0.02633 2 10159.08739 1111 1562.92615 8 63456.88887 64 +CotColor ss715578444 phavu.Chr02 11718596 3.74945 0.02383 0.00522 6.71834 0.00967 0.78188 0.37676 2 5883.39341 1117 1569.13378 8 62918.07369 42 +CotColor ss715578446 phavu.Chr02 11872068 2.23867 0.13488 0.00156 2.23867 0.13488 NaN NaN 1 3521.89984 1119 1573.21116 7 71084.47627 44 +CotColor ss715578448 phavu.Chr02 11941473 3.73973 0.02406 0.0052 1.29473 0.25542 6.17874 0.01307 2 5870.57225 1117 1569.78311 8 62865.41716 102 +CotColor ss715578449 phavu.Chr02 11988400 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1120 1575.31485 6 82342.41402 0 +CotColor ss715578452 phavu.Chr02 12260888 4.97722 0.00705 0.00692 7.56459 0.00605 2.3805 0.12314 2 7800.16933 1116 1567.17335 8 63179.70257 72 +CotColor ss715578467 phavu.Chr02 12937837 6.04471 0.00245 0.00839 7.82216 0.00525 4.24452 0.03961 2 9452.31616 1115 1563.73443 8 63662.51389 68 +CotColor ss715578471 phavu.Chr02 13099009 0.81163 0.36783 5.6764E-4 0.81163 0.36783 NaN NaN 1 1280.6442 1118 1577.86757 7 70287.55918 1 +CotColor ss715578479 phavu.Chr02 13365532 0.63663 0.52927 8.8813E-4 0.01959 0.88872 1.25366 0.2631 2 1002.78298 1118 1575.15368 8 62144.68388 112 +CotColor ss715578480 phavu.Chr02 13411319 3.21582 0.04049 0.00449 1.21245 0.27109 5.21461 0.02259 2 5051.68801 1115 1570.88832 8 62613.28306 96 +CotColor ss715578608 phavu.Chr02 14138718 0.81305 0.36741 5.6787E-4 0.81305 0.36741 NaN NaN 1 1281.20942 1118 1575.80645 7 70630.41165 1 +CotColor ss715578606 phavu.Chr02 14193902 0.84583 0.42948 0.00118 1.48893 0.22264 0.20379 0.65177 2 1335.48595 1116 1578.90728 8 61570.14661 104 +CotColor ss715578604 phavu.Chr02 14237127 0.8119 0.36775 5.6748E-4 0.8119 0.36775 NaN NaN 1 1278.90312 1117 1575.18998 7 70596.15756 1 +CotColor ss715578596 phavu.Chr02 14460880 0.80233 0.44854 0.00112 1.26213 0.26149 0.34328 0.55806 2 1265.111 1115 1576.79104 8 61843.4769 105 +CotColor ss715578594 phavu.Chr02 14522196 1.91134 0.16709 0.00133 1.91134 0.16709 NaN NaN 1 3006.85809 1118 1573.1671 7 71117.74453 1 +CotColor ss715578592 phavu.Chr02 14599403 0.81406 0.36712 5.6757E-4 0.81406 0.36712 NaN NaN 1 1281.58202 1119 1574.31264 7 70908.06369 1 +CotColor ss715578590 phavu.Chr02 14664794 2.08389 0.12493 0.00291 1.58213 0.20872 2.58341 0.10827 2 3279.38042 1115 1573.68201 8 62212.65606 102 +CotColor ss715578586 phavu.Chr02 14901290 0.90525 0.40474 0.00127 0.69985 0.40301 1.11059 0.29218 2 1427.37512 1114 1576.76846 8 61673.65452 92 +CotColor ss715639143 phavu.Chr02 15183989 5.39291 0.00467 0.00746 1.01611 0.31366 9.76175 0.00183 2 8428.06182 1119 1562.80472 8 63934.40824 100 +CotColor ss715578627 phavu.Chr02 16863260 0.80766 0.36901 5.6401E-4 0.80766 0.36901 NaN NaN 1 1272.13087 1118 1575.07956 7 70651.28638 1 +CotColor ss715578630 phavu.Chr02 16940732 0.44954 0.63803 6.2804E-4 0.46061 0.49748 0.4387 0.50788 2 709.03984 1118 1577.24422 8 61824.82135 1 +CotColor ss715578631 phavu.Chr02 16990519 0.42838 0.65167 5.9764E-4 0.81164 0.36783 0.04581 0.83055 2 675.40879 1119 1576.66112 8 61996.24498 1 +CotColor ss715578637 phavu.Chr02 17214250 0.81163 0.36783 5.6764E-4 0.81163 0.36783 NaN NaN 1 1280.6442 1118 1577.86757 7 70287.55918 1 +CotColor ss715578644 phavu.Chr02 17436477 1.99072 0.13708 0.00279 3.02731 0.08215 0.95427 0.32885 2 3130.72174 1110 1572.65423 8 61895.66351 17 +CotColor ss715578645 phavu.Chr02 17495414 4.22343 0.01488 0.00587 8.10517 0.00449 0.34642 0.55626 2 6616.9944 1117 1566.73547 8 63257.65956 31 +CotColor ss715578650 phavu.Chr02 17642372 4.84181 0.00806 0.00671 8.77397 0.00312 0.91036 0.34023 2 7580.79922 1118 1565.69468 8 63498.05554 28 +CotColor ss715578652 phavu.Chr02 17744551 1.88494 0.17005 0.00131 1.88494 0.17005 NaN NaN 1 2965.53257 1119 1573.27709 7 71115.06564 2 +CotColor ss715578657 phavu.Chr02 17900123 4.3351 0.01332 0.00605 8.30841 0.00402 0.36651 0.54503 2 6811.78783 1115 1571.30862 8 62555.16519 30 +CotColor ss715578667 phavu.Chr02 18443259 15.95497 1.4722E-7 0.02169 31.65294 2.3286E-8 0.27746 0.59847 2 24472.80744 1117 1533.86725 8 67946.36104 292 +CotColor ss715578670 phavu.Chr02 18701052 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578680 phavu.Chr02 19000262 15.25077 2.9202E-7 0.02075 30.16671 4.905E-8 0.35229 0.55294 2 23423.04326 1118 1535.85976 8 67611.52137 288 +CotColor ss715578681 phavu.Chr02 19044380 16.18241 1.1804E-7 0.022 32.23034 1.7446E-8 0.15876 0.69038 2 24807.04787 1116 1532.9633 8 68043.7089 291 +CotColor ss715578686 phavu.Chr02 19301492 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578691 phavu.Chr02 19626913 4.79416 0.00845 0.00664 8.7502 0.00316 0.83937 0.35977 2 7500.28294 1119 1564.46295 8 63702.46352 27 +CotColor ss715578693 phavu.Chr02 19710863 0.04366 0.83452 3.0481E-5 0.04366 0.83452 NaN NaN 1 68.83947 1119 1576.66112 7 70589.04623 1 +CotColor ss715578695 phavu.Chr02 19775307 3.86022 0.02135 0.00543 5.94871 0.01488 1.76761 0.18395 2 6085.78097 1110 1576.53805 8 61359.92504 14 +CotColor ss715578699 phavu.Chr02 19997024 0.21422 0.80721 2.9897E-4 0.17963 0.67177 0.24892 0.61793 2 337.87502 1119 1577.26439 8 61911.86154 2 +CotColor ss715578702 phavu.Chr02 20305604 3.20995 0.04073 0.00449 6.3569 0.01183 0.0683 0.79387 2 5050.64837 1115 1573.43526 8 62194.09819 345 +CotColor ss715578704 phavu.Chr02 20362413 5.27288 0.00526 0.0073 9.9326 0.00167 0.61656 0.4325 2 8236.37319 1117 1562.02654 8 63970.55092 32 +CotColor ss715578705 phavu.Chr02 20384801 2.88206 0.05644 0.004 5.72382 0.0169 0.04517 0.83173 2 4524.20264 1119 1569.78213 8 62958.44344 352 +CotColor ss715578708 phavu.Chr02 20440348 3.07978 0.04636 0.00429 3.43691 0.06402 2.71735 0.09954 2 4830.97619 1115 1568.61301 8 63078.48287 211 +CotColor ss715578718 phavu.Chr02 20785378 1.73622 0.17666 0.00242 3.39573 0.06563 0.07949 0.77804 2 2731.04742 1119 1572.98705 8 62510.15464 7 +CotColor ss715578723 phavu.Chr02 20892406 7.04241 9.134E-4 0.00975 14.04686 1.8744E-4 0.04991 0.82327 2 10985.68684 1116 1559.93303 8 64205.96293 406 +CotColor ss715578732 phavu.Chr02 21336814 0.42962 0.65086 6.0406E-4 0.81451 0.36698 0.04543 0.83126 2 678.98446 1113 1580.42863 8 61130.26 1 +CotColor ss715578735 phavu.Chr02 21453879 5.21268 0.00558 0.00725 10.17259 0.00147 0.25952 0.61055 2 8163.22474 1115 1566.03243 8 63266.95532 34 +CotColor ss715578739 phavu.Chr02 21705175 1.89756 0.15042 0.00267 3.30751 0.06923 0.48914 0.48446 2 2981.20567 1103 1571.07494 8 62065.79288 5 +CotColor ss715578745 phavu.Chr02 21992016 5.28375 0.0052 0.00733 10.32091 0.00135 0.25349 0.61473 2 8273.55286 1117 1565.8475 8 63358.96486 35 +CotColor ss715578751 phavu.Chr02 22123142 2.51496 0.08132 0.00352 5.00936 0.02541 0.02493 0.87457 2 3960.46296 1116 1574.76202 8 61995.12395 375 +CotColor ss715578756 phavu.Chr02 22279847 4.63648 0.00988 0.00642 8.78683 0.0031 0.49014 0.48401 2 7255.63555 1119 1564.90021 8 63641.30167 31 +CotColor ss715578758 phavu.Chr02 22338107 0.44468 0.64115 6.2036E-4 0.73824 0.39041 0.15167 0.69702 2 701.08232 1119 1576.61523 8 62002.66336 3 +CotColor ss715578759 phavu.Chr02 22369413 0.83365 0.36142 5.8933E-4 0.83365 0.36142 NaN NaN 1 1320.5318 1111 1584.02696 7 68695.68106 3 +CotColor ss715578762 phavu.Chr02 22425345 0.42894 0.6513 6.0253E-4 0.81345 0.3673 0.04513 0.8318 2 677.43246 1113 1579.30429 8 61355.79265 1 +CotColor ss715578764 phavu.Chr02 22446750 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1120 1575.31485 6 82342.41402 0 +CotColor ss715578766 phavu.Chr02 22584903 4.19793 0.01527 0.00588 7.66336 0.00573 0.73434 0.39167 2 6602.68606 1112 1572.84259 8 62138.29982 23 +CotColor ss715578774 phavu.Chr02 22792138 4.07942 0.01717 0.00566 7.80348 0.0053 0.35982 0.54873 2 6394.44662 1118 1567.49008 8 63222.93761 28 +CotColor ss715578779 phavu.Chr02 22962093 4.56563 0.0106 0.00636 7.57842 0.006 1.54911 0.21353 2 7165.86257 1115 1569.52461 8 62762.43599 22 +CotColor ss715578781 phavu.Chr02 23041534 4.81106 0.00831 0.00666 8.67055 0.0033 0.95194 0.32944 2 7526.50001 1119 1564.41609 8 63709.01778 30 +CotColor ss715578782 phavu.Chr02 23147121 0.64787 0.52336 9.0897E-4 1.26048 0.2618 0.03635 0.84883 2 1023.20607 1115 1579.34161 8 61296.95054 4 +CotColor ss715578786 phavu.Chr02 23246736 9.67485 0.00192 0.00671 9.67485 0.00192 NaN NaN 1 15137.39165 1118 1564.61225 7 72490.46028 46 +CotColor ss715578789 phavu.Chr02 23391913 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578793 phavu.Chr02 23561948 0.81291 0.36745 5.6758E-4 0.81291 0.36745 NaN NaN 1 1281.68636 1119 1576.66112 7 70555.08285 1 +CotColor ss715578797 phavu.Chr02 23814920 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578798 phavu.Chr02 23904568 4.1494 0.01602 0.00576 7.77378 0.00539 0.5283 0.46747 2 6504.52371 1118 1567.58178 8 63201.61763 36 +CotColor ss715578804 phavu.Chr02 24154959 4.35044 0.01312 0.00606 8.6036 0.00342 0.10419 0.74692 2 6819.09844 1114 1567.45011 8 63083.54239 37 +CotColor ss715578809 phavu.Chr02 24297264 1.93894 0.14434 0.00272 0.07178 0.78881 3.80591 0.05132 2 3050.75602 1111 1573.41826 8 62123.88596 3 +CotColor ss715578823 phavu.Chr02 24565748 2.43331 0.08821 0.00343 3.18128 0.07476 1.68339 0.19474 2 3841.82419 1111 1578.84647 8 61065.76185 19 +CotColor ss715578824 phavu.Chr02 24611011 2.29061 0.10168 0.00323 3.6325 0.05692 0.94888 0.33022 2 3621.46781 1111 1581.00715 8 60967.76228 27 +CotColor ss715578825 phavu.Chr02 24643305 0.60274 0.54749 8.4063E-4 0.47845 0.48927 0.72714 0.394 2 950.01393 1119 1576.17031 8 62064.89626 2 +CotColor ss715578828 phavu.Chr02 24685926 0.42707 0.65252 5.9733E-4 0.80803 0.3689 0.0468 0.82876 2 673.3208 1116 1576.60282 8 61868.43491 1 +CotColor ss715578829 phavu.Chr02 24729115 1.15148 0.28347 8.023E-4 1.15148 0.28347 NaN NaN 1 1813.39744 1120 1574.84037 7 70918.93424 1 +CotColor ss715578831 phavu.Chr02 24816261 0.8121 0.3677 5.6578E-4 0.8121 0.3677 NaN NaN 1 1277.36041 1119 1572.91832 7 71084.31897 1 +CotColor ss715578835 phavu.Chr02 25019562 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578837 phavu.Chr02 25114399 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578840 phavu.Chr02 25311369 2.38881 0.09221 0.00335 2.06369 0.15113 2.71075 0.09996 2 3766.20986 1113 1576.60778 8 61532.07298 16 +CotColor ss715578843 phavu.Chr02 25358507 1.15148 0.28347 8.023E-4 1.15148 0.28347 NaN NaN 1 1813.39744 1120 1574.84037 7 70918.93424 1 +CotColor ss715578844 phavu.Chr02 25390596 1.14941 0.2839 8.0135E-4 1.14941 0.2839 NaN NaN 1 1809.90873 1119 1574.64499 7 70933.91628 1 +CotColor ss715578846 phavu.Chr02 25465382 1.14223 0.31949 0.00163 0.06802 0.79429 2.21637 0.13684 2 1802.10618 1091 1577.70693 8 60543.69106 8 +CotColor ss715578851 phavu.Chr02 25626349 19.38237 5.3118E-9 0.02624 38.18314 9.0098E-10 0.59542 0.44049 2 29571.87414 1116 1525.71021 8 68907.55633 75 +CotColor ss715578852 phavu.Chr02 25675205 0.81109 0.36799 5.6604E-4 0.81109 0.36799 NaN NaN 1 1275.67763 1117 1572.78468 7 70984.6111 1 +CotColor ss715578853 phavu.Chr02 25703053 0.0147 0.90351 1.0255E-5 0.0147 0.90351 NaN NaN 1 23.1782 1120 1576.43878 7 70663.18863 2 +CotColor ss715578855 phavu.Chr02 25794724 2.31637 0.0991 0.00322 3.68513 0.05515 0.94778 0.3305 2 3639.85248 1119 1571.36274 8 62737.3559 30 +CotColor ss715578857 phavu.Chr02 25854967 5.77836 0.01639 0.00404 5.77836 0.01639 NaN NaN 1 9095.36239 1115 1574.03948 7 70643.23432 30 +CotColor ss715578859 phavu.Chr02 25928596 0.34352 0.55792 2.3952E-4 0.34352 0.55792 NaN NaN 1 541.37599 1120 1575.9761 7 70737.21689 42 +CotColor ss715578861 phavu.Chr02 25952266 3.34134 0.03574 0.00467 6.33412 0.01198 0.35223 0.55297 2 5258.64114 1114 1573.81421 8 62159.86884 34 +CotColor ss715578863 phavu.Chr02 26041050 1.75954 0.1726 0.00247 3.40759 0.06516 0.1142 0.73547 2 2777.37355 1112 1578.46436 8 61242.36649 27 +CotColor ss715578866 phavu.Chr02 26100753 2.04311 0.13011 0.00286 3.54424 0.06001 0.54343 0.46117 2 3217.31362 1115 1574.71555 8 62005.14975 29 +CotColor ss715578871 phavu.Chr02 26204557 0.40992 0.52214 2.858E-4 0.40992 0.52214 NaN NaN 1 645.98963 1120 1575.88269 7 70752.1617 8 +CotColor ss715578875 phavu.Chr02 26305441 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578877 phavu.Chr02 26329021 0.98073 0.37536 0.00137 0.30886 0.57849 1.65242 0.1989 2 1545.38866 1118 1575.75268 8 62119.21777 1 +CotColor ss715578883 phavu.Chr02 26561841 1.14088 0.2857 7.948E-4 1.14088 0.2857 NaN NaN 1 1794.42615 1119 1572.83758 7 71099.32335 1 +CotColor ss715578886 phavu.Chr02 26607449 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715578888 phavu.Chr02 26639178 2.94561 0.05298 0.00413 4.10871 0.0429 1.77963 0.18247 2 4632.77539 1113 1572.77176 8 61935.39096 21 +CotColor ss715578890 phavu.Chr02 26704049 1.88274 0.1703 0.00131 1.88274 0.1703 NaN NaN 1 2963.07608 1120 1573.81387 7 71083.17405 2 +CotColor ss715578891 phavu.Chr02 26747534 2.93178 0.05371 0.0041 4.89176 0.02719 0.97191 0.32442 2 4615.03015 1114 1574.14127 8 62062.9298 28 +CotColor ss715578893 phavu.Chr02 26824346 2.8036 0.06102 0.00391 5.50236 0.01916 0.10921 0.7411 2 4407.97451 1117 1572.25737 8 62504.89731 34 +CotColor ss715578896 phavu.Chr02 26926537 2.99383 0.0505 0.00416 5.49363 0.01926 0.4965 0.48119 2 4698.72711 1119 1569.4702 8 63002.07456 34 +CotColor ss715578898 phavu.Chr02 27034164 1.91692 0.16648 0.00135 1.91692 0.16648 NaN NaN 1 3023.02631 1108 1577.0243 7 69980.85343 2 +CotColor ss715578900 phavu.Chr02 27098957 9.29256 9.9425E-5 0.01281 18.03664 2.347E-5 0.55565 0.45618 2 14444.92754 1117 1554.46128 8 64967.11263 354 +CotColor ss7155789phavu.Chr02 01 27230466 31.08698 7.2581E-14 0.04112 62.06552 7.8204E-15 0.15526 0.69363 2 46469.236 1119 1494.81335 8 73444.70178 458 +CotColor ss715578904 phavu.Chr02 27319719 10.06477 4.6545E-5 0.01382 19.47387 1.1186E-5 0.66157 0.41618 2 15572.49422 1116 1547.22736 8 65812.34001 352 +CotColor ss715578908 phavu.Chr02 27486344 0.92116 0.39836 0.00128 0.02261 0.88049 1.81968 0.17762 2 1451.07251 1119 1575.27476 8 62190.16091 49 +CotColor ss715578909 phavu.Chr02 27562614 20.72836 1.4482E-9 0.02797 41.02859 2.2053E-10 0.44837 0.50325 2 31560.20467 1117 1522.5617 8 69461.62947 405 +CotColor ss715578911 phavu.Chr02 27641493 21.89152 4.7284E-10 0.02957 43.48592 6.579E-11 0.3235 0.56963 2 33267.17916 1114 1519.63777 8 69675.07037 399 +CotColor ss715578915 phavu.Chr02 27781076 8.07964 3.2824E-4 0.01113 16.11431 6.3606E-5 0.05854 0.80887 2 12554.107 1117 1553.79582 8 65041.36367 339 +CotColor ss715578918 phavu.Chr02 27904127 0.42838 0.65167 5.9764E-4 0.81164 0.36783 0.04581 0.83055 2 675.40879 1119 1576.66112 8 61996.24498 1 +CotColor ss715578920 phavu.Chr02 28018669 0.04773 0.95339 6.7395E-5 0.02256 0.88064 0.07292 0.78718 2 75.59332 1111 1583.82186 8 60458.17394 44 +CotColor ss715578921 phavu.Chr02 28041072 25.84284 1.0711E-11 0.03476 51.30606 1.4351E-12 0.40698 0.52364 2 39011.36112 1111 1509.56145 8 70962.86223 441 +CotColor ss715578924 phavu.Chr02 28154191 27.73725 1.7547E-12 0.03711 55.28548 2.0729E-13 0.22732 0.63361 2 41733.43997 1114 1504.59924 8 71651.44481 439 +CotColor ss715578926 phavu.Chr02 28205896 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1120 1575.31485 6 82342.41402 0 +CotColor ss715578927 phavu.Chr02 28238375 0.81133 0.36792 5.6628E-4 0.81133 0.36792 NaN NaN 1 1275.86212 1117 1572.5471 7 70932.03382 1 +CotColor ss715578929 phavu.Chr02 28292204 0.03259 0.85676 2.2732E-5 0.03259 0.85676 NaN NaN 1 51.38024 1120 1576.4136 7 70667.2175 63 +CotColor ss715578931 phavu.Chr02 28349014 1.52314 0.21848 0.00212 0.03349 0.85482 3.01272 0.08289 2 2398.62048 1118 1574.78812 8 62136.93533 56 +CotColor ss715578932 phavu.Chr02 28374475 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578933 phavu.Chr02 28402659 26.46304 5.9143E-12 0.03543 51.18012 1.5235E-12 1.71323 0.19084 2 39878.18902 1114 1506.9388 8 71512.01685 438 +CotColor ss715578935 phavu.Chr02 28477581 18.56485 1.1765E-8 0.02546 33.48173 9.3721E-9 3.56975 0.0591 2 28299.75544 1099 1524.37334 8 68513.46881 392 +CotColor ss715578938 phavu.Chr02 28567716 23.31488 1.2036E-10 0.03136 46.54737 1.4635E-11 0.11913 0.73004 2 35313.80751 1115 1514.64679 8 70458.20519 384 +CotColor ss715578940 phavu.Chr02 28625066 1.01305 0.36345 0.00142 0.17162 0.67875 1.85434 0.17355 2 1593.83367 1115 1573.30862 8 62305.5968 61 +CotColor ss715578950 phavu.Chr02 28817299 28.95508 5.4932E-13 0.03844 56.19326 1.3321E-13 1.68262 0.19484 2 43396.88583 1118 1498.76582 8 72804.45986 443 +CotColor ss715578952 phavu.Chr02 28841188 24.78506 2.9376E-11 0.0332 47.97149 7.2825E-12 1.574 0.20989 2 37452.79726 1117 1511.1036 8 71073.21732 382 +CotColor ss715578953 phavu.Chr02 28982389 29.05546 5.0027E-13 0.03862 56.88028 9.5699E-14 1.21944 0.26971 2 43466.46444 1115 1495.9829 8 72896.10236 445 +CotColor ss715578498 phavu.Chr02 29141519 27.99054 1.377E-12 0.03736 54.3228 3.3025E-13 1.62775 0.20228 2 42122.99975 1116 1504.90114 8 71943.23351 444 +CotColor ss715578500 phavu.Chr02 29205531 25.10604 2.1605E-11 0.03358 48.64616 5.2366E-12 1.54232 0.21453 2 37873.59357 1117 1508.54529 8 71327.65806 380 +CotColor ss715578502 phavu.Chr02 29238153 28.32505 1.0008E-12 0.03769 56.67683 1.0547E-13 0.0228 0.87999 2 42513.19371 1117 1500.90462 8 72446.0602 445 +CotColor ss715578504 phavu.Chr02 29270116 23.56896 9.4277E-11 0.0316 45.88716 2.0217E-11 1.24086 0.26554 2 35608.38824 1116 1510.81723 8 70964.8378 381 +CotColor ss715578495 phavu.Chr02 29470510 23.94485 6.5767E-11 0.0321 46.54351 1.4662E-11 1.33234 0.24864 2 36140.5677 1115 1509.325 8 71094.02031 380 +CotColor ss715578493 phavu.Chr02 29502546 25.68332 1.245E-11 0.0344 51.14987 1.5453E-12 0.2511 0.6164 2 38740.04793 1115 1508.3737 8 71334.53378 381 +CotColor ss715578491 phavu.Chr02 29550337 25.07655 2.2234E-11 0.0336 48.71677 5.0614E-12 1.4181 0.23397 2 37869.18593 1116 1510.14344 8 71067.43503 380 +CotColor ss715594297 phavu.Chr02 29715237 0.47843 0.61989 6.6995E-4 0.06982 0.79165 0.88704 0.34648 2 754.57772 1115 1577.21074 8 61755.1305 11 +CotColor ss715578505 phavu.Chr02 29852273 28.71834 6.8886E-13 0.0382 57.08003 8.6829E-14 0.38793 0.53352 2 43064.33465 1116 1499.54118 8 72614.68099 445 +CotColor ss715578511 phavu.Chr02 29945598 29.63201 2.9102E-13 0.0397 51.72412 1.174E-12 7.24797 0.00721 2 44357.47274 1106 1496.9443 8 72375.66658 423 +CotColor ss715578514 phavu.Chr02 30066989 25.74676 1.1724E-11 0.03458 49.70023 3.1344E-12 1.75943 0.18497 2 38900.73389 1114 1510.89844 8 70853.39571 377 +CotColor ss715578515 phavu.Chr02 30160150 0.07036 0.93207 9.8534E-5 0.02559 0.87295 0.11515 0.73442 2 111.15918 1117 1579.95086 8 61430.69347 63 +CotColor ss715578516 phavu.Chr02 30222680 2.15789 0.11605 0.00301 0.25682 0.61241 4.05825 0.0442 2 3394.37031 1118 1573.00681 8 62385.87279 58 +CotColor ss715578517 phavu.Chr02 30258610 25.68019 1.2481E-11 0.03432 49.19108 4.0148E-12 2.11997 0.14567 2 38677.82889 1116 1506.13507 8 71655.44893 378 +CotColor ss715578519 phavu.Chr02 30401332 24.5617 3.6382E-11 0.03296 47.88681 7.5904E-12 1.22687 0.26825 2 37180.35807 1117 1513.75326 8 70650.97026 382 +CotColor ss715578521 phavu.Chr02 30442329 3.74119 0.02402 0.00519 7.48792 0.00631 0.00116 0.97281 2 5857.88886 1117 1565.78027 8 63341.42045 370 +CotColor ss715578524 phavu.Chr02 30490467 1.20164 0.30109 0.00168 0.18104 0.67057 2.22205 0.13634 2 1890.02028 1116 1572.8665 8 62398.7003 57 +CotColor ss715578526 phavu.Chr02 30513781 0.04879 0.82522 3.4142E-5 0.04879 0.82522 NaN NaN 1 76.97683 1117 1577.71556 7 70330.81839 64 +CotColor ss715578528 phavu.Chr02 30530043 27.87383 1.538E-12 0.03709 55.48156 1.8816E-13 0.30079 0.58349 2 41837.4753 1117 1500.95921 8 72442.10838 443 +CotColor ss715578531 phavu.Chr02 30575401 24.63192 3.4E-11 0.03299 48.48817 5.6543E-12 0.78499 0.37581 2 37248.74846 1118 1512.21452 8 70976.35705 432 +CotColor ss715578553 phavu.Chr02 30900581 0.03662 0.84827 2.5582E-5 0.03662 0.84827 NaN NaN 1 57.73599 1118 1576.5222 7 70620.01015 65 +CotColor ss715578557 phavu.Chr02 31005638 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578560 phavu.Chr02 31164305 0.17715 0.67391 1.2406E-4 0.17715 0.67391 NaN NaN 1 279.65684 1117 1578.64194 7 70112.3748 65 +CotColor ss715578564 phavu.Chr02 31278484 25.75116 1.1663E-11 0.03447 50.55921 2.0596E-12 0.94557 0.33106 2 38864.46806 1116 1509.23168 8 71348.47028 383 +CotColor ss715578566 phavu.Chr02 31367193 25.43957 1.5718E-11 0.0341 48.43162 5.8208E-12 2.38731 0.12261 2 38394.11916 1115 1509.22851 8 71117.44735 379 +CotColor ss715578569 phavu.Chr02 31480079 28.62806 7.5155E-13 0.03823 54.48874 3.0505E-13 2.68503 0.10158 2 43026.50165 1114 1502.94875 8 72050.25959 443 +CotColor ss715578576 phavu.Chr02 31670462 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715578579 phavu.Chr02 31735859 25.399 1.638E-11 0.03417 46.36379 1.6046E-11 4.29664 0.03842 2 38308.07609 1110 1508.25113 8 71038.76048 379 +CotColor ss715578580 phavu.Chr02 31804544 29.71833 2.7164E-13 0.04084 58.92991 3.6234E-14 0.5321 0.46588 2 44765.47131 1087 1506.32502 8 69376.70286 396 +CotColor ss715578582 phavu.Chr02 31855504 23.56277 9.4878E-11 0.03168 46.62945 1.4058E-11 0.51631 0.47257 2 35669.03675 1115 1513.78761 8 70451.51563 378 +CotColor ss715578583 phavu.Chr02 31899713 24.34242 4.4887E-11 0.03264 47.32029 1.0015E-11 1.34975 0.24557 2 36820.52342 1117 1512.60707 8 70830.28827 381 +CotColor ss715578958 phavu.Chr02 31963232 28.38773 9.4232E-13 0.03772 55.8445 1.5772E-13 0.93423 0.33397 2 42587.64779 1118 1500.21347 8 72602.15035 444 +CotColor ss715578959 phavu.Chr02 31988179 0.44642 0.64003 6.2623E-4 0.02717 0.86911 0.86568 0.35236 2 703.21207 1112 1575.22003 8 61778.87771 55 +CotColor ss715578961 phavu.Chr02 32009623 29.12644 4.6823E-13 0.03877 55.08544 2.2853E-13 3.06532 0.08025 2 43569.56527 1113 1495.87668 8 72824.50705 444 +CotColor ss715578962 phavu.Chr02 32039264 0.07572 0.92708 1.0587E-4 0.04749 0.82753 0.10398 0.74716 2 119.46414 1117 1577.77138 8 61815.15792 64 +CotColor ss715578963 phavu.Chr02 32099532 0.96889 0.37982 0.00135 0.00352 0.95267 1.93425 0.16457 2 1527.48543 1118 1576.52946 8 61925.29103 65 +CotColor ss715578965 phavu.Chr02 32139114 10.10214 4.4865E-5 0.01389 19.45509 1.1293E-5 0.75348 0.38556 2 15664.98297 1117 1550.6603 8 65504.75229 475 +CotColor ss715578966 phavu.Chr02 32182609 27.17701 2.9911E-12 0.0363 54.3208 3.3076E-13 0.0781 0.77995 2 40874.54805 1115 1504.0119 8 71905.32684 442 +CotColor ss715578967 phavu.Chr02 32253597 0.47736 0.62054 6.6819E-4 0.38779 0.53359 0.56708 0.45158 2 752.8635 1116 1577.13085 8 61669.32961 58 +CotColor ss715578969 phavu.Chr02 32308595 24.8898 2.661E-11 0.03343 46.90384 1.2296E-11 2.80004 0.09454 2 37602.27112 1114 1510.75007 8 70848.54116 372 +CotColor ss715578971 phavu.Chr02 32357994 1.35733 0.25777 0.00189 0.05137 0.82073 2.66321 0.10297 2 2138.2077 1118 1575.30508 8 62120.55839 65 +CotColor ss715578973 phavu.Chr02 32474967 24.29281 4.7307E-11 0.03302 46.86176 1.2596E-11 1.69447 0.19328 2 36874.09264 1106 1517.9014 8 69313.67544 417 +CotColor ss715578976 phavu.Chr02 32557459 0.03615 0.84924 2.5314E-5 0.03615 0.84924 NaN NaN 1 57.04019 1116 1577.96251 7 70325.39333 64 +CotColor ss715578979 phavu.Chr02 32689784 0.27936 0.75632 3.9043E-4 0.28699 0.59226 0.27192 0.60215 2 440.42321 1117 1576.52129 8 61887.71105 61 +CotColor ss715578980 phavu.Chr02 32722974 0.93159 0.39423 0.0013 1.75438 0.1856 0.1102 0.73998 2 1468.50092 1118 1576.33158 8 61999.64807 75 +CotColor ss715578981 phavu.Chr02 32744066 17.15074 4.6142E-8 0.02336 34.13694 6.7386E-9 0.18941 0.66349 2 26226.00696 1112 1529.14698 8 68114.65042 365 +CotColor ss715578984 phavu.Chr02 32803083 17.34545 3.822E-8 0.02373 34.14338 6.7202E-9 0.56101 0.45401 2 26609.52296 1110 1534.09231 8 67462.45345 367 +CotColor ss715578985 phavu.Chr02 32907963 18.9112 8.3814E-9 0.02564 37.24844 1.433E-9 0.58774 0.44346 2 28859.79509 1114 1526.06863 8 68838.47282 365 +CotColor ss715578989 phavu.Chr02 33067880 24.98677 2.4229E-11 0.03342 49.97701 2.7358E-12 0.03951 0.84247 2 37667.71231 1116 1507.506 8 71484.15208 447 +CotColor ss715578991 phavu.Chr02 33156545 16.78103 6.6026E-8 0.02281 25.46011 5.2696E-7 7.94341 0.00491 2 25662.86284 1114 1529.27772 8 68298.04391 445 +CotColor ss715578992 phavu.Chr02 33203133 14.93785 3.9808E-7 0.02101 29.84301 5.8024E-8 0.05847 0.80897 2 23058.61683 1089 1543.63703 8 64199.42679 67 +CotColor ss715578993 phavu.Chr02 33254055 0.50763 0.60206 7.1608E-4 0.77023 0.38033 0.24556 0.62032 2 802.08401 1109 1580.04429 8 60990.42983 69 +CotColor ss715578994 phavu.Chr02 33299981 2.36398 0.09452 0.0033 0.22479 0.63551 4.50247 0.03407 2 3716.80851 1117 1572.26472 8 62468.55866 90 +CotColor ss715578995 phavu.Chr02 33366518 4.29429 0.01387 0.00596 8.09299 0.00452 0.49921 0.47999 2 6714.93681 1116 1563.69086 8 63666.56318 241 +CotColor ss715578997 phavu.Chr02 33441397 0.98307 0.37448 0.00137 1.9671 0.16103 8.0482E-4 0.97737 2 1546.62584 1117 1573.25377 8 62371.70051 315 +CotColor ss715578999 phavu.Chr02 33501463 1.5279 0.21745 0.00214 0.33473 0.56301 2.72055 0.09935 2 2410.09631 1113 1577.39163 8 61655.46095 78 +CotColor ss715579002 phavu.Chr02 33557981 0.83012 0.43627 0.00116 1.58655 0.20808 0.07499 0.78426 2 1307.8717 1119 1575.53071 8 62154.36071 288 +CotColor ss715579004 phavu.Chr02 33629363 0.44505 0.64091 6.2568E-4 0.88257 0.3477 0.00832 0.92735 2 703.82848 1114 1581.45745 8 61009.65865 204 +CotColor ss715579005 phavu.Chr02 33652273 0.66739 0.51326 9.9209E-4 0.0386 0.84427 1.29616 0.25517 2 1069.46726 1068 1602.46995 8 55569.76641 211 +CotColor ss715579007 phavu.Chr02 33747039 0.39196 0.67582 5.4687E-4 0.78462 0.37592 1.8352E-6 0.99892 2 618.03045 1119 1576.76367 8 61981.90039 206 +CotColor ss715579008 phavu.Chr02 33791555 0.33748 0.71364 4.7512E-4 0.67539 0.41135 1.6635E-4 0.98971 2 533.39033 1111 1580.52396 8 61166.24126 202 +CotColor ss715579009 phavu.Chr02 33840560 0.91008 0.40279 0.00127 1.27601 0.25888 0.54468 0.46066 2 1434.75736 1118 1576.51238 8 61895.96955 208 +CotColor ss715579phavu.Chr020 01 33861185 0.76743 0.46445 0.00108 1.3512 0.24532 0.18464 0.6675 2 1212.7403 1115 1580.27057 8 61206.4641 208 +CotColor ss715579phavu.Chr021 01 33888966 0.89006 0.41092 0.00125 0.7587 0.38392 1.02141 0.3124 2 1404.70573 1111 1578.21019 8 61446.89925 211 +CotColor ss715579phavu.Chr025 01 33980947 2.29382 0.10136 0.00321 1.14256 0.28534 3.44258 0.0638 2 3612.53362 1116 1574.89751 8 62057.61047 213 +CotColor ss715579phavu.Chr027 01 34050470 0.8146 0.44308 0.00114 0.4617 0.49697 1.16743 0.28016 2 1285.94442 1112 1578.61784 8 61448.56408 290 +CotColor ss715579022 phavu.Chr02 34143512 1.00004 0.36819 0.00139 1.00347 0.31669 0.99662 0.31835 2 1572.19233 1117 1572.12307 8 62468.34548 320 +CotColor ss715579026 phavu.Chr02 34197502 1.46674 0.22612 0.00103 1.46674 0.22612 NaN NaN 1 2310.30544 1116 1575.12853 7 70604.81095 318 +CotColor ss715579030 phavu.Chr02 34325402 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1120 1575.31485 6 82342.41402 0 +CotColor ss715579034 phavu.Chr02 34406159 2.69106 0.10119 0.00188 2.69106 0.10119 NaN NaN 1 4237.19177 1117 1574.54583 7 70792.24738 1 +CotColor ss715579036 phavu.Chr02 34467391 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715579039 phavu.Chr02 34545797 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1104 1572.06944 6 81458.33926 0 +CotColor ss715579043 phavu.Chr02 34616571 0.42838 0.65167 5.9764E-4 0.81164 0.36783 0.04581 0.83055 2 675.40879 1119 1576.66112 8 61996.24498 1 +CotColor ss715579130 phavu.Chr02 34693033 0.15097 0.69768 1.0547E-4 0.15097 0.69768 NaN NaN 1 238.15691 1119 1577.50611 7 70418.02981 90 +CotColor ss715579047 phavu.Chr02 34809346 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715579051 phavu.Chr02 34924874 0.11037 0.73979 7.7232E-5 0.11037 0.73979 NaN NaN 1 174.1595 1117 1577.96466 7 70345.77732 79 +CotColor ss715579059 phavu.Chr02 35249061 2.22214 0.10886 0.0031 2.56878 0.10927 1.8735 0.17135 2 3495.15802 1118 1572.87794 8 62478.51046 310 +CotColor ss715579064 phavu.Chr02 35321449 0.89389 0.34463 6.2355E-4 0.89389 0.34463 NaN NaN 1 1408.33599 1119 1575.51725 7 70797.09471 71 +CotColor ss715579066 phavu.Chr02 35374527 1.19501 0.30309 0.00167 1.89609 0.16879 0.49479 0.48195 2 1883.08511 1118 1575.79148 8 62034.66547 314 +CotColor ss715579067 phavu.Chr02 35419697 0.09692 0.90764 1.3535E-4 0.03177 0.85856 0.16209 0.68731 2 152.81112 1118 1576.67127 8 61906.57117 238 +CotColor ss715579072 phavu.Chr02 35658721 0.02197 0.97827 3.0667E-5 7.902E-5 0.99291 0.04385 0.83417 2 34.65806 1119 1577.80634 8 61836.05729 62 +CotColor ss715579075 phavu.Chr02 35710797 0.04791 0.82678 3.3416E-5 0.04791 0.82678 NaN NaN 1 75.52884 1120 1576.39203 7 70670.6673 62 +CotColor ss715579077 phavu.Chr02 35821409 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1121 1575.05317 6 82436.52371 0 +CotColor ss715579078 phavu.Chr02 35847759 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1077 1592.14769 6 74793.39475 0 +CotColor ss715579080 phavu.Chr02 35932877 14.85169 4.3108E-7 0.02027 25.84685 4.3328E-7 3.79177 0.05176 2 22789.18155 1113 1534.44999 8 67588.05374 333 +CotColor ss715579084 phavu.Chr02 36041544 0.26059 0.77065 3.6632E-4 0.02501 0.87437 0.49618 0.48133 2 411.7184 1113 1579.96283 8 61169.06243 90 +CotColor ss715579085 phavu.Chr02 36090860 1.32689 0.24961 9.2437E-4 1.32689 0.24961 NaN NaN 1 2089.30973 1120 1574.59402 7 70958.35028 31 +CotColor ss715579086 phavu.Chr02 36114072 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715579087 phavu.Chr02 36145075 0.61518 0.43301 4.296E-4 0.61518 0.43301 NaN NaN 1 970.10726 1119 1576.93956 7 70510.57155 56 +CotColor ss715579088 phavu.Chr02 36173725 0.42838 0.65167 5.9764E-4 0.81164 0.36783 0.04581 0.83055 2 675.40879 1119 1576.66112 8 61996.24498 1 +CotColor ss715579090 phavu.Chr02 36215024 0.77845 0.45937 0.00109 0.01881 0.89094 1.53808 0.21516 2 1227.58228 1117 1576.95423 8 61920.8883 93 +CotColor ss715579095 phavu.Chr02 36300382 0.95927 0.38349 0.00134 5.8928E-4 0.98064 1.91795 0.16636 2 1512.23651 1117 1576.44456 8 61992.05186 88 +CotColor ss715579098 phavu.Chr02 36347594 0.2445 0.78314 3.4094E-4 0.00102 0.97451 0.48798 0.48498 2 384.92438 1118 1574.34446 8 62234.74045 41 +CotColor ss715579099 phavu.Chr02 36420185 0.34451 0.70864 4.8214E-4 0.00173 0.96683 0.68729 0.40727 2 543.64357 1116 1578.03095 8 61757.7115 91 +CotColor ss7155791phavu.Chr02 01 36452277 0.44653 0.63996 6.2425E-4 0.79772 0.37197 0.09599 0.75676 2 703.57433 1116 1575.63544 8 61966.40274 29 +CotColor ss715579103 phavu.Chr02 36523544 0.36976 0.69099 5.1591E-4 0.64948 0.42047 0.09056 0.76352 2 583.04259 1119 1576.8262 8 61973.15343 31 +CotColor ss715579105 phavu.Chr02 36583766 0.92815 0.39559 0.00129 1.41374 0.23469 0.44327 0.50569 2 1462.07831 1119 1575.25509 8 62192.91236 67 +CotColor ss715579106 phavu.Chr02 36617765 0.58579 0.55684 8.2224E-4 0.76699 0.38134 0.40499 0.52465 2 925.4197 1114 1579.79285 8 61384.65659 63 +CotColor ss715579109 phavu.Chr02 36683395 0.25466 0.77523 3.5574E-4 0.33295 0.56405 0.17662 0.67438 2 401.70419 1118 1577.41433 8 61857.23656 64 +CotColor ss715579110 phavu.Chr02 36708547 0.42564 0.65346 5.9537E-4 0.43314 0.51059 0.41836 0.51789 2 670.9274 1116 1576.28407 8 61833.83495 62 +CotColor ss715579113 phavu.Chr02 36764795 0.27883 0.75672 3.9081E-4 0.02545 0.87327 0.53221 0.46583 2 440.17808 1115 1578.66812 8 61551.70933 93 +CotColor ss715579114 phavu.Chr02 36786717 1.04646 0.30655 7.2919E-4 1.04646 0.30655 NaN NaN 1 1648.15861 1120 1574.9879 7 70895.32869 30 +CotColor ss715579115 phavu.Chr02 36823412 0.27274 0.76134 3.8086E-4 0.04282 0.8361 0.50268 0.47847 2 430.09455 1118 1576.95344 8 61942.33826 95 +CotColor ss715579116 phavu.Chr02 36847726 0.82955 0.43652 0.00116 0.00582 0.93918 1.65327 0.19878 2 1307.73472 1117 1576.44278 8 61959.12412 92 +CotColor ss715579117 phavu.Chr02 36871319 0.7264 0.39424 5.0723E-4 0.7264 0.39424 NaN NaN 1 1145.32787 1119 1576.72539 7 70522.35449 32 +CotColor ss715579122 phavu.Chr02 36962671 0.46714 0.62691 6.5377E-4 0.44256 0.50603 0.49193 0.48322 2 737.53924 1117 1578.82932 8 61587.28849 94 +CotColor ss715579124 phavu.Chr02 36992932 0.59917 0.54945 8.3798E-4 0.91333 0.33944 0.28559 0.59317 2 944.66966 1116 1576.64273 8 61887.54143 79 +CotColor ss715579126 phavu.Chr02 37037878 0.39281 0.53095 2.7388E-4 0.39281 0.53095 NaN NaN 1 619.03365 1120 1575.90676 7 70748.31084 80 +CotColor ss715579128 phavu.Chr02 37053946 0.22453 0.79893 3.3615E-4 0.08924 0.76521 0.35988 0.5487 2 351.99297 1038 1567.69003 8 58376.06103 53 +CotColor ss715639168 phavu.Chr02 37083775 0.24206 0.78505 3.3884E-4 0.38275 0.53626 0.10168 0.74988 2 381.87795 1116 1577.6044 8 61679.94602 66 +CotColor ss715639169 phavu.Chr02 37086198 1.71016 0.18131 0.00238 0.01465 0.90369 3.40564 0.06524 2 2691.98903 1118 1574.11344 8 62297.32321 93 +CotColor ss715579140 phavu.Chr02 37206666 0.44181 0.64298 6.2062E-4 0.08966 0.76467 0.79398 0.37309 2 698.32546 1114 1580.60225 8 61204.17604 101 +CotColor ss715579142 phavu.Chr02 37237362 0.00356 0.95244 2.4867E-6 0.00356 0.95244 NaN NaN 1 5.61545 1119 1577.71392 7 70384.8096 103 +CotColor ss715579143 phavu.Chr02 37276870 0.08555 0.76997 5.9959E-5 0.08555 0.76997 NaN NaN 1 135.15619 1117 1579.86578 7 69919.29889 100 +CotColor ss715579146 phavu.Chr02 37316206 0.0039 0.95019 2.7363E-6 0.0039 0.95019 NaN NaN 1 6.16808 1117 1579.98126 7 69900.87202 90 +CotColor ss715579148 phavu.Chr02 37355026 0.49377 0.61046 6.8878E-4 0.611 0.43458 0.37687 0.53941 2 778.40955 1119 1576.47702 8 62021.99517 102 +CotColor ss715579149 phavu.Chr02 37384525 1.14725 0.28436 7.9935E-4 1.14725 0.28436 NaN NaN 1 1806.73824 1120 1574.84631 7 70917.98293 94 +CotColor ss715579151 phavu.Chr02 37425915 0.16665 0.68318 1.1648E-4 0.16665 0.68318 NaN NaN 1 262.85119 1118 1577.26404 7 70472.42998 92 +CotColor ss715579152 phavu.Chr02 37452526 0.03991 0.8417 2.788E-5 0.03991 0.8417 NaN NaN 1 62.95668 1119 1577.66268 7 70393.0012 90 +CotColor ss715579154 phavu.Chr02 37494944 0.23395 0.79144 3.2756E-4 0.10643 0.74431 0.36154 0.54777 2 369.52513 1117 1579.48825 8 61495.28496 86 +CotColor ss715579155 phavu.Chr02 37532295 0.08192 0.77477 5.7133E-5 0.08192 0.77477 NaN NaN 1 129.13408 1120 1576.34417 7 70678.32519 87 +CotColor ss715579158 phavu.Chr02 37596589 0.28184 0.75445 4.0095E-4 0.54607 0.46008 0.01809 0.89302 2 447.35556 1105 1587.26255 8 59694.40958 73 +CotColor ss715579159 phavu.Chr02 37628389 0.40867 0.66463 5.7205E-4 0.10892 0.74144 0.70846 0.40014 2 644.26739 1115 1576.48974 8 61835.89866 95 +CotColor ss715579162 phavu.Chr02 37695663 0.10552 0.74536 7.3845E-5 0.10552 0.74536 NaN NaN 1 166.59887 1118 1578.76253 7 70144.80641 84 +CotColor ss715579163 phavu.Chr02 37728046 0.47053 0.62479 6.6034E-4 0.4036 0.52537 0.53763 0.46357 2 743.07986 1114 1579.23358 8 61416.71401 94 +CotColor ss715579165 phavu.Chr02 37759422 0.20393 0.65165 1.4238E-4 0.20393 0.65165 NaN NaN 1 321.58162 1119 1576.89529 7 70580.84737 98 +CotColor ss715579170 phavu.Chr02 37882509 0.0104 0.98966 1.4517E-5 0.02038 0.88651 4.3453E-4 0.98337 2 16.40592 1119 1577.83896 8 61831.49426 18 +CotColor ss715579171 phavu.Chr02 37915052 0.41 0.66375 5.7261E-4 6.9846E-4 0.97892 0.81931 0.36558 2 646.67313 1118 1577.23276 8 61916.10847 67 +CotColor ss715579173 phavu.Chr02 38000382 8.5633E-4 0.97666 5.9934E-7 8.5633E-4 0.97666 NaN NaN 1 1.35216 1118 1579.01184 7 70104.80317 22 +CotColor ss715579175 phavu.Chr02 38027910 0.68565 0.40783 4.9484E-4 0.68565 0.40783 NaN NaN 1 1081.00048 1085 1576.59544 7 67702.74442 21 +CotColor ss715579178 phavu.Chr02 38064415 0.68533 0.50414 9.6629E-4 0.27768 0.59833 1.09295 0.29605 2 1081.98894 1108 1578.79303 8 61272.1576 86 +CotColor ss715579180 phavu.Chr02 38090469 0.15891 0.85309 2.2439E-4 0.2561 0.61291 0.06194 0.80351 2 251.4771 1110 1582.50048 8 60600.93866 106 +CotColor ss715579182 phavu.Chr02 38133222 0.78381 0.45692 0.00111 0.35218 0.553 1.21537 0.27051 2 1231.92941 1098 1571.71958 8 61395.19597 24 +CotColor ss715579184 phavu.Chr02 38164659 0.89555 0.40868 0.00125 1.75588 0.18541 0.03673 0.84804 2 1413.26192 1117 1578.09698 8 61681.97512 90 +CotColor ss715579186 phavu.Chr02 38191875 0.88028 0.41496 0.00123 1.72744 0.18901 0.0346 0.85246 2 1386.77696 1119 1575.38968 8 62174.08702 90 +CotColor ss715579187 phavu.Chr02 38212681 0.80593 0.44693 0.00113 1.60089 0.20604 0.01238 0.91143 2 1270.88069 1116 1576.91735 8 61836.65628 109 +CotColor ss715579188 phavu.Chr02 38235629 0.06476 0.9373 9.0407E-5 0.05938 0.80752 0.07019 0.79111 2 102.17171 1119 1577.68567 8 61852.93571 25 +CotColor ss715579191 phavu.Chr02 38320262 0.6993 0.49715 9.7908E-4 1.35054 0.24543 0.04922 0.82448 2 1103.48329 1116 1577.98185 8 61637.21974 89 +CotColor ss715579192 phavu.Chr02 38360550 0.33499 0.71542 4.6911E-4 0.61219 0.43413 0.0583 0.80924 2 529.15408 1117 1579.61129 8 61443.44273 111 +CotColor ss715579194 phavu.Chr02 38407658 0.35474 0.70144 4.9678E-4 0.69001 0.40634 0.02008 0.88735 2 560.36602 1117 1579.65494 8 61440.59381 111 +CotColor ss715579196 phavu.Chr02 38438736 0.66346 0.51527 9.2687E-4 1.27794 0.25852 0.05007 0.82298 2 1046.5429 1118 1577.39352 8 61836.59272 112 +CotColor ss715579197 phavu.Chr02 38461485 0.04328 0.95764 6.072E-5 0.08653 0.76869 1.1395E-4 0.99148 2 68.31784 1114 1578.36926 8 61495.42056 23 +CotColor ss715579199 phavu.Chr02 38517922 0.65178 0.52132 9.1276E-4 1.25263 0.26329 0.05199 0.81967 2 1026.80417 1114 1575.38801 8 61864.60168 112 +CotColor ss715579202 phavu.Chr02 38557327 0.46139 0.63052 6.4366E-4 0.00136 0.97061 0.92143 0.33731 2 727.41921 1119 1576.56816 8 62009.24758 23 +CotColor ss715579203 phavu.Chr02 38600822 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715579204 phavu.Chr02 38642722 0.24705 0.61926 1.7344E-4 0.24705 0.61926 NaN NaN 1 390.61352 1115 1581.1021 7 69893.66018 24 +CotColor ss715579211 phavu.Chr02 38747376 6.98131 9.7028E-4 0.00969 10.98432 9.4841E-4 2.959 0.08568 2 10897.81892 1114 1560.99885 8 63902.640157 +CotColor ss715579212 phavu.Chr02 38791329 7.39338 6.4592E-4 0.0102 11.99569 5.5344E-4 2.77204 0.0962 2 11503.22394 1116 1555.88135 8 64791.16351 156 +CotColor ss715579215 phavu.Chr02 38839329 1.16909 0.31103 0.00163 2.30961 0.12886 0.03057 0.86123 2 1842.35212 1118 1575.88186 8 62048.092 102 +CotColor ss715579217 phavu.Chr02 38890432 6.78925 0.00117 0.00941 11.68582 6.5249E-4 1.88344 0.17022 2 10605.48231 1116 1562.09875 8 63898.50018 161 +CotColor ss715579220 phavu.Chr02 38991215 1.25026 0.28683 0.00174 2.45417 0.1175 0.04843 0.82586 2 1968.34403 1119 1574.35024 8 62319.47879 126 +CotColor ss715579223 phavu.Chr02 39059282 1.26484 0.28269 0.00177 2.50429 0.11382 0.02757 0.86816 2 1992.89734 1118 1575.61255 8 62085.72831 126 +CotColor ss715579225 phavu.Chr02 39085506 5.47057 0.00432 0.00768 10.94377 9.6949E-4 0.00722 0.93229 2 8554.68101 1103 1563.76282 8 62995.6176 163 +CotColor ss715579227 phavu.Chr02 39112133 0.07523 0.78393 5.2464E-5 0.07523 0.78393 NaN NaN 1 118.58173 1120 1576.35359 7 70676.81771 23 +CotColor ss715579229 phavu.Chr02 39140734 3.99959 0.01859 0.00557 7.93008 0.00495 0.07571 0.78324 2 6242.04638 1109 1560.66968 8 63705.14877 176 +CotColor ss715579234 phavu.Chr02 39225427 0.00766 0.93027 5.4816E-6 0.00766 0.93027 NaN NaN 1 12.173 1103 1589.1146 7 66844.48888 34 +CotColor ss715579236 phavu.Chr02 39258554 0.45311 0.501 3.3396E-4 0.45311 0.501 NaN NaN 1 726.28856 1081 1602.88979 7 63152.04596 28 +CotColor ss715579237 phavu.Chr02 39280018 1.27569 0.27964 0.00178 2.50675 0.11364 0.04678 0.82881 2 2010.77294 1117 1576.21893 8 62000.21992 126 +CotColor ss715579238 phavu.Chr02 39309715 1.14422 0.31885 0.00161 1.94447 0.16346 0.34511 0.55702 2 1806.40783 1114 1578.72798 8 61501.12133 124 +CotColor ss715579240 phavu.Chr02 39333728 0.98907 0.37225 0.00138 1.92819 0.16523 0.0516 0.82035 2 1560.25744 1116 1577.49216 8 61783.87795 123 +CotColor ss715579241 phavu.Chr02 39381297 0.87587 0.41679 0.00123 1.32039 0.25077 0.43203 0.51113 2 1378.98973 1113 1574.41946 8 61928.21656 124 +CotColor ss715579242 phavu.Chr02 39405104 7.02752 9.2693E-4 0.00971 12.43038 4.3953E-4 1.61778 0.20367 2 10944.13064 1116 1557.32572 8 64594.42007 157 +CotColor ss715579243 phavu.Chr02 39438340 0.84219 0.43104 0.00117 1.68175 0.19496 0.00413 0.94877 2 1326.86923 1119 1575.49675 8 62159.11009 124 +CotColor ss715579253 phavu.Chr02 39646861 4.9484 0.00725 0.00686 9.54356 0.00206 0.35872 0.54934 2 7733.28382 1116 1562.78524 8 63806.7084 58 +CotColor ss715579254 phavu.Chr02 39694318 1.09691 0.33426 0.00153 1.98078 0.15959 0.21443 0.64341 2 1728.91253 1118 1576.16586 8 61985.58775 103 +CotColor ss715579257 phavu.Chr02 39770819 0.00457 0.9461 3.1897E-6 0.00457 0.9461 NaN NaN 1 7.20946 1120 1576.45303 7 70660.90739 22 +CotColor ss715579259 phavu.Chr02 39798890 6.68168 0.00131 0.00951 10.60229 0.00116 2.74414 0.0979 2 10473.91957 1092 1567.55857 8 61436.28056 61 +CotColor ss715579269 phavu.Chr02 39902829 5.87236 0.0029 0.00815 11.47717 7.2912E-4 0.275 0.6001 2 9179.57373 1116 1563.18354 8 63651.3638 164 +CotColor ss715579272 phavu.Chr02 39979488 5.03022 0.00669 0.00699 9.39381 0.00223 0.66941 0.41343 2 7866.81599 1115 1563.91162 8 63536.49116 58 +CotColor ss715579273 phavu.Chr02 40014278 11.02679 9.27E-4 0.00762 11.02679 9.27E-4 NaN NaN 1 17213.80596 1120 1561.09 7 73118.9926 60 +CotColor ss715579274 phavu.Chr02 40040347 5.92598 0.00275 0.0082 11.66166 6.6089E-4 0.19866 0.65589 2 9260.35234 1118 1562.66918 8 63918.14856 177 +CotColor ss715579276 phavu.Chr02 40092915 10.74019 2.3979E-5 0.01474 19.95487 8.7333E-6 1.51631 0.21844 2 16639.90498 1118 1549.31131 8 65723.62174 200 +CotColor ss715579278 phavu.Chr02 40173679 8.38933 2.4188E-4 0.01154 15.5287 8.6293E-5 1.24654 0.26445 2 13041.70292 1119 1554.55871 8 65087.81851 233 +CotColor ss715579279 phavu.Chr02 40218622 9.06921 1.2385E-4 0.01247 17.97623 2.4213E-5 0.17544 0.6754 2 14085.51839 1118 1553.11394 8 65294.61123 240 +CotColor ss715579282 phavu.Chr02 40261579 7.20849 7.7518E-4 0.00995 12.69753 3.8156E-4 1.71139 0.19107 2 11237.86355 1118 1558.97507 8 64373.11138 229 +CotColor ss715579283 phavu.Chr02 40288008 6.50996 0.00155 0.009 13.00127 3.2501E-4 0.02993 0.86268 2 10147.8842 1117 1558.8249 8 64286.67697 231 +CotColor ss715579285 phavu.Chr02 40317959 8.3335 2.5558E-4 0.01148 16.67624 4.7489E-5 0.00557 0.94051 2 12959.65476 1118 1555.12801 8 65013.14533 241 +CotColor ss715579287 phavu.Chr02 40359069 8.58777 1.9896E-4 0.01183 16.64128 4.8362E-5 0.5411 0.46213 2 13342.97542 1117 1553.71815 8 65058.970238 +CotColor ss715579288 phavu.Chr02 40383855 7.57141 5.4191E-4 0.0105 14.8545 1.2276E-4 0.29768 0.58545 2 11816.83276 1114 1560.71787 8 63976.18306 237 +CotColor ss715579290 phavu.Chr02 40427922 8.4469 2.2864E-4 0.01166 15.4038 9.2139E-5 1.48332 0.22351 2 13090.87539 1112 1549.78399 8 65167.98492 231 +CotColor ss715579292 phavu.Chr02 40449654 7.77549 4.4314E-4 0.01074 15.43643 9.0583E-5 0.12666 0.72199 2 12053.99515 1112 1550.25508 8 65133.12624 232 +CotColor ss715579293 phavu.Chr02 40482690 5.91493 0.00278 0.00822 11.40463 7.5787E-4 0.43105 0.51161 2 9249.37086 1115 1563.73173 8 63525.39577 228 +CotColor ss715579297 phavu.Chr02 40584802 7.83416 4.1811E-4 0.0108 13.8209 2.1104E-4 1.83707 0.17557 2 12197.24519 1118 1556.93181 8 64733.23486 225 +CotColor ss715579298 phavu.Chr02 40615333 7.54431 5.565E-4 0.01041 13.38453 2.6552E-4 1.69576 0.19311 2 11754.4169 1118 1558.05101 8 64502.24972 228 +CotColor ss715579300 phavu.Chr02 40652161 7.25815 7.3819E-4 0.01004 13.78749 2.1481E-4 0.73212 0.39238 2 11302.35394 1115 1557.19478 8 64378.05581 238 +CotColor ss715579302 phavu.Chr02 40710407 0.00457 0.9461 3.1897E-6 0.00457 0.9461 NaN NaN 1 7.20946 1120 1576.45303 7 70660.90739 22 +CotColor ss715579304 phavu.Chr02 40775244 9.77252 6.2027E-5 0.01346 19.55761 1.0714E-5 0.00486 0.94444 2 15168.99206 1116 1552.20849 8 65245.48391 237 +CotColor ss715579307 phavu.Chr02 40878607 9.34414 9.4553E-5 0.01295 18.64488 1.7153E-5 0.05919 0.80783 2 14508.08295 1110 1552.64007 8 64692.43795 239 +CotColor ss715579308 phavu.Chr02 40910831 9.56585 7.5994E-5 0.01317 17.62033 2.9109E-5 1.50344 0.2204 2 14856.85927 1117 1553.11457 8 65253.60864 236 +CotColor ss715579310 phavu.Chr02 40936653 18.07364 2.3024E-5 0.0124 18.07364 2.3024E-5 NaN NaN 1 28005.23592 1119 1549.50721 7 74832.50191 244 +CotColor ss715579311 phavu.Chr02 40964222 9.13474 1.1612E-4 0.01257 17.27714 3.477E-5 0.99246 0.31936 2 14192.55088 1118 1553.68941 8 65111.78321 240 +CotColor ss715579312 phavu.Chr02 41020729 9.10184 1.1993E-4 0.0125 17.66285 2.8472E-5 0.54796 0.45931 2 14131.61303 1119 1552.61071 8 65360.29604 239 +CotColor ss715579314 phavu.Chr02 41068135 9.52736 7.8926E-5 0.0131 19.06418 1.3811E-5 0.00745 0.93122 2 14769.59312 1117 1550.23006 8 65467.47283 236 +CotColor ss715579315 phavu.Chr02 41099473 6.98666 9.6514E-4 0.00967 13.88461 2.0412E-4 0.09991 0.752 2 10884.82025 1115 1557.94372 8 64273.67239 210 +CotColor ss715579317 phavu.Chr02 41141330 9.29568 9.9128E-5 0.0128 18.52218 1.8266E-5 0.08436 0.77153 2 14428.62062 1116 1552.1859 8 65185.69297 244 +CotColor ss715579318 phavu.Chr02 41176370 0.42292 0.65523 5.9395E-4 0.08645 0.76879 0.75941 0.3837 2 667.27676 1112 1577.77041 8 61555.51398 265 +CotColor ss715579320 phavu.Chr02 41261358 3.89569 0.02061 0.00543 6.13874 0.01337 1.64905 0.19936 2 6101.21444 1112 1566.14501 8 63264.34696 65 +CotColor ss715579323 phavu.Chr02 41331273 6.77158 0.00119 0.00937 13.38952 2.6486E-4 0.16367 0.68588 2 10554.00311 1115 1558.57352 8 64353.06687 551 +CotColor ss715579325 phavu.Chr02 41361574 7.4156 6.3191E-4 0.01026 14.78679 1.2717E-4 0.0569 0.81151 2 11563.54958 1116 1559.35402 8 64313.29257 551 +CotColor ss715579326 phavu.Chr02 41412525 8.33556 2.5522E-4 0.01163 16.07262 6.5043E-5 0.60425 0.43713 2 12986.91659 1106 1558.01305 8 63856.25757 548 +CotColor ss715579328 phavu.Chr02 41434481 0.30277 0.73883 4.2692E-4 0.27298 0.60145 0.33273 0.56417 2 477.25599 1107 1576.29088 8 61356.94005 26 +CotColor ss715579329 phavu.Chr02 41463652 8.12728 3.1328E-4 0.01124 15.89721 7.1242E-5 0.36641 0.54509 2 12614.0196 1111 1552.05864 8 64979.405548 +CotColor ss715579336 phavu.Chr02 41586720 9.00954 1.3137E-4 0.01246 17.41859 3.2319E-5 0.60664 0.43622 2 14036.23733 1115 1557.93005 8 64378.74829 223 +CotColor ss715579367 phavu.Chr02 41786607 0.02265 0.9776 3.1693E-5 0.00476 0.94502 0.04055 0.84045 2 35.75198 1117 1578.30019 8 61650.39701 22 +CotColor ss715579369 phavu.Chr02 41828455 0.86241 0.42243 0.00121 1.48816 0.22276 0.23767 0.62599 2 1356.30368 1113 1572.69599 8 62260.76353 291 +CotColor ss715579371 phavu.Chr02 41908385 0.91093 0.40245 0.00127 1.4777 0.22439 0.34502 0.55707 2 1432.34517 1117 1572.40613 8 62392.47866 290 +CotColor ss715579373 phavu.Chr02 41946239 0.00216 0.96293 1.5071E-6 0.00216 0.96293 NaN NaN 1 3.40643 1120 1576.45643 7 70660.3641 22 +CotColor ss715579374 phavu.Chr02 41969316 0.84163 0.43128 0.00117 1.59947 0.20624 0.0851 0.77056 2 1323.93977 1118 1573.06634 8 62412.50455 290 +CotColor ss715579376 phavu.Chr02 42013575 0.86279 0.42227 0.00123 1.64211 0.20031 0.08484 0.7709 2 1357.28481 1093 1573.13959 8 61101.8975 268 +CotColor ss715579380 phavu.Chr02 42067284 0.41685 0.65922 5.8396E-4 0.06146 0.80425 0.77226 0.37971 2 657.72715 1115 1577.83583 8 61670.40288 21 +CotColor ss715579382 phavu.Chr02 42155458 3.32712 0.03625 0.00464 6.60501 0.0103 0.05483 0.81491 2 5214.51145 1112 1567.27718 8 63041.43438 542 +CotColor ss715579384 phavu.Chr02 42255290 9.06012 1.2496E-4 0.01245 17.52007 3.0656E-5 0.60633 0.43634 2 14067.87471 1119 1552.72463 8 65344.36146 224 +CotColor ss715579387 phavu.Chr02 42292513 0.63364 0.53085 8.8775E-4 1.22474 0.26867 0.04358 0.83467 2 997.98363 1113 1575.00954 8 61920.0988 279 +CotColor ss715579389 phavu.Chr02 42337744 1.0679 0.34408 0.00149 0.54861 0.45904 1.58691 0.20803 2 1679.51751 1117 1572.72746 8 62388.31157 23 +CotColor ss715579390 phavu.Chr02 42372540 8.54739 2.0702E-4 0.01176 16.50052 5.2028E-5 0.60017 0.43868 2 13271.87467 1118 1552.7395 8 65229.79943 223 +CotColor ss715579392 phavu.Chr02 42417680 1.38691 0.25028 0.00195 2.43048 0.11928 0.34477 0.55721 2 2183.34685 1108 1574.2563 8 61768.86649 264 +CotColor ss715579393 phavu.Chr02 42448605 8.22381 2.8473E-4 0.01132 15.87665 7.1977E-5 0.57697 0.44766 2 12788.12576 1119 1555.01194 8 65024.42422 243 +CotColor ss715579394 phavu.Chr02 42469258 9.62901 7.1419E-5 0.01322 17.75838 2.7102E-5 1.49183 0.22219 2 14918.80855 1117 1549.36043 8 65708.74502 242 +CotColor ss715579397 phavu.Chr02 42606583 7.48339 5.9116E-4 0.01041 14.24943 1.6857E-4 0.72092 0.39603 2 11669.90487 1111 1559.4416 8 63740.18782 218 +CotColor ss715579398 phavu.Chr02 42672105 0.19433 0.82341 2.7354E-4 0.23584 0.62732 0.15301 0.69575 2 306.75148 1110 1578.48038 8 61341.93503 25 +CotColor ss715579404 phavu.Chr02 42838494 7.33555 6.8389E-4 0.01014 14.33864 1.6083E-4 0.34093 0.55941 2 11423.04383 1115 1557.21728 8 64481.58044 251 +CotColor ss715579407 phavu.Chr02 42926060 0.86769 0.4202 0.00122 0.37973 0.53788 1.35553 0.24456 2 1369.09003 1115 1577.85823 8 61704.45294 26 +CotColor ss715579409 phavu.Chr02 42965909 0.2079 0.81232 2.9175E-4 0.30421 0.58137 0.11184 0.73813 2 328.20455 1114 1578.65465 8 61406.69366 20 +CotColor ss715579411 phavu.Chr02 42996883 0.17766 0.83725 2.4821E-4 0.34793 0.55541 0.00771 0.93006 2 280.06972 1117 1576.39929 8 61980.03354 309 +CotColor ss715579413 phavu.Chr02 43043525 5.49956 0.0042 0.00776 9.44454 0.00217 1.54981 0.21343 2 8520.36392 1086 1549.27991 8 64103.02479 233 +CotColor ss715579414 phavu.Chr02 43073364 4.9151 0.00749 0.0068 9.09714 0.00262 0.73521 0.39138 2 7672.94691 1117 1561.09717 8 63974.52131 227 +CotColor ss715579416 phavu.Chr02 43128583 1.99859 0.13601 0.00279 6.7173E-4 0.97933 3.99651 0.04584 2 3137.33929 1114 1569.77615 8 62634.63616 194 +CotColor ss715579417 phavu.Chr02 43166682 1.29851 0.27335 0.00183 2.57379 0.10893 0.02549 0.87319 2 2044.66885 1108 1574.62985 8 61673.06814 290 +CotColor ss715579419 phavu.Chr02 43193982 1.21588 0.29684 0.0017 2.43202 0.11916 0.00193 0.965 2 1916.99973 1115 1576.63115 8 61635.66154 298 +CotColor ss715579420 phavu.Chr02 43219027 1.23016 0.29264 0.00171 0.18424 0.66784 2.27587 0.13168 2 1933.78321 1118 1571.97539 8 62564.96541 238 +CotColor ss715579421 phavu.Chr02 43240525 0.12402 0.88337 1.7515E-4 0.23204 0.63011 0.01621 0.89871 2 195.34923 1104 1575.11431 8 61472.86863 478 +CotColor ss715579423 phavu.Chr02 43313817 0.27797 0.75737 3.9123E-4 0.05074 0.82182 0.50522 0.47736 2 437.05043 1106 1572.28893 8 61911.0743 233 +CotColor ss715579424 phavu.Chr02 43384073 1.04096 0.35346 0.00145 1.90604 0.16768 0.17729 0.6738 2 1638.00496 1118 1573.55143 8 62321.332 306 +CotColor ss715579425 phavu.Chr02 43411717 1.64453 0.19357 0.0023 3.04698 0.08116 0.24414 0.62133 2 2590.5462 1115 1575.25402 8 61951.6632 283 +CotColor ss715579426 phavu.Chr02 43438850 0.87202 0.41839 0.00122 1.73797 0.18767 0.00761 0.9305 2 1374.69479 1115 1576.45257 8 61726.1941 302 +CotColor ss715579427 phavu.Chr02 43470524 0.82636 0.43791 0.00115 1.42287 0.23318 0.23083 0.63101 2 1301.96547 1119 1575.54127 8 62152.88415 303 +CotColor ss715579429 phavu.Chr02 43509234 0.93079 0.39455 0.0013 1.4541 0.22813 0.40826 0.52299 2 1465.40951 1112 1574.37097 8 61957.39556 295 +CotColor ss715579431 phavu.Chr02 43546615 0.79109 0.4536 0.00111 1.5803 0.20898 0.00329 0.95424 2 1249.62669 1116 1579.62653 8 61358.75927 297 +CotColor ss715579434 phavu.Chr02 43632505 0.34607 0.70754 4.8334E-4 0.69026 0.40625 0.00249 0.9602 2 545.86749 1118 1577.33514 8 61909.69851 522 +CotColor ss715579437 phavu.Chr02 43694104 0.31983 0.72634 4.4628E-4 0.62769 0.42837 0.01251 0.91095 2 504.35653 1119 1576.96684 8 61953.48191 262 +CotColor ss715579443 phavu.Chr02 43812689 0.85757 0.42447 0.0012 0.24482 0.62084 1.47022 0.22557 2 1351.62305 1117 1576.09975 8 61893.5006 241 +CotColor ss715579445 phavu.Chr02 43851078 0.10884 0.89689 1.5193E-4 0.11248 0.7374 0.10529 0.74563 2 171.69755 1119 1577.5614 8 61870.31717 253 +CotColor ss715579447 phavu.Chr02 43903960 1.27261 0.2805 0.00177 2.14634 0.14319 0.40003 0.5272 2 2003.457 1119 1574.28748 8 62328.25703 108 +CotColor ss715579448 phavu.Chr02 43931909 1.53639 0.21561 0.00215 1.79904 0.1801 1.2733 0.25939 2 2418.93381 1112 1574.42908 8 61917.56911 247 +CotColor ss715579449 phavu.Chr02 43971846 0.97466 0.37764 0.00136 0.95073 0.32975 0.99859 0.31787 2 1532.71957 1115 1572.57116 8 62270.05104 297 +CotColor ss715579450 phavu.Chr02 44006187 0.06331 0.93866 8.8847E-5 0.08923 0.76521 0.03746 0.84657 2 100.03219 1115 1580.13869 8 61242.91017 136 +CotColor ss715579454 phavu.Chr02 44086839 2.52922 0.08018 0.00354 1.56439 0.21129 3.49055 0.06198 2 3975.48412 1112 1571.81982 8 62199.037 212 +CotColor ss715579456 phavu.Chr02 44117573 2.34071 0.09673 0.00327 4.56542 0.03284 0.11961 0.72953 2 3685.7105 1116 1574.60946 8 62094.29293 553 +CotColor ss715579459 phavu.Chr02 44210028 2.2331 0.10767 0.00311 3.46261 0.06303 1.00357 0.31666 2 3508.99964 1118 1571.35939 8 62700.13983 553 +CotColor ss715579460 phavu.Chr02 44243447 5.04262 0.00661 0.00705 9.96221 0.00164 0.13085 0.71762 2 7869.52719 1106 1560.60261 8 63378.14947 512 +CotColor ss715579462 phavu.Chr02 44276483 6.58688 0.00143 0.00909 13.16727 2.9776E-4 0.01806 0.89313 2 10246.2532 1116 1555.55588 8 64739.32865 458 +CotColor ss715579463 phavu.Chr02 44297292 7.6166 5.1833E-4 0.01059 13.6369 2.3254E-4 1.58908 0.20772 2 11896.85564 1112 1561.96373 8 63707.70643 459 +CotColor ss715579465 phavu.Chr02 44323825 10.85366 2.1457E-5 0.01488 21.72069 3.5335E-6 0.00596 0.93848 2 16760.98249 1116 1544.26992 8 66256.10873 358 +CotColor ss715579467 phavu.Chr02 44373187 1.30334 0.27204 0.00183 2.52653 0.11223 0.08222 0.77436 2 2053.91896 1113 1575.8923 8 61733.42886 524 +CotColor ss715579469 phavu.Chr02 44405328 4.97852 0.00704 0.00693 9.87824 0.00172 0.08692 0.76819 2 7787.60613 1112 1564.23965 8 63489.52965 243 +CotColor ss715579470 phavu.Chr02 44442390 1.84081 0.15917 0.00258 1.66846 0.19673 2.01163 0.15638 2 2889.79392 1109 1569.85276 8 62395.75062 274 +CotColor ss715579471 phavu.Chr02 44486013 1.89164 0.15131 0.00263 3.3789 0.0663 0.40618 0.52405 2 2974.70515 1119 1572.55156 8 62571.06907 270 +CotColor ss715579477 phavu.Chr02 44633514 2.54521 0.07891 0.00355 3.27142 0.07077 1.81662 0.17799 2 3999.10909 1115 1571.22663 8 62594.68769 275 +CotColor ss715579478 phavu.Chr02 44673660 2.94128 0.05321 0.0041 5.88023 0.01547 0.00756 0.93073 2 4615.2038 1115 1569.11264 8 62879.00315 278 +CotColor ss715579480 phavu.Chr02 44723768 2.92617 0.05401 0.00407 5.55045 0.01865 0.30533 0.58067 2 4586.04086 1116 1567.25278 8 63157.92244 269 +CotColor ss715579485 phavu.Chr02 44860334 1.69014 0.18496 0.00236 2.26173 0.13289 1.11831 0.29051 2 2660.81707 1118 1574.31863 8 62228.84976 264 +CotColor ss715579486 phavu.Chr02 44896742 2.77196 0.06297 0.00385 4.95376 0.02623 0.59197 0.44182 2 4347.66843 1118 1568.44498 8 63058.57988 279 +CotColor ss715579487 phavu.Chr02 44937500 2.36812 0.09413 0.00329 4.71693 0.03008 0.02343 0.87837 2 3715.34094 1117 1568.89994 8 62928.91171 279 +CotColor ss715579488 phavu.Chr02 44965732 3.31653 0.03664 0.00463 5.42505 0.02003 1.20699 0.27217 2 5201.37173 1113 1568.31865 8 62773.35919 281 +CotColor ss715579490 phavu.Chr02 45006011 2.74248 0.06485 0.00383 5.36285 0.02075 0.12632 0.72235 2 4299.22492 1112 1567.64016 8 62862.45914 273 +CotColor ss715579492 phavu.Chr02 45056798 3.57467 0.02835 0.00497 5.80888 0.01611 1.3387 0.24751 2 5611.55308 1117 1569.80989 8 62898.6441 282 +CotColor ss715579494 phavu.Chr02 45082898 2.99472 0.05045 0.00417 4.84383 0.02795 1.14497 0.28484 2 4700.01478 1115 1569.43617 8 62899.02854 278 +CotColor ss715579496 phavu.Chr02 45169738 0.56777 0.56695 7.9519E-4 0.91154 0.33991 0.22464 0.63562 2 893.70625 1113 1574.05312 8 61983.66948 294 +CotColor ss715579497 phavu.Chr02 45198324 2.61808 0.07339 0.00365 4.45212 0.03508 0.78489 0.37584 2 4116.17997 1117 1572.21589 8 62431.70584 276 +CotColor ss715579498 phavu.Chr02 45235552 2.39443 0.09169 0.00333 4.40835 0.03599 0.38295 0.53616 2 3764.99574 1118 1572.39522 8 62545.96989 282 +CotColor ss715579500 phavu.Chr02 45269288 0.79982 0.44967 0.00112 0.6123 0.43409 0.98735 0.32061 2 1260.96616 1115 1576.55643 8 61771.62985 241 +CotColor ss715579502 phavu.Chr02 45309459 1.83465 0.16015 0.00256 1.71277 0.1909 1.95506 0.16232 2 2889.84708 1116 1575.15091 8 62066.40012 244 +CotColor ss715579504 phavu.Chr02 45347268 1.06687 0.34443 0.00149 1.15924 0.28185 0.97453 0.32377 2 1681.32843 1117 1575.94057 8 61932.31276 239 +CotColor ss715579513 phavu.Chr02 45535684 1.33249 0.26424 0.00186 2.02932 0.15457 0.63632 0.42522 2 2096.92182 1116 1573.68823 8 62317.6465 103 +CotColor ss715579514 phavu.Chr02 45577562 18.5904 1.1428E-8 0.0252 36.84539 1.7499E-9 0.35666 0.55049 2 28397.13394 1115 1527.51566 8 68778.70772 203 +CotColor ss715579516 phavu.Chr02 45626060 5.83471 0.00301 0.00808 11.61181 6.7869E-4 0.0673 0.79536 2 9109.68667 1116 1561.2929 8 63956.91557 39 +CotColor ss715579517 phavu.Chr02 45661398 18.77262 9.5837E-9 0.02549 37.10166 1.5413E-9 0.4615 0.49707 2 28686.42924 1114 1528.09953 8 68609.58112 207 +CotColor ss715579518 phavu.Chr02 45706109 22.88521 1.8168E-10 0.03071 45.55717 2.3759E-11 0.24405 0.62139 2 34645.72232 1117 1513.89129 8 70655.48419 247 +CotColor ss715579519 phavu.Chr02 45728502 6.5128 0.00154 0.00906 12.48773 4.2641E-4 0.54298 0.46136 2 10188.41469 1114 1564.36893 8 63434.9998 31 +CotColor ss715579520 phavu.Chr02 45754481 13.86114 2.0663E-4 0.00956 13.86114 2.0663E-4 NaN NaN 1 21588.48718 1119 1557.48315 7 73679.97345 37 +CotColor ss715579523 phavu.Chr02 45893563 15.26565 2.8798E-7 0.02083 30.55739 4.034E-8 0.00126 0.97165 2 23461.26975 1115 1536.86625 8 67381.95131 199 +CotColor ss715579524 phavu.Chr02 45935654 16.44011 9.186E-8 0.02232 31.8401 2.1199E-8 1.03902 0.30827 2 25203.45481 1118 1533.04626 8 68057.67422 106 +CotColor ss715579525 phavu.Chr02 45965901 16.37697 9.7671E-8 0.02225 31.39439 2.65E-8 1.34973 0.24557 2 25126.668 1118 1534.26846 8 67857.153104 +CotColor ss715579528 phavu.Chr02 46036919 16.67047 7.3508E-8 0.02271 31.79578 2.1692E-8 1.53004 0.21637 2 25552.57638 1114 1532.80515 8 67877.22917 191 +CotColor ss715579532 phavu.Chr02 46103306 11.00231 1.8548E-5 0.01513 21.77978 3.4287E-6 0.23968 0.62453 2 17039.58779 1115 1548.72831 8 65662.25833 25 +CotColor ss715579533 phavu.Chr02 46125766 32.79048 1.3176E-8 0.02222 32.79048 1.3176E-8 NaN NaN 1 50222.48245 1120 1531.61797 7 77834.51781 59 +CotColor ss715579535 phavu.Chr02 46166859 30.7338 3.6916E-8 0.02095 30.7338 3.6916E-8 NaN NaN 1 47236.14419 1117 1536.94459 7 76805.6262 55 +CotColor ss715579538 phavu.Chr02 46230017 0.81436 0.36703 5.6818E-4 0.81436 0.36703 NaN NaN 1 1283.0544 1119 1575.53267 7 70737.69483 1 +CotColor ss715579541 phavu.Chr02 46337600 NaN NaN 0 NaN NaN NaN NaN 0 NaN 1120 1575.31485 6 82342.41402 0 +CotColor ss715579543 phavu.Chr02 46376169 9.39446 8.9932E-5 0.0129 18.71628 1.6521E-5 0.08788 0.76695 2 14578.43327 1119 1551.8121 8 65472.00131 +CotColor ss715579549 phavu.Chr02 46542618 2.75653 0.06394 0.00384 3.80161 0.05145 1.70905 0.19138 2 4332.25274 1117 1571.62989 8 62654.07051 122 +CotColor ss715579550 phavu.Chr02 46610204 3.07839 0.04642 0.00427 3.72036 0.05401 2.43165 0.11919 2 4825.33957 1118 1567.48945 8 63191.51797 124 +CotColor ss715579552 phavu.Chr02 46662438 0.84893 0.42815 0.00119 0.34429 0.55748 1.35346 0.24492 2 1336.60819 1111 1574.45891 8 61871.76312 95 +CotColor ss715579553 phavu.Chr02 46694829 7.73907 4.5919E-4 0.01066 15.39815 9.2377E-5 0.09248 0.76111 2 12044.6339 1119 1556.34079 8 64838.55126 28 +CotColor ss715579555 phavu.Chr02 46745928 7.79718 4.337E-4 0.01077 14.32658 1.6185E-4 1.26438 0.26107 2 12123.39624 1115 1554.84388 8 64785.29971 486 +CotColor ss715579557 phavu.Chr02 46770056 7.58067 5.3698E-4 0.01049 14.04614 1.8752E-4 1.11376 0.2915 2 11800.94123 1114 1556.71549 8 64441.13393 488 +CotColor ss715579558 phavu.Chr02 46798900 7.60152 5.2597E-4 0.01051 14.75908 1.2903E-4 0.45121 0.5019 2 11851.60061 1117 1559.10852 8 64334.31774 25 +CotColor ss715579560 phavu.Chr02 46851639 6.60415 0.00141 0.00918 11.65578 6.6309E-4 1.5468 0.21387 2 10316.37295 1112 1562.10396 8 63858.62531 529 +CotColor ss715579568 phavu.Chr02 47064250 9.24914 1.0379E-4 0.01273 15.01679 1.1277E-4 3.44851 0.06357 2 14320.35423 1114 1548.29056 8 65628.41972 491 +CotColor ss715579569 phavu.Chr02 47109955 8.02239 3.4751E-4 0.01117 15.87986 7.1914E-5 0.17674 0.67427 2 12470.86696 1105 1554.50736 8 64384.29119 475 +CotColor ss715579572 phavu.Chr02 47187003 8.21658 2.8686E-4 0.01139 15.32992 9.5757E-5 1.10184 0.29409 2 12801.49508 1113 1558.00755 8 64315.51217 489 +CotColor ss715579574 phavu.Chr02 47239868 7.58629 5.3411E-4 0.01055 11.66451 6.6006E-4 3.482 0.0623 2 11832.04 1110 1559.66166 8 63911.16678 466 +CotColor ss715579577 phavu.Chr02 47245656 0.04924 0.95196 6.8738E-5 0.08719 0.76784 0.01136 0.91512 2 77.68296 1119 1577.72944 8 61846.81352 17 +CotColor ss715579579 phavu.Chr02 47343692 0.45902 0.63202 6.4121E-4 0.33902 0.56051 0.57915 0.44681 2 723.91066 1118 1577.08811 8 61846.64083 95 +CotColor ss715579580 phavu.Chr02 47383563 7.15893 8.1426E-4 0.00994 14.32963 1.6162E-4 0.00109 0.97369 2 11172.33934 1112 1560.61625 8 63928.00902 484 +CotColor ss715579581 phavu.Chr02 47407070 7.53405 5.6241E-4 0.01044 14.91735 1.1883E-4 0.16202 0.68738 2 11688.90804 1108 1551.47766 8 64986.89363 481 +CotColor ss715579582 phavu.Chr02 47417553 9.73352 6.4446E-5 0.01338 19.09682 1.3581E-5 0.3808 0.5373 2 15098.32692 1117 1551.168 8 65476.24456 556 +CotColor ss715579583 phavu.Chr02 47422654 1.51883 0.21942 0.00212 0.37459 0.54064 2.66252 0.10302 2 2392.07169 1118 1574.93858 8 62173.50463 98 +CotColor ss715579586 phavu.Chr02 47428124 5.35119 0.00486 0.00742 10.68687 0.00111 0.02483 0.87482 2 8364.52809 1116 1563.11608 8 63730.74459 527 +CotColor ss715579587 phavu.Chr02 47434861 5.33197 0.00496 0.00739 10.55523 0.00119 0.11708 0.73229 2 8307.79482 1113 1558.1089 8 64274.83502 532 +CotColor ss715579591 phavu.Chr02 47462126 10.26001 3.8418E-5 0.01406 19.61091 1.0423E-5 0.91068 0.34014 2 15872.64982 1118 1547.04003 8 66049.68206 558 +CotColor ss715579592 phavu.Chr02 47476910 5.50696 0.00417 0.0077 10.99023 9.4553E-4 0.03326 0.85531 2 8633.09121 1110 1567.66773 8 62872.96792 533 +CotColor ss715579593 phavu.Chr02 47478669 0.81304 0.36742 5.6769E-4 0.81304 0.36742 NaN NaN 1 1281.84597 1119 1576.60339 7 70541.85707 1 +CotColor ss715579594 phavu.Chr02 47481216 9.63297 7.1137E-5 0.01324 19.16095 1.3139E-5 0.12005 0.72904 2 14946.94003 1118 1551.64441 8 65402.39817 555 +CotColor ss715579596 phavu.Chr02 47492612 0.07782 0.78032 5.4277E-5 0.07782 0.78032 NaN NaN 1 122.67871 1120 1576.34994 7 70677.40299 18 +CotColor ss715579597 phavu.Chr02 47495955 5.6205 0.00373 0.00776 11.02076 9.3007E-4 0.22786 0.63321 2 8749.35525 1116 1556.68526 8 64538.16256 532 +CotColor ss715579598 phavu.Chr02 47500350 0.61907 0.43156 4.3168E-4 0.61907 0.43156 NaN NaN 1 974.91044 1119 1574.79122 7 70888.32614 2 +CotColor ss715579599 phavu.Chr02 47503665 9.39685 8.9733E-5 0.01294 18.81031 1.5742E-5 2.1301E-4 0.98836 2 14593.145 1117 1552.98236 8 65152.69424 556 +CotColor ss7155796phavu.Chr02 01 47516500 6.44581 0.00165 0.00892 12.3937 4.4816E-4 0.50344 0.47814 2 10047.11401 1115 1558.7047 8 64279.46854 529 +CotColor ss715579603 phavu.Chr02 47545922 0.81379 0.3672 5.6718E-4 0.81379 0.3672 NaN NaN 1 1281.97811 1120 1575.31485 7 70843.01719 1 +CotColor ss715579604 phavu.Chr02 47548257 5.93998 0.00272 0.00822 10.98174 9.4961E-4 0.89921 0.3432 2 9279.88 1118 1562.27421 8 63985.21183 531 +CotColor ss715579605 phavu.Chr02 47549333 1.24827 0.2874 0.00174 0.42338 0.51539 2.07276 0.15023 2 1963.25427 1117 1572.7787 8 62431.83177 96 +CotColor ss715579606 phavu.Chr02 47553127 7.75322 4.5287E-4 0.0107 15.06102 1.1018E-4 0.45279 0.50115 2 12068.96278 1117 1556.63815 8 64635.27309 26 +CotColor ss715579607 phavu.Chr02 47559286 7.89349 3.9441E-4 0.01094 15.28389 9.8076E-5 0.5098 0.47538 2 12316.32743 1115 1560.31546 8 64025.55604 22 +CotColor ss715579608 phavu.Chr02 47574690 0.0054 0.94146 3.7698E-6 0.0054 0.94146 NaN NaN 1 8.51277 1119 1577.79889 7 70373.20091 15 +CotColor ss715579610 phavu.Chr02 47607800 7.81446 4.2635E-4 0.01081 14.55035 1.4393E-4 1.07755 0.29947 2 12189.5289 1116 1559.86931 8 64178.30124 20 +CotColor ss715579613 phavu.Chr02 47641758 4.93237 0.00737 0.00684 9.70526 0.00188 0.16672 0.68312 2 7715.275 1116 1564.21295 8 63635.39194 544 +CotColor ss715579614 phavu.Chr02 47643080 9.01058 1.3122E-4 0.01241 17.88483 2.5386E-5 0.14993 0.69868 2 13997.86913 1117 1553.49263 8 65180.57291 546 diff --git a/src/canvas/DrawLazilyMixin.js b/src/canvas/DrawLazilyMixin.js index 3bc75441..b53901d7 100644 --- a/src/canvas/DrawLazilyMixin.js +++ b/src/canvas/DrawLazilyMixin.js @@ -7,14 +7,19 @@ import {Bounds} from '../model/Bounds'; export let DrawLazilyMixin = (superclass) => class extends superclass { + /** + * + * @param wantedBounds + */ + drawLazily(wantedBounds) { - if(wantedBounds.area === 0) return; - if(this._drawLazilyTimeoutId) clearTimeout(this._drawLazilyTimeoutId); - if(! Bounds.areaEquals(this.lastDrawnMithrilBounds, wantedBounds)) { + if (wantedBounds.area === 0) return; + if (this._drawLazilyTimeoutId) clearTimeout(this._drawLazilyTimeoutId); + if (!Bounds.areaEquals(this.lastDrawnMithrilBounds, wantedBounds)) { console.log('waiting for wantedBounds from mithril: ', wantedBounds.width, wantedBounds.height); let tid1 = this._drawLazilyTimeoutId = setTimeout(() => { - if(tid1 !== this._drawLazilyTimeoutId) return; + if (tid1 !== this._drawLazilyTimeoutId) return; this.drawLazily(wantedBounds); }); } @@ -22,8 +27,8 @@ export let DrawLazilyMixin = (superclass) => class extends superclass { console.log('scheduling lazy draw for: ', wantedBounds.width, wantedBounds.height); let tid2 = this._drawLazilyTimeoutId = setTimeout(() => { - if(tid2 !== this._drawLazilyTimeoutId) return; - if(! Bounds.areaEquals(this.lastDrawnCanvasBounds, wantedBounds)) { + if (tid2 !== this._drawLazilyTimeoutId) return; + if (!Bounds.areaEquals(this.lastDrawnCanvasBounds, wantedBounds)) { this.draw(); } }); diff --git a/src/canvas/README.md b/src/canvas/README.md index a3238f13..c06f811f 100644 --- a/src/canvas/README.md +++ b/src/canvas/README.md @@ -4,6 +4,11 @@ There are: * multiple canvas DOM elements, drawn offscreen and composited. * the src/canvas classes are organized into a "scenegraph" with parent-child relationships. + +## Directory Contents +`/canvas` - Top level drawing entities that bind a canvas context +`/layout` - Grouped entities +`/geometry` - Individual constituent geometries of a layout ## Node Classes: * `SceneGraphNodeBase` - base node that all others are based on. @@ -16,7 +21,7 @@ There are: of the same overall drawn geometry. SceneGraphNodeGroup could be extended to be a "track" style node setup for drawing complex groups of items, such as FeatureMap. * `SceneGraphNodeCanvas` - top level node for a given canvas * `BioMap` - A Canvas that contains at minimum a 'MapTrack' (mapbackbone + markers) - * `CorrespondenceMap` - A Canvas that contains only a Corresponence Map + * `CorrespondenceMap` - A Canvas that contains only a Correspondence Map * `SceneGraphNodeRoot` - NOT REIMPLEMENTED: SceneGraphNodeBase with explicit 0,0 top and left. Used for navigating rtrees between canvases. * `SceneGraphNodeText` - NOT IMPLEMENTED: Like Geometry, but for text items. diff --git a/src/canvas/TextAtlas.js b/src/canvas/TextAtlas.js index 615f13d2..b54f45dd 100644 --- a/src/canvas/TextAtlas.js +++ b/src/canvas/TextAtlas.js @@ -1,27 +1,27 @@ /** - * Pre-render text to an offscreen canvas, the technique is based on: - * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas - */ + * Pre-render text to an offscreen canvas, the technique is based on: + * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas + */ export class TextAtlas { /** * create a TextAtlas * @param Object - parameters object with properties: - * @param Array - words: an array of words of phrases to be rendered - * @param Array - fonts: array of font definitions, default ['12px sans-serif'] - * @param Number - lineHeight: will default to 1.5 times the font size in pixels - * @param Boolean - fill: whether to fill, or stroke (default true) + * @param {Array} words - an array of words of phrases to be rendered + * @param {Array} fonts - array of font definitions, default ['12px sans-serif'] + * @param {Boolean} fill - whether to fill, or stroke (default true) */ + constructor({ - words, - fonts=['16px sans-serif'], - fill=true, - }) { + words, + fonts = ['16px sans-serif'], + fill = true, + }) { this.words = words; this.fonts = fonts; this.fill = fill; this.atlases = {}; - this.words.sort( (a, b) => a.length > b.length ? -1 : 1); + this.words.sort((a, b) => a.length > b.length ? -1 : 1); this._initCanvases(); this._prepareAtlases(); } @@ -29,8 +29,9 @@ export class TextAtlas { /** * create a data structure for each font -> { canvas, 2d context, etc.. } */ + _initCanvases() { - this.fonts.forEach( font => { + this.fonts.forEach(font => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx.font = font; @@ -43,28 +44,29 @@ export class TextAtlas { // setting the width and height resets the context, so set the font again. ctx.font = font; ctx.save(); - this.atlases[font] = { ctx, canvas, lineHeight, lineWidth }; + this.atlases[font] = {ctx, canvas, lineHeight, lineWidth}; }); } /** * draw all the words on each font -> atlas and build an index for retrieval */ + _prepareAtlases() { this.index = {}; - this.fonts.forEach( font => { + this.fonts.forEach(font => { const atlas = this.atlases[font]; const lineHeight = atlas.lineHeight; const ctx = atlas.ctx; let cursor = lineHeight; ctx.font = font; - this.words.forEach( word => { - if(! this.index[word]) { + this.words.forEach(word => { + if (!this.index[word]) { this.index[word] = {}; } const textWidth = Math.ceil(ctx.measureText(word).width); ctx.fillText(word, 0, cursor); - this.index[word][font] = { x: 0, y: cursor, width: textWidth, height: lineHeight }; + this.index[word][font] = {x: 0, y: cursor, width: textWidth, height: lineHeight}; cursor += lineHeight; }); }); @@ -73,12 +75,13 @@ export class TextAtlas { /** * draw some text using the offscreen canvas technique. * @param Object params - an object with the following properties: - * @param String font - which font specification to use - * @param String word - which word or phrase to draw - * @param Object ctx - canvas context2d to draw into - * @param Number x - the horizontal coordinate - * @param Number y - ths vertical coordinate + * @param {String} font which font specification to use + * @param {String} word - which word or phrase to draw + * @param {Object} ctx - canvas context2d to draw into + * @param {Number} x - the horizontal coordinate + * @param {Number} y - ths vertical coordinate */ + draw({font, word, ctx, x, y}) { const srcBounds = this.index[word][font]; const srcCanvas = this.atlases[font].canvas; diff --git a/src/canvas/TextRendererBenchmark.js b/src/canvas/TextRendererBenchmark.js index 283c9d7c..b6d89e3e 100644 --- a/src/canvas/TextRendererBenchmark.js +++ b/src/canvas/TextRendererBenchmark.js @@ -4,37 +4,38 @@ * timings vary greatly depending on which font is set in the drawing context, * and so several font results are averaged. -Drawing directly to canvas is not effected by large typefaces, e.g. > 14px + Drawing directly to canvas is not effected by large typefaces, e.g. > 14px -non-optimized canvas.fillText() of 1000 words using 18px monospace took 38 ms -non-optimized canvas.fillText() of 1000 words using 14px serif took 42 ms -non-optimized canvas.fillText() of 1000 words using 12px sans-serif took 41 ms -average ms 40 + non-optimized canvas.fillText() of 1000 words using 18px monospace took 38 ms + non-optimized canvas.fillText() of 1000 words using 14px serif took 42 ms + non-optimized canvas.fillText() of 1000 words using 12px sans-serif took 41 ms + average ms 40 -Using the offscreen drawing method with large typefaces results in a huge blowout -* presumably because of the increased pixel area used by drawImage + Using the offscreen drawing method with large typefaces results in a huge blowout + * presumably because of the increased pixel area used by drawImage -new TextAtlas() of 1000 words took 151 ms (one time) + new TextAtlas() of 1000 words took 151 ms (one time) -optimized canvas.drawImage() of 1000 words using 18px monospace took 5627 ms******* -optimized canvas.drawImage() of 1000 words using 14px serif took 21 ms -optimized canvas.drawImage() of 1000 words using 12px sans-serif took 17 ms -average ms 1888 + optimized canvas.drawImage() of 1000 words using 18px monospace took 5627 ms******* + optimized canvas.drawImage() of 1000 words using 14px serif took 21 ms + optimized canvas.drawImage() of 1000 words using 12px sans-serif took 17 ms + average ms 1888 -However, for small typefaces, i.e. < 14px, there is a nice speedup using the -offscreen drawing, which is more than 2X faster. + However, for small typefaces, i.e. < 14px, there is a nice speedup using the + offscreen drawing, which is more than 2X faster. -non-optimized canvas.fillText() of 1000 words using 10px monospace took 36 ms -non-optimized canvas.fillText() of 1000 words using 14px serif took 47 ms -non-optimized canvas.fillText() of 1000 words using 12px sans-serif took 50 ms -average ms 44 + non-optimized canvas.fillText() of 1000 words using 10px monospace took 36 ms + non-optimized canvas.fillText() of 1000 words using 14px serif took 47 ms + non-optimized canvas.fillText() of 1000 words using 12px sans-serif took 50 ms + average ms 44 -new TextAtlas() of 1000 words took 146 ms (one time) -optimized canvas.drawImage() of 1000 words using 10px monospace took 14 ms -optimized canvas.drawImage() of 1000 words using 14px serif took 19 ms -optimized canvas.drawImage() of 1000 words using 12px sans-serif took 18 ms -average ms 17 + new TextAtlas() of 1000 words took 146 ms (one time) + optimized canvas.drawImage() of 1000 words using 10px monospace took 14 ms + optimized canvas.drawImage() of 1000 words using 14px serif took 19 ms + optimized canvas.drawImage() of 1000 words using 12px sans-serif took 18 ms + average ms 17 */ + import m from 'mithril'; import {TextAtlas} from './TextAtlas'; @@ -49,11 +50,12 @@ export class TextRendererBenchmark { /** * create a TextRendererBenchmark * @param Object - parameters object with following properties: - * @param Array - words, an array of words to rendered - * @param Number - width, of canvas - * @param Number - height, of canvas - * @param Boolean - useOffscreenAtlas, offscreen drawing optimization + * @param {Array} words - an array of words to rendered + * @param {Number} width - of canvas + * @param {Number} height - of canvas + * @param {Boolean} useOffscreenAtlas - offscreen drawing optimization */ + constructor({words, width, height, useOffscreenAtlas}) { this.words = words; this.width = width; @@ -64,11 +66,25 @@ export class TextRendererBenchmark { /** * mithril lifecycle method */ + oncreate(vnode) { this.canvas = vnode.dom; this.useOffscreenAtlas ? this._drawOffScreen() : this._drawOnScreen(); } + /** + * mithril render method + */ + + view() { + return m('canvas', {width: this.width, height: this.height, 'moz-opaque': ''}); + } + + /** + * + * @private + */ + _drawOffScreen() { let elapsedTime = 0; let t0 = performance.now(); @@ -77,17 +93,17 @@ export class TextRendererBenchmark { fonts: fonts }); let t1 = performance.now(); - let delta = Math.floor(t1 - t0); + let delta = Math.floor(t1 - t0); // eslint-disable-next-line no-console console.log(`new TextAtlas() of ${this.words.length} words took ${delta} ms (one time)`); const ctx = this.canvas.getContext('2d'); elapsedTime = 0; - fonts.forEach( font => { + fonts.forEach(font => { ctx.font = font; t0 = performance.now(); - this.words.forEach( word => { - const x = Math.floor(this.width * Math.random() ); - const y = Math.floor(this.height * Math.random() ); + this.words.forEach(word => { + const x = Math.floor(this.width * Math.random()); + const y = Math.floor(this.height * Math.random()); atlas.draw({ ctx: ctx, word: word, @@ -97,7 +113,7 @@ export class TextRendererBenchmark { }); }); t1 = performance.now(); - delta = Math.floor(t1 - t0); + delta = Math.floor(t1 - t0); // eslint-disable-next-line no-console console.log(`optimized canvas.drawImage() of ${this.words.length} words using ${font} took ${delta} ms`); elapsedTime += delta; @@ -107,19 +123,24 @@ export class TextRendererBenchmark { console.log('average ms', avg); } + /** + * + * @private + */ + _drawOnScreen() { const ctx = this.canvas.getContext('2d'); let elapsedTime = 0; - fonts.forEach( font => { + fonts.forEach(font => { ctx.font = font; const t0 = performance.now(); - this.words.forEach( word => { - const x = Math.floor(this.width * Math.random() ); - const y = Math.floor(this.height * Math.random() ); + this.words.forEach(word => { + const x = Math.floor(this.width * Math.random()); + const y = Math.floor(this.height * Math.random()); ctx.fillText(word, x, y); }); const t1 = performance.now(); - const delta = Math.floor(t1 - t0); + const delta = Math.floor(t1 - t0); // eslint-disable-next-line no-console console.log(`non-optimized canvas.fillText() of ${this.words.length} words using ${font} took ${delta} ms`); elapsedTime += delta; @@ -128,11 +149,4 @@ export class TextRendererBenchmark { // eslint-disable-next-line no-console console.log('average ms', avg); } - - /** - * mithril render method - */ - view() { - return m('canvas', { width: this.width, height: this.height, 'moz-opaque' : ''} ); - } } diff --git a/src/canvas/canvas/BioMap.js b/src/canvas/canvas/BioMap.js new file mode 100644 index 00000000..4af2dfbc --- /dev/null +++ b/src/canvas/canvas/BioMap.js @@ -0,0 +1,593 @@ +/** + * Class representing a biological map and its associated tracks + * @class BioMap + * @extends SceneGraphNodeCanvas + */ + + +import m from 'mithril'; +//import PubSub from 'pubsub-js'; +//import {featureUpdate, dataLoaded} from '../../topics'; + +import {Bounds} from '../../model/Bounds'; +import {pageToCanvas} from '../../util/CanvasUtil'; + +import {SceneGraphNodeCanvas} from '../node/SceneGraphNodeCanvas'; +import {SceneGraphNodeGroup as Group} from '../node/SceneGraphNodeGroup'; +import {MapTrack} from '../layout/MapTrack'; +import {FeatureTrack} from '../layout/FeatureTrack'; +import {Ruler} from '../geometry/Ruler'; + +export class BioMap extends SceneGraphNodeCanvas { + + /** + * Create a new bio map + * + * @param {object} bioMapModel - Parsed model of the bio map to be drawn + * @param {object} appState - Application's meta state object + * @param {object} layoutBounds - Bounds object of position on screen + * @param {number} bioMapIndex - bio map's order on screen, + * @param {object} initialView - bio map's original layout, used for resetting view + */ + + constructor({bioMapModel, appState, layoutBounds, bioMapIndex, initialView}) { + super({model: bioMapModel}); + this.initialView = initialView; + this.bioMapIndex = bioMapIndex; + this.model.visible = { + start: this.model.coordinates.start, + stop: this.model.coordinates.stop + }; + this.model.view = { + base: { + start: this.model.coordinates.start, + stop: this.model.coordinates.stop + }, + visible: { + start: this.model.coordinates.start, + stop: this.model.coordinates.stop + }, + invert : this.model.config.invert + }; + + this.model.manhattanPlot = this.initialView.manhattan || null; + this.zoomDelta = (this.model.view.base.stop - this.model.view.base.start) / this.model.config.ruler.steps; + // set up coordinate bounds for view scaling + this.appState = appState; + this.verticalScale = 0; + this.backbone = null; + this.featureMarks = []; + this.featureLabels = []; + this.info = { + top: 0, + left: 0, + display: 'none' + + }; + + // create some regular expressions for faster dispatching of events + this._gestureRegex = { + pan: new RegExp('^pan'), + pinch: new RegExp('^pinch'), + tap: new RegExp('^tap'), + wheel: new RegExp('^wheel') + }; + this._layout(layoutBounds); + this.dirty = true; + } + + /* + * debug draw to test that the layer is positioned properly + */ + /* + draw(ctx){ + ctx = this.context2d; + this.children.forEach(child => child.draw(ctx)); + ctx.save(); + ctx.globalAlpha = .5; + ctx.fillStyle = '#f442e2'; + this.children.forEach( child => { + let cb = child.globalBounds; + ctx.fillRect( + Math.floor(cb.left), + Math.floor(cb.top), + Math.floor(cb.width), + Math.floor(cb.height) + ); + }); + ctx.restore(); + } +*/ + /** + * Mithril lifecycle method on initial vnode creation. + * @param {object} vnode - node on mithril vnode representing this canvas item + */ + + oncreate(vnode) { + super.oncreate(vnode); +// PubSub.subscribe(featureUpdate, () => { +// this._layout(this.lb); +// this._redrawViewport(this.model.view.visible); +// }); + } + + // getters and setters + + /** + * Culls elements to draw. + * + * @return array of elements visible in current canvas' viewport + * + */ + + get visible() { + let vis = []; + let cVis = this.children.map(child => { + return child.visible; + }); + cVis.forEach(item => { + vis = vis.concat(item); + }); + return vis; + } + + /** + * getter for the R-tree of subnodes. + * @return rbush R-tree + */ + + get hitMap() { + return this.locMap; + } + + // Private functions + + // gesture components + + /** + * Handles mouse wheel zoom + * @param evt - zoom event + * @returns {boolean} returns true to stop event propagation further down layers + * @private + */ + + _onZoom(evt) { + // TODO: send zoom event to the scenegraph elements which compose the biomap + // (don't scale the canvas element itself) + console.warn('BioMap -> onZoom', evt); + // normalise scroll delta + this.verticalScale = evt.deltaY < 0 ? -this.zoomDelta : this.zoomDelta; + let mcv = this.model.view.base; + let zStart = (this.model.view.visible.start + this.verticalScale); + let zStop = (this.model.view.visible.stop - this.verticalScale); + if (zStop - zStart < .01) { + this.verticalScale -= 0.5; + return true; + } + if (zStart < mcv.start) { + zStart = mcv.start; + } else if (zStart > zStop) { + zStart = zStop; + } + + if (zStop > mcv.stop) { + zStop = mcv.stop; + } else if (zStop < zStart) { + zStop = zStart; + } + this._redrawViewport({start: zStart, stop: zStop}); + return true; // stop event propagation + } + + /** + * Handles tap/click event on canvas + * @param evt - tap event + * @returns {boolean} return true to stop event prorogation further down layers + * @private + */ + + _onTap(evt) { + console.log('BioMap -> onTap', evt, this); + let globalPos = pageToCanvas(evt, this.canvas); + this._loadHitMap(); + let hits = []; + + this.hitMap.search({ + minX: globalPos.x, + maxX: globalPos.x, + minY: globalPos.y - 2, + maxY: globalPos.y + 2 + }).forEach(hit => { + // temp fix, find why hit map stopped updating properly + if ((hit.data.model.coordinates.start >= this.model.view.visible.start) && + (hit.data.model.coordinates.start <= this.model.view.visible.stop)) { + hits.push(hit.data); + } else if ((hit.data.model.coordinates.stop >= this.model.view.visible.start) && + (hit.data.model.coordinates.stop <= this.model.view.visible.stop)) { + hits.push(hit.data); + } + }); + if (hits.length > 0) { + hits.sort((a, b) => { + return a.model.coordinates.start - b.model.coordinates.start; + }); + this.info.display = 'inline-block'; + this.info.top = hits[0].globalBounds.top; + this.info.left = hits[0].globalBounds.right; + this.info.data = hits; + let names = hits.map(hit => { + return hit.model.name; + }); + //@awilkey: is this obsolete? + //TODO: Revisit info popovers + this.info.innerHTML = `

${names.join('\n')}

`; + m.redraw(); + } else if (this.info.display !== 'none') { + this.info.display = 'none'; + m.redraw(); + } + + return true; + } + + /** + * Handle start of pan events on canvas, box zooms if ruler is part of selected or + * mass select if not. + * + * @param evt - tap event + * @returns {boolean} return true to stop event prorogation further down layers + * @private + */ + + _onPanStart(evt) { + // TODO: send pan events to the scenegraph elements which compose the biomap + // (don't scale the canvas element itself) + this.zoomP = { + start: 0, + end: 0, + pStart: true, + ruler: false, + delta: 0, + corner: 0 + }; + this.zoomP.pStart = true; + console.warn('BioMap -> onPanStart -- vertically; implement me', evt); + let globalPos = pageToCanvas(evt, this.canvas); + let left = this.ruler.globalBounds.left; + // scroll view vs box select + if (left < (globalPos.x - evt.deltaX) && + (globalPos.x - evt.deltaX) < (left + this.ruler.bounds.width)) { + this.zoomP.ruler = true; + this._moveRuler(evt); + } else { + this.zoomP.ruler = false; + this.zoomP.start = this._pixelToCoordinate(globalPos.y - this.ruler.globalBounds.top - evt.deltaY); + if (this.zoomP.start < this.model.view.base.start) { + this.zoomP.start = this.model.view.base.start; + } + let ctx = this.context2d; + this.zoomP.corner = {top: globalPos.y - evt.deltaY, left: globalPos.x - evt.deltaX}; + ctx.lineWidth = 1.0; + ctx.strokeStyle = 'black'; + // noinspection JSSuspiciousNameCombination + ctx.strokeRect( + Math.floor(globalPos.x - evt.deltaX), + Math.floor(globalPos.y - evt.deltaY), + Math.floor(evt.deltaX), + Math.floor(evt.deltaY) + ); + } + return true; + } + + /** + * Pans ruler's position box on click-and-pan + * + * @param evt - pan event + * @private + */ + + _moveRuler(evt) { + if (this.model.config.invert) { + evt.deltaY = -evt.deltaY; + } + + let delta = (evt.deltaY - this.zoomP.delta) / this.model.view.pixelScaleFactor; + if (this.model.view.visible.start + delta < this.model.view.base.start) { + delta = this.model.view.base.start - this.model.view.visible.start; + } else if (this.model.view.visible.stop + delta > this.model.view.base.stop) { + delta = this.model.view.base.stop - this.model.view.visible.stop; + } + this.model.view.visible.start += delta; + this.model.view.visible.stop += delta; + this._redrawViewport({start: this.model.view.visible.start, stop: this.model.view.visible.stop}); + this.zoomP.delta = evt.deltaY; + } + + /** + * Continue pre-exiting pan event + * + * @param evt - continuation of pan event + * @returns {boolean} return true to stop event prorogation further down layers + * @private + */ + + _onPan(evt) { + // block propagation if pan hasn't started + if (!this.zoomP || !this.zoomP.pStart) return true; + if (this.zoomP && this.zoomP.ruler) { + this._moveRuler(evt); + } else { + let globalPos = pageToCanvas(evt, this.canvas); + this.draw(); + let ctx = this.context2d; + ctx.lineWidth = 1.0; + ctx.strokeStyle = 'black'; + // noinspection JSSuspiciousNameCombination + ctx.strokeRect( + Math.floor(this.zoomP.corner.left), + Math.floor(this.zoomP.corner.top), + Math.floor(globalPos.x - this.zoomP.corner.left), + Math.floor(globalPos.y - this.zoomP.corner.top) + ); + } + return true; + } + + /** + * Finalize pan event + * + * @param evt - tap event + * @returns {boolean} return true to stop event propagation further down layers + * @private + */ + + _onPanEnd(evt) { + // TODO: send pan events to the scenegraph elements which compose the biomap + // (don't scale the canvas element itself) + console.warn('BioMap -> onPanEnd -- vertically; implement me', evt, this.model.view.base); + // block propagation if pan hasn't started + if (!this.zoomP || !this.zoomP.pStart) return true; + if (this.zoomP && this.zoomP.ruler) { + this._moveRuler(evt); + } else { + let globalPos = pageToCanvas(evt, this.canvas); + + // test if any part of the box select is in the ruler zone + let rLeft = this.ruler.globalBounds.left; + let rRight = this.ruler.globalBounds.right; + let lCorner = this.zoomP.corner.left < globalPos.x ? this.zoomP.corner.left : globalPos.x; + let rCorner = lCorner === this.zoomP.corner.left ? globalPos.x : this.zoomP.corner.left; + // if zoom rectangle contains the ruler, zoom, else populate popover + if (((lCorner <= rLeft) && (rCorner >= rLeft)) || ((lCorner <= rRight && rCorner >= rRight))) { + this.model.view.visible = this.model.view.base; + + this.zoomP.start = this._pixelToCoordinate(this.zoomP.corner.top - this.ruler.globalBounds.top); + this.zoomP.stop = this._pixelToCoordinate(globalPos.y - this.ruler.globalBounds.top); + let swap = this.zoomP.start < this.zoomP.stop; + let zStart = swap ? this.zoomP.start : this.zoomP.stop; + let zStop = swap ? this.zoomP.stop : this.zoomP.start; + + if (zStart < this.model.view.base.start) { + zStart = this.model.view.base.start; + } + if (zStop > this.model.view.base.stop) { + zStop = this.model.view.base.stop; + } + + this._redrawViewport({start: zStart, stop: zStop}); + } else { + + this._loadHitMap(); + let hits = []; + let swap = this.zoomP.corner.left < globalPos.x; + let swapV = this.zoomP.corner.top < globalPos.y; + this.hitMap.search({ + minX: swap ? this.zoomP.corner.left : globalPos.x, + maxX: swap ? globalPos.x : this.zoomP.corner.left, + minY: swapV ? this.zoomP.corner.top : globalPos.y, + maxY: swapV ? globalPos.y : this.zoomP.corner.top + }).forEach(hit => { + // temp fix, find why hit map stopped updating properly + if(!hit.data.model) return; + if ((hit.data.model.coordinates.start >= this.model.view.visible.start) && + (hit.data.model.coordinates.start <= this.model.view.visible.stop)) { + hits.push(hit.data); + } else if ((hit.data.model.coordinates.stop >= this.model.view.visible.start) && + (hit.data.model.coordinates.stop <= this.model.view.visible.stop)) { + hits.push(hit.data); + } + }); + if (hits.length > 0) { + hits.sort((a, b) => { + return a.model.coordinates.start - b.model.coordinates.start; + }); + this.info.display = 'inline-block'; + this.info.top = this.ruler.globalBounds.top; + this.info.left = 0; + this.info.data = hits; + let names = hits.map(hit => { + return hit.model.name; + }); + //@awilkey: is this obsolete? + this.info.innerHTML = `

${names.join('\n')}

`; + m.redraw(); + } else if (this.info.display !== 'none') { + this.info.display = 'none'; + m.redraw(); + } + } + } + this.draw(); + this.zoomP = { + start: 0, + end: 0, + pStart: false, + ruler: false, + delta: 0, + corner: { + top: 0, + left: 0 + } + }; +// this.zoomP.ruler = false; +// this.zoomP.pStart = false; + return true; // do not stop propagation + } + + + /** + * Converts a pixel position to the canvas' backbone coordinate system. + * + * @param {number} point - pixel position on screen + * @return {number} backbone position + * + * @private + */ + + _pixelToCoordinate(point) { + let coord = this.model.view.base; + let visc = this.model.view.visible; + let psf = this.model.view.pixelScaleFactor; + return ((visc.start * (coord.stop * psf - point) + visc.stop * (point - coord.start * psf)) / (psf * (coord.stop - coord.start))) - (coord.start * -1); + } + + /** + * perform layout of backbone, feature markers, and feature labels. + * + * @param {object} layoutBounds - bounds object representing bounds of this canvas + * + * @private + */ + + _layout(layoutBounds) { + // TODO: calculate width based on # of SNPs in layout, and width of feature + // labels + // Setup Canvas + //const width = Math.floor(100 + Math.random() * 200); + this.lb = this.lb || layoutBounds; + console.log('BioMap -> layout'); + const width = Math.floor(this.lb.width / this.appState.bioMaps.length); + this.children = []; + this.domBounds = this.domBounds || new Bounds({ + left: this.lb.left, + top: this.lb.top, + width: width > 300 ? width : 300, + height: this.lb.height + }); + + this.bounds = this.bounds || new Bounds({ + left: 0, + top: this.lb.top + 40, + width: this.domBounds.width, + height: Math.floor(this.domBounds.height - 140) // set to reasonably re-size for smaller windows + }); + //Add children tracks + this.bbGroup = new Group({parent: this}); + this.bbGroup.bounds = new Bounds({ + top: this.bounds.top, + left: this.model.config.ruler.labelSize * 10, + width: 10, + height: this.bounds.height + }); + this.bbGroup.model = this.model; + this.backbone = new MapTrack({parent: this}); + this.bbGroup.addChild(this.backbone); + this.model.view.backbone = this.backbone.backbone.globalBounds; + this.ruler = new Ruler({parent: this, bioMap: this.model, config: this.model.config.ruler}); + this.bbGroup.addChild(this.ruler); + this.backbone.children.forEach(child => { + if (child.globalBounds.left < this.bbGroup.bounds.left) { + this.bbGroup.bounds.left = child.globalBounds.left; + } + if (child.globalBounds.right > this.bbGroup.bounds.right) { + this.bbGroup.bounds.right = child.globalBounds.right; + } + }); + this.children.push(this.bbGroup); + + this.tracksRight =[]; + this.tracksLeft = []; + if(this.model.tracks) { + this.model.tracks.forEach((track,order) => { + track.tracksIndex = order; + if (track.position === -1) { + this.tracksRight.push(track); + } else { + this.tracksLeft.push(track); + } + }); + } + + let qtlRight = new FeatureTrack({parent:this,position:1}); + let qtlLeft = new FeatureTrack({parent:this,position:-1}); + // let qtlRight = {}; + //let qtlRight = new QtlTrack({parent: this , position: 1}); + //let qtlLeft = new QtlTrack({parent: this, position: -1}); + this.addChild(qtlRight); + this.addChild(qtlLeft); + + if (qtlLeft && qtlLeft.bounds.right > this.bbGroup.bounds.left) { + const bbw = this.bbGroup.bounds.width; + this.bbGroup.bounds.left = qtlLeft.globalBounds.right + 100; + this.bbGroup.bounds.width = bbw; + const qrw = qtlRight.bounds.width; + qtlRight.bounds.left += qtlLeft.globalBounds.right; + qtlRight.bounds.right = qtlRight.bounds.left + qrw; + } + + if (this.domBounds.width < qtlRight.globalBounds.right + 30) { + this.domBounds.width = qtlRight.globalBounds.right + 50; + } + + //load local rBush tree for hit detection + this._loadHitMap(); + //let layout know that width has changed on an element; + //m.redraw(); + this.dirty = true; + } + + /** + * Adds children nodes to the R-tree + * + * @private + */ + + _loadHitMap() { + let hits = []; + let childrenHits = this.children.map(child => { + return child.hitMap; + }); + childrenHits.forEach(child => { + hits = hits.concat(child); + }); + this.locMap.clear();// = rbush(); + this.locMap.load(hits); + } + + /** + * Redraw restricted view + * + * @param coordinates + * @private + */ + + _redrawViewport(coordinates) { + this.model.view.visible = { + start: coordinates.start, + stop: coordinates.stop + }; + this.backbone.loadLabelMap(); + this.draw(); + + let cMaps = document.getElementsByClassName('cmap-correspondence-map'); + [].forEach.call(cMaps, el => { + el.mithrilComponent.draw(); + }); + // move top of popover if currently visible + if (this.info.display !== 'none') { + this.info.top = this.info.data[0].globalBounds.top; + } + m.redraw(); + } +} diff --git a/src/canvas/layout/CorrespondenceMap.js b/src/canvas/canvas/CorrespondenceMap.js similarity index 69% rename from src/canvas/layout/CorrespondenceMap.js rename to src/canvas/canvas/CorrespondenceMap.js index 8031f25a..8b6513e9 100644 --- a/src/canvas/layout/CorrespondenceMap.js +++ b/src/canvas/canvas/CorrespondenceMap.js @@ -1,8 +1,10 @@ /** - * CorrespondenceMap - * Mithril component for correspondence lines between 2 or more BioMaps with an - * html5 canvas element. - */ + * Mithril component for correspondence lines between 2 or more BioMaps with an + * html5 canvas element. + * + * @extends SceneGraphNodeCanvas + * + */ import m from 'mithril'; import {Bounds} from '../../model/Bounds'; import {SceneGraphNodeCanvas} from '../node/SceneGraphNodeCanvas'; @@ -10,9 +12,10 @@ import {SceneGraphNodeGroup} from '../node/SceneGraphNodeGroup'; import {CorrespondenceMark} from '../geometry/CorrespondenceMark'; import {featuresInCommon} from '../../model/Feature'; -export class CorrespondenceMap extends SceneGraphNodeCanvas{ +export class CorrespondenceMap extends SceneGraphNodeCanvas { constructor({bioMapComponents, appState, layoutBounds}) { super({}); + console.log('CorrespondenceMap -> constructor'); this.bioMapComponents = bioMapComponents; this.appState = appState; this.verticalScale = 1; @@ -25,12 +28,14 @@ export class CorrespondenceMap extends SceneGraphNodeCanvas{ */ draw() { let ctx = this.context2d; - if(! ctx) return; - if(! this.domBounds) return; + if (!ctx) return; + if (!this.domBounds) return; ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); let gb = this.globalBounds || {}; ctx.save(); ctx.globalAlpha = 0; + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination ctx.fillRect( Math.floor(gb.left), Math.floor(gb.top), @@ -51,17 +56,18 @@ export class CorrespondenceMap extends SceneGraphNodeCanvas{ let leftFeatures = this.bioMapComponents[0].model.features; let rightFeatures = this.bioMapComponents[1].model.features; //let leftFeatures = this.bioMapComponents[0].backbone.filteredFeatures; - //let rightFeatures = this.bioMapComponents[1].backbone.filteredFeatures; - let common = featuresInCommon(leftFeatures, rightFeatures); - return common; + //let rightFeatures = this.bioMapComponents[1].backbone.filteredFeat + return featuresInCommon(leftFeatures, rightFeatures); + //return common; } /** - * + * mithril component render callback + * mithril component render callback * */ + view() { - if(this.domBounds && ! this.domBounds.isEmptyArea) { + if (this.domBounds && !this.domBounds.isEmptyArea) { this.lastDrawnMithrilBounds = this.domBounds; } let b = this.domBounds || {}; @@ -74,12 +80,18 @@ export class CorrespondenceMap extends SceneGraphNodeCanvas{ }); } + /** + * Lay out correspondence lines between features + * @param layoutBounds - bounds of the linked canvas + * @private + */ + _layout(layoutBounds) { this.domBounds = layoutBounds; // this.bounds (scenegraph) has the same width and height, but zero the // left/top because we are the root node in a canvas sceneGraphNode - // heirarchy. - let gb1 = this.bioMapComponents[0].backbone.backbone.globalBounds; + // hierarchic. + let gb1 = this.bioMapComponents[0].backbone.markerGroup.globalBounds; this.bounds = new Bounds({ allowSubpixel: false, left: 1, @@ -87,9 +99,9 @@ export class CorrespondenceMap extends SceneGraphNodeCanvas{ width: this.domBounds.width, height: this.domBounds.height }); - + let corrData = []; - let coorGroup = new SceneGraphNodeGroup({parent:this}); + let coorGroup = new SceneGraphNodeGroup({parent: this}); coorGroup.bounds = new Bounds({ allowSubpixel: false, top: gb1.top, @@ -98,33 +110,35 @@ export class CorrespondenceMap extends SceneGraphNodeCanvas{ height: gb1.height, }); this.addChild(coorGroup); - console.log('childBounds', this.globalBounds, coorGroup.globalBounds); let bioMapCoordinates = [ - this.bioMapComponents[0].mapCoordinates, + this.bioMapComponents[0].mapCoordinates, this.bioMapComponents[1].mapCoordinates ]; - this.commonFeatures.forEach( feature => { + this.commonFeatures.forEach(feature => { let corrMark = new CorrespondenceMark({ parent: coorGroup, featurePair: feature, - mapCoordinates:bioMapCoordinates, - bioMap : this.bioMapComponents + mapCoordinates: bioMapCoordinates, + bioMap: this.bioMapComponents }); coorGroup.addChild(corrMark); corrData.push({ - minX:this.bounds.left, - maxX:this.bounds.right , - minY:feature[0].coordinates.start, + minX: this.bounds.left, + maxX: this.bounds.right, + minY: feature[0].coordinates.start, maxY: feature[1].coordinates.start, data: corrMark }); }); - this.locMap.load(corrData); - console.log('bioMap', this.locMap.all()); + this.locMap.load(corrData); } - get visible(){ + /** + * Return visible elements in R-Tree + */ + + get visible() { return this.locMap.all(); } } diff --git a/src/canvas/geometry/CorrespondenceMark.js b/src/canvas/geometry/CorrespondenceMark.js index 671f2dd1..a27a9c69 100644 --- a/src/canvas/geometry/CorrespondenceMark.js +++ b/src/canvas/geometry/CorrespondenceMark.js @@ -1,55 +1,83 @@ /** - * FeatureMarker - * A SceneGraphNode representing a feature on a Map with a line or hash mark. - */ + * FeatureMarker + * A SceneGraphNode representing a feature on a Map with a line or hash mark. + * + * @extends SceneGraphNodeBase + */ + import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; import {Bounds} from '../../model/Bounds'; import {translateScale} from '../../util/CanvasUtil'; export class CorrespondenceMark extends SceneGraphNodeBase { + /** + * Construct the CorrespondenceMark layer + * @param parent - parent scene graph node + * @param featurePair - array of features being compared + * @param mapCoordinates - current zoom level of each feature maps + * @param bioMap - array of both sets of map data + */ + + /** + * TODO: Allow configuration as part of config file + */ + constructor({parent, featurePair, mapCoordinates, bioMap}) { super({parent}); this.model = featurePair; this.mapCoordinates = mapCoordinates; this.lineWidth = 1.0; this.bioMap = bioMap; - + this.invert = [bioMap[0].model.config.invert, bioMap[1].model.config.invert]; + this.pixelScaleFactor = [ - bioMap[0].model.view.pixelScaleFactor, - bioMap[1].model.view.pixelScaleFactor, + bioMap[0].model.view.pixelScaleFactor, + bioMap[1].model.view.pixelScaleFactor, ]; + let leftY = translateScale( - this.model[0].coordinates.start, - bioMap[0].model.view.base, - bioMap[0].model.view.visible) * this.pixelScaleFactor[0]; + this.model[0].coordinates.start, + bioMap[0].model.view.base, + bioMap[0].model.view.visible, + this.invert[0]) * this.pixelScaleFactor[0]; + let rightY = translateScale( - this.model[1].coordinates.start, - bioMap[1].model.view.base, - bioMap[1].model.view.visible) * this.pixelScaleFactor[1]; + this.model[1].coordinates.start, + bioMap[1].model.view.base, + bioMap[1].model.view.visible, + this.invert[1]) * this.pixelScaleFactor[1]; this.bounds = new Bounds({ allowSubpixel: false, top: leftY, left: parent.bounds.left, - height: leftY-rightY, + height: leftY - rightY, width: parent.bounds.width }); } + /** + * Draw the correspondence marks + * @param {object} ctx - canvas context 2D + */ + draw(ctx) { - var bioMap = this.bioMap; + let bioMap = this.bioMap; let leftYStart = translateScale( this.model[0].coordinates.start, bioMap[0].model.view.base, - bioMap[0].model.view.visible) * this.pixelScaleFactor[0]; + bioMap[0].model.view.visible, + this.invert[0]) * this.pixelScaleFactor[0]; + let rightYStart = translateScale( this.model[1].coordinates.start, bioMap[1].model.view.base, - bioMap[1].model.view.visible) * this.pixelScaleFactor[1]; + bioMap[1].model.view.visible, + this.invert[1]) * this.pixelScaleFactor[1]; if (this.model[0].coordinates.start === this.model[0].coordinates.stop - && this.model[1].coordinates.start === this.model[1].coordinates.stop) { + && this.model[1].coordinates.start === this.model[1].coordinates.stop) { // correspondence line this.bounds.top = leftYStart; this.bounds.bottom = rightYStart; @@ -58,7 +86,9 @@ export class CorrespondenceMark extends SceneGraphNodeBase { ctx.lineWidth = this.lineWidth; ctx.strokeStyle = '#CAA91E'; ctx.globalAlpha = 0.7; + // noinspection JSSuspiciousNameCombination ctx.moveTo(Math.floor(gb.left), Math.floor(gb.top)); + // noinspection JSSuspiciousNameCombination ctx.lineTo(Math.floor(gb.right), Math.floor(gb.bottom)); ctx.stroke(); } @@ -67,11 +97,13 @@ export class CorrespondenceMark extends SceneGraphNodeBase { let leftYStop = translateScale( this.model[0].coordinates.stop, bioMap[0].model.view.base, - bioMap[0].model.view.visible) * this.pixelScaleFactor[0]; + bioMap[0].model.view.visible, + this.invert[0]) * this.pixelScaleFactor[0]; let rightYStop = translateScale( this.model[1].coordinates.stop, bioMap[1].model.view.base, - bioMap[1].model.view.visible) * this.pixelScaleFactor[1]; + bioMap[1].model.view.visible, + this.invert[1]) * this.pixelScaleFactor[1]; this.bounds.top = leftYStart; this.bounds.bottom = leftYStop; @@ -87,10 +119,14 @@ export class CorrespondenceMark extends SceneGraphNodeBase { ctx.beginPath(); ctx.lineWidth = this.lineWidth; ctx.globalAlpha = 0.2; - ctx.fillStyle = '#7C6400';//'#A4870C'; + ctx.fillStyle = '#7C6400'; //'#A4870C'; + // noinspection JSSuspiciousNameCombination ctx.moveTo(Math.floor(gbLeft.left), Math.floor(gbLeft.top)); + // noinspection JSSuspiciousNameCombination ctx.lineTo(Math.floor(gbLeft.left), Math.floor(gbLeft.bottom)); + // noinspection JSSuspiciousNameCombination ctx.lineTo(Math.floor(gbRight.right), Math.floor(gbRight.bottom)); + // noinspection JSSuspiciousNameCombination ctx.lineTo(Math.floor(gbRight.right), Math.floor(gbRight.top)); ctx.fill(); } diff --git a/src/canvas/geometry/Dot.js b/src/canvas/geometry/Dot.js new file mode 100644 index 00000000..178cc614 --- /dev/null +++ b/src/canvas/geometry/Dot.js @@ -0,0 +1,82 @@ +/** + * + * A SceneGraphNode representing a circular mark. + * + * @extends SceneGraphNodeBase + */ + +import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; +import {Bounds} from '../../model/Bounds'; +import {translateScale} from '../../util/CanvasUtil'; + +export class Dot extends SceneGraphNodeBase { + /** + * Constructor + * + * @param parent - parent scene graph node + * @param bioMap - map data + * @param featureModel - feature data + * @param config - configuration information object + */ + + constructor({parent, bioMap, featureModel, config}) { + super({parent, tags: [featureModel.name]}); + //setup config + this.config = config; + this.model = featureModel; + this.view = bioMap.view; + this.pixelScaleFactor = this.view.pixelScaleFactor; + this.invert = bioMap.view.invert; + this.start = this.model.coordinates.start; + this.radius = config.width; + this.depth = 0; + + // setup initial placement + if (this.model.coordinates.depth) { + this.depth = translateScale(this.model.coordinates.depth, { + start: 0, + stop: config.displayWidth + }, config.view, false); + } + this.bounds = new Bounds({ + top: 0, + left: 0, + width: 2 * this.radius, //this.fontSize*(this.model.name.length), + height: 2 * this.radius, + allowSubpixel: false + }); + } + + /** + * Draw label on cmap canvas context + * @param ctx + */ + + draw(ctx) { + //Setup a base offset based on parent track + if (this.start < this.view.visible.start || this.start > this.view.visible.stop) return; + if (!this.offset) { + const left = this.globalBounds.left; + const top = this.globalBounds.top; + this.offset = {top: top, left: left}; + } + let config = this.config; + let y = translateScale(this.start, this.view.base, this.view.visible, this.invert) * this.pixelScaleFactor; + let x = this.depth; + + // Draw dot + ctx.beginPath(); + ctx.fillStyle = config.fillColor; + ctx.arc(x + this.offset.left, y + this.offset.top, this.radius, 0, 2 * Math.PI, false); + ctx.fill(); + ctx.lineWidth = config.lineWeight; + ctx.strokeStyle = config.lineColor; + ctx.stroke(); + + //update bounding box + this.bounds.top = y - this.radius; + this.bounds.left = x - this.radius; + this.bounds.width = 2 * this.radius; + this.bounds.height = 2 * this.radius; + } +} \ No newline at end of file diff --git a/src/canvas/geometry/FeatureLabel.js b/src/canvas/geometry/FeatureLabel.js index a0617d0a..09679920 100644 --- a/src/canvas/geometry/FeatureLabel.js +++ b/src/canvas/geometry/FeatureLabel.js @@ -1,41 +1,57 @@ /** - * FeatureLabel - * A SceneGraphNode representing a text label for a feature on a Map. - */ + * + * A SceneGraphNode representing a text label for a feature on a Map. + * + * @extends SceneGraphNodeBase + */ + import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; import {Bounds} from '../../model/Bounds'; import {translateScale} from '../../util/CanvasUtil'; export class FeatureLabel extends SceneGraphNodeBase { + /** + * Constructor + * + * @param parent - parent scene graph node + * @param bioMap - map data + * @param featureModel - feature data + */ - constructor({parent, bioMap, featureModel}) { + constructor({parent, bioMap, featureModel,config}) { super({parent, tags: [featureModel.name]}); + this.config = config; this.model = featureModel; this.view = bioMap.view; - this.fontSize = bioMap.config.markerLabelSize; - this.fontFace = bioMap.config.markerLabelFace; - this.fontColor = bioMap.config.markerLabelColor; this.pixelScaleFactor = this.view.pixelScaleFactor; + this.invert = bioMap.view.invert; + this.start = this.model.coordinates.start; this.bounds = new Bounds({ - allowSubpixel: false, top: 0, left: 5, - width: parent.bounds.width, - height: 12 + width: 200, //this.fontSize*(this.model.name.length), + height: 12, + allowSubpixel: false }); } + /** + * Draw label on cmap canvas context + * @param ctx + */ + draw(ctx) { - let y = translateScale(this.model.coordinates.start,this.view.base,this.view.visible) * this.pixelScaleFactor; + let config = this.config; + let y = translateScale(this.start, this.view.base, this.view.visible, this.invert) * this.pixelScaleFactor; this.bounds.top = y; - this.bounds.bottom = y + this.fontSize; + this.bounds.bottom = y + config.labelSize; let gb = this.globalBounds || {}; - ctx.font = `${this.fontSize}px ${this.fontFace}`; + ctx.font = `${config.labelSize}px ${config.labelFace}`; ctx.textAlign = 'left'; - ctx.fillStyle = this.fontColor; - ctx.fillText(this.model.name,gb.left, gb.top); + ctx.fillStyle = config.labelColor; + ctx.fillText(this.model.name, gb.left, gb.top); // reset bounding box to fit the new stroke location/width - this.bounds.right = this.bounds.left + Math.floor(ctx.measureText(this.model.name).width)+1; - if(this.parent.bounds.width < this.bounds.width) this.parent.bounds.width = this.bounds.width; + this.bounds.width = this.bounds.left + Math.floor(ctx.measureText(this.model.name).width) + 1; + if (this.parent.bounds.width < this.bounds.width) this.parent.bounds.width = this.bounds.width; } } diff --git a/src/canvas/geometry/FeatureMark.js b/src/canvas/geometry/FeatureMark.js index 7dc34087..ee50675a 100644 --- a/src/canvas/geometry/FeatureMark.js +++ b/src/canvas/geometry/FeatureMark.js @@ -1,21 +1,31 @@ /** - * FeatureMarker - * A SceneGraphNode representing a feature on a Map with a line or hash mark. - */ + * FeatureMarker + * A SceneGraphNode representing a feature on a Map with a line or hash mark. + * + * @extends SceneGraphNodeBase + */ import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; import {Bounds} from '../../model/Bounds'; import {translateScale} from '../../util/CanvasUtil'; export class FeatureMark extends SceneGraphNodeBase { - constructor({parent, bioMap, featureModel}) { + /** + * Constructor + * @param parent - parent scene graph node + * @param bioMap - map data + * @param featureModel - feature data + */ + + constructor({parent, bioMap, featureModel,config}) { super({parent, tags: [featureModel.name]}); this.model = featureModel; this.featureMap = bioMap; + this.config = config; - this.offset = this.featureMap.view.base.start*-1; - this.lineWidth = bioMap.config.markerWeight; - this.strokeStyle = bioMap.config.markerColor; + this.offset = this.featureMap.view.base.start * -1; + this.invert = this.featureMap.view.invert; + this.start = this.model.coordinates.start; this.pixelScaleFactor = this.featureMap.view.pixelScaleFactor; this.bounds = new Bounds({ @@ -23,23 +33,31 @@ export class FeatureMark extends SceneGraphNodeBase { top: 0, left: 0, width: parent.bounds.width, - height: this.lineWidth + height: this.lineWeight }); } + /** + * Draw the marker + * @param ctx - active canvas2D context + */ + draw(ctx) { - let y = translateScale(this.model.coordinates.start, this.featureMap.view.base, this.featureMap.view.visible) * this.pixelScaleFactor; + let config = this.config; + let y = translateScale(this.start, this.featureMap.view.base, this.featureMap.view.visible, this.invert) * this.pixelScaleFactor; this.bounds.top = y; let gb = this.globalBounds || {}; ctx.beginPath(); - ctx.strokeStyle = this.strokeStyle; - ctx.lineWidth = this.lineWidth; + ctx.strokeStyle = config.lineColor; + ctx.lineWidth = config.lineWeight; + // noinspection JSSuspiciousNameCombination ctx.moveTo(Math.floor(gb.left), Math.floor(gb.top)); + // noinspection JSSuspiciousNameCombination ctx.lineTo(Math.floor(gb.right), Math.floor(gb.top)); ctx.stroke(); // reset bounding box to fit the new stroke location/width // lineWidth adds equal percent of passed width above and below path - this.bounds.top = Math.floor(y - this.lineWidth/2); - this.bounds.bottom = Math.floor( y + this.lineWidth/2); + this.bounds.top = Math.floor(y - config.lineWeight / 2); + this.bounds.bottom = Math.floor(y + config.lineWeight / 2); } } diff --git a/src/canvas/geometry/MapBackbone.js b/src/canvas/geometry/MapBackbone.js index b65797e7..dceaf04d 100644 --- a/src/canvas/geometry/MapBackbone.js +++ b/src/canvas/geometry/MapBackbone.js @@ -1,38 +1,67 @@ /** - * MapBackbone - * A SceneGraphNode representing a backbone, simply a rectangle representing - * the background. - */ + * MapBackbone + * A SceneGraphNode representing a backbone, simply a rectangle enclosing the upper and + * lower bounds of the map of the current feature, providing a delineated region to draw + * features of interest + * + * @extends SceneGraphNodeBase + */ + import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; import {Bounds} from '../../model/Bounds'; export class MapBackbone extends SceneGraphNodeBase { - constructor({parent, bioMap}) { + /** + * Constructor + * @param parent - Parent scene graph node + * @param bioMap - Map data + */ + + constructor({parent, bioMap,config}) { super({parent}); + this.config = config; const b = parent.bounds; - const config = bioMap.config; - const backboneWidth = config.backboneWidth; - this.fillStyle = config.backboneColor; + const backboneWidth = config.width; this.bounds = new Bounds({ allowSubpixel: false, top: 0, left: b.width * 0.5 - backboneWidth * 0.5, - width: backboneWidth, + width: backboneWidth + config.lineWeight, height: b.height }); bioMap.view.backbone = this.globalBounds; } + /** + * Draw the map backbone, then iterate through and draw its children + * @param ctx - currently active canvas2D context + */ + draw(ctx) { + let config = this.config; let gb = this.globalBounds || {}; - ctx.fillStyle = this.fillStyle; + ctx.fillStyle = config.fillColor; + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination ctx.fillRect( Math.floor(gb.left), Math.floor(gb.top), Math.floor(gb.width), Math.floor(gb.height) ); - this.children.forEach( child => child.draw(ctx)); + + if(this.lineWidth > 0) { + ctx.strokeStyle = config.lineColor; + ctx.lineWidth = config.lineWeight; + ctx.strokeRect( + Math.floor(gb.left), + Math.floor(gb.top), + Math.floor(gb.width), + Math.floor(gb.height) + ); + } + + this.children.forEach(child => child.draw(ctx)); } } diff --git a/src/canvas/geometry/QTL.js b/src/canvas/geometry/QTL.js index f10a165e..da301ef9 100644 --- a/src/canvas/geometry/QTL.js +++ b/src/canvas/geometry/QTL.js @@ -1,50 +1,68 @@ /** - * QTL - A feature with a length and width drawn as part of a group of similar - * features - * - */ + * QTL - A feature with a length and width drawn as part of a group of similar + * features + * + * @extends SceneGraphNodeBase + */ + import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; import {Bounds} from '../../model/Bounds'; import {translateScale} from '../../util/CanvasUtil'; export class QTL extends SceneGraphNodeBase { - constructor({parent, bioMap, featureModel, initialConfig}) { + /** + * Construct the QTL feature + * @param parent - parent scene graph node + * @param bioMap - map data + * @param featureModel - feature data + * @param initialConfig - configuration object for display variables + */ + + constructor({parent, bioMap, featureModel, initialConfig,config}) { super({parent, tags: [featureModel.name]}); - let config = bioMap.config; this.model = featureModel; this.featureMap = bioMap; this.view = this.featureMap.view; this.lineWidth = 1.0; //min and max location in pixels this.pixelScaleFactor = this.featureMap.view.pixelScaleFactor; - this.fill = initialConfig.trackColor[initialConfig.filters.indexOf(this.model.tags[0])]||initialConfig.trackColor[0] || config.trackColor ; - this.width = initialConfig.trackWidth || config.trackWidth; - this.trackSpacing = initialConfig.trackSpacing || config.trackSpacing; - this.labelColor = config.trackLabelColor; - this.labelSize = config.trackLabelSize; - this.labelFace = config.trackLabelFace; - this.offset = this.trackSpacing + this.labelSize; + this.fill = config.fillColor; + if(initialConfig.fillColor) { + this.fill = initialConfig.fillColor[initialConfig.filters.indexOf(this.model.tags[0])] || initialConfig.fillColor[0]; + } + + this.width = initialConfig.width || config.width; + this.trackSpacing = initialConfig.internalPadding || config.internalPadding; + this.labelColor = initialConfig.labelColor || config.labelColor; + this.labelSize = initialConfig.labelSize || config.labelSize; + this.labelFace = initialConfig.labelFace || config.labelFace; + this.offset = this.trackSpacing + this.labelSize; + this.invert = this.view.invert; + this.start = this.invert ? this.model.coordinates.stop : this.model.coordinates.start; + this.stop = this.invert ? this.model.coordinates.start : this.model.coordinates.stop; // Calculate start/end position, then // Iterate across QTLs in group and try to place QTL region where it can // minimize stack width in parent group - let y1 = translateScale(this.model.coordinates.start,this.view.base, this.view.visible) * this.pixelScaleFactor; - let y2 = translateScale(this.model.coordinates.stop, this.view.base, this.view.visible) * this.pixelScaleFactor; + let y1 = translateScale(this.start, this.view.base, this.view.visible, this.invert) * this.pixelScaleFactor; + let y2 = translateScale(this.stop, this.view.base, this.view.visible, this.invert) * this.pixelScaleFactor; let leftLoc = 0; - let leftArr = []; + let leftArr; leftArr = this.parent.locMap.search({ minY: this.model.coordinates.start, maxY: this.model.coordinates.stop, minX: 0, - maxX:10000 + maxX: 10000 + }); + leftArr = leftArr.sort((a, b) => { + return a.data.bounds.right - b.data.bounds.right; }); - leftArr = leftArr.sort((a,b)=>{return a.data.bounds.right-b.data.bounds.right;}); let stepOffset = this.width + this.offset; let stackEnd = leftArr.length; - for( let i = 0; i <= stackEnd; ++i){ - leftLoc = i*(stepOffset); - if( leftArr[i] && leftArr[i].data.bounds.left !== leftLoc){ + for (let i = 0; i <= stackEnd; ++i) { + leftLoc = i * (stepOffset); + if (leftArr[i] && leftArr[i].data.bounds.left !== leftLoc) { break; } } @@ -54,25 +72,37 @@ export class QTL extends SceneGraphNodeBase { top: y1, left: leftLoc, width: this.width, - height: y2-y1 + height: y2 - y1 }); } + /** + * + * @param ctx + */ + draw(ctx) { // Get start and stop of QTL on current region, if it isn't located in // current view, don't draw, else cutoff when it gets to end of currently // visible region. - if( this.model.coordinates.stop < this.view.visible.start || - this.model.coordinates.start > this.view.visible.stop) return; - var y1pos = this.model.coordinates.start > this.view.visible.start ? this.model.coordinates.start : this.view.visible.start; - var y2pos = this.model.coordinates.stop < this.view.visible.stop ? this.model.coordinates.stop : this.view.visible.stop; - let y1 = translateScale(y1pos,this.view.base,this.view.visible) * this.pixelScaleFactor; - let y2 = translateScale(y2pos,this.view.base,this.view.visible) * this.pixelScaleFactor; + if (this.model.coordinates.stop < this.view.visible.start || + this.model.coordinates.start > this.view.visible.stop) return; + let y1pos = this.model.coordinates.start > this.view.visible.start ? this.model.coordinates.start : this.view.visible.start; + let y2pos = this.model.coordinates.stop < this.view.visible.stop ? this.model.coordinates.stop : this.view.visible.stop; + this.start = y1pos; + this.stop = y2pos; + if (this.invert) { + this.start = y2pos; + this.stop = y1pos; + } + + let y1 = translateScale(this.start, this.view.base, this.view.visible, this.invert) * this.pixelScaleFactor; + let y2 = translateScale(this.stop, this.view.base, this.view.visible, this.invert) * this.pixelScaleFactor; //setup bounds and draw this.bounds = new Bounds({ top: y1, - height: y2-y1, + height: y2 - y1, left: this.bounds.left, width: this.width }); @@ -82,30 +112,32 @@ export class QTL extends SceneGraphNodeBase { let fontStyle = this.labelFace; ctx.font = `${fontSize}px ${fontStyle}`; ctx.fillStyle = this.fill; + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination ctx.fillRect( Math.floor(gb.left), Math.floor(gb.top), Math.floor(this.width), Math.floor(qtlHeight) ); - let textWidth = ctx.measureText(this.model.name).width + (ctx.measureText('M').width*6); - let textStop = this.model.coordinates.stop - (translateScale(textWidth/this.pixelScaleFactor,this.view.base,this.view.visible)+this.view.base.start); + let textWidth = ctx.measureText(this.model.name).width + (ctx.measureText('M').width * 6); + let textStop = this.stop - (translateScale(textWidth / this.pixelScaleFactor, this.view.base, this.view.visible) + this.view.base.start); let overlap = this.parent.locMap.search({ minY: textStop > this.view.visible.start ? textStop : this.view.visible.start, - maxY: this.model.coordinates.stop, + maxY: this.stop, minX: gb.left, maxX: gb.right }); - if(overlap.length <=1 || textWidth <= gb.height){ + if (overlap.length <= 1 || textWidth <= gb.height) { ctx.save(); - ctx.translate(gb.left,gb.top); + ctx.translate(gb.left, gb.top); ctx.fillStyle = this.labelColor; - ctx.rotate(-Math.PI /2); - ctx.fillText(this.model.name,-gb.height,this.width+fontSize+1); + ctx.rotate(-Math.PI / 2); + ctx.fillText(this.model.name, -gb.height, this.width + fontSize + 1); ctx.restore(); } // Draw any children - this.children.forEach( child => child.draw(ctx)); + this.children.forEach(child => child.draw(ctx)); } } diff --git a/src/canvas/geometry/Ruler.js b/src/canvas/geometry/Ruler.js index 07c3bb97..fb62598a 100644 --- a/src/canvas/geometry/Ruler.js +++ b/src/canvas/geometry/Ruler.js @@ -1,75 +1,118 @@ /** - * ruler - * A SceneGraphNode representing ruler and zoom position for a given backbone - * - */ + * ruler + * A SceneGraphNode representing ruler and zoom position for a given backbone + * + * @extends SceneGraphNodeBase + */ + import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; import {Bounds} from '../../model/Bounds'; +import {translateScale} from '../../util/CanvasUtil'; export class Ruler extends SceneGraphNodeBase { - constructor({parent, bioMap}) { + /** + * Constructor + * @param parent - parent scene graph node + * @param bioMap - map data + * @param config - ruler configuration object + */ + + constructor({parent, bioMap, config}) { super({parent}); - let config = bioMap.config; this.config = config; this.mapCoordinates = bioMap.view; - this.offset = this.mapCoordinates.base.start*-1; + this.offset = this.mapCoordinates.base.start * -1; this.pixelScaleFactor = this.mapCoordinates.pixelScaleFactor; - this.fillColor = config.rulerColor; - this.textFace = config.rulerLabelFace; - this.textSize = config.rulerLabelSize; - this.textColor = config.rulerLabelColor; - this.rulerPrecision = config.rulerPrecision; + this.invert = this.mapCoordinates.invert; + this.fillColor = config.fillColor; + this.textFace = config.labelFace; + this.textSize = config.labelSize; + this.textColor = config.labelColor; + this.rulerPrecision = config.precision; + this.rulerWidth = config.width; + this.rulerPadding = config.padding; + this.innerSize = config.innerLineWeight; + this.innerColor = config.innerLineColor; const b = this.parent.backbone.bounds; this.bounds = new Bounds({ allowSubpixel: false, - top: this.parent.bounds.top, - left: b.left- config.rulerWidth - config.rulerSpacing , //arbritray spacing to look goo - width: config.rulerWidth, - height: b.height + top: 0, + left: b.left - config.width - config.padding - config.lineWeight, //arbitrary spacing to look goo + width: config.width, + height: b.height }); } - draw(ctx) { + /** + * Draw ruler and zoom bar + * @param ctx - linked canvas2D context + */ - let start = (this.mapCoordinates.visible.start+this.offset) * this.pixelScaleFactor; - let stop = (this.mapCoordinates.visible.stop+this.offset) * this.pixelScaleFactor; - let text = [this.mapCoordinates.base.start.toFixed(this.rulerPrecision),this.mapCoordinates.base.stop.toFixed(this.rulerPrecision)]; + draw(ctx) { + let config = this.config; + let vStart = this.invert ? this.mapCoordinates.visible.stop : this.mapCoordinates.visible.start; + let vStop = this.invert ? this.mapCoordinates.visible.start : this.mapCoordinates.visible.stop; + let start = translateScale(vStart, this.mapCoordinates.base, this.mapCoordinates.base, this.invert) * this.pixelScaleFactor; + let stop = translateScale(vStop, this.mapCoordinates.base, this.mapCoordinates.base, this.invert) * this.pixelScaleFactor; + let text = [this.mapCoordinates.base.start.toFixed(config.precision), this.mapCoordinates.base.stop.toFixed(config.precision)]; - let w = ctx.measureText(text[0]).width > ctx.measureText(text[1]).width ? ctx.measureText(text[0]).width : ctx.measureText(text[1]).width; - this.textWidth = w; + this.textWidth = ctx.measureText(text[0]).width > ctx.measureText(text[1]).width ? ctx.measureText(text[0]).width : ctx.measureText(text[1]).width; let gb = this.globalBounds || {}; // draw baseline labels - ctx.font = `${this.textSize}px ${this.textFace}`; + ctx.font = `${config.labelSize}px ${config.labelFace}`; + ctx.fillStyle = config.labelColor; ctx.textAlign = 'left'; - ctx.fillStyle = this.textColor; - ctx.fillText(text[0],gb.left - ctx.measureText(text[0]).width - (gb.width/2),Math.floor(gb.top - this.textSize/2)); - ctx.fillText(text[1],gb.left - ctx.measureText(text[1]).width - (gb.width/2),Math.floor(gb.bottom+this.textSize)); + if (this.invert) { + ctx.fillText(text[1], gb.left - ctx.measureText(text[1]).width - (gb.width / 2), Math.floor(gb.top - config.labelSize / 2)); + ctx.fillText(text[0], gb.left - ctx.measureText(text[0]).width - (gb.width / 2), Math.floor(gb.bottom + config.labelSize)); + } else { + ctx.fillText(text[0], gb.left - ctx.measureText(text[0]).width - (gb.width / 2), Math.floor(gb.top - config.labelSize / 2)); + ctx.fillText(text[1], gb.left - ctx.measureText(text[1]).width - (gb.width / 2), Math.floor(gb.bottom + config.labelSize)); + } // Draw zoom position labels - text = [this.mapCoordinates.visible.start.toFixed(this.rulerPrecision),this.mapCoordinates.visible.stop.toFixed(this.rulerPrecision)]; - - ctx.fillText(text[0],gb.left + this.config.rulerWidth + this.config.rulerSpacing , Math.floor(gb.top - this.textSize/2)); - ctx.fillText(text[1],gb.left + this.config.rulerWidth + this.config.rulerSpacing ,(gb.bottom + this.textSize)); + text = [this.mapCoordinates.visible.start.toFixed(config.precision), this.mapCoordinates.visible.stop.toFixed(config.precision)]; + if (this.invert) { + ctx.fillText(text[1], gb.left + config.width + config.padding, Math.floor(gb.top - config.labelSize / 2)); + ctx.fillText(text[0], gb.left + config.width + config.padding, (gb.bottom + config.labelSize)); + } else { + ctx.fillText(text[0], gb.left + config.width + config.padding, Math.floor(gb.top - config.labelSize / 2)); + ctx.fillText(text[1], gb.left + config.width + config.padding, (gb.bottom + config.labelSize)); + } //Draw baseline ruler - ctx.beginPath(); - ctx.lineWidth = 1.0; - ctx.strokeStyle = 'black'; - ctx.moveTo(Math.floor(gb.left + gb.width/2), Math.floor(gb.top)); - ctx.lineTo(Math.floor(gb.left + gb.width/2), Math.floor(gb.bottom)); + ctx.beginPath(); + ctx.lineWidth = config.innerLineWeight; + ctx.strokeStyle = config.innerLineColor; + // noinspection JSSuspiciousNameCombination + ctx.moveTo(Math.floor(gb.left + gb.width / 2), Math.floor(gb.top)); + // noinspection JSSuspiciousNameCombination + ctx.lineTo(Math.floor(gb.left + gb.width / 2), Math.floor(gb.bottom)); ctx.stroke(); // Draw "zoom box" - ctx.fillStyle = this.fillColor;//'aqua'; - var height = stop - start > 1 ? stop-start : 1.0; + ctx.fillStyle = config.fillColor;//'aqua'; + let height = stop - start > 1 ? stop - start : 1.0; + // noinspection JSSuspiciousNameCombination ctx.fillRect( Math.floor(gb.left), Math.floor(start + gb.top), Math.floor(gb.width), Math.floor(height) ); + //draw border if asked for + if(config.lineWeight > 0) { + ctx.strokeStyle = config.lineColor; + ctx.lineWidth = config.lineWeight; + ctx.strokeRect( + Math.floor(gb.left), + Math.floor(start + gb.top), + Math.floor(gb.width), + Math.floor(height) + ); + } ////debugging rectangle to test group bounds //ctx.fillStyle = 'red'; //ctx.fillRect( @@ -79,10 +122,16 @@ export class Ruler extends SceneGraphNodeBase { // Math.floor(gb.height) //); - this.children.forEach( child => child.draw(ctx)); + this.children.forEach(child => child.draw(ctx)); } - get visible(){ - return {data:this}; + /** + * Return the ruler as data for an scenegraph visibility check. (Ruler by definition is + * always visible, and does own logic for the position bar) + * @returns {{data: Ruler}} + */ + + get visible() { + return {data: this}; } } diff --git a/src/canvas/geometry/manhattanRuler.js b/src/canvas/geometry/manhattanRuler.js new file mode 100644 index 00000000..7554ccbf --- /dev/null +++ b/src/canvas/geometry/manhattanRuler.js @@ -0,0 +1,104 @@ +/** + * QTL - A feature with a length and width drawn as part of a group of similar + * features + * + * @extends SceneGraphNodeBase + */ + +import {SceneGraphNodeBase} from '../node/SceneGraphNodeBase'; +import {Bounds} from '../../model/Bounds'; +import {translateScale} from '../../util/CanvasUtil'; + +export class manhattanRuler extends SceneGraphNodeBase { + + /** + * Construct the QTL feature + * @param parent - parent scene graph node + * @param bioMap - map data + * @param featureModel - feature data + * @param initialConfig - configuration object for display variables + */ + + constructor({parent, featureModel,config}) { + super({parent}); + this.config = config; + this.manhattanPlot = featureModel; + this.bounds = new Bounds({ + allowSubpixel: false, + top:0, + left:0, + width: config.displayWidth, + height: this.parent.bounds.height + }); + } + + /** + * + * @param ctx + */ + + draw(ctx) { + let config = this.config; + ctx.save(); + ctx.globalAlpha = .5; + let cb = this.globalBounds; + let depth = 0; + if (this.manhattanPlot) { + //Draw "ruler" + ctx.strokeStyle = config.rulerColor; + ctx.lineWidth = config.rulerWeight; + + //Baseline marks + ctx.beginPath(); + + ctx.moveTo(cb.left, cb.top); + ctx.lineTo(cb.right, cb.top); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(cb.left, cb.bottom); + ctx.lineTo(cb.right, cb.bottom); + ctx.stroke(); + + //Ruler + for (let i = 0; i <= this.manhattanPlot.view.stop; i++) { + if (i % config.rulerMinorMark === 0 || i % config.rulerMajorMark === 0) { + depth = translateScale(i, { + start: 0, + stop: config.displayWidth + }, this.manhattanPlot.view, false); + ctx.beginPath(); + ctx.moveTo(cb.left + depth, cb.top); + ctx.lineTo(cb.left + depth, cb.top - 10); + ctx.stroke(); + if (i % config.rulerMajorMark === 0) { + ctx.font = config.labelSize; + ctx.fillStyle = config.labelColor; + ctx.textAlign = 'center'; + ctx.fillText(String(i), cb.left + depth, cb.top - 11); + } + } + } + ctx.fillText('-log10(p)', cb.left + config.displayWidth / 2, cb.top - 25); + + // Reference lines + + if (this.manhattanPlot.lines) { + this.manhattanPlot.lines.forEach(line => { + depth = translateScale(line.value, { + start: 0, + stop: config.displayWidth + }, this.manhattanPlot.view, false); + ctx.strokeStyle = line.lineColor; + ctx.lineWidth = line.lineWeight; + ctx.beginPath(); + ctx.moveTo(cb.left + depth, cb.top); + ctx.lineTo(cb.left + depth, cb.bottom); + ctx.stroke(); + }); + } + } + ctx.restore(); + + this.children.forEach(child => child.draw(ctx)); + } +} diff --git a/src/canvas/layout/BioMap.js b/src/canvas/layout/BioMap.js deleted file mode 100644 index 49d21918..00000000 --- a/src/canvas/layout/BioMap.js +++ /dev/null @@ -1,423 +0,0 @@ -/** - this.info.top = this.info.data.globalBounds.top; - m.redraw(); - * BioMap - * - * SceneGraphNodeCanvas representing a biological map and its associated tracks - * - */ -import m from 'mithril'; -import PubSub from 'pubsub-js'; - -import {featureUpdate,dataLoaded} from '../../topics'; - -import {Bounds} from '../../model/Bounds'; -import {SceneGraphNodeCanvas} from '../node/SceneGraphNodeCanvas'; -import {SceneGraphNodeGroup as Group} from '../node/SceneGraphNodeGroup'; -import {MapTrack} from './MapTrack'; -import {QtlTrack} from './QtlTrack'; -import {Ruler} from '../geometry/Ruler'; -import {pageToCanvas} from '../../util/CanvasUtil'; - -export class BioMap extends SceneGraphNodeCanvas { - - constructor({bioMapModel, appState, layoutBounds, bioMapIndex}) { - super({model:bioMapModel}); - this.bioMapIndex = bioMapIndex; - this.model.visible = { - start: this.model.coordinates.start, - stop: this.model.coordinates.stop - }; - this.model.view = { - base: { - start: this.model.coordinates.start, - stop: this.model.coordinates.stop - }, - visible: { - start: this.model.coordinates.start, - stop: this.model.coordinates.stop - } - }; - this.zoomDelta = (this.model.view.base.stop - this.model.view.base.start)/this.model.config.rulerSteps; - // set up coordinate bounds for view scaling - this.appState = appState; - this.verticalScale = 0; - this.backbone = null; - this.featureMarks = []; - this.featureLabels = []; - this.info = { - top:0, - left:0, - display:'none' - }; - - // create some regular expressions for faster dispatching of events - this._gestureRegex = { - pan: new RegExp('^pan'), - pinch: new RegExp('^pinch'), - tap: new RegExp('^tap'), - wheel: new RegExp('^wheel') - }; - this._layout(layoutBounds); - - } - - oncreate(vnode) { - super.oncreate(vnode); - PubSub.subscribe(featureUpdate, () => { - this._layout(this.lb); - this._redrawViewport(this.model.view.visible); - }); - } - /** - * culls elements to draw down to only those visible within the view - * bounds - */ - get visible(){ - let vis = []; - let cVis = this.children.map(child => { - return child.visible; - }); - cVis.forEach(item => { - vis = vis.concat(item); - }); - return vis; - } - /** - * children.visible() culls hits based on map coordiantes - * the hitMap is based on canvas global coordinates. - * */ - get hitMap(){ - return this.locMap; - } - - /** - * - * Re-implement lifecycle/gestrue components as needed to appease - * the items on the canvas. - * - */ - - // mousewheel event - _onZoom(evt) { - // TODO: send zoom event to the scenegraph elements which compose the biomap - // (dont scale the canvas element itself) - console.warn('BioMap -> onZoom', evt); - // normalise scroll delta - this.verticalScale = evt.deltaY < 0 ? -this.zoomDelta : this.zoomDelta; - let mcv = this.model.view.base; - let zStart = (this.model.view.visible.start + this.verticalScale); - let zStop = (this.model.view.visible.stop - this.verticalScale); - if(zStop - zStart < .01){ - this.verticalScale -=0.5; - return true; - } - if(zStart < mcv.start) { - zStart = mcv.start; - } else if ( zStart > zStop ){ - zStart = zStop; - } - - if(zStop > mcv.stop) { - zStop = mcv.stop; - } else if ( zStop < zStart ){ - zStop = zStart; - } - this._redrawViewport({start:zStart,stop:zStop}); - return true; // stop event propagation - } - - // return hits in case of tap/click event - _onTap(evt) { - console.log('BioMap -> onTap', evt, this); - let globalPos = pageToCanvas(evt,this.canvas); - this._loadHitMap(); - let hits = []; - - this.hitMap.search({ - minX: globalPos.x, - maxX: globalPos.x, - minY: globalPos.y-2, - maxY: globalPos.y+2 - }).forEach(hit => { - // temp fix, find why hit map stopped updating properly - if((hit.data.model.coordinates.start >= this.model.view.visible.start) && - (hit.data.model.coordinates.start <= this.model.view.visible.stop)){ - hits.push(hit.data); - } else if((hit.data.model.coordinates.stop >= this.model.view.visible.start) && - (hit.data.model.coordinates.stop <= this.model.view.visible.stop)){ - hits.push(hit.data); - } - }); - if(hits.length > 0){ - hits.sort((a,b) => { return a.model.coordinates.start - b.model.coordinates.start;}); - this.info.display = 'inline-block'; - this.info.top = hits[0].globalBounds.top; - this.info.left = hits[0].globalBounds.right; - this.info.data = hits; - let names = hits.map(hit => { return hit.model.name; }); - //@awilkey: is this obsolete? - this.info.innerHTML= `

${names.join('\n')} <\p>`; - m.redraw(); - } else if(this.info.display !== 'none'){ - this.info.display = 'none'; - m.redraw(); - } - - return true; - } - - // Setup selection context for pan event - _onPanStart(evt) { - // TODO: send pan events to the scenegraph elements which compose the biomap - // (dont scale the canvas element itself) - this.zoomP = { - start:0, - end:0, - pStart: true, - ruler: false, - delta:0, - corner: 0 - }; - this.zoomP.pStart = true; - console.warn('BioMap -> onPanStart -- vertically; implement me', evt); - let globalPos = pageToCanvas(evt, this.canvas); - let left = this.ruler.globalBounds.left; - // scroll view vs box select - if(left < (globalPos.x-evt.deltaX) && - (globalPos.x-evt.deltaX) < (left+this.ruler.bounds.width)){ - this.zoomP.ruler = true; - this._moveRuler(evt); - } else { - this.zoomP.ruler = false; - this.zoomP.start = this._pixelToCoordinate(globalPos.y-this.ruler.globalBounds.top-evt.deltaY); - if(this.zoomP.start < this.model.view.base.start){ - this.zoomP.start = this.model.view.base.start; - } - let ctx = this.context2d; - this.zoomP.corner = {top:globalPos.y-evt.deltaY,left:globalPos.x-evt.deltaX}; - ctx.lineWidth = 1.0; - ctx.strokeStyle = 'black'; - ctx.strokeRect( - Math.floor(globalPos.x-evt.deltaX), - Math.floor(globalPos.y-evt.deltaY), - Math.floor(evt.deltaX), - Math.floor(evt.deltaY) - ); - } - return true; - } - - // Moves ruler position on drag event - _moveRuler(evt){ - let delta = (evt.deltaY - this.zoomP.delta) / this.model.view.pixelScaleFactor; - if(this.model.view.visible.start+delta < this.model.view.base.start){ - delta = this.model.view.base.start - this.model.view.visible.start; - } else if(this.model.view.visible.stop+delta > this.model.view.base.stop){ - delta = this.model.view.base.stop - this.model.view.visible.stop; - } - this.model.view.visible.start += delta; - this.model.view.visible.stop += delta; - this._redrawViewport({start:this.model.view.visible.start, stop:this.model.view.visible.stop}); - this.zoomP.delta = evt.deltaY; - } - _onPan(evt){ - // block propegation if pan hasn't started - if (!this.zoomP || !this.zoomP.pStart) return true; - if(this.zoomP && this.zoomP.ruler){ - this._moveRuler(evt); - } else { - let globalPos = pageToCanvas(evt,this.canvas); - this.draw(); - let ctx = this.context2d; - ctx.lineWidth = 1.0; - ctx.strokeStyle = 'black'; - ctx.strokeRect( - Math.floor(this.zoomP.corner.left), - Math.floor(this.zoomP.corner.top), - Math.floor(globalPos.x - this.zoomP.corner.left), - Math.floor(globalPos.y -this.zoomP.corner.top ) - ); - } - return true; - } - _onPanEnd(evt) { - // TODO: send pan events to the scenegraph elements which compose the biomap - // (dont scale the canvas element itself) - console.warn('BioMap -> onPanEnd -- vertically; implement me', evt,this.model.view.base); - // block propegation if pan hasn't started - if (!this.zoomP || !this.zoomP.pStart) return true; - if(this.zoomP && this.zoomP.ruler){ - this._moveRuler(evt); - } else { - let globalPos = pageToCanvas(evt,this.canvas); - - // test if any part of the box select is in the ruler zone - let rLeft = this.ruler.globalBounds.left; - let rRight = this.ruler.globalBounds.right; - let lCorner = this.zoomP.corner.left < globalPos.x ? this.zoomP.corner.left : globalPos.x; - let rCorner = lCorner == this.zoomP.corner.left ? globalPos.x : this.zoomP.corner.left; - // if zoom rectangle contains the ruler, zoom, else populate popover - if(((lCorner <= rLeft) && (rCorner >= rLeft)) || ((lCorner <= rRight && rCorner >= rRight))){ - this.model.view.visible = this.model.view.base; - - this.zoomP.start = this._pixelToCoordinate(this.zoomP.corner.top-this.ruler.globalBounds.top); - this.zoomP.stop = this._pixelToCoordinate(globalPos.y-this.ruler.globalBounds.top); - let swap = this.zoomP.start < this.zoomP.stop; - let zStart = swap ? this.zoomP.start: this.zoomP.stop; - let zStop = swap ? this.zoomP.stop: this.zoomP.start; - - if(zStart < this.model.view.base.start){ - zStart = this.model.view.base.start; - } - if(zStop > this.model.view.base.stop){ - zStop = this.model.view.base.stop; - } - - this._redrawViewport({start:zStart, stop:zStop}); - } else { - - this._loadHitMap(); - let hits = []; - let swap = this.zoomP.corner.left < globalPos.x; - let swapV = this.zoomP.corner.top < globalPos.y; - this.hitMap.search({ - minX: swap ? this.zoomP.corner.left: globalPos.x, - maxX: swap ? globalPos.x : this.zoomP.corner.left, - minY: swapV ? this.zoomP.corner.top : globalPos.y, - maxY: swapV ? globalPos.y : this.zoomP.corner.top - }).forEach(hit => { - // temp fix, find why hit map stopped updating properly - if((hit.data.model.coordinates.start >= this.model.view.visible.start) && - (hit.data.model.coordinates.start <= this.model.view.visible.stop)){ - hits.push(hit.data); - } else if((hit.data.model.coordinates.stop >= this.model.view.visible.start) && - (hit.data.model.coordinates.stop <= this.model.view.visible.stop)){ - hits.push(hit.data); - } - }); - if(hits.length > 0){ - hits.sort((a,b) => { return a.model.coordinates.start - b.model.coordinates.start;}); - this.info.display = 'inline-block'; - this.info.top = this.ruler.globalBounds.top; - this.info.left = 0; - this.info.data = hits; - let names = hits.map(hit => { return hit.model.name; }); - //@awilkey: is this obsolete? - this.info.innerHTML= `

${names.join('\n')} <\p>`; - m.redraw(); - } else if(this.info.display !== 'none'){ - this.info.display = 'none'; - m.redraw(); - } - } - } - this.draw(); - this.zoomP = { - start:0, - end:0, - pStart: false, - ruler: false, - delta:0, - corner: { - top: 0, - left: 0 - } - }; -// this.zoomP.ruler = false; -// this.zoomP.pStart = false; - return true; // do not stop propagation - } - /** - * Converts a pixel position to the canvas' backbone coordinate system. - * - */ - _pixelToCoordinate(point){ - let coord = this.model.view.base; - let visc = this.model.view.visible; - let psf = this.model.view.pixelScaleFactor; - return ((visc.start*(coord.stop*psf - point) + visc.stop*(point - coord.start* psf))/(psf*(coord.stop - coord.start)))-(coord.start*-1); - } - - /** - * perform layout of backbone, feature markers, and feature labels. - */ - - _layout(layoutBounds) { - // TODO: calculate width based on # of SNPs in layout, and width of feature - // labels - // Setup Canvas - //const width = Math.floor(100 + Math.random() * 200); - this.lb = layoutBounds; - console.log('BioMap -> layout'); - const width = Math.floor(layoutBounds.width/this.appState.bioMaps.length); - this.children = []; - this.domBounds = new Bounds({ - left:layoutBounds.left, - top: layoutBounds.top, - width: width > 300 ? width:300, - height: layoutBounds.height - }); - - this.bounds = new Bounds({ - left: 0, - top: layoutBounds.top + 40, - width: this.domBounds.width, - height: Math.floor(this.domBounds.height - 140) // set to reasonably re-size for smaller windows - }); - //Add children tracks - this.bbGroup = new Group({parent:this}); - this.bbGroup.bounds = new Bounds({ - top:0, - left:0, - width:10 - }); - this.bbGroup.model = this.model; - this.backbone = new MapTrack({parent:this}); - this.bbGroup.addChild(this.backbone); - this.model.view.backbone = this.backbone.backbone.globalBounds; - this.ruler = new Ruler({parent:this, bioMap:this.model}); - this.bbGroup.addChild(this.ruler); - this.children.push(this.bbGroup); - let qtl = new QtlTrack({parent:this}); - if(this.domBounds.width < qtl.globalBounds.right+30){ - this.domBounds.width = qtl.globalBounds.right + 50; - } - this.children.push(qtl); - //load local rBush tree for hit detection - this._loadHitMap(); - //let layout know that width has changed on an element; - m.redraw(); - } - - _loadHitMap(){ - let hits = []; - let childrenHits = this.children.map(child => { - return child.hitMap; - }); - childrenHits.forEach(child =>{ - hits = hits.concat(child); - }); - this.locMap.clear();// = rbush(); - this.locMap.load(hits); - } - - _redrawViewport(coordinates){ - this.model.view.visible = { - start: coordinates.start, - stop: coordinates.stop - }; - this.backbone.loadLabelMap(); - this.draw(); - - let cMaps = document.getElementsByClassName('cmap-correspondence-map'); - [].forEach.call(cMaps, el =>{ - el.mithrilComponent.draw(); - }); - // move top of popover if currently visible - if(this.info.display !== 'none'){ - this.info.top = this.info.data[0].globalBounds.top; - } - m.redraw(); - } -} diff --git a/src/canvas/layout/FeatureTrack.js b/src/canvas/layout/FeatureTrack.js new file mode 100644 index 00000000..4265a902 --- /dev/null +++ b/src/canvas/layout/FeatureTrack.js @@ -0,0 +1,145 @@ +/** + * FeatureTrack + * A SceneGraphNode representing a collection of tracks. + * + * @extends SceneGraphNodeTrack + */ + +import {Bounds} from '../../model/Bounds'; + +import {SceneGraphNodeGroup} from '../node/SceneGraphNodeGroup'; +import {SceneGraphNodeTrack} from '../node/SceneGraphNodeTrack'; +import {ManhattanPlot} from './ManhattanPlot'; +import {QtlTrack} from './QtlTrack'; + +export class FeatureTrack extends SceneGraphNodeTrack { + + /** + * Constructor - sets up a track that's a group of QTL rectangles + * @param params + */ + + constructor(params) { + super(params); + this.model = this.parent.model; + const b = this.parent.bounds; + this.trackPos = params.position || 1; + + let left = this.trackPos < 0 ? 10 : this.parent.bbGroup.bounds.right; + this.bounds = new Bounds({ + allowSubpixel: false, + top: b.top, + left: left, + width: 0, + height: b.height + }); + if(this.parent.model.tracks) { + let tracks = this.trackPos === 1 ? this.parent.tracksLeft : this.parent.tracksRight; + tracks.forEach((track, order) => { + // newFeatureTrack is a group with two components, the feature data track, and the feature label track + //track.appState = this.parent.appState; + let newFeatureTrack = new SceneGraphNodeGroup({parent:this}); + newFeatureTrack.model = this.model; + newFeatureTrack.config = track; + newFeatureTrack.order = order; + + let trackLeft = order === 0 ? 0 : this.children[order-1].bounds.right; + trackLeft += this.model.config.qtl.padding; + + newFeatureTrack.bounds = new Bounds({ + allowSubpixel: false, + top: 0, + left: trackLeft, + width: this.model.config.qtl.trackMinWidth, + height: b.height + }); + + + let featureData = {}; + if(track.type === 'qtl') { + newFeatureTrack.title = track.title || this.model.config.qtl.title || track.filters[0]; + featureData = new QtlTrack({parent:newFeatureTrack , config: track}); + } else if( track.type === 'manhattan') { + newFeatureTrack.sources = this.parent.appState.sources; + newFeatureTrack.title = track.title || this.model.config.manhattan.title || 'Manhattan'; + featureData = new ManhattanPlot({parent:newFeatureTrack, config: track}); + } + + newFeatureTrack.addChild(featureData); + if(featureData.globalBounds.right > newFeatureTrack.globalBounds.right){ + newFeatureTrack.bounds.right += featureData.bounds.right; + } + + if(newFeatureTrack.globalBounds.right > this.globalBounds.right){ + this.bounds.right = this.bounds.left + (newFeatureTrack.globalBounds.right - this.globalBounds.left); + } + + this.addChild(newFeatureTrack); + }); + } else { + this.parent.model.tracks = []; + } + + } + + /** + * + */ + + get visible() { + let visible = []; + this.children.forEach(child => { + visible = visible.concat(child.visible); + }); + return visible; + //return visible.concat([{data:this}]); // debugging statement to test track width bounds + } + + /** + * Debug draw to check track positioning + * @param ctx + */ + + draw(ctx) { + ctx.save(); + ctx.globalAlpha = .5; + ctx.fillStyle = '#ADD8E6'; + this.children.forEach(child => { + let cb = child.globalBounds; + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination + ctx.fillRect( + Math.floor(cb.left), + Math.floor(cb.top), + Math.floor(cb.width), + Math.floor(cb.height) + ); + }); + //ctx.fillStyle = 'red'; + //let cb = this.globalBounds; + //ctx.fillRect( + // Math.floor(cb.left), + // Math.floor(cb.top), + // Math.floor(cb.width), + // Math.floor(cb.height) + //); + ctx.restore(); + } + + /** + * Get RTree children that are visible in the canvas' current zoom bounds + * @returns {Array} + */ + + get hitMap() { + //return []; + // console.log('hits child',child); + let hits = []; + this.children.forEach(child => { + return child.children.map(qtlGroup => { + hits = hits.concat(qtlGroup.hitMap); + }); + }); + return hits; + } +} diff --git a/src/canvas/layout/ManhattanPlot.js b/src/canvas/layout/ManhattanPlot.js new file mode 100644 index 00000000..e569bc67 --- /dev/null +++ b/src/canvas/layout/ManhattanPlot.js @@ -0,0 +1,175 @@ +/** + * ManhattanPlot + * A SceneGraphNodeTrack representing a Manhattan Plot. + * + * @extends SceneGraphNodeTrack + */ +import {Bounds} from '../../model/Bounds'; + +import {SceneGraphNodeTrack} from '../node/SceneGraphNodeTrack'; +import {Dot} from '../geometry/Dot'; +import {manhattanRuler} from '../geometry/manhattanRuler'; + +export class ManhattanPlot extends SceneGraphNodeTrack { + + /** + * Constructor - sets up a track that's a group of QTL rectangles + * @param params + */ + + constructor(params) { + super(params,); + console.log('manhattan -> constructor', params); + let manhattanPlot = params.config; + const b = this.parent.bounds; + this.trackPos = params.position || 1; + this.bounds = new Bounds({ + allowSubpixel: false, + top: 0, + left: 0, + width: 0, + height: b.height + }); + if (manhattanPlot !== null) { + let manhattanInfo = manhattanPlot; + //merge configuration information with default config + for( let key in this.parent.model.config.manhattan){ + if(!manhattanInfo.hasOwnProperty(key)){ + manhattanInfo[key] = this.parent.model.config.manhattan[key]; + } + } + manhattanInfo.lines.forEach(line => { + if(!line.lineWeight){ + line.lineWeigth = manhattanInfo.featureLineWeight; + } + if(!line.lineColor){ + line.lineColor = manhattanInfo.featureLineColor; + } + }); + + // If data hasn't been attached to this map to plot, filter and attach it. + if (manhattanInfo.data === undefined) { + manhattanInfo.view = { + start: 0, + stop: manhattanInfo.maxValue || 0 + }; + let baseData = this.parent.sources.filter(model => { + return model.id === manhattanInfo.dataId; + }); + + let prefix = manhattanInfo.prefix || ''; + manhattanInfo.data = baseData[0].parseResult.data.filter(mdata => { + if (prefix + mdata[manhattanInfo.targetField] === this.parent.model.name) { + if (manhattanInfo.max === undefined && -Math.log10(mdata[manhattanInfo.pField]) >= manhattanInfo.view.stop) { //determine max value while filtering data + manhattanInfo.view.stop = Math.ceil(-Math.log10(mdata[manhattanInfo.pField])); + + } + return true; + } + return false; + }); + } + + //Draw manhattan plot + //let left = this.parent.bbGroup.bounds.right; + + this.bounds = new Bounds({ + allowSubpixel: false, + top: 0, + left: 0, + width: manhattanInfo.displayWidth || 0, + height: b.height + }); + + let fmData = []; + let locData = []; + this.fmData = fmData; + + this.manhattanMarks = manhattanInfo.data.map(model => { + model.coordinates = { + start: model[manhattanInfo.posField], + depth: -Math.log10(model[manhattanInfo.pField]) + }; + if((model.coordinates.start > this.parent.model.view.base.stop) || + (model.coordinates.start < this.parent.model.view.base.start) ){ return; } + + let fm = new Dot({ + featureModel: model, + parent: this, + bioMap: this.parent.model, + config: manhattanInfo + }); + fmData.push(fm); + + let loc = { + minY: model.coordinates.start, + maxY: model.coordinates.start, + minX: fm.globalBounds.left, + maxX: fm.globalBounds.right, + data: fm + }; + + locData.push(loc); + return fm; + }); + + this.ruler ={ + minY: 0, + maxY: 100000000, + minX: this.globalBounds.left, + maxX: this.globalBounds.right, + data: new manhattanRuler({ + featureModel : manhattanInfo, + parent: this, + config: manhattanInfo + }) + }; + + this.locMap.load(locData); + this.tags = ['manhattan']; + } + } + + /** + * + */ + + get visible() { + return this.locMap.all().concat(this.ruler); + } + + // /** + // * Debug draw to check track positioning + // * @param ctx + // */ + + draw(ctx) { + // ctx.save(); + // ctx.globalAlpha = .5; + // ctx.fillStyle = 'green'; + // let cb = this.globalBounds; + // ctx.fillRect(cb.left,cb.top,cb.width,cb.height); + // ctx.restore(); + + this.children.forEach(child => child.draw(ctx)); + } + + // /** + // * Get RTree children that are visible in the canvas' current zoom bounds +// * @returns {Array} +// */ +// + get hitMap() { + //return this.locMap.all(); + return this.children.map(child => { + return { + minY: child.globalBounds.top, + maxY: child.globalBounds.bottom, + minX: child.globalBounds.left, + maxX: child.globalBounds.right, + data: child + }; + }); + + } +} \ No newline at end of file diff --git a/src/canvas/layout/MapTrack.js b/src/canvas/layout/MapTrack.js index dc4fa951..0e0182bb 100644 --- a/src/canvas/layout/MapTrack.js +++ b/src/canvas/layout/MapTrack.js @@ -1,8 +1,8 @@ /** - * MapTrack - * A SceneGraphNode representing a backbone, simply a rectangle representing - * the background. - */ + * MapTrack + * A SceneGraphNode representing a backbone, simply a rectangle representing + * the background. + */ import knn from 'rbush-knn'; import {SceneGraphNodeTrack} from '../node/SceneGraphNodeTrack'; @@ -12,65 +12,72 @@ import {FeatureMark} from '../geometry/FeatureMark'; import {MapBackbone} from '../geometry/MapBackbone'; import {FeatureLabel} from '../geometry/FeatureLabel'; -export class MapTrack extends SceneGraphNodeTrack { +export class MapTrack extends SceneGraphNodeTrack { + + /** + * + * @param params + */ constructor(params) { - console.log("MapTrack-> Constructing Map"); + console.log('MapTrack-> Constructing Map'); super(params); const b = this.parent.bounds; this.model = this.parent.model; //const backboneWidth = b.width * 0.2; - const backboneWidth = this.model.config.backboneWidth; + const backboneWidth = this.model.config.backbone.width; this.bounds = new Bounds({ allowSubpixel: false, - top: b.top, - left: this.model.config.rulerLabelSize * 10,//b.width * 0.5 - backboneWidth * 0.5, + top: 0, + left: 0, width: backboneWidth, height: b.height }); this.mC = this.parent.mapCoordinates; - this.backbone = new MapBackbone({ parent: this, bioMap: this.model}); + this.backbone = new MapBackbone({parent: this, bioMap: this.model,config: this.model.config.backbone}); this.addChild(this.backbone); // calculate scale factor between backbone coordinates in pixels - this.model.view.pixelScaleFactor = this.backbone.bounds.height/this.model.length; + this.model.view.pixelScaleFactor = this.backbone.bounds.height / this.model.length; this.model.view.backbone = this.globalBounds; // Setup groups for markers and labels - let markerGroup = new SceneGraphNodeGroup({parent:this}); + let markerGroup = new SceneGraphNodeGroup({parent: this}); this.addChild(markerGroup); this.markerGroup = markerGroup; markerGroup.bounds = this.backbone.bounds; this.addChild(markerGroup); - let labelGroup = new SceneGraphNodeGroup({parent:this}); + let labelGroup = new SceneGraphNodeGroup({parent: this}); this.addChild(labelGroup); this.labelGroup = labelGroup; labelGroup.bounds = new Bounds({ top: 0, left: this.backbone.bounds.right + 1, height: this.bounds.height, - width: 20 + width: 0 }); // Filter features for drawing - this.filteredFeatures = this.model.features.filter( model => { + this.filteredFeatures = this.model.features.filter(model => { return model.length <= 0.00001; }); //Place features and their labels, prepare to add to rtree let fmData = []; let lmData = []; - this.featureMarks = this.filteredFeatures.map( model => { + this.featureMarks = this.filteredFeatures.map(model => { let fm = new FeatureMark({ featureModel: model, parent: this.backbone, - bioMap: this.model + bioMap: this.model, + config: this.model.config.marker }); let lm = new FeatureLabel({ featureModel: model, parent: this.labelGroup, - bioMap: this.parent.model + bioMap: this.parent.model, + config: this.model.config.marker }); markerGroup.addChild(fm); labelGroup.addChild(lm); @@ -79,28 +86,35 @@ export class MapTrack extends SceneGraphNodeTrack { maxY: model.coordinates.stop, minX: fm.globalBounds.left, maxX: fm.globalBounds.right, - data:fm + data: fm }); lmData.push({ minY: model.coordinates.start, maxY: model.coordinates.stop, minX: lm.globalBounds.left, - maxX: lm.globalBounds.right, + maxX: lm.globalBounds.left + this.labelGroup.bounds.width, data: lm }); + if (lm.bounds.right > this.labelGroup.bounds.right) this.labelGroup.bounds.right = lm.bounds.right; return fm; }); - // Load group rtrees for markers and labels + // Load group rTrees for markers and labels markerGroup.locMap.load(fmData); labelGroup.locMap.load(lmData); // load this rtree with markers (elements that need hit detection) this.locMap.load(fmData); } - get visible(){ + /** + * + * @returns {*[]} + */ + + get visible() { let coord = this.parent.model.view.base; let visc = this.parent.model.view.visible; + let vis = [{ minX: this.bounds.left, maxX: this.bounds.right, @@ -117,57 +131,69 @@ export class MapTrack extends SceneGraphNodeTrack { let labels = []; let start = visc.start; let stop = visc.stop; - let psf = this.labelGroup.children[0].pixelScaleFactor; - let step =((visc.start*(coord.stop*psf - 12) + visc.stop*(12 - coord.start* psf))/(psf*(coord.stop - coord.start)) - start) - (coord.start*-1); - for(let i = start; i < stop; i+=step){ - - let item = knn( this.labelGroup.locMap, this.labelGroup.children[0].globalBounds.left,i,1)[0]; - if(labels.length === 0){ - labels.push(item); - continue; - } - let last = labels[labels.length-1]; - if(item != last && (item.minY > (last.maxY + step))){ - labels.push(item); - } + let psf = this.labelGroup.children[0].pixelScaleFactor; + let step = ((visc.start * (coord.stop * psf - 12) + visc.stop * (12 - coord.start * psf)) / (psf * (coord.stop - coord.start)) - start) - (coord.start * -1); + for (let i = start; i < stop; i += step) { + + let item = knn(this.labelGroup.locMap, this.labelGroup.children[0].globalBounds.left, i, 1)[0]; + if (labels.length === 0) { + labels.push(item); + continue; + } + let last = labels[labels.length - 1]; + if (item !== last && (item.minY > (last.maxY + step))) { + labels.push(item); + } } vis = vis.concat(labels); - //vis = vis.concat([{data:this}]); return vis; } - get hitMap(){ + /** + * + */ + + get hitMap() { let bbGb = this.backbone.globalBounds; - return this.markerGroup.children.map( child =>{ + return this.markerGroup.children.map(child => { return { - minY: child.globalBounds.bottom+1, - maxY: child.globalBounds.top-1, - minX: bbGb.left , - maxX: bbGb.right , + minY: child.globalBounds.bottom + 1, + maxY: child.globalBounds.top - 1, + minX: bbGb.left, + maxX: bbGb.right, data: child }; }); } - draw(ctx){ + /** + * + * @param ctx + */ + + draw(ctx) { let gb = this.globalBounds || {}; ctx.fillStyle = 'blue'; + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination ctx.fillRect( Math.floor(gb.left), Math.floor(gb.top), Math.floor(gb.width), Math.floor(gb.height) - ); + ); ctx.fillStyle = 'green'; gb = this.labelGroup.globalBounds || {}; + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination ctx.fillRect( Math.floor(gb.left), Math.floor(gb.top), Math.floor(gb.width), Math.floor(gb.height) - ); + ); } - loadLabelMap(){ + loadLabelMap() { } } diff --git a/src/canvas/layout/QtlTrack.js b/src/canvas/layout/QtlTrack.js index 6f567d45..45d8f3f1 100644 --- a/src/canvas/layout/QtlTrack.js +++ b/src/canvas/layout/QtlTrack.js @@ -1,110 +1,122 @@ /** - * QtlTrack - * A SceneGraphNode representing a collection of QTLs. - */ + * QtlTrack + * A SceneGraphNode representing a collection of QTLs. + * + * @extends SceneGraphNodeTrack + */ import {SceneGraphNodeTrack} from '../node/SceneGraphNodeTrack'; -import {SceneGraphNodeGroup} from '../node/SceneGraphNodeGroup'; import {Bounds} from '../../model/Bounds'; import {QTL} from '../geometry/QTL'; -export class QtlTrack extends SceneGraphNodeTrack { +export class QtlTrack extends SceneGraphNodeTrack { + + /** + * Constructor - sets up a track that's a group of QTL rectangles + * @param params + */ + + /** + * TODO: Allow for subtracks + */ constructor(params) { super(params); - console.log('QtlTrack -> constructor',this.parent.domBounds); - const b = this.parent.bounds; + + this.filteredFeatures = []; + let b = this.parent.bounds; this.bounds = new Bounds({ allowSubpixel: false, - top: this.parent.bounds.top, - left: this.parent.backbone.bounds.right + 100, - width: 50, + top: 0, + left: 0, + width: this.parent.model.config.qtl.trackMinWidth, height: b.height }); - if(this.parent.model.qtlGroups && this.parent.model.qtlGroups.length > 0){ - console.log('qtlGroups', this.parent.model.qtlGroups); - let qtlGroups = this.parent.model.qtlGroups; - for( let i = 0 ; i < qtlGroups.length; i++){ - let qtlConf = qtlGroups[i]; - if (typeof qtlConf.filters === 'string'){ qtlConf.filters = [qtlConf.filters];} - if (typeof qtlConf.trackColor === 'string'){ qtlConf.trackColor = [qtlConf.trackColor];} - let qtlGroup = new SceneGraphNodeGroup({parent:this, tags:qtlConf.filters.slice(0)}); - this.addChild(qtlGroup); - let offset = this.qtlGroup !== undefined ? this.qtlGroup.bounds.right + 20 : 0; - this.qtlGroup = qtlGroup; - qtlGroup.bounds = new Bounds({ - top:0, - left: offset, - width:20, - height: b.height - }); - - this.mapCoordinates = this.parent.mapCoordinates; - this.filteredFeatures = []; - qtlConf.filters.forEach( (filter,order) => { - var test = this.parent.model.features.filter( model => { - return model.tags[0].match(filter) !== null; - }) - if(test.length === 0){ - // get rid of any tags that don't actually get used - qtlConf.filters.splice(order,1); - } else { - this.filteredFeatures = this.filteredFeatures.concat(test); - } - }); - this.filteredFeatures.sort((a,b)=>{return a.coordinates.start - b.coordinates.start;}); - let fmData = []; - this.maxLoc = 0; - this.qtlMarks = this.filteredFeatures.map( model => { - let fm = new QTL ({ - featureModel: model, - parent: this.qtlGroup, - bioMap: this.parent.model, - initialConfig:this.parent.model.qtlGroups[i] - }); - qtlGroup.addChild(fm); - let loc = { - minY: model.coordinates.start, - maxY: model.coordinates.stop, - minX: fm.globalBounds.left, - maxX: fm.globalBounds.right, - data:fm - }; - qtlGroup.locMap.insert(loc); - fmData.push(loc); - if(fm.globalBounds.right > this.globalBounds.right){ - this.maxLoc = this.globalBounds.right; - this.bounds.right = this.globalBounds.left + (fm.globalBounds.right - this.globalBounds.left); - qtlGroup.bounds.right = qtlGroup.bounds.left + (fm.globalBounds.right - qtlGroup.globalBounds.left) + fm.offset; //set to fm.textWidth - } - return fm; - }); - this.locMap.load(fmData); + + let qtlConf = params.config; + for( let key in this.parent.model.config.qtl){ + if(!qtlConf.hasOwnProperty(key)){ + qtlConf[key] = this.parent.model.config.qtl[key]; } - } else { // TODO: Rewrite so that this isn't required to be here - let qtlGroup = new SceneGraphNodeGroup({parent:this}); - this.addChild(qtlGroup); - this.qtlGroup = qtlGroup; - - qtlGroup.bounds = new Bounds({ - top:0, - left:0, - width:0, - height: b.height - }); } + + qtlConf.filters.forEach( (filter,order) => { + var test = this.parent.model.features.filter( model => { + return model.tags[0].match(filter) !== null; + }); + if(test.length === 0){ + // get rid of any tags that don't actually get used + qtlConf.filters.splice(order,1); + } else { + this.filteredFeatures = this.filteredFeatures.concat(test); + } + }); + + this.filteredFeatures.sort((a,b)=>{return a.coordinates.start - b.coordinates.start;}); + let fmData = []; + + + this.maxLoc = 0; + this.qtlMarks = this.filteredFeatures.map( model => { + let fm = new QTL ({ + featureModel: model, + parent: this, + bioMap: this.parent.model, + initialConfig: qtlConf, + config: this.parent.model.config.qtl + }); + + this.addChild(fm); + + let loc = { + minY: model.coordinates.start, + maxY: model.coordinates.stop, + minX: fm.globalBounds.left, + maxX: fm.globalBounds.right, + data:fm + }; + + this.locMap.insert(loc); + + fmData.push(loc); + + if(fm.globalBounds.right > this.globalBounds.right){ + this.bounds.right = fm.globalBounds.right - this.globalBounds.left; + } + + return fm; + }); + this.locMap.clear(); + this.locMap.load(fmData); } - get visible(){ + /** + * + */ + + get visible() { + // let visible = []; + // this.children.forEach(child => { + // visible = visible.concat(child.locMap.all()); + // }); + // + // return visible; return this.locMap.all(); //return this.locMap.all().concat([{data:this}]); // debugging statement to test track width bounds - } - - draw(ctx){ + } + + /** + * Debug draw to check track positioning + * @param ctx + */ + + draw(ctx) { ctx.save(); ctx.globalAlpha = .5; ctx.fillStyle = '#ADD8E6'; - this.children.forEach( child => { + this.children.forEach(child => { let cb = child.globalBounds; + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination ctx.fillRect( Math.floor(cb.left), Math.floor(cb.top), @@ -115,31 +127,21 @@ export class QtlTrack extends SceneGraphNodeTrack { ctx.restore(); } - get hitMap(){ - //return []; - let hits = []; - let childPos = this.children.map(child => { - return child.children.map( qtlGroup =>{ - return { - minY: qtlGroup.globalBounds.top, - maxY: qtlGroup.globalBounds.bottom, - minX: qtlGroup.globalBounds.left, - maxX: qtlGroup.globalBounds.right , - data: qtlGroup - }; - }); - }); - childPos.forEach( childArray =>{ - hits = hits.concat(childArray); + /** + * Get RTree children that are visible in the canvas' current zoom bounds + * @returns {Array} + */ + + get hitMap() { + //return this.locMap.all(); + return this.children.map(child => { + return { + minY: child.globalBounds.top, + maxY: child.globalBounds.bottom, + minX: child.globalBounds.left, + maxX: child.globalBounds.right, + data: child + }; }); - return hits; - // return { - // minY: child.globalBounds.top, - // maxY: child.globalBounds.bottom, - // minX: child.globalBounds.left, - // maxX: child.globalBounds.right , - // data: child - // }; - //}); } } diff --git a/src/canvas/node/SceneGraphNodeBase.js b/src/canvas/node/SceneGraphNodeBase.js index e15f2583..b3810c3f 100644 --- a/src/canvas/node/SceneGraphNodeBase.js +++ b/src/canvas/node/SceneGraphNodeBase.js @@ -1,27 +1,27 @@ /** - * SceneGraphNodeBase - * Base Class representing a drawable element in canvas scenegraph - */ + * SceneGraphNodeBase + * Base Class representing a drawable element in canvas scenegraph + */ -import rbush from 'rbush'; +import rbush from 'rbush'; -import { Bounds } from '../../model/Bounds'; +import {Bounds} from '../../model/Bounds'; export class SceneGraphNodeBase { /** - * Create a SceneGraphNode. - * Constructor uses ES6 destructuring of parameters from an object. - * e.g. new SceneGraphNode({param: .., param2, etc.}) - * - * @param {Object} params - having the following properties: - * @param {String} tag - an label or slug - * @param {Object} parent - the parent node - * @param {Object} bounds - local Canvas bounds, relative to our parent. - This is not the same as DOM bounds of the canvas element! - * @param {Number} rotation - degrees, default 0. - * @returns {Object} - */ + * Create a SceneGraphNode. + * Constructor uses ES6 destructuring of parameters from an object. + * e.g. new SceneGraphNode({param: .., param2, etc.}) + * + * @param {Object} params - having the following properties: + * @param {Array} tags - an label or slug + * @param {Object} parent - the parent node + * @param {Object} bounds - local Canvas bounds, relative to our parent. + * This is not the same as DOM bounds of the canvas element! + * @param {Number} rotation - degrees, default 0. + */ + constructor({parent, bounds, rotation = 0, tags = []}) { this.parent = parent; this._rotation = rotation; @@ -32,29 +32,56 @@ export class SceneGraphNodeBase { this._visble = []; } - /* getters and setters */ + /* getters and setters */ /* define getters for our properties; note subclasses can override setters, e.g. to perform layout or calculations based on new state */ + /* getters */ - get children() { return this._children; } - get bounds() { return this._bounds; } - get rotation() { return this._rotation; } - get tags() { return this._tags; } - /* setters */ - set children(b) { this._children = b;} - set bounds(b) { this._bounds = b; } - set rotation(degrees) { this._rotation = degrees; } - set tags(tags) { this._tags = tags; } + /** + * Children scene graph nodes + * @returns {Array|*} any child nodes this node has + */ + + get children() { + return this._children; + } + + /** + * Local bounds + * @returns {*} local bounds + */ + + get bounds() { + return this._bounds; + } + + /** + * Rotation applied on this and subsequent children + * @returns {*} rotation + */ + + get rotation() { + return this._rotation; + } /** - * Traverse all parents bounds to calculate self Bounds on Canvas. - * - * @returns {Object} - Bounds instance - */ + * Info tags + * @returns {*} tags + */ + + get tags() { + return this._tags; + } + + /** + * Traverse all parents bounds to calculate self Bounds on Canvas. + * @returns {Object} - Bounds instance + */ + get globalBounds() { console.assert(this.bounds, 'bounds missing'); - if(! this.parent) return this.bounds; + if (!this.parent) return this.bounds; let gb = this.parent.globalBounds; return new Bounds({ top: this.bounds.top + gb.top, @@ -67,46 +94,90 @@ export class SceneGraphNodeBase { } /** - * Use rbush to returni children nodes that may be visible. + * Use rbush to return children nodes that may be visible. * At this level, it is assumed that there is no viewport * constraints to the filter. * - * @retrun {Array} - array of rbush nodes + * @return {Array} - array of rbush nodes */ - get visible(){ + + get visible() { let vis = []; - let childVisible = this.children.map( child => { + let childVisible = this.children.map(child => { return child.locMap.all(); }); - childVisible.forEach(item =>{ vis = vis.concat(item);}); + childVisible.forEach(item => { + vis = vis.concat(item); + }); return vis; } /** * Traverse children, returning hitmap - * * @returns {Array} - array of rbush entries */ - get hitMap(){ + get hitMap() { let hits = []; - let childMap = this.children.map( child => { + let childMap = this.children.map(child => { return child.hitMap; }); - childMap.forEach(item =>{ hits = hits.concat(item);}); + childMap.forEach(item => { + hits = hits.concat(item); + }); return hits; } - /* public methods/* + /* setters */ + + /** + * Child scene graph nodes + * @param {Array|*} b + */ + + set children(b) { + this._children = b; + } + + /** + * Nodes local bounds + * @param b - bounds object + */ + + set bounds(b) { + this._bounds = b; + } + + /** + * Rotation + * @param {number} degrees - rotation in degrees + */ + + set rotation(degrees) { + this._rotation = degrees; + } + + /** + * Tags + * @param {array} tags - object's descriptive tags + */ + + set tags(tags) { + this._tags = tags; + } + + /* public methods */ + /** * Translate coordinates to canvas space. When an element wants to draw on * canvas, it requires translating into global coordinates for the canvas. * * @param {Object} params - object with following properties: - * @param {Number} x - * @param {Number} y - * @returns {Object} - { x, y } + * @param {Number} x - x location + * @param {Number} y - y location + * @returns {Object} - { x, y } x,y location in global terms */ + translatePointToGlobal({x, y}) { let gb = this.globalBounds; return {x: x + gb.left, y: y + gb.top}; @@ -118,35 +189,38 @@ export class SceneGraphNodeBase { * * @param {object} node - SceneGraphNode derived item to insert as a child **/ - addChild(node){ - if(node.parent){ + + addChild(node) { + if (node.parent) { node.parent.removeChild(node); } node.parent = this; - if(this._children.indexOf(node) === -1) this._children.push(node); + if (this._children.indexOf(node) === -1) this._children.push(node); } /** * Removes a child node from the _children array - * and changes child node's parent to undefined + * and changes child node's parent to undefined * * @param {object} node - SceneGraphNode derived node to remove **/ - removeChild(node){ + + removeChild(node) { //TODO: May need to use a indexOf polyfill if targeting IE < 9 let index = this._children.indexOf(node); - if(index > -1){ - this._children.splice(index,1); + if (index > -1) { + this._children.splice(index, 1); } node.parent = null; } + /** * Traverse children and call their draw on the provided context * - * #param {object} ctx - canvas context - * + * @param {object} ctx - canvas context */ - draw(ctx){ + + draw(ctx) { this.children.forEach(child => child.draw(ctx)); } } diff --git a/src/canvas/node/SceneGraphNodeCanvas.js b/src/canvas/node/SceneGraphNodeCanvas.js index d44b1a0f..c16e7709 100644 --- a/src/canvas/node/SceneGraphNodeCanvas.js +++ b/src/canvas/node/SceneGraphNodeCanvas.js @@ -1,8 +1,8 @@ /** - * SceneGraphNodeCanvas - * Mithril component representing a html5 canvas element. - * - */ + * SceneGraphNodeCanvas + * Mithril component representing a html5 canvas element. + * + */ import m from 'mithril'; import PubSub from 'pubsub-js'; @@ -17,10 +17,15 @@ import {selectedMap} from '../../topics'; import {Bounds} from '../../model/Bounds'; import {SceneGraphNodeBase} from './SceneGraphNodeBase'; +export class SceneGraphNodeCanvas + extends mix(SceneGraphNodeBase) + .with(DrawLazilyMixin, RegisterComponentMixin) { -export class SceneGraphNodeCanvas - extends mix(SceneGraphNodeBase) - .with(DrawLazilyMixin, RegisterComponentMixin) { + /** + * constructor + * @param model - data model for canvas + * @param appState - app state model + */ constructor({model, appState}) { super({}); @@ -28,26 +33,32 @@ export class SceneGraphNodeCanvas this.appState = appState; this.verticalScale = 1; this.info = { - visible:false, - top:0, - left:0 + visible: false, + top: 0, + left: 0 }; this._gestureRegex = { - pan: new RegExp('^pan'), + pan: new RegExp('^pan'), pinch: new RegExp('^pinch'), - tap: new RegExp('^tap'), + tap: new RegExp('^tap'), wheel: new RegExp('^wheel') }; } + /** + * Getter if canvas has focus + * @returns {boolean} + */ + get selected() { return this.appState.selection.bioMaps.indexOf(this) !== -1; } - /** * mithril lifecycle method + * @param vnode */ + oncreate(vnode) { super.oncreate(vnode); this.canvas = this.el = vnode.dom; @@ -57,12 +68,14 @@ export class SceneGraphNodeCanvas /** * mithril lifecycle method + * @param vnode - current virtual dom node */ + onupdate(vnode) { // TODO: remove this development assistive method console.assert(this.el === vnode.dom); let b = new Bounds(this.el.getBoundingClientRect()); - console.log('BioMap.onupdate', b.width, b.height, this.el); + console.log('BioMap.onupdate', this.el.mithrilComponent, b); } /** @@ -70,28 +83,29 @@ export class SceneGraphNodeCanvas */ view() { // store these bounds, for checking in drawLazily() - if(this.domBounds && ! this.domBounds.isEmptyArea) { + if (this.domBounds && !this.domBounds.isEmptyArea) { this.lastDrawnMithrilBounds = this.domBounds; } let b = this.domBounds || {}; let selectedClass = this.selected ? 'selected' : ''; - return m('canvas', { - class: `cmap-canvas cmap-biomap ${selectedClass}`, - style: `left: ${b.left}px; top: ${b.top}px; + return m('canvas', { + class: `cmap-canvas cmap-biomap ${selectedClass}`, + style: `left: ${b.left}px; top: ${b.top}px; width: ${b.width}px; height: ${b.height}px; transform: rotate(${this.rotation}deg);`, - width: b.width, - height: b.height - }); + width: b.width, + height: b.height + }); } /** - * draw our scenegraph children our canvas element + * draw our scene graph children on canvas element */ + draw() { let ctx = this.context2d; - if(! ctx) return; - if(! this.domBounds) return; + if (!ctx) return; + if (!this.domBounds) return; ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ctx.save(); //ctx.translate(0.5, 0.5); // prevent subpixel rendering of 1px lines @@ -99,44 +113,63 @@ export class SceneGraphNodeCanvas ctx.restore(); // store these bounds, for checking in drawLazily() this.lastDrawnCanvasBounds = this.bounds; + this.dirty = false; } - /** + + /** * custom gesture event dispatch listener; see LayoutContainer + * @param evt + * @returns {boolean} Don't stop event propagation */ + handleGesture(evt) { - if(evt.type.match(this._gestureRegex.tap)) { + if (evt.type.match(this._gestureRegex.tap)) { return this._onTap(evt); } else if (evt.type.match(this._gestureRegex.pinch)) { return this._onZoom(evt); } - else if(evt.type.match(this._gestureRegex.wheel)) { + else if (evt.type.match(this._gestureRegex.wheel)) { return this._onZoom(evt); } - else if(evt.type.match(this._gestureRegex.pan)) { - if(evt.type === 'panend'){ + else if (evt.type.match(this._gestureRegex.pan)) { + if (evt.type === 'panend') { return this._onPanEnd(evt); - } else if ( evt.type === 'panstart'){ + } else if (evt.type === 'panstart') { return this._onPanStart(evt); } else { return this._onPan(evt); } } - return false; // dont stop evt propagation + return false; // don't stop evt propagation } + /** + * Handle zoom event + * @param evt - zoom event (mousewheel or gesture) + * @returns {boolean} don't stop event propagation + * @private + */ + _onZoom(evt) { // TODO: send zoom event to the scenegraph elements which compose the biomap - // (dont scale the canvas element itself) + // (don't scale the canvas element itself) console.warn('BioMap -> onZoom -- implement me', evt); return false; // stop event propagation } + /** + * Tap/Click event + * @param evt + * @returns {boolean} Don't stop event propagation by default + * @private + */ + _onTap(evt) { let sel = this.appState.selection.bioMaps; let i = sel.indexOf(this); - if(i === -1) { + if (i === -1) { sel.push(this); } else { @@ -149,24 +182,48 @@ export class SceneGraphNodeCanvas }); return false; } + + /** + * Pan event that isn't first or last in sequence + * @param evt + * @returns {boolean} + * @private + */ + _onPan(evt) { // TODO: send pan events to the scenegraph elements which compose the biomap - // (dont scale the canvas element itself) - if(evt.direction & Hammer.DIRECTION_VERTICAL) { + // (don't scale the canvas element itself) + if (evt.direction && Hammer.DIRECTION_VERTICAL) { console.warn('BioMap -> onPan -- vertically; implement me', evt); return false; // stop event propagation } return false; // do not stop propagation } + + /** + * First pan event in sequence + * @param evt + * @returns {boolean} + * @private + */ + _onPanStart(evt) { // TODO: send pan events to the scenegraph elements which compose the biomap - // (dont scale the canvas element itself) - console.warn('BioMap -> onPanStart -- vertically; implement me', evt); - return false; + // (don't scale the canvas element itself) + console.warn('BioMap -> onPanStart -- vertically; implement me', evt); + return false; } + + /** + * Final pan event in sequence + * @param evt + * @returns {boolean} + * @private + */ + _onPanEnd(evt) { // TODO: send pan events to the scenegraph elements which compose the biomap - // (dont scale the canvas element itself) + // (don't scale the canvas element itself) console.warn('BioMap -> onPanEnd -- vertically; implement me', evt); return false; // do not stop propagation } diff --git a/src/canvas/node/SceneGraphNodeGroup.js b/src/canvas/node/SceneGraphNodeGroup.js index 9dd0a755..693767c2 100644 --- a/src/canvas/node/SceneGraphNodeGroup.js +++ b/src/canvas/node/SceneGraphNodeGroup.js @@ -1,15 +1,26 @@ /** - * FeatureMarker - * A SceneGraphNode representing a feature on a Map with a line or hash mark. - */ + * FeatureMarker + * A SceneGraphNode representing a feature on a Map with a line or hash mark. + */ import {SceneGraphNodeBase} from './SceneGraphNodeBase'; export class SceneGraphNodeGroup extends SceneGraphNodeBase { + /** + * constructor + * @param params + */ + constructor(params) { super(params); } - get visible(){ + + /** + * Return visible children elements + * @returns {Array} + */ + + get visible() { let vis = []; let cVis = this.children.map(child => { return child.visible; diff --git a/src/canvas/node/SceneGraphNodeTrack.js b/src/canvas/node/SceneGraphNodeTrack.js index d3ad518c..0ea7340e 100644 --- a/src/canvas/node/SceneGraphNodeTrack.js +++ b/src/canvas/node/SceneGraphNodeTrack.js @@ -1,11 +1,15 @@ /** - * FeatureMarker - * A SceneGraphNode representing a feature on a Map with a line or hash mark. - */ + * Placeholder for advanced group nodes + */ import {SceneGraphNodeBase} from './SceneGraphNodeBase'; export class SceneGraphNodeTrack extends SceneGraphNodeBase { + /** + * Constructor + * @param params + */ + constructor(params) { super(params); } diff --git a/src/developmentTooling.js b/src/developmentTooling.js index 7ef62933..e42ccd77 100644 --- a/src/developmentTooling.js +++ b/src/developmentTooling.js @@ -1,17 +1,23 @@ /** - * development tooling: conditionally run code based on the ENV string, + * @file + * Development tooling: conditionally run code based on the ENV string, * which is interpolated by a plugin in the rollup.config.js. + * */ import PubSub from 'pubsub-js'; import * as topics from './topics'; +/** + * @description Logger to check that pub-sub events propagate. + */ + const monitorPubSub = () => { let logger = (topic, data) => { // eslint-disable-next-line no-console console.log(`[${topic}]`, data); }; - Object.keys(topics).forEach( t => { + Object.keys(topics).forEach(t => { //console.log(`subscribing to: ${t}`); PubSub.subscribe(t, logger); }); diff --git a/src/main.js b/src/main.js index 9d6584fb..d6b4631f 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ /** - * main + * @file * Instantiate the CMAP class, and initialize it. * Also the entry point for bundling of javascript and css. */ @@ -14,6 +14,12 @@ import './util/concatAll'; import {CMAP} from './ui/CMAP'; /* istanbul ignore next: unable to test this module because of css imports */ +/** + * @description Initializes CMAP window with CSS and makes sure that the initial + * DOM events are properly handled. + * + */ + const main = () => { // FIXME: this way of exposing the cmap object seems kind of clunky. For // implementing a js api, maybe using this rollup plugin would be diff --git a/src/model/AppModel.js b/src/model/AppModel.js index 4e6428fe..77873ddf 100644 --- a/src/model/AppModel.js +++ b/src/model/AppModel.js @@ -12,12 +12,16 @@ import {DataSourceModel} from './DataSourceModel'; export class AppModel { + /** + * + */ + constructor() { // sources and bioMaps arrays will be populated in load() this.sources = []; this.bioMaps = []; this.tools = { - zoomFactor : 1, + zoomFactor: 1, layout: HorizontalLayout // the default layout }; this.selection = { @@ -33,8 +37,12 @@ export class AppModel { /** * load the app model - * @param Object - object with properties defined in cmap.json + * @param header + * @param attribution + * @param sources + * @param initialView */ + load({header, attribution, sources, initialView}) { let sourceConfigs = sources; this.header = header; @@ -42,26 +50,23 @@ export class AppModel { this.initialView = initialView || []; this.biomaps = []; let promises = sourceConfigs.map(config => { - console.log('config',config); let dsm = new DataSourceModel(config); this.sources.push(dsm); - console.log('push dsm',dsm); return dsm.load(); }); // wait for all data sources are loaded, then set this.bioMaps with // only the maps named in initialView // - Promise.all(promises).then( () => { - this.allMaps = this.sources.map( src => Object.values(src.bioMaps) ).concatAll(); - if(! this.initialView.length) { + Promise.all(promises).then(() => { + this.allMaps = this.sources.map(src => Object.values(src.bioMaps)).concatAll(); + if (!this.initialView.length) { this.defaultInitialView(); } else { this.setupInitialView(); } PubSub.publish(dataLoaded); - }). - catch( err => { + }).catch(err => { // TODO: make a nice mithril component to display errors in the UI const msg = `While fetching data source(s), ${err}`; console.error(msg); @@ -74,13 +79,14 @@ export class AppModel { /** * create this.bioMaps based on initialView of config file. */ + setupInitialView() { - this.bioMaps = this.initialView.map( viewConf => { + this.bioMaps = this.initialView.map(viewConf => { const res = this.allMaps.filter(map => { return (viewConf.source === map.source.id && - viewConf.map === map.name); + viewConf.map === map.name); }); - if(res.length == 0) { + if (res.length === 0) { // TODO: make a nice mithril component to display errors in the UI const info = JSON.stringify(viewConf); const msg = `failed to resolve initialView entry: ${info}`; @@ -88,7 +94,10 @@ export class AppModel { console.trace(); alert(msg); } - if(viewConf.qtl){ + if(viewConf.tracks){ + res[0].tracks = viewConf.tracks; + } + if (viewConf.qtl) { res[0].qtlGroups = viewConf.qtl; } return res; @@ -100,24 +109,27 @@ export class AppModel { * initialView was not defined in config file). */ defaultInitialView() { - this.bioMaps = this.sources.map( src => Object.values(src.bioMaps)[0] ); + this.bioMaps = this.sources.map(src => Object.values(src.bioMaps)[0]); } /** * Add map at the given index (note, this is called by MapAdditionDialog) - * @param Object bioMap - a bioMap from one of the already loaded data sources. - * @param Number index - zero based index into the bioMaps array. + * @param {Object} bioMap - a bioMap from one of the already loaded data sources. + * @param {Number} index - zero based index into the bioMaps array. */ - addMap(bioMap, index=0) { + + addMap(bioMap, index = 0) { this.bioMaps.splice(index, 0, bioMap); PubSub.publish(mapAdded, bioMap); } /** * PubSub event handler + * @private */ + _onReset() { - this.tools.zoomFactor = 1; + this.tools.zoomFactor = 1; this.tools.layout = HorizontalLayout; } } diff --git a/src/model/BioMapConfigModel.js b/src/model/BioMapConfigModel.js index b3abed27..28bdd32c 100644 --- a/src/model/BioMapConfigModel.js +++ b/src/model/BioMapConfigModel.js @@ -7,37 +7,98 @@ export class BioMapConfigModel { /** * create a BioMapConfigModel + * @param url + * @param method */ - constructor({url, method} ) { + + constructor({url, method}) { this.url = url; this.method = method; } - load(){ + /** + * + */ + load() { return m.request(this); } } +/** + * Constant that defines the default configuration of cmap maps + * when no other configuration information is present. + * + * @type {{backboneWidth: number, backboneColor: string, invert: boolean, markerColor: string, markerWeight: number, markerLabelSize: number, markerLabelFace: string, markerLabelColor: string, rulerWidth: number, rulerSpacing: number, rulerColor: string, rulerLabelFace: string, rulerLabelSize: number, rulerLabelColor: string, rulerPrecision: number, rulerSteps: number, trackWidth: number, trackSpacing: number, fillColor: string, trackLabelSize: number, trackLabelFace: string, trackLabelColor: string}} + */ + export const defaultConfig = { - 'backboneWidth' : 20, - 'backboneColor' : '#fff6e8', - 'markerColor' : 'black', - 'markerWeight': 1, - 'markerLabelSize' : 12, - 'markerLabelFace': 'Nunito', - 'markerLabelColor' : 'black', - 'rulerWidth' : 10 , - 'rulerSpacing' : 5, - 'rulerColor' : 'aqua', - 'rulerLabelFace' : 'Nunito', - 'rulerLabelSize' : 12, - 'rulerLabelColor' : 'black', - 'rulerPrecision' : 2, - 'rulerSteps' : 100, - 'trackWidth' : 5, - 'trackSpacing' : 5, - 'trackColor' : '#636081', - 'trackLabelSize' : 12, - 'trackLabelFace' : 'Nunito', - 'trackLabelColor' : 'black' - }; + 'backbone' : { + 'width' : 20, + 'fillColor' : '#fff6e8', + 'lineWeight' : 0, + 'lineColor' : 'black' + }, + 'ruler' : { + 'width' : 10, + 'padding' : 5, + 'fillColor' : 'aqua', + 'lineWeight' : 0, + 'lineColor' : 'black', + 'labelFace' : 'Nunito', + 'labelSize' : 12, + 'labelColor' : 'black', + 'innerLineWeight' : 1.0, + 'innerLineColor' : 'black', + 'precision' : 2, + 'steps' : 100 + }, + 'track' : { + 'width' : 5, + 'padding' : 5, + 'fillColor' : '#636081', + 'lineWeight' : 0, + 'lineColor' : 'black', + 'labelFace' : 'Nunito', + 'labelSize' : 12, + 'labelColor' : 'black', + 'internalPadding' : '5' + }, + 'marker':{ + 'lineWeight' : 1, + 'lineColor' : 'black', + 'labelFace' : 'Nunito', + 'labelSize' : 12, + 'labelColor' : 'black' + }, + + 'manhattan' :{ + 'width' : 2, + 'fillColor':'green', + 'lineWeight':1, + 'lineColor':'black', + 'labelFace' : 'Nunito', + 'labelSize' : 10, + 'labelColor' : 'black', + 'displayWidth' : 50, + 'featureLineWeight' : 3, + 'featureLineColor' : 'red', + 'rulerWeight' : 2, + 'rulerColor' : 'black', + 'rulerMajorMark':10, + 'rulerMinorMark':2, + 'type':'manhattan' + }, + 'qtl':{ + 'padding' : 20, + 'width': 5, + 'fillColor': ['green'], + 'labelSize': 12, + 'labelFace': 'Nunito', + 'labelColor': 'black', + 'trackMinWidth' : 50, + 'internalPadding': 5, + 'position' : 1, + 'type':'qtl' + }, + 'invert': false, +}; diff --git a/src/model/BioMapModel.js b/src/model/BioMapModel.js index 67a82843..7b419002 100644 --- a/src/model/BioMapModel.js +++ b/src/model/BioMapModel.js @@ -1,24 +1,26 @@ /** * BioMap data model */ + export class BioMapModel { /** * create a BioMapModel - * @param Object params having the following properties: - * @param String name - the map name - * @param Object source - the DataSourceModel where bioMap was loaded from - * @param Object coordinates - object w/ start and stop props - * @param Array features - an array of Feature instances. + * @param {Object} params having the following properties: + * @param {String} name - the map name + * @param {Object} source - the DataSourceModel where bioMap was loaded from + * @param {Object} coordinates - object w/ start and stop props + * @param {Array} features - an array of Feature instances. */ + constructor({ - name, - source, - features, - tags, - coordinates = { start: 0, stop: 0}, - config - }) { + name, + source, + features, + tags, + coordinates = {start: 0, stop: 0}, + config + }) { this.name = name; this.source = source; this.features = features; @@ -29,14 +31,19 @@ export class BioMapModel { /** * getter for length (coordinates.stop - coordinates.start) + * @returns {number} */ + get length() { return this.coordinates.stop - this.coordinates.start; } /** + * * getter for unique name (prefix map name the id of data source) + * @returns {string} */ + get uniqueName() { return `${this.source.id}/${this.name}`; } diff --git a/src/model/Bounds.js b/src/model/Bounds.js index 38883a82..92261344 100644 --- a/src/model/Bounds.js +++ b/src/model/Bounds.js @@ -1,26 +1,27 @@ /** - * Bounds - * Class representing a 2D bounds, having the same properties as a DOMRect. - * This class can be instantiated by script, unlike DOMRect object itself which - * comes from the browser's DOM by getBoundingClientRect(). - * https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect - */ + * @description + * Bounds + * Class representing a 2D bounds, having the same properties as a DOMRect. + * This class can be instantiated by script, unlike DOMRect object itself which + * comes from the browser's DOM by getBoundingClientRect(). + * https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect + */ import {isNil} from '../util/isNil'; export class Bounds { /** - * Create a Bounds - * - * @param {Object} params - having the following properties: - * @param {Number} bottom - * @param {Number} left - * @param {Number} right - * @param {Number} top - * @param {Number} width - * @param {Number} height - * @returns {Object} - */ - constructor({top, left, bottom, right, width, height, allowSubpixel=true}) { + * Create a Bounds + * + * @param {Object} params - having the following properties: + * @param {Number} bottom + * @param {Number} left + * @param {Number} right + * @param {Number} top + * @param {Number} width + * @param {Number} height + * @returns {Object} + */ + constructor({top, left, bottom, right, width, height, allowSubpixel = true}) { this._bottom = bottom; this._left = left; this._right = right; @@ -29,22 +30,28 @@ export class Bounds { this._width = width; this.allowSubpixel = allowSubpixel; - if(isNil(this.width)) this._width = this.right - this.left; - if(isNil(this.height)) this._height = this.bottom - this.top; - if(isNil(this.bottom)) this._bottom = this.top + this.height; - if(isNil(this.right)) this._right = this.left + this.width; + if (isNil(this.width)) this._width = this.right - this.left; + if (isNil(this.height)) this._height = this.bottom - this.top; + if (isNil(this.bottom)) this._bottom = this.top + this.height; + if (isNil(this.right)) this._right = this.left + this.width; - if(! allowSubpixel) { + if (!allowSubpixel) { + // noinspection JSSuspiciousNameCombination this._bottom = Math.floor(this.bottom); + // noinspection JSSuspiciousNameCombination this._top = Math.floor(this.top); this._left = Math.floor(this.left); this._right = Math.floor(this.right); this._width = Math.floor(this.width); + // noinspection JSSuspiciousNameCombination this._height = Math.floor(this.height); - if(this.x) this.x = Math.floor(this.x); - if(this.y) this.y = Math.floor(this.y); + if (this.x) this.x = Math.floor(this.x); + if (this.y) { // noinspection JSSuspiciousNameCombination + this.y = Math.floor(this.y); + } } } + /** * Getters and setters, should be allowed to update bounds without having to * resort to making a new bounds object. @@ -53,9 +60,9 @@ export class Bounds { get bottom() { return this._bottom; } - - set bottom(val){ - if(this.allowSubpixel){ + + set bottom(val) { + if (this.allowSubpixel) { this._bottom = val; this._height = this._bottom - this._top; } else { @@ -67,9 +74,9 @@ export class Bounds { get top() { return this._top; } - - set top(val){ - if(this.allowSubpixel){ + + set top(val) { + if (this.allowSubpixel) { this._top = val; this._height = this._bottom - this._top; } else { @@ -81,9 +88,9 @@ export class Bounds { get left() { return this._left; } - - set left(val){ - if(this.allowSubpixel){ + + set left(val) { + if (this.allowSubpixel) { this._left = val; this._width = this._right - this._left; } else { @@ -95,9 +102,9 @@ export class Bounds { get right() { return this._right; } - - set right(val){ - if(this.allowSubpixel){ + + set right(val) { + if (this.allowSubpixel) { this._right = val; this._width = this._right - this._left; } else { @@ -109,9 +116,9 @@ export class Bounds { get width() { return this._width; } - - set width(val){ - if(this.allowSubpixel){ + + set width(val) { + if (this.allowSubpixel) { this._width = val; this._right = this._left + this._width; } else { @@ -123,9 +130,9 @@ export class Bounds { get height() { return this._height; } - - set height(val){ - if(this.allowSubpixel){ + + set height(val) { + if (this.allowSubpixel) { this._height = val; this._bottom = this._top + this._height; } else { @@ -133,12 +140,12 @@ export class Bounds { this._bottom = Math.floor(this._top + this._height); } } - + /** * Check if width or height is zero, making the Bounds effectively empty. */ get isEmptyArea() { - return ! this.width || ! this.height; + return !this.width || !this.height; } /** @@ -149,39 +156,39 @@ export class Bounds { } /** - * Class method- test whether two bounds are equal (rounds to nearest pixel) - * - * @param bounds1 - DOMRect or Bounds instance - * @param bounds2 - DOMRect or Bounds instance - * @returns Boolean - */ + * Class method- test whether two bounds are equal (rounds to nearest pixel) + * + * @param bounds1 - DOMRect or Bounds instance + * @param bounds2 - DOMRect or Bounds instance + * @returns Boolean + */ static equals(bounds1, bounds2) { let p, n1, n2; - if(! bounds1 || ! bounds2) + if (!bounds1 || !bounds2) return false; // check for null args - for (var i = 0; i < PROPS.length; i++) { + for (let i = 0; i < PROPS.length; i++) { p = PROPS[i]; n1 = bounds1[p]; n2 = bounds2[p]; - if(n1 === undefined || n2 === undefined) { // skip test, see note about x,y + if (n1 === undefined || n2 === undefined) { // skip test, see note about x,y continue; } // cast properties from float to int before equality comparison - if(Math.floor(n1) !== Math.floor(n2)) + if (Math.floor(n1) !== Math.floor(n2)) return false; } return true; } /** - * Class method- test whether two bounds are equal in area (rounds to nearest pixel) - * - * @param bounds1 - DOMRect or Bounds instance - * @param bounds2 - DOMRect or Bounds instance - * @returns Boolean - */ + * Class method- test whether two bounds are equal in area (rounds to nearest pixel) + * + * @param bounds1 - DOMRect or Bounds instance + * @param bounds2 - DOMRect or Bounds instance + * @returns Boolean + */ static areaEquals(bounds1, bounds2) { - if(! bounds1 || ! bounds2) + if (!bounds1 || !bounds2) return false; // check for null args return Math.floor(bounds1.area) === Math.floor(bounds2.area); } diff --git a/src/model/DataSourceModel.js b/src/model/DataSourceModel.js index 162753ad..2c9d3de4 100644 --- a/src/model/DataSourceModel.js +++ b/src/model/DataSourceModel.js @@ -1,12 +1,13 @@ /** * Data source model */ + import m from 'mithril'; import parser from 'papaparse'; import {BioMapModel} from './BioMapModel'; import {Feature} from './Feature'; -import {BioMapConfigModel,defaultConfig} from './BioMapConfigModel'; +import {BioMapConfigModel, defaultConfig} from './BioMapConfigModel'; // TODO: implement filtering at data loading time @@ -14,51 +15,60 @@ export class DataSourceModel { /** * create a DataSourceModel - * @param Object params having the following properties: - * @param String id - uniqueId string for the data source (required) - * @param String method - HTTP method, get or post (required) - * @param String url - HTTP URL (required) - * @param Object data - query string parameters for the request (optional) + * @param {Object} params having the following properties: + * @param {String} id - uniqueId string for the data source (required) + * @param {String} method - HTTP method, get or post (required) + * @param {String} url - HTTP URL (required) + * @param {Object} data - query string parameters for the request (optional) */ - constructor({id, method, data, url, filters, linkouts,config}) { + + constructor({id, method, data, url, filters, linkouts, config}) { this.id = id; this.method = method; this.data = data; this.url = url; this.config = config || {}; - this.bioConfig = {"default":defaultConfig}; + this.bioConfig = {'default': defaultConfig}; // request bioconfig urlpage as a promise, if it is gettable, fill in all // default values that aren't defined using the base config, otherwise // set the default values to the base config (found in BioMapConfigModel). - (( ) => { // promise generator + (() => { // promise generator let cfg = new BioMapConfigModel(this.config); return cfg.load(); })().then( // promise resolution - (item)=>{ // success + (item) => { // success this.bioConfig = item; - for(const configGroup of Object.keys(this.bioConfig)){ - for( const key of Object.keys(defaultConfig)){ - if(this.bioConfig[configGroup][key] === undefined){ - this.bioConfig[configGroup][key] = defaultConfig[key]; + for (const configGroup of Object.keys(this.bioConfig)) { + for (const key of Object.keys(defaultConfig)) { + if (this.bioConfig[configGroup][key] === undefined) { + this.bioConfig[configGroup][key] = this.bioConfig.default[key] || defaultConfig[key]; + } + for(const subkey of Object.keys(defaultConfig[key])) { + if (this.bioConfig[configGroup][key][subkey] === undefined) { + this.bioConfig[configGroup][key][subkey] = this.bioConfig.default[key][subkey] || defaultConfig[key][subkey]; + } } } } - }, - ()=>{ // failure + }, + () => { // failure this.bioConfig.default = defaultConfig; } ); this.filters = filters || []; this.linkouts = linkouts || []; - this.linkouts.forEach(l => {l.featuretypePattern != undefined ? l.featuretypePattern = new RegExp(l.featuretypePattern) : undefined;}); + this.linkouts.forEach(l => { + l.featuretypePattern !== undefined ? l.featuretypePattern = new RegExp(l.featuretypePattern) : undefined; + }); this.background = true; // mithril not to redraw upon completion } /** - * Load the data source with mithril request - * @return Promise + *Load the data source with mithril request + * @returns {*} */ + load() { return m.request(this); } @@ -67,20 +77,21 @@ export class DataSourceModel { * Callback from mithril request(); instead of the default deserialization * which is JSON, use the papaparse library to parse csv or tab delimited * content. - * @param String delimited text - csv or tsv + * @param {String} data - delimited text, csv or tsv */ + deserialize(data) { const res = parser.parse(data, { header: true, dynamicTyping: true, skipEmptyLines: true }); - if(res.errors.length) { + if (res.errors.length) { console.error(res.errors); alert(`There were parsing errors in ${this.url}, please see console.`); } // apply filters from config file - res.data = res.data.filter( d => this.includeRecord(d) ); + res.data = res.data.filter(d => this.includeRecord(d)); this.parseResult = res; } @@ -89,27 +100,28 @@ export class DataSourceModel { * processed sequentially and the result is all or nothing, effectively like * SQL AND. * - * @param Object d - key/value properies of 1 record - * @return Boolean - true for include, false for exclude + * @param {Object} d - key/value properties of 1 record + * @return {Boolean} true for include, false for exclude */ + includeRecord(d) { let hits = 0; - this.filters.forEach( f => { + this.filters.forEach(f => { let col = f.column; - if(d.hasOwnProperty(col)) { + if (d.hasOwnProperty(col)) { let testVal = d[col]; let match; - if(f.operator === 'equals') { + if (f.operator === 'equals') { match = (testVal === f.value); } - else if(f.operator === 'regex') { + else if (f.operator === 'regex') { match = testVal.match(f.value); } - if(f.not) { - if(! match) ++hits; + if (f.not) { + if (!match) ++hits; } else { - if(match) ++hits; + if (match) ++hits; } } }); @@ -120,8 +132,9 @@ export class DataSourceModel { * bioMaps getter; return a mapping of the uniquified map name to * an instance of BioMapModel. * - * @return Object - key: prefix + map_name -> val: BioMapModel instance + * @return {Object} key: prefix + map_name -> val: BioMapModel instance */ + get bioMaps() { const res = {}; try { @@ -156,12 +169,12 @@ export class DataSourceModel { ); if(d[typeField] !== '' && res[uniqueMapName].tags.indexOf(d[typeField]) === -1){ res[uniqueMapName].tags.push(d[typeField]); - } - }); - } catch(e) { - console.trace(); - console.error(e); - } - return res; + } + }); + } catch (e) { + console.trace(); + console.error(e); + } + return res; } } diff --git a/src/model/Feature.js b/src/model/Feature.js index 292a2cde..14f72e52 100644 --- a/src/model/Feature.js +++ b/src/model/Feature.js @@ -13,13 +13,14 @@ class Feature { * @param {Object} aliases - array of alternate names, optional * @returns {Object} */ + constructor({ - source, - coordinates = { start: 0, stop: 0}, - name, - tags=[], - aliases=[], - }) { + source, + coordinates = {start: 0, stop: 0}, + name, + tags = [], + aliases = [], + }) { this.source = source; this.coordinates = Object.freeze(coordinates); // object w/ start and end props this.name = name; @@ -27,48 +28,71 @@ class Feature { this.aliases = aliases; } + /** + * + * @returns {number} + */ + get length() { return this.coordinates.stop - this.coordinates.start; } + /** + * + * @returns {boolean} + */ + get typeHasLinkouts() { return this.source.linkouts.some(l => { - return this.typeLinkedBy(l); - }); + return this.typeLinkedBy(l); + }); } + /** + * + * @param linkout + * @returns {Array|*|boolean} + */ + typeLinkedBy(linkout) { - return linkout.featuretypePattern != undefined ? - this.tags.some(t => {return linkout.featuretypePattern.test(t);}) - : this.tags.includes(linkout.featuretype); + return linkout.featuretypePattern !== undefined ? + this.tags.some(t => { + return linkout.featuretypePattern.test(t); + }) + : this.tags.includes(linkout.featuretype); } } /** - * Find the common features based on name and aliases. * @param Array features1 - 1st collection of features * @param Array features2 - 2nd collection of features * @return Array - tuples of results in common [[feat1, feat2], ...] */ +/** + * Find the common features based on name and aliases. + * @param features1 + * @param features2 + * @returns {any[]} + */ + // TODO: support more than two collections of features function featuresInCommon(features1, features2) { const setupDict = (features) => { let dict = {}; - features.forEach( f => { + features.forEach(f => { dict[f.name] = f; - f.aliases.forEach( a => { - if(a) dict[a] = f; + f.aliases.forEach(a => { + if (a) dict[a] = f; }); }); return dict; }; let dict1 = setupDict(features1); let dict2 = setupDict(features2); - let intersectedKeys = Object.keys(dict1).filter( key => dict2[key] ); - return intersectedKeys.map( key => { - return [ dict1[key], dict2[key] ]; + let intersectedKeys = Object.keys(dict1).filter(key => dict2[key]); + return intersectedKeys.map(key => { + return [dict1[key], dict2[key]]; }); } - export {Feature, featuresInCommon}; diff --git a/src/polyfill/elementsFromPoint.js b/src/polyfill/elementsFromPoint.js index d5ae686f..caa41a96 100644 --- a/src/polyfill/elementsFromPoint.js +++ b/src/polyfill/elementsFromPoint.js @@ -13,8 +13,8 @@ if (!document.elementsFromPoint) { /* istanbul ignore next: depends on browser native elementFromPoint(x,y) */ function elementsFromPoint(x, y) { - var parents = []; - var parent = void 0; + let parents = []; + let parent = void 0; do { if (parent !== document.elementFromPoint(x, y)) { parent = document.elementFromPoint(x, y); diff --git a/src/polyfill/index.js b/src/polyfill/index.js index 4df06769..6d89a910 100644 --- a/src/polyfill/index.js +++ b/src/polyfill/index.js @@ -3,7 +3,7 @@ * Javascript, HTML5, or CSS3 features. */ - // adds Math.scale, fscale, clamp, radians, degrees +// adds Math.scale, fscale, clamp, radians, degrees import 'ecma-proposal-math-extensions'; // adds elementsFromPoint diff --git a/src/topics.js b/src/topics.js index 2c4e7c6b..ac12c5d5 100644 --- a/src/topics.js +++ b/src/topics.js @@ -1,7 +1,8 @@ /** - * topics - * define constants for all PubSub message topics used by cmap + * @file + * defines constants for all PubSub message topics used by cmap */ + export const selectedMap = 'selectedMap'; // the selected map (canvas) changed export const reset = 'reset'; // reset button click export const layout = 'layout'; // layout selection changed @@ -11,5 +12,6 @@ export const dataLoaded = 'loaded'; // data finished loading, or was filtered/up // by user export const mapRemoved = 'mapRemoved'; export const mapAdded = 'mapAdded'; +export const mapReorder = 'mapReorder'; export const featureUpdate = 'featureUpdate'; // change to qtlConfig from modal diff --git a/src/ui/CMAP.js b/src/ui/CMAP.js index bb022412..93a57e1e 100644 --- a/src/ui/CMAP.js +++ b/src/ui/CMAP.js @@ -1,15 +1,20 @@ /** - * CMAP - */ + * CMAP + */ + import m from 'mithril'; import {AppModel} from './../model/AppModel'; import {UI} from './UI'; - /* istanbul ignore next: mithril-query does not work with m.mount, and dom id is hardcoded as well */ export class CMAP { + /** + * + * @param configURL + */ + load(configURL) { this.rootElement = document.getElementById('cmap-ui'); this.appState = new AppModel({}); @@ -23,16 +28,16 @@ export class CMAP { this.appState.status = 'loading configuration file...'; this.appState.busy = true; - m.request(configURL).then( config => { + m.request(configURL).then(config => { let numSources = config.sources.length; - let plural = numSources > 1 ? 's': ''; + let plural = numSources > 1 ? 's' : ''; this.appState.status = `loading ${numSources} data file${plural}...`; let promises = this.appState.load(config); - Promise.all(promises).then( () => { + Promise.all(promises).then(() => { this.appState.status = ''; this.appState.busy = false; }); - }).catch( err => { + }).catch(err => { // TODO: make a nice mithril component to display errors in the UI console.error(err); console.trace(); diff --git a/src/ui/Header.js b/src/ui/Header.js index 8b04f968..07101174 100644 --- a/src/ui/Header.js +++ b/src/ui/Header.js @@ -5,11 +5,20 @@ import m from 'mithril'; export class Header { // constructor() - prefer do not use in mithril components + /** + * mithril lifecycle method + * @param vnode + */ oninit(vnode) { this.appState = vnode.attrs.appState; } + /** + * Mithril lifecycle component + * @returns {*} cmap-header + */ + view() { return m('div.cmap-hbox', m('h4.cmap-header', [ diff --git a/src/ui/README.md b/src/ui/README.md new file mode 100644 index 00000000..820b8f65 --- /dev/null +++ b/src/ui/README.md @@ -0,0 +1,14 @@ + #src/ui/ # +Contains user-interface elements based on MithrilJs components, i.e. +they are populated into the DOM, not drawing with the Canvas API. +___ + +`/css` - self explanatory + +`/layout` - layout templates for canvas elements + +`/layout/components` - mithril wrappers for commonly re-used layout components + +`/menus` - full screen modal menus + +`/tools` - menus that populate as dropdowns from buttons in the header diff --git a/src/ui/README.txt b/src/ui/README.txt deleted file mode 100644 index 38b38a60..00000000 --- a/src/ui/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -src/ui/ contains user-interface elements based on MithrilJs components, i.e. -they are populated into the DOM, not drawing with the Canvas API. diff --git a/src/ui/RegisterComponentMixin.js b/src/ui/RegisterComponentMixin.js index 89230b9d..2fe82ac4 100644 --- a/src/ui/RegisterComponentMixin.js +++ b/src/ui/RegisterComponentMixin.js @@ -1,24 +1,39 @@ /** - * Store a reference to the mithril component on it's corresponding dom - * element. - */ + * Store a reference to the mithril component on it's corresponding dom + * element. + */ export let RegisterComponentMixin = (superclass) => class extends superclass { + /** + * + * @param vnode + */ + oninit(vnode) { - if(super.oninit) super.oninit(vnode); - if(vnode.attrs && vnode.attrs.registerComponentCallback) { + if (super.oninit) super.oninit(vnode); + if (vnode.attrs && vnode.attrs.registerComponentCallback) { vnode.attrs.registerComponentCallback(this); } } + /** + * + * @param vnode + */ + oncreate(vnode) { - if(super.oncreate) super.oncreate(vnode); + if (super.oncreate) super.oncreate(vnode); vnode.dom.mithrilComponent = this; } + /** + * + * @param vnode + */ + onbeforeremove(vnode) { - if(super.onbeforeremove) super.onbeforeremove(vnode); + if (super.onbeforeremove) super.onbeforeremove(vnode); delete vnode.dom.mithrilComponent; } }; diff --git a/src/ui/StatusBar.js b/src/ui/StatusBar.js index 3621f11a..fa0768dc 100644 --- a/src/ui/StatusBar.js +++ b/src/ui/StatusBar.js @@ -1,20 +1,30 @@ /** - * StatusBar - * A mithril component of a status bar and/or footer. - */ + * StatusBar + * A mithril component of a status bar and/or footer. + */ import m from 'mithril'; export class StatusBar { // constructor() - prefer do not use in mithril components + /** + * + * @param vnode + */ + oninit(vnode) { this.appState = vnode.attrs.appState; } + /** + * + * @returns {*} + */ + view() { return m('div', [ m('div.cmap-attribution', this.appState.attribution), - m('div',{id:'cmap-disclaimer'}, 'cmap-js is still in alpha. As the software is still in development, the current state of the project may not reflect the final release.'), + m('div', {id: 'cmap-disclaimer'}, 'cmap-js is still in alpha. As the software is still in development, the current state of the project may not reflect the final release.'), m('div.cmap-footer', [ this.appState.busy ? m('img[src=images/ajax-loader.gif]') : '', this.appState.status diff --git a/src/ui/UI.js b/src/ui/UI.js index 2577bd54..a93a8903 100644 --- a/src/ui/UI.js +++ b/src/ui/UI.js @@ -1,7 +1,7 @@ /** - * UI - * A mithril component presenting all DOM aspects of user-interface. - */ + * UI + * A mithril component presenting all DOM aspects of user-interface. + */ import m from 'mithril'; import Hammer from 'hammerjs'; import Hamster from 'hamsterjs'; @@ -19,16 +19,19 @@ export class UI extends mix().with(RegisterComponentMixin) { /** * Create a UI instance - * @param Object - the appState, instance of model/AppModel. + * @param {Object} appState - instance of model/AppModel. */ + constructor(appState) { super(); this.appState = appState; } /** - * mithril lifecycle method + * Mithril lifecycle method + * @param vnode */ + oncreate(vnode) { super.oncreate(vnode); this.el = vnode.dom; @@ -37,7 +40,9 @@ export class UI extends mix().with(RegisterComponentMixin) { /** * mithril component render callback + * @returns {*} mithril vnode component */ + view() { const childAttrs = { appState: this.appState, @@ -46,8 +51,9 @@ export class UI extends mix().with(RegisterComponentMixin) { return m('div.cmap-layout.cmap-vbox', [ m(Header, childAttrs), m(Tools, childAttrs), - [ m('div.cmap-menu-viewport#cmap-menu-viewport',{style:'display:none'}), - m('div.cmap-layout-viewport.cmap-hbox', { id: 'cmap-layout-viewport',style:'position:relative;' }, + m('div#cmap-layout-titles', {style: 'display:inline-flex;'}), + [m('div.cmap-menu-viewport#cmap-menu-viewport', {style: 'display:none;'}), + m('div.cmap-layout-viewport.cmap-hbox', {id: 'cmap-layout-viewport', style: 'position:relative;'}, m(LayoutContainer, { appState: this.appState, registerComponentCallback: (comp) => this._layoutContainer = comp @@ -57,12 +63,22 @@ export class UI extends mix().with(RegisterComponentMixin) { ]); } + /** + * + * @private + */ + _logRenders() { - if(! this.count) this.count = 0; + if (!this.count) this.count = 0; this.count += 1; console.log(`*** mithril render #${this.count} ***`); } + /** + * + * @private + */ + _setupEventHandlers() { this._setupMousewheel(); this._setupGestures(); @@ -71,7 +87,9 @@ export class UI extends mix().with(RegisterComponentMixin) { /** * Setup pubsub subscriptions + * @private */ + _setupPubSub() { PubSub.subscribe(reset, () => { // if the viewport were refactored into it's own mithril component, then @@ -84,16 +102,18 @@ export class UI extends mix().with(RegisterComponentMixin) { /** * setup mouse wheel (hamsterjs) handlers. + * @private */ + _setupMousewheel() { const hamster = Hamster(this.el); const hamsterHandler = (evt, delta, deltaX, deltaY) => { - // hamsterjs claims to normalizizing the event object, across browsers, + // hamsterjs claims to normalizing the event object, across browsers, // but at least in firefox it is not because deltaY is not on the evt. evt.deltaY = deltaY; // workaround // add an additional property to make it similar enough to the pinch // gesture so event consumers can just implement one 'zoom', if they want. - evt.center = { x: evt.originalEvent.x, y: evt.originalEvent.y }; + evt.center = {x: evt.originalEvent.x, y: evt.originalEvent.y}; this._dispatchGestureEvt(evt); }; hamster.wheel(hamsterHandler); @@ -101,44 +121,52 @@ export class UI extends mix().with(RegisterComponentMixin) { /** * setup gesture (hammerjs) handlers. + * @private */ + _setupGestures() { const hammer = Hammer(this.el); const hammerHandler = (evt) => this._dispatchGestureEvt(evt); const hammerEvents = 'panmove panend panstart pinchmove pinchend tap'; - hammer.get('pan').set({ direction: Hammer.DIRECTION_ALL }); - hammer.get('pinch').set({ enable: true }); + hammer.get('pan').set({direction: Hammer.DIRECTION_ALL}); + hammer.get('pinch').set({enable: true}); hammer.on(hammerEvents, hammerHandler); } /** - * Custom dispatch of ui events. Layout elements like BioMap and - * CorrespondenceMap are visually overlapping, and so do not fit cleanly into - * the js event capture or bubbling phases. Query the dom at the events - * coordinates, and dispatch the event to child who - * a) intersects with this point - * b) wants to handle this event (it can decide whether to based on it's - * canvas own scenegraph contents, etc.) - */ + * Custom dispatch of ui events. Layout elements like BioMap and + * CorrespondenceMap are visually overlapping, and so do not fit cleanly into + * the js event capture or bubbling phases. Query the dom at the events + * coordinates, and dispatch the event to child who + * a) intersects with this point + * b) wants to handle this event (it can decide whether to based on it's + * canvas own scenegraph contents, etc.) + * + * @param evt + * @private + */ + _dispatchGestureEvt(evt) { let hitElements = document.elementsFromPoint(evt.center.x, evt.center.y); - let filtered = hitElements.filter( el => { + let filtered = hitElements.filter(el => { return (el.mithrilComponent && el.mithrilComponent.handleGesture); }); // dispatch event to all the mithril components, until one returns true; // effectively the same as 'stopPropagation' on a normal event bubbling. - filtered.some( el =>{ - var state = el.mithrilComponent.handleGesture(evt); - return state; + filtered.some(el => { + return el.mithrilComponent.handleGesture(evt); }); } + /** * Gesture event recapture and force upon the LayoutContainer. This is to * prevent the the layout container from missing events after it has partially * moved out of the viewport. + * + * @param evt */ + handleGesture(evt) { this._layoutContainer.handleGesture(evt); } - } diff --git a/src/ui/css/cmap.css b/src/ui/css/cmap.css index b77fe6c6..32e1a684 100644 --- a/src/ui/css/cmap.css +++ b/src/ui/css/cmap.css @@ -1,89 +1,90 @@ html, body { - width: 100%; - height: 100%; - margin: 0; - overflow: hidden; - font-family: Nunito,HelveticaNeue,Helvetica Neue,Helvetica,Arial,sans-serif; + width: 100%; + height: 100%; + margin: 0; + overflow: hidden; + font-family: Nunito, HelveticaNeue, Helvetica Neue, Helvetica, Arial, sans-serif; } #cmap-ui { - width: calc(100% - 2rem); - height: calc(100% - 2rem); - margin: 2rem; - margin-top: 0rem; + width: calc(100% - 2rem); + height: calc(100% - 2rem); + margin: 0 2rem 2rem; } + #cmap-disclaimer { - font-size: 20px; - color: red; - border: solid 2px black; - border-radius: 8px; - padding: 10px; + font-size: 20px; + color: red; + border: solid 2px black; + border-radius: 8px; + padding: 10px; } .cmap-vbox { - display: flex; - flex-direction: column; - align-content: stretch; + display: flex; + flex-direction: column; + align-content: stretch; } .cmap-hbox { - display: flex; - flex-direction: row; - align-content: stretch; - justify-content: start; + display: flex; + flex-direction: row; + align-content: stretch; + justify-content: start; } .cmap-layout { - width: calc(100% - 2rem); - height: calc(100% - 2rem); + width: calc(100% - 2rem); + height: calc(100% - 2rem); } .cmap-layout-viewport { - overflow: auto; /* overflow the content using scrollbars */ - flex: auto; /* expand to fill the vertical space allowed by flexbox */ - position: relative; + overflow-y: hidden; /* overflow the content using scrollbars */ + overflow-x: hidden; + flex: auto; /* expand to fill the vertical space allowed by flexbox */ + position: relative; } .cmap-layout-container { - position: absolute;; - width: calc(100% - 2rem); - height: calc(100% - 2.5rem); + position: absolute;; + width: calc(100% - 2rem); + height: calc(100% - 2.5rem); } .cmap-canvas { - position: absolute; + position: absolute; } .cmap-layout-horizontal { - width: calc(100% - 2rem); - height: calc(100% - 2rem); - position: relative; + width: calc(100% - 2rem); + height: calc(100% - 2rem); + position: relative; } .cmap-layout-circos { - width: calc(100% - 2rem); - height: calc(100% - 2rem); - position: relative; + width: calc(100% - 2rem); + height: calc(100% - 2rem); + position: relative; } .cmap-correspondence-map { - /* border: dashed 2px cyan; */ + /* border: dashed 2px cyan; */ } .cmap-biomap { - /* border: dashed 2px grey; */ + border: dashed 2px grey; } .cmap-biomap.selected { - border: dashed 2px magenta; + border: dashed 2px magenta; } h4.cmap-header { - margin-bottom: 0; + margin-bottom: 0; } .cmap-tools h5 { - margin-bottom: 0; + margin-bottom: 0; } span.cmap-header { @@ -96,124 +97,130 @@ span.cmap-header { } .cmap-map-removal-dialog, .cmap-map-addition-dialog { - padding-left: 2rem; - border-left: solid 4px grey; + padding-left: 2rem; + border-left: solid 4px grey; } .cmap-map-addition-dialog label { - display: inline-block; - margin: 0 0.5rem 0 0.5rem; + display: inline-block; + margin: 0 0.5rem 0 0.5rem; } -button > i{ - vertical-align: -30%; +button > i { + vertical-align: -30%; } div.cmap-tools button { - margin-right: 0.5rem; - padding-left: 1rem; - padding-right: 1rem; + margin-right: 0.5rem; + padding-left: 1rem; + padding-right: 1rem; } button[disabled] { - background:#ccc; - border-color: #ccc; - text-shadow:none; + background: #ccc; + border-color: #ccc; + text-shadow: none; } span.cmap-map-name-chip { - background: #eee; - border-radius: 4px; - padding: 0.2rem 1rem 0.2rem 1rem; - margin-right: 0.5rem; + background: #eee; + border-radius: 4px; + padding: 0.2rem 1rem 0.2rem 1rem; + margin-right: 0.5rem; } .biomap-info { - width: 15em; - height: 10em; - border: 1px solid #bbb; - border-radius: 4px; - background: white; - position: absolute; - display: inline-block; - overflow-y: auto; - z-index: 10; + width: 15em; + height: 10em; + border: 1px solid #bbb; + border-radius: 4px; + background: white; + position: absolute; + display: inline-block; + overflow-y: auto; + z-index: 10; } .biomap-info-name { - margin: 5px; - border: 1px solid #bbb; - border-radius: 4px; - background:white; - text-align: center; + margin: 5px; + border: 1px solid #bbb; + border-radius: 4px; + background: white; + text-align: center; } .biomap-info-name:hover { - background:#ccc; + background: #ccc; } .biomap-info-data { - margin: 5px 20px 5px 20px; -} -.swap-div{ - opacity:50%; - z-index:100; - display:flex; -} -.swap-map-order{ - margin: 5px 10px 5px 10px; - padding: 5px 10px 5px 10px; - border: 1px solid #bbb; - border-radius: 4px; - background: white; - text-align:center; - opacity:0.5; + margin: 5px 20px 5px 20px; +} + +.swap-div { + opacity: 0.6; + z-index: 10000; + border: 1px solid #bbb; + border-radius: 4px; +} + +.swap-map-order { + margin: 5px 10px 5px 10px; + padding: 5px 10px 5px 10px; + border: 1px solid #bbb; + border-radius: 4px; + background: white; + text-align: center; + opacity: 0.5; } + .swap-map-order:hover { - background:#ccc; -} -.map-title{ - margin: 5px 10px 5px 10px; - padding: 5px 10px 5px 10px; - text-align: center; - background: white; -} - -.feature-title{ - z-index:1000; - background:white; - border: 1px solid black; - border-radius: 4px; - padding: 5px 0px 5px 0px; - margin: 5px 0px 5px 0px; - text-align: center; - white-space:nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.feature-menu{ - z-index: 10000; - background:white; - border: 1px solid black; - border-radius: 4px; - padding: 5px 0px 5px 0px; - margin: 5px 0px 5px 0px; - text-align: center; -} - -#cmap-menu-viewport{ - margin: 5px 5px 5px 5px; - padding: 5px 10px 5px 10px; - border: 1px solid #bbb; - border-radius: 4px; - background: white; -} - -.cmap-biomap{ - border: 1px dashed black; - border-radius: 4px; -} -.cmap-correspondence-map{ - /*border:1px dashed aqua;*/ + background: #ccc; +} + +.map-title { + margin: 5px 10px 5px 10px; + padding: 5px 10px 5px 10px; + text-align: center; + background: white; +} + +.feature-title { + z-index: 1000; + background: white; + border: 1px solid black; + border-radius: 4px; + padding: 5px 0 5px 0; + margin: 5px 0 5px 0; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.feature-menu { + z-index: 10000; + background: white; + border: 1px solid black; + border-radius: 4px; + padding: 5px 0 5px 0; + margin: 5px 0 5px 0; + text-align: center; +} + +#cmap-menu-viewport { + margin: 5px 5px 5px 5px; + padding: 5px 10px 5px 10px; + border: 1px solid #bbb; + border-radius: 4px; + background: white; +} + +.cmap-biomap { + /* border: 1px dashed black; + border-radius: 4px; */ +} + +.cmap-correspondence-map { + /*border:1px dashed aqua;*/ } diff --git a/src/ui/layout/CircosLayout.js b/src/ui/layout/CircosLayout.js index 90b88be8..c511e105 100644 --- a/src/ui/layout/CircosLayout.js +++ b/src/ui/layout/CircosLayout.js @@ -1,7 +1,7 @@ /** - * CircosLayout - * A mithril component for circos-style layout of BioMaps. - */ + * CircosLayout + * A mithril component for circos-style layout of BioMaps. + */ import m from 'mithril'; import {mix} from '../../../mixwith.js/src/mixwith'; @@ -10,28 +10,33 @@ import {Bounds} from '../../model/Bounds'; import {RegisterComponentMixin} from '../RegisterComponentMixin'; const radians = degrees => degrees * Math.PI / 180; + //const degrees = radians => radians * 180 / Math.PI; // TODO: remove if unused export class CircosLayout - extends mix(LayoutBase) - .with(RegisterComponentMixin) { + extends mix(LayoutBase) + .with(RegisterComponentMixin) { // constructor() - prefer do not use in mithril components + /** + * + * @private + */ _layout() { let domRect = this.el.getBoundingClientRect(); - if(! domRect.width || ! domRect.height) { + if (!domRect.width || !domRect.height) { // may occur when component is created but dom element has not yet filled // available space; expect onupdate() will occur. console.warn('deferring layout'); return; } let newBounds = new Bounds(domRect); - let dirty = ! Bounds.equals(this.domBounds, newBounds); + let dirty = !Bounds.equals(this.domBounds, newBounds); this.domBounds = newBounds; /* update child elements with their bounds */ let radius = this.domBounds.width > this.domBounds.height - ? this.domBounds.height * 0.4 : this.domBounds.width * 0.4; + ? this.domBounds.height * 0.4 : this.domBounds.width * 0.4; let n = this.bioMaps.length; let center = { x: Math.floor(this.domBounds.width * 0.5), @@ -42,24 +47,29 @@ export class CircosLayout let childHeight = Math.floor(childWidth * 0.6); let startDegrees = -180; let degrees = startDegrees; - this.bioMaps.forEach( child => { + this.bioMaps.forEach(child => { let rad = radians(degrees); let x = center.x - Math.floor(childWidth * 0.5) + Math.floor(radius * Math.cos(rad)); let y = center.y - Math.floor(childHeight * 0.5) + Math.floor(radius * Math.sin(rad)); - let bounds = new Bounds({ + // noinspection JSSuspiciousNameCombination + // noinspection JSSuspiciousNameCombination + child.domBounds = new Bounds({ left: x, top: y, width: childHeight, // swap the width and height height: childWidth }); - child.domBounds = bounds; child.rotation = Math.floor(degrees) + startDegrees; degrees += degreesPerChild; }); - if(dirty) m.redraw(); + if (dirty) m.redraw(); } /* mithril render callback */ + /** + * + * @returns {*} + */ view() { return m('div.cmap-layout-circos', this.children.map(m) diff --git a/src/ui/layout/HorizontalLayout.js b/src/ui/layout/HorizontalLayout.js index 7e0fa5c3..fc1f6eb3 100644 --- a/src/ui/layout/HorizontalLayout.js +++ b/src/ui/layout/HorizontalLayout.js @@ -1,38 +1,46 @@ /** - * HorizontalLayout (left to right) - * A mithril component for horizontal layout of BioMaps. - */ + * HorizontalLayout (left to right) + * A mithril component for horizontal layout of BioMaps. + */ import m from 'mithril'; import {mix} from '../../../mixwith.js/src/mixwith'; import PubSub from 'pubsub-js'; -import {dataLoaded, mapAdded, mapRemoved, reset, featureUpdate} from '../../topics'; +import {dataLoaded, mapAdded, mapRemoved, mapReorder, reset, featureUpdate} from '../../topics'; import {LayoutBase} from './LayoutBase'; import {Bounds} from '../../model/Bounds'; -import {BioMap as BioMapComponent} from '../../canvas/layout/BioMap'; -import {CorrespondenceMap as CorrMapComponent} from '../../canvas/layout/CorrespondenceMap'; -import {QtlTrack} from '../../canvas/layout/QtlTrack'; +import {BioMap as BioMapComponent} from '../../canvas/canvas/BioMap'; +import {CorrespondenceMap as CorrMapComponent} from '../../canvas/canvas/CorrespondenceMap'; +import {FeatureTrack} from '../../canvas/layout/FeatureTrack'; import {Popover} from '../menus/Popover'; import {FeatureMenu} from '../menus/FeatureMenu'; import {RegisterComponentMixin} from '../RegisterComponentMixin'; +import {TitleComponent} from './components/TitleComponent'; +import {BioMapComponent as BioMapVnode} from './components/BioMapComponent'; export class HorizontalLayout - extends mix(LayoutBase) - .with(RegisterComponentMixin) { + extends mix(LayoutBase) + .with(RegisterComponentMixin) { // constructor() - prefer do not use in mithril components /** * mithril lifecycle method + * @param vnode */ + oninit(vnode) { super.oninit(vnode); + this.contentBounds = vnode.attrs.contentBounds; + this.vnode = vnode; this.bioMapComponents = []; this.correspondenceMapComponents = []; - this.popoverComponents=[]; - this.swapComponents=[]; - this.featureControls=[]; - this.modal=[]; + this.popoverComponents = []; + this.swapComponents = []; + this.featureControls = []; + this.modal = []; + this.bioMapOrder = []; + this.test = 0; const handler = () => this._onDataLoaded(); this.subscriptions = [ // all of these topics have effectively the same event handler for @@ -40,140 +48,195 @@ export class HorizontalLayout PubSub.subscribe(dataLoaded, handler), PubSub.subscribe(mapRemoved, handler), PubSub.subscribe(mapAdded, handler), - PubSub.subscribe(reset,() => { this._onReset();}), - PubSub.subscribe(featureUpdate, ()=>{this._onFeatureUpdate();}) + PubSub.subscribe(reset, () => { + this._onReset(); + }), + PubSub.subscribe(featureUpdate, (msg, data) => { + this._onFeatureUpdate(data); + }), + PubSub.subscribe(mapReorder, () => { + this._onReorder(); + }) ]; } + /** + * + * @param vnode + */ + + onupdate(vnode) { + this.contentBounds = vnode.attrs.contentBounds; + } + /** * mithril lifecycle method */ + onremove() { - this.subscriptions.forEach( token => PubSub.unsubscribe(token) ); + this.subscriptions.forEach(token => PubSub.unsubscribe(token)); } /** * mithril component render method + * @returns {*} mithril vnode */ + view() { + //m.mount(document.getElementById('cmap-layout-titles'),null); + // let mo = this.bioMapOrder.map(i => { + // return this.bioMapComponents[i]; + // }); return m('div.cmap-layout-horizontal', - [this.swapComponents,this.bioMapComponents.map(m),this.featureControls, - //this.modal.map(modal =>{ return m(modal,{info:modal.info, bounds: modal.bounds, order:modal.order}); }), - this.correspondenceMapComponents.map(m), - this.popoverComponents.map(popover =>{ return m(popover,{info:popover.info, domBounds:popover.domBounds});})] + [//this.swapComponents, + this.correspondenceMapComponents.map(m), + this.bioMapOrder.map((i) => { + return m(BioMapVnode, {bioMap: this.bioMapComponents[i]}); + }), this.featureControls, + //this.modal.map(modal =>{ return m(modal,{info:modal.info, bounds: modal.bounds, order:modal.order}); }), + this.popoverComponents.map(popover => { + return m(popover, {info: popover.info, domBounds: popover.domBounds}); + })] ); } /** * pub/sub event handler + * + * @private */ + _onDataLoaded() { this._layoutBioMaps(); - this._layoutSwapComponents(); + this._layoutSwapComponents(); this._layoutFeatureControls(); this._layoutCorrespondenceMaps(); this._layoutPopovers(); m.redraw(); } - _layoutSwapComponents(){ - this.swapComponents = []; - let n = this.bioMapComponents.length; - let maps = this; - for (var i = 0; i < n; i++) { - let bMap = this.bioMapComponents[i]; - const b = i; - let left ='',right=''; - if(b>0){ - left = m('div', {class:'swap-map-order', onclick: function() { - if(b > 0){ - const tmp = maps.appState.bioMaps[b-1]; - maps.appState.bioMaps[b-1] = maps.appState.bioMaps[b]; - maps.appState.bioMaps[b] = tmp; - maps._onDataLoaded(); - } - } - },'<'); - } else { - left = m('div', {class:'swap-map-order',style:'background:#ccc;'},'<'); - } + /** + * + * @private + */ - if(b< n-1){ - right = m('div', {class:'swap-map-order', onclick: function() { - if(b < n-1){ - const tmp = maps.appState.bioMaps[b]; - maps.appState.bioMaps[b] = maps.appState.bioMaps[b+1]; - maps.appState.bioMaps[b+1] = tmp; - maps._onDataLoaded(); - } - }},'>'); - } else { - right = m('div', {class:'swap-map-order',style:'background:#ccc;'},'>'); + _onReorder() { + let left = 0; + let bmaps = this.bioMapComponents; + let sc = this.bioMapOrder; + bmaps.forEach(comp => { + comp.dirty = true; + }); + for (let i = 0; i < bmaps.length; i++) { + let map = bmaps[sc[i]]; + //const mapC = bmaps[sc[i]].domBounds; + const mw = map.domBounds.width; + map.domBounds.left = left; + map.domBounds.right = left + mw; + left = map.domBounds.right; + } + this._layoutCorrespondenceMaps(); + this._layoutFeatureControls(); + m.mount(document.getElementById('cmap-layout-titles'), null); + m.redraw(); + this._layoutSwapComponents(); + } + + /** + * + * @private + */ + + _layoutSwapComponents() { + this.swapComponents = []; + let sc = this.bioMapOrder; + let maps = this; + let cb = this.contentBounds; + //let bmaps = this.bioMapComponents; + let pan = []; + pan[0] = false; + m.mount(document.getElementById('cmap-layout-titles'), { + view: function () { + return sc.map((order) => { + return m(TitleComponent, { + bioMaps: maps.bioMapComponents, + order: order, + titleOrder: sc, + contentBounds: cb, + pan: pan + }); + }); } - - console.log('swap comp',bMap,bMap.p); - this.swapComponents.push( m('div', { - class: 'swap-div', id: `swap-${i}`, - style: `position:absolute; left: ${Math.floor(bMap.domBounds.left+bMap.ruler.globalBounds.left/2)}px; top: ${bMap.domBounds.top}px;`}, - [left,m('div',{class:'map-title',style:'display:inline-block;'}, [bMap.model.name,m('br'),bMap.model.source.id]), right])); - } - - } + }); - _layoutFeatureControls(){ - this.featureControls = []; - let n = this.bioMapComponents.length; - let maps = this; - this.bioMapComponents.forEach( component => { - component.children.forEach( child => { - if( child instanceof QtlTrack){ - for( let i = 0; i < child.children.length; i++){ - if(child.children[i].bounds.width > 0){ + } + + /** + * + * @private + */ + + _layoutFeatureControls() { + this.featureControls = []; + //let n = this.bioMapComponents.length; + //let maps = this; + this.bioMapComponents.forEach(component => { + component.children.forEach(child => { + if (child instanceof FeatureTrack) { + for (let i = 0; i < child.children.length; i++) { + if (child.children[i].bounds.width > 0) { let featureGroup = child.children[i]; - this.featureControls.push( + this.featureControls.push( m('div', { class: 'feature-title', id: `feature-${component.model.name}-${i}`, - style: `position:absolute; left: ${Math.floor(component.domBounds.left + featureGroup.globalBounds.left)}px; + style: `position:absolute; left: ${Math.floor(component.domBounds.left + featureGroup.globalBounds.left)}px; top: ${component.domBounds.top}px; width: ${featureGroup.globalBounds.width}px;`, - onclick: function(){ - let info = child.children[0]; - let order = i; - new FeatureMenu(info,order); - } - }, featureGroup.tags[0]) - ); + onclick: function () { + let info = child.children[i]; + info.position = child.trackPos; + new FeatureMenu(info, child.children[i].config.tracksIndex); + } + }, featureGroup.title) + ); } } // push controller to add new track - this.featureControls.push( - m('div', { - class: 'feature-title', - id: `feature-${component.model.name}-new`, - style: `position:absolute; left: ${Math.floor(component.domBounds.left + child.globalBounds.right + 20)}px; + this.featureControls.push( + m('div', { + class: 'feature-title', + id: `feature-${component.model.name}-new`, + style: `position:absolute; left: ${Math.floor(component.domBounds.left + child.globalBounds.right + 20)}px; top: ${component.domBounds.top}px; width: 20px;`, - onclick: function(){ - let info = child.children[0]; - let order = child.children.lenght; - new FeatureMenu(info,order); - } - },`+`) - ); - } + onclick: function () { + let info = component.model; + info.position = child.trackPos; + let order = child.model.tracks.length; + new FeatureMenu(info, order); + } + }, '+') + ); + } }); }); - } + } + /** - * Horizonal (left to right) layout of BioMaps + * Horizontal (left to right) layout of BioMaps + * + * @returns {Array} + * @private */ + _layoutBioMaps() { - if(! this.bounds) return []; // early out if the layout bounds is unknown + if (!this.bounds) return []; // early out if the layout bounds is unknown let n = this.appState.bioMaps.length; let padding = Math.floor(this.bounds.width * 0.1 / n); padding = 0; // TODO: decide whether to add padding between the biomaps let childHeight = Math.floor(this.bounds.height * 0.95); let cursor = Math.floor(padding * 0.5); - this.bioMapComponents = this.appState.bioMaps.map( (model,mapIndex) => { + this.bioMapComponents = this.appState.bioMaps.map((model, mapIndex) => { + this.bioMapOrder.push(mapIndex); let layoutBounds = new Bounds({ left: cursor, top: 10, @@ -184,7 +247,8 @@ export class HorizontalLayout bioMapModel: model, layoutBounds: layoutBounds, appState: this.appState, - bioMapIndex: mapIndex + bioMapIndex: mapIndex, + initialView : this.appState.initialView[mapIndex] }); model.component = component; // save a reference for mapping model -> component cursor += component.domBounds.width + padding; @@ -192,8 +256,14 @@ export class HorizontalLayout }); } - _layoutPopovers(){ - this.popoverComponents = this.bioMapComponents.map( model => { + + /** + * + * @private + */ + + _layoutPopovers() { + this.popoverComponents = this.bioMapComponents.map(model => { let component = new Popover(); component.info = model.info; component.domBounds = model.domBounds; @@ -201,63 +271,66 @@ export class HorizontalLayout }); } - /** + /** * Horizontal layout of Correspondence Maps. In this layout, for N maps there * are N -1 correspondence maps. + * @returns {Array} + * @private */ + _layoutCorrespondenceMaps() { - if(! this.bounds) return []; // early out if our canvas bounds is unknown + if (!this.bounds) return []; // early out if our canvas bounds is unknown let childHeight = Math.floor(this.bounds.height * 0.95); let n = this.bioMapComponents.length; this.correspondenceMapComponents = []; - for (var i = 0; i < n-1; i++) { + for (let i = 0; i < n - 1; i++) { let left = this.bioMapComponents[i]; - let right = this.bioMapComponents[i+1]; + let right = this.bioMapComponents[i + 1]; let layoutBounds = new Bounds({ - left: Math.floor(left.domBounds.left+left.backbone.globalBounds.right), - right: Math.floor(right.domBounds.left+right.backbone.globalBounds.left), + left: Math.floor(left.domBounds.left + left.backbone.globalBounds.right), + right: Math.floor(right.domBounds.left + right.backbone.globalBounds.left), top: 10, height: childHeight }); let component = new CorrMapComponent({ - bioMapComponents: [ left, right ], + bioMapComponents: [left, right], appState: this.appState, layoutBounds: layoutBounds }); this.correspondenceMapComponents.push(component); } } + /** * Reset local zoom here. Easier to iterate through base element * and redraw components once from the base layout than deal with - * it through the individual components. + * it through the individual components. * (Difficulty in reaching the mithril component to get canvas context) - * + * @private */ - _onReset(){ + + _onReset() { this.bioMapComponents.forEach(item => { item.model.view.visible = item.model.view.base; item.verticalScale = 1.0; item.info.visible = 'hidden'; }); - [].forEach.call(document.getElementsByClassName('cmap-canvas'), el =>{ - el.mithrilComponent.draw(); + [].forEach.call(document.getElementsByClassName('cmap-canvas'), el => { + el.mithrilComponent.draw(); }); m.redraw(); } - - _onFeatureUpdate(msg,data){ - this._layoutBioMaps(); - this._layoutSwapComponents(); - this._layoutFeatureControls(); - var rightShift = 0; - this.appState.bioMaps.map( bmap => { - bmap.component.lb.left = rightShift; - bmap.component.domBounds.left = rightShift; - rightShift += bmap.component.domBounds.width; - }) - this._layoutCorrespondenceMaps(); - this._layoutPopovers(); - } + /** + * + * @param data + * @private + */ + + _onFeatureUpdate(data) { + //this._onDataLoaded(); + this.bioMapComponents[data.mapIndex]._layout(); + m.redraw(); + this._onReorder(); + } } diff --git a/src/ui/layout/LayoutBase.js b/src/ui/layout/LayoutBase.js index a5bdc336..234ffa35 100644 --- a/src/ui/layout/LayoutBase.js +++ b/src/ui/layout/LayoutBase.js @@ -1,24 +1,28 @@ /** - * LayoutBase - * A Mithril component Base class for Layouts, e.g. HorizontalLayout and - * CircosLayout. - */ + * LayoutBase + * A Mithril component Base class for Layouts, e.g. HorizontalLayout and + * CircosLayout. + */ import {Bounds} from '../../model/Bounds'; -export class LayoutBase { +export class LayoutBase { // constructor() - prefer do not use in mithril components /** * mithril lifecycle callback + * @param vnode */ + oninit(vnode) { this.appState = vnode.attrs.appState; } /** * mithril lifecycle method + * @param vnode */ + oncreate(vnode) { // save a reference to this component's dom element this.el = vnode.dom; @@ -27,7 +31,9 @@ export class LayoutBase { /** * mithril lifecycle method + * @param vnode */ + onupdate(vnode) { this.bounds = new Bounds(vnode.dom.getBoundingClientRect()); } diff --git a/src/ui/layout/LayoutContainer.js b/src/ui/layout/LayoutContainer.js index 1f73a035..6e4a0568 100644 --- a/src/ui/layout/LayoutContainer.js +++ b/src/ui/layout/LayoutContainer.js @@ -1,8 +1,8 @@ /** - * LayoutContainer - * A mithril component to wrap the current layout component inside of a - * scrollable div. - */ + * LayoutContainer + * A mithril component to wrap the current layout component inside of a + * scrollable div. + */ import m from 'mithril'; import PubSub from 'pubsub-js'; import {mix} from '../../../mixwith.js/src/mixwith'; @@ -14,7 +14,7 @@ import {Bounds} from '../../model/Bounds'; import {RegisterComponentMixin} from '../RegisterComponentMixin'; // define allowed min/max range for scale (zoom operation) -const SCALE = Object.freeze({ min: 0.05, max: 2}); +const SCALE = Object.freeze({min: 0.05, max: 2}); export class LayoutContainer extends mix().with(RegisterComponentMixin) { @@ -22,31 +22,35 @@ export class LayoutContainer extends mix().with(RegisterComponentMixin) { /** * mithril lifecycle method + * @param vnode */ + oninit(vnode) { super.oninit(vnode); this.appState = vnode.attrs.appState; - + this.vnode = vnode; PubSub.subscribe(reset, () => this._onReset()); // create some regular expressions for faster dispatching of events this._gestureRegex = { - pan: new RegExp('^pan'), + pan: new RegExp('^pan'), pinch: new RegExp('^pinch'), - tap: new RegExp('^tap'), + tap: new RegExp('^tap'), wheel: new RegExp('^wheel') }; } /** * mithril lifecycle method + * @param vnode */ + oncreate(vnode) { super.oncreate(vnode); this.el = vnode.dom; // this is the outer m('div') from view() //this._setupEventHandlers(this.el); - this.bounds = new Bounds(this.el.getBoundingClientRect()); - this.contentBounds = new Bounds({ + vnode.state.bounds = this.bounds = new Bounds(this.el.getBoundingClientRect()); + vnode.state.contentBounds = this.contentBounds = new Bounds({ left: 0, top: 0, width: this.bounds.width, @@ -58,15 +62,20 @@ export class LayoutContainer extends mix().with(RegisterComponentMixin) { /** * mithril lifecycle method + * @param vnode */ + onupdate(vnode) { this.bounds = new Bounds(vnode.dom.getBoundingClientRect()); } /** * mithril component render method + * @param vnode + * @returns {*} */ - view() { + + view(vnode) { let b = this.contentBounds || {}; // relative bounds of the layout-container let scale = this.appState.tools.zoomFactor; return m('div.cmap-layout-container', { @@ -75,19 +84,26 @@ export class LayoutContainer extends mix().with(RegisterComponentMixin) { transform: scale(${scale})` }, [ this.appState.tools.layout === HorizontalLayout - ? - m(HorizontalLayout, {appState: this.appState, layoutBounds: this.bounds }) - : - m(CircosLayout, {appState: this.appState, layoutBounds: this.bounds}) + ? + m(HorizontalLayout, { + appState: this.appState, + layoutBounds: this.bounds, + contentBounds: vnode.state.contentBounds + }) + : + m(CircosLayout, {appState: this.appState, layoutBounds: this.bounds}) ]); } /** * handle the event from _dispatchGestureEvt. Returns true or false * to stop or continue event propagation. + * @param evt + * @returns {boolean} */ + handleGesture(evt) { - if(evt.type.match(this._gestureRegex.pan)) { + if (evt.type.match(this._gestureRegex.pan)) { return this._onPan(evt); } else if (evt.type.match(this._gestureRegex.pinch)) { @@ -99,25 +115,40 @@ export class LayoutContainer extends mix().with(RegisterComponentMixin) { return false; // do not stop event propagation } + /** + * + * @param evt + * @returns {boolean} + * @private + */ + _onZoom(evt) { // TODO: utilize the distance of touch event for better interaction const normalized = evt.deltaY / this.bounds.height; const z = this.appState.tools.zoomFactor + normalized; + // noinspection Annotator this.appState.tools.zoomFactor = Math.clamp(z, SCALE.min, SCALE.max); m.redraw(); return true; // stop evt propagation } + /** + * + * @param evt + * @returns {boolean} + * @private + */ + _onPan(evt) { console.log('LayoutContainer -> onPan', evt); // hammer provides the delta x,y in a distance since the start of the // gesture so need to convert it to delta x,y for this event. - if(evt.type === 'panend') { + if (evt.type === 'panend') { this.lastPanEvent = null; - return; + return true; } let delta = {}; - if(this.lastPanEvent) { + if (this.lastPanEvent) { delta.x = -1 * (this.lastPanEvent.deltaX - evt.deltaX); delta.y = -1 * (this.lastPanEvent.deltaY - evt.deltaY); } @@ -126,18 +157,19 @@ export class LayoutContainer extends mix().with(RegisterComponentMixin) { delta.y = evt.deltaY; } this.contentBounds.left += delta.x; - this.contentBounds.top += delta.y; + //this.contentBounds.top += delta.y; m.redraw(); this.lastPanEvent = evt; return true; // stop event propagation } /** - * PubSub event handler + * + * @private */ + _onReset() { this.contentBounds = new Bounds(this.originalContentBounds); m.redraw(); } - } diff --git a/src/ui/layout/components/BioMapComponent.js b/src/ui/layout/components/BioMapComponent.js new file mode 100644 index 00000000..f756e7ad --- /dev/null +++ b/src/ui/layout/components/BioMapComponent.js @@ -0,0 +1,67 @@ +/** + * + * Base Component, placeholder for other canvas components + * + */ + +import m from 'mithril'; +//import {Bounds} from '../../../model/Bounds'; + +export class BioMapComponent { + constructor(vnode) { + console.log(vnode); + } + + oncreate(vnode) { + //have state be tied to passed attributes + vnode.state = vnode.attrs; + + //dom components and state + vnode.state.canvas = vnode.state.bioMap.canvas = vnode.dom; + vnode.state.domBounds = vnode.state.bioMap.domBounds; + vnode.state.context2d = vnode.state.bioMap.context2d = vnode.state.canvas.getContext('2d'); + vnode.state.context2d.imageSmoothingEnabled = false; + + //setup vnode.dom for ui gesture handling + vnode.dom.mithrilComponent = this; + + //store vnode to be able to access state for non mithril lifecycle commands + this.vnode = vnode; + } + + onupdate(vnode) { + //redraw biomap if dirty (drawing has changed, instead of just changing position) + if (vnode.state.bioMap.dirty === true) { + vnode.state.context2d.clearRect(0, 0, vnode.state.canvas.width, vnode.state.canvas.height); + vnode.state.bioMap.draw(); + } + } + + view(vnode) { + // store these bounds, for checking in drawLazily() + let domBounds = vnode.state.domBounds || null; + if (domBounds && !domBounds.isEmptyArea) { + this.lastDrawnMithrilBounds = domBounds; + } + let b = domBounds || {}; + let selectedClass = vnode.state.selected ? 'selected' : ''; + return m('canvas', { + class: `cmap-canvas cmap-biomap ${selectedClass}`, + style: `left: ${b.left}px; top: ${b.top}px; + width: ${b.width}px; height: ${b.height}px; + transform: rotate(${vnode.state.rotation}deg);`, + width: b.width, + height: b.height + }); + } + + handleGesture(evt) { + let state = this.vnode.state; + if (state.bioMap.handleGesture(evt)) { + state.bioMap.dirty = true; + return true; + } + return false; + } +} + diff --git a/src/ui/layout/components/TitleComponent.js b/src/ui/layout/components/TitleComponent.js new file mode 100644 index 00000000..be55afc4 --- /dev/null +++ b/src/ui/layout/components/TitleComponent.js @@ -0,0 +1,153 @@ +/** + * + * Base Component, placeholder for other canvas components + * + */ + +import m from 'mithril'; +import PubSub from 'pubsub-js'; + +import {mapReorder} from '../../../topics'; + +export let TitleComponent = { + oninit: function (vnode) { + vnode.state = vnode.attrs; + vnode.state.left = 0; + vnode.state.domOrder = vnode.state.titleOrder.indexOf(vnode.state.order); + vnode.state.leftBound = vnode.state.bioMaps[vnode.state.order].domBounds.left; + vnode.state.rightBound = vnode.state.bioMaps[vnode.state.order].domBounds.right; + vnode.state.leftStart = vnode.state.bioMaps[vnode.state.order].domBounds.left; + vnode.state._gestureRegex = { + pan: new RegExp('^pan') + }; + }, + + oncreate: function (vnode) { + // register mithrilComponent for gesture handling + vnode.dom.mithrilComponent = this; + // register functions to state/dom for gesture handling + vnode.dom.mithrilComponent.handleGesture = vnode.tag.handleGesture; + vnode.state._onPan = vnode.tag._onPan; + vnode.state.zIndex = 0; + this.vnode = vnode; + }, + + onbeforeupdate: function (vnode) { + vnode.state.bioMaps = vnode.attrs.bioMaps; + if (this.titleOrder[this.domOrder] !== this.order) { + this.domOrder = this.titleOrder.indexOf(this.order); + } + }, + + onupdate: function (vnode) { + let dispOffset = vnode.state.bioMaps[vnode.state.order].domBounds.left - vnode.state.leftStart; + if (vnode.state.left !== dispOffset && !vnode.state.swap) { + this.left = dispOffset; + this.dirty = true; + } + if (vnode.state.swap) { + this.left = 0; + this.swap = false; + this.dirty = true; + this.left = 0; + } + if (this.dirty) { // trigger redraw on changed canvas that has possibly edited bounds in process of view layout + this.dirty = false; + m.redraw(); + } + }, + + view: function (vnode) { + if (!vnode.attrs || !vnode.state.contentBounds) return; + let bMap = vnode.state.bioMaps[vnode.state.order]; + vnode.state.contentBounds.left = vnode.state.contentBounds.right - vnode.state.contentBounds.width; + let left = vnode.state.left + vnode.state.contentBounds.left; + return m('div', { + class: 'swap-div', id: `swap-${vnode.state.domOrder}`, + style: `display:grid; position:relative; left:${left}px; min-width:${bMap.domBounds.width}px; z-index:${vnode.state.zIndex};` + }, + [m('div', {class: 'map-title', style: 'display:inline-block;'}, [bMap.model.name, m('br'), bMap.model.source.id]) + ] + ); + }, + + handleGesture: function (evt) { + if (evt.type.match(this._gestureRegex.pan)) { + return this._onPan(evt); + } + return true; + }, + + _onPan: function (evt) { + //Start pan move zIndex up to prevent interrupting pan early + if (evt.type === 'panstart') { + this.vnode.state.zIndex = 1000; + this.lastPanEvent = null; + this.left = 0; + } + //End pan to set rearrangement + if (evt.type === 'panend') { + this.vnode.state.zIndex = 0; + PubSub.publish(mapReorder, null); + return; + } + + //Pan the title + //Calculate map movement + let delta = {}; + if (this.lastPanEvent) { + delta.x = -1 * (this.lastPanEvent.deltaX - evt.deltaX); + } else { + delta.x = Math.round(evt.deltaX); + } + this.left += delta.x; + + //Setup maps and swap points + let selLeftEdge = this.left + this.leftStart; + //let selRightEdge = selLeftEdge + this.bioMaps[this.order].domBounds.width; + const leftMap = this.domOrder > 0 ? this.titleOrder[this.domOrder - 1] : null; + const rightMap = this.titleOrder[this.domOrder + 1] > -1 ? this.titleOrder[this.domOrder + 1] : null; + const leftSwapBound = leftMap ? this.leftBound - this.bioMaps[leftMap].domBounds.width : null; + const rightSwapBound = rightMap ? this.leftBound + this.bioMaps[rightMap].domBounds.width : null; + + if (leftMap && selLeftEdge < leftSwapBound) { // Swap Left + this.leftBound -= this.bioMaps[leftMap].domBounds.width; + this.rightBound -= this.bioMaps[leftMap].domBounds.width; + + this.titleOrder[this.domOrder] = this.titleOrder[this.domOrder - 1];//= this.titleOrder[rightMap]; + this.titleOrder[this.domOrder - 1] = this.order; + this.domOrder = this.titleOrder[this.domOrder]; + + } else if (rightMap && selLeftEdge > rightSwapBound) { // Swap Right + this.leftBound += this.bioMaps[rightMap].domBounds.width; + this.rightBound += this.bioMaps[rightMap].domBounds.width; + + this.titleOrder[this.domOrder] = this.titleOrder[this.domOrder + 1];//= this.titleOrder[rightMap]; + this.titleOrder[this.domOrder + 1] = this.order; + this.domOrder = this.titleOrder[this.domOrder]; + + } else if (!(!leftMap && selLeftEdge <= 0) && !(!rightMap && selLeftEdge > this.leftBound)) { //Move current map and its left/right partner + + let movedMap = rightMap; + + if (selLeftEdge < this.leftBound || (selLeftEdge === this.leftBound && delta.x < 0)) { + movedMap = leftMap; + } + + let shiftScale = this.bioMaps[this.order].domBounds.width / this.bioMaps[movedMap].domBounds.width; + this.bioMaps[this.order].domBounds.left += delta.x; + this.bioMaps[this.order].domBounds.right += delta.x; + const mw = this.bioMaps[movedMap].domBounds.width; + this.bioMaps[movedMap].domBounds.left -= delta.x * shiftScale; + this.bioMaps[movedMap].domBounds.right = this.bioMaps[movedMap].domBounds.left + mw; + + } else { // edge case don't move map + this.left -= delta.x; + } + + this.lastPanEvent = evt; + m.redraw(); + return true; + } +}; + diff --git a/src/ui/menus/ColorPicker.js b/src/ui/menus/ColorPicker.js index 73e39ca8..9d39789a 100644 --- a/src/ui/menus/ColorPicker.js +++ b/src/ui/menus/ColorPicker.js @@ -8,47 +8,78 @@ import PubSub from 'pubsub-js'; import {pageToCanvas} from '../../util/CanvasUtil'; +/** + * + * @type {{oninit: ColorPicker.oninit, onupdate: ColorPicker.onupdate, view: ColorPicker.view}} + */ export let ColorPicker = { - oninit: function(vnode){ + /** + * + * @param vnode + */ + + oninit: function (vnode) { vnode.state = vnode.attrs; vnode.state.colors = { - baseColor : vnode.attrs.settings.trackColor[vnode.attrs.order] || 'red', - currentColor : null, - hueValueColor : null + baseColor: vnode.attrs.settings.fillColor[vnode.attrs.order] || 'red', + currentColor: null, + hueValueColor: null }; }, - onupdate: function(vnode){ - vnode.attrs.settings.trackColor[vnode.attrs.order] = vnode.state.colors.baseColor; + + /** + * + * @param vnode + */ + + onupdate: function (vnode) { + vnode.attrs.settings.fillColor[vnode.attrs.order] = vnode.state.colors.baseColor; }, - view: function(vnode) { + /** + * + * @param vnode + * @returns {*[]} + */ + + view: function (vnode) { // store these bounds, for checking in drawLazily() - return [ m('div.color-picker',{style:`display:${vnode.state.hidden[vnode.state.order]}`}, [ - m(BaseSelector,{info:vnode.state}), - m(SaturationSelector,{info:vnode.state}), - m(ColorPreview,{info:vnode.state}), - m('div#color-apply-controls',{style:'text-align:center; margin-left:10px; display:inline-block; padding:auto'}, - [m(ColorBox,{info:vnode.state}),//,settings:vnode.attrs.settings}), - m(ColorApplyButton ,{info:vnode.state,settings:vnode.state.settings}), - m(ColorResetButton,{info:vnode.state}) - ] - ) - ]) + return [m('div.color-picker', {style: `display:${vnode.state.hidden[vnode.state.order]}`}, [ + m(BaseSelector, {info: vnode.state}), + m(SaturationSelector, {info: vnode.state}), + m(ColorPreview, {info: vnode.state}), + m('div#color-apply-controls', {style: 'text-align:center; margin-left:10px; display:inline-block; padding:auto'}, + [m(ColorBox, {info: vnode.state}),//,settings:vnode.attrs.settings}), + m(ColorApplyButton, {info: vnode.state, settings: vnode.state.settings}), + m(ColorResetButton, {info: vnode.state}) + ] + ) + ]) ]; } }; +/** + * + * @type {{oncreate: BaseSelector.oncreate, onupdate: BaseSelector.onupdate, view: BaseSelector.view, draw: BaseSelector.draw, handleGesture: BaseSelector.handleGesture, _locationChange: BaseSelector._locationChange, _changeColor: BaseSelector._changeColor, _posFromHsv: BaseSelector._posFromHsv, _hsvFromPos: BaseSelector._hsvFromPos}} + */ export let BaseSelector = { - oncreate:function(vnode) { + + /** + * + * @param vnode + */ + + oncreate: function (vnode) { vnode.dom.mithrilComponent = this; this.vnode = vnode; vnode.state = vnode.attrs; vnode.state.canvas = this.el = vnode.dom; vnode.state.context2d = vnode.dom.getContext('2d'); - if(!vnode.state.info.currentColor || !vnode.state.info.hueValueColor){ + if (!vnode.state.info.currentColor || !vnode.state.info.hueValueColor) { vnode.state.context2d.fillStyle = vnode.state.info.colors.baseColor; - //use the context to convert the original color into a hex string - //avoiding needing to parse html color words + //use the context to convert the original color into a hex string + //avoiding needing to parse html color words vnode.state.info.colors.baseColor = vnode.state.context2d.fillStyle; vnode.state.info.colors.currentColor = vnode.state.context2d.fillStyle; vnode.state.info.colors.hueValueColor = rgbToHsv(hexToRgb(vnode.state.context2d.fillStyle)); @@ -63,18 +94,22 @@ export let BaseSelector = { /** * mithril lifecycle method + * @param vnode */ - onupdate: function(vnode) { + + onupdate: function (vnode) { vnode.state.ptrPos = vnode.dom.mithrilComponent._posFromHsv(vnode.state.info.colors.hueValueColor); - vnode.dom.mithrilComponent.draw(); + vnode.dom.mithrilComponent.draw(); }, /** - * mithril component render method + * + * @returns {*} */ - view: function() { + + view: function () { // store these bounds, for checking in drawLazily() - return m('canvas', { + return m('canvas', { class: 'color-canvas-main', style: 'width: 200; height: 100;', width: 200, @@ -82,7 +117,11 @@ export let BaseSelector = { }); }, - draw: function(){ + /** + * + */ + + draw: function () { let canvas = this.vnode.state.canvas; let ctx = this.vnode.state.context2d; let ptrPos = this.vnode.state.ptrPos; @@ -108,104 +147,144 @@ export let BaseSelector = { // Draw the selection pointer ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.strokeStyle='black'; - ctx.lineWidth=1; - ctx.strokeRect(0,0,canvas.width, canvas.height); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 1; + ctx.strokeRect(0, 0, canvas.width, canvas.height); ctx.lineWidth = 2; ctx.beginPath(); - ctx.moveTo(ptrPos.x-10,ptrPos.y); - ctx.lineTo(ptrPos.x-3,ptrPos.y); + ctx.moveTo(ptrPos.x - 10, ptrPos.y); + ctx.lineTo(ptrPos.x - 3, ptrPos.y); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = 'white'; - ctx.moveTo(ptrPos.x-3,ptrPos.y); - ctx.lineTo(ptrPos.x-1,ptrPos.y); - ctx.moveTo(ptrPos.x+1,ptrPos.y); - ctx.lineTo(ptrPos.x+3,ptrPos.y); + ctx.moveTo(ptrPos.x - 3, ptrPos.y); + ctx.lineTo(ptrPos.x - 1, ptrPos.y); + ctx.moveTo(ptrPos.x + 1, ptrPos.y); + ctx.lineTo(ptrPos.x + 3, ptrPos.y); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = 'black'; - ctx.moveTo(ptrPos.x+3,ptrPos.y); - ctx.lineTo(ptrPos.x+10,ptrPos.y); + ctx.moveTo(ptrPos.x + 3, ptrPos.y); + ctx.lineTo(ptrPos.x + 10, ptrPos.y); ctx.stroke(); ctx.beginPath(); - ctx.moveTo(ptrPos.x,ptrPos.y-10); - ctx.lineTo(ptrPos.x,ptrPos.y-3); + ctx.moveTo(ptrPos.x, ptrPos.y - 10); + ctx.lineTo(ptrPos.x, ptrPos.y - 3); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = 'white'; - ctx.moveTo(ptrPos.x,ptrPos.y-3); - ctx.lineTo(ptrPos.x,ptrPos.y-1); - ctx.moveTo(ptrPos.x,ptrPos.y+1); - ctx.lineTo(ptrPos.x,ptrPos.y+3); + ctx.moveTo(ptrPos.x, ptrPos.y - 3); + ctx.lineTo(ptrPos.x, ptrPos.y - 1); + ctx.moveTo(ptrPos.x, ptrPos.y + 1); + ctx.lineTo(ptrPos.x, ptrPos.y + 3); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = 'black'; - ctx.moveTo(ptrPos.x,ptrPos.y+3); - ctx.lineTo(ptrPos.x,ptrPos.y+10); + ctx.moveTo(ptrPos.x, ptrPos.y + 3); + ctx.lineTo(ptrPos.x, ptrPos.y + 10); ctx.stroke(); }, - handleGesture: function(evt){ - if(evt.type.match(this._gestureRegex.tap) || - evt.type.match(this._gestureRegex.pan)){ + /** + * + * @param evt + * @returns {boolean} + */ + + handleGesture: function (evt) { + if (evt.type.match(this._gestureRegex.tap) || + evt.type.match(this._gestureRegex.pan)) { let point = pageToCanvas(evt, this.vnode.state.canvas); this._locationChange(point); } return true; }, - _locationChange: function(evt){ + /** + * + * @param evt + * @private + */ + + _locationChange: function (evt) { let hueValue = this.vnode.state.info.colors.hueValueColor; this.vnode.state.ptrPos = { - x:evt.x, - y:evt.y + x: evt.x, + y: evt.y }; let hsv = this._hsvFromPos(this.vnode.state.ptrPos); hueValue[0] = hsv[0]; hueValue[2] = hsv[2]; - if(!hueValue[1]){ + if (!hueValue[1]) { hueValue[1] = 100; } this._changeColor(); }, - _changeColor: function(){ + /** + * + * @private + */ + + _changeColor: function () { //PubSub to alert the Saturation slider that the position has changed //order is passed to not update *every* color selector //Can be removed, but using PubSub means dynamic response from other forms - PubSub.publish('hueValue',{color:this.vnode.state.colors, order:this.vnode.state.info.order}); + PubSub.publish('hueValue', {color: this.vnode.state.colors, order: this.vnode.state.info.order}); this.draw(); - }, + }, - _posFromHsv: function(hsv){ + /** + * + * @param hsv + * @returns {{x: number, y: number}} + * @private + */ + + _posFromHsv: function (hsv) { // Math.round to avoid annoying sub-pixel rendering - hsv[0] = Math.max(0,Math.min(360,hsv[0])); - hsv[2] = Math.max(0,Math.min(100,hsv[2])); + hsv[0] = Math.max(0, Math.min(360, hsv[0])); + hsv[2] = Math.max(0, Math.min(100, hsv[2])); return { - x: parseFloat(hsv[0]/360)*(this.vnode.state.canvas.width), - y: (1-(hsv[2]/100))*(this.vnode.state.canvas.height) + x: parseFloat(hsv[0] / 360) * (this.vnode.state.canvas.width), + y: (1 - (hsv[2] / 100)) * (this.vnode.state.canvas.height) }; }, - _hsvFromPos: function(pos){ - let h = Math.max(0,(pos.x*360)/this.vnode.state.canvas.width); + /** + * + * @param pos + * @returns {*[]} + * @private + */ + + _hsvFromPos: function (pos) { + let h = Math.max(0, (pos.x * 360) / this.vnode.state.canvas.width); let s = 100; - let l = 100*(1-(pos.y/this.vnode.state.canvas.height)); - return [h,s,l]; + let l = 100 * (1 - (pos.y / this.vnode.state.canvas.height)); + return [h, s, l]; } }; +/** + * + * @type {{oncreate: SaturationSelector.oncreate, onupdate: SaturationSelector.onupdate, view: SaturationSelector.view, draw: SaturationSelector.draw, handleGesture: SaturationSelector.handleGesture, _changeColor: SaturationSelector._changeColor, _hueUpdated: SaturationSelector._hueUpdated, _posFromHsv: SaturationSelector._posFromHsv, _sFromPos: SaturationSelector._sFromPos}} + */ export let SaturationSelector = { - oncreate: function(vnode) { + /** + * + * @param vnode + */ + + oncreate: function (vnode) { vnode.dom.mithrilComponent = this; this.vnode = vnode; vnode.state = vnode.attrs; vnode.state.canvas = this.el = vnode.dom; vnode.state.context2d = vnode.dom.getContext('2d'); - if(!vnode.state.info.colors.hueValueColor[1]){ + if (!vnode.state.info.colors.hueValueColor[1]) { vnode.context2d.fillStyle = vnode.state.info.colors.baseColor; - //use the context to convert the original color into a hex string - //avoiding needing to parse html color words + //use the context to convert the original color into a hex string + //avoiding needing to parse html color words vnode.state.info.colors.hueValueColor[1] = rgbToHsv(hexToRgb(vnode.state.context2d.fillStyle))[1]; } vnode.state.ptrPos = this._posFromHsv(vnode.state.info.colors.hueValueColor); @@ -213,7 +292,9 @@ export let SaturationSelector = { pan: new RegExp('^pan'), tap: new RegExp('^tap') }; - PubSub.subscribe('hueValue', (msg,data)=>{if(data.order === vnode.state.info.order) this._hueUpdated(data.color);}); + PubSub.subscribe('hueValue', (msg, data) => { + if (data.order === vnode.state.info.order) this._hueUpdated(data.color); + }); this._gestureRegex = { pan: new RegExp('^pan'), tap: new RegExp('^tap') @@ -221,101 +302,153 @@ export let SaturationSelector = { this.draw(); }, - onupdate: function(vnode) { + /** + * + * @param vnode + */ + + onupdate: function (vnode) { vnode.state.ptrPos = vnode.dom.mithrilComponent._posFromHsv(vnode.state.info.colors.hueValueColor); - PubSub.publish('satUpdated',{order:vnode.state.info.order,currentColors:vnode.state.info.colors}); // keeps hex value in sync - vnode.dom.mithrilComponent.draw(); + PubSub.publish('satUpdated', {order: vnode.state.info.order, currentColors: vnode.state.info.colors}); // keeps hex value in sync + vnode.dom.mithrilComponent.draw(); }, /** * mithril component render method + * @returns {*} */ - view: function() { + + view: function () { // store these bounds, for checking in drawLazily() - return m('canvas', { - class: 'color-canvas-sat', + return m('canvas#color-canvas-sat', { style: 'margin-left:10px; width: 20; height: 100;', width: 20, height: 100 }); }, - draw: function(){ + /** + * + */ + + draw: function () { let canvas = this.vnode.state.canvas; let ctx = this.vnode.state.context2d; let ptrPos = this.vnode.state.ptrPos; // clear and redraw gradient slider for current picked HueValue color; ctx.clearRect(0, 0, canvas.width, canvas.height); - var grad = ctx.createLinearGradient(0, 0, 0,canvas.height); + let grad = ctx.createLinearGradient(0, 0, 0, canvas.height); let hueValueColor = this.vnode.state.info.colors.hueValueColor; - let rgbStart = hsvToRgb([hueValueColor[0],100,hueValueColor[2]]).map(color => { + let rgbStart = hsvToRgb([hueValueColor[0], 100, hueValueColor[2]]).map(color => { return Math.floor(color); }); - let rgbStop = hsvToRgb([hueValueColor[0],0,hueValueColor[2]]).map(color => { + let rgbStop = hsvToRgb([hueValueColor[0], 0, hueValueColor[2]]).map(color => { return Math.floor(color); }); - grad.addColorStop(0 , `rgba(${rgbStart[0]},${rgbStart[1]},${rgbStart[2]},1)`); - grad.addColorStop(1 , `rgba(${rgbStop[0]},${rgbStop[1]},${rgbStop[2]},1)`); + grad.addColorStop(0, `rgba(${rgbStart[0]},${rgbStart[1]},${rgbStart[2]},1)`); + grad.addColorStop(1, `rgba(${rgbStop[0]},${rgbStop[1]},${rgbStop[2]},1)`); ctx.fillStyle = grad; ctx.fillRect(0, 0, canvas.width, canvas.height); // draw slider pointer - ctx.strokeStyle='black'; - ctx.lineWidth=1; - ctx.strokeRect(0,0,canvas.width, canvas.height); - ctx.fillStyle = 'black'; + ctx.strokeStyle = 'black'; + ctx.lineWidth = 1; + ctx.strokeRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = 'black'; ctx.beginPath(); ctx.strokeStyle = 'white'; - ctx.moveTo(canvas.width,ptrPos+5); - ctx.lineTo(canvas.width/2, ptrPos); - ctx.lineTo(canvas.width,ptrPos-5); + ctx.moveTo(canvas.width, ptrPos + 5); + ctx.lineTo(canvas.width / 2, ptrPos); + ctx.lineTo(canvas.width, ptrPos - 5); ctx.closePath(); ctx.fill(); ctx.stroke(); }, - handleGesture: function(evt){ - if(evt.type.match(this._gestureRegex.tap) || - evt.type.match(this._gestureRegex.pan)){ - var point = pageToCanvas(evt, this.vnode.state.canvas); + /** + * + * @param evt + * @returns {boolean} + */ + + handleGesture: function (evt) { + if (evt.type.match(this._gestureRegex.tap) || + evt.type.match(this._gestureRegex.pan)) { + let point = pageToCanvas(evt, this.vnode.state.canvas); this._changeColor(point); } return true; }, - _changeColor: function(evt){ + /** + * + * @param evt + * @private + */ + + _changeColor: function (evt) { this.vnode.state.ptrPos = evt.y; this.vnode.state.info.colors.hueValueColor[1] = this._sFromPos(this.vnode.state.ptrPos); this._hueUpdated(this.vnode.state.info.colors); - }, + }, + + /** + * + * @private + */ - _hueUpdated: function(){ - PubSub.publish('satUpdated',{order:this.vnode.state.info.order,currentColors:this.vnode.state.info.colors}); + _hueUpdated: function () { + PubSub.publish('satUpdated', {order: this.vnode.state.info.order, currentColors: this.vnode.state.info.colors}); this.draw(); }, - _posFromHsv: function(hsv){ - return Math.round((1-(hsv[1]/100))*this.vnode.state.canvas.height); + /** + * + * @param hsv + * @returns {number} + * @private + */ + + _posFromHsv: function (hsv) { + return Math.round((1 - (hsv[1] / 100)) * this.vnode.state.canvas.height); }, - _sFromPos: function(pos){ - return 100*(1-(pos/this.vnode.state.canvas.height)); + /** + * + * @param pos + * @returns {number} + * @private + */ + + _sFromPos: function (pos) { + return 100 * (1 - (pos / this.vnode.state.canvas.height)); } }; -export let ColorPreview = { - oncreate: function(vnode) { +/** + * + * @type {{oncreate: ColorPreview.oncreate, onupdate: ColorPreview.onupdate, view: ColorPreview.view, draw: ColorPreview.draw}} + */ + +export let ColorPreview = { + + /** + * + * @param vnode + */ + + oncreate: function (vnode) { this.order = vnode.attrs.info.order; this.colors = vnode.attrs.info.colors; this.canvas = this.el = vnode.dom; this.context2d = this.canvas.getContext('2d'); - PubSub.subscribe('satUpdated',(msg,data) =>{ - if(this.order === data.order){ + PubSub.subscribe('satUpdated', (msg, data) => { + if (this.order === data.order) { let fillColor = hsvToRgb(data.currentColors.hueValueColor).map(color => { return Math.floor(color); }); - this.context2d.fillStyle = `rgba(${fillColor[0]},${fillColor[1]},${fillColor[2]},1)`; - this.colors.currentColor = this.context2d.fillStyle; + this.context2d.fillStyle = `rgba(${fillColor[0]},${fillColor[1]},${fillColor[2]},1)`; + this.colors.currentColor = this.context2d.fillStyle; this.draw(); } }); @@ -324,174 +457,247 @@ export let ColorPreview = { /** * mithril lifecycle method + * */ - onupdate: function() { - this.draw(); + + onupdate: function () { + this.draw(); }, /** * mithril component render method + * @returns {*} */ - view: function() { - return m('canvas#color-canvas-preview', { + + view: function () { + return m('canvas#color-canvas-preview', { style: 'margin-left:10px; width: 20; height: 100;', width: 20, height: 100 - }); + }); }, - draw: function(){ + /** + * + */ + + draw: function () { let ctx = this.context2d; ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ctx.fillStyle = this.colors.currentColor; ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); - ctx.strokeStyle='black'; - ctx.lineWidth=1; - ctx.strokeRect(0,0,this.canvas.width, this.canvas.height); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 1; + ctx.strokeRect(0, 0, this.canvas.width, this.canvas.height); } }; -// Use currently selected color +/** + * Use currently selected color + * @type {{view: ColorApplyButton.view}} + */ + export let ColorApplyButton = { + /** * mithril component render method + * @param vnode + * @returns {*} */ - view: function(vnode) { + + view: function (vnode) { // store these bounds, for checking in drawLazily() - return m('button.approve-button', { + return m('button.approve-button', { style: 'display:block; width:100%;', - onclick:()=>{ + onclick: () => { vnode.attrs.info.colors.baseColor = vnode.attrs.info.colors.currentColor; vnode.attrs.info.colors.hueValueColor = rgbToHsv(hexToRgb(vnode.attrs.info.colors.baseColor)); vnode.attrs.settings.nodeColor[vnode.attrs.info.order] = vnode.attrs.info.colors.baseColor; - PubSub.publish('satUpdated',{order:vnode.attrs.info.order,currentColors:vnode.attrs.info.colors}); + PubSub.publish('satUpdated', {order: vnode.attrs.info.order, currentColors: vnode.attrs.info.colors}); } - },'Apply'); + }, 'Apply'); } }; -// Reset color to prior +/** + * Reset color to prior + * @type {{view: ColorResetButton.view}} + */ + export let ColorResetButton = { + /** * mithril component render method + * @param vnode + * @returns {*} */ - view: function(vnode) { + + view: function (vnode) { // store these bounds, for checking in drawLazily() - return m('button.reset-button', { + return m('button.reset-button', { style: 'display:block; width:100%', - onclick:()=>{ + onclick: () => { vnode.attrs.info.colors.currentColor = vnode.attrs.info.colors.baseColor; vnode.attrs.info.colors.hueValueColor = rgbToHsv(hexToRgb(vnode.attrs.info.colors.baseColor)); - PubSub.publish('satUpdated',{order:vnode.attrs.info.order,currentColors:vnode.attrs.info.colors}); - } - },'Reset'); + PubSub.publish('satUpdated', {order: vnode.attrs.info.order, currentColors: vnode.attrs.info.colors}); + } + }, 'Reset'); } }; -// Text Box to find color +/** + * Text Box to find color + * @type {{oninit: ColorBox.oninit, view: ColorBox.view, handleGesture: ColorBox.handleGesture}} + */ + export let ColorBox = { - oninit: function(vnode) { + + /** + * + * @param vnode + */ + + oninit: function (vnode) { this.canvas = this.el = vnode.dom; this.order = vnode.attrs.info.order; vnode.state.value = vnode.attrs.info.colors.currentColor; - PubSub.subscribe('satUpdated',(msg,data) =>{ - if(this.order === data.order){ + PubSub.subscribe('satUpdated', (msg, data) => { + if (this.order === data.order) { vnode.dom.value = vnode.attrs.info.colors.currentColor; } }); }, + /** * mithril component render method + * @param vnode + * @returns {*} */ - view: function(vnode) { + + view: function (vnode) { // store these bounds, for checking in drawLazily() - return m('input[type=text].color-input', { - style: 'display:block; width:100%;', - oninput: m.withAttr('value', function(value) { - try { - let code = value.match(/^#?([a-f\d]*)$/i); - let str = code[1]; - if(code[1].length === 3){ - str = `#${str[0]}${str[0]}${str[1]}${str[1]}${str[2]}${str[2]}`; - } - vnode.attrs.info.colors.currentColor = value; - vnode.attrs.info.colors.hueValueColor = rgbToHsv(hexToRgb(str)); - } catch(e) { - // expect this to fail silently, as most typing will not actually give - // a proper hex triplet/sextet + return m('input[type=text].color-input', { + style: 'display:block; width:100%;', + oninput: m.withAttr('value', function (value) { + try { + let code = value.match(/^#?([a-f\d]*)$/i); + let str = code[1]; + if (code[1].length === 3) { + str = `#${str[0]}${str[0]}${str[1]}${str[1]}${str[2]}${str[2]}`; } - }) - }); + vnode.attrs.info.colors.currentColor = value; + vnode.attrs.info.colors.hueValueColor = rgbToHsv(hexToRgb(str)); + } catch (e) { + // expect this to fail silently, as most typing will not actually give + // a proper hex triplet/sextet + } + }) + }); }, - handleGesture: function(){ + /** + * + * @returns {boolean} + */ + + handleGesture: function () { return true; } }; -// #FFFFFF ->[0-255,0-255,0-255] -export function hexToRgb(hex){ - var result = hex.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i); - return [parseInt(result[1], 16),parseInt(result[2], 16),parseInt(result[3], 16)]; +/** + * convert hex triplet to RGB + * #FFFFFF ->[0-255,0-255,0-255] + * @param hex + * @returns {*[]} + */ + +export function hexToRgb(hex) { + let result = hex.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i); + return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)]; } -// [0-255,0-255,0-255] -> #FFFFFF -export function rgbToHex(rgb){ - return ( +/** + * convert RGB triplet to hex + * [0-255,0-255,0-255] -> #FFFFFF + * @param rgb + * @returns {string} + */ + +export function rgbToHex(rgb) { + return ( (0x100 | Math.round(rgb[0])).toString(16).substr(1) + (0x100 | Math.round(rgb[1])).toString(16).substr(1) + (0x100 | Math.round(rgb[2])).toString(16).substr(1) - ); + ); } -// [0-255,0-255,0-255] -> [0-360,0-100,0-100] -export function rgbToHsv(rgb){ - //make sure RGB values are within 0-255 range - //and convert to decimal - rgb = rgb.map(component =>{ - return Math.max(0,Math.min(255,component))/255; - }); +/** + * Convert RGB triplet ot HSV + * [0-255,0-255,0-255] -> [0-360,0-100,0-100] + * @param rgb + * @returns {*[]} + */ + +export function rgbToHsv(rgb) { + //make sure RGB values are within 0-255 range + //and convert to decimal + rgb = rgb.map(component => { + return Math.max(0, Math.min(255, component)) / 255; + }); // Conversion from RGB -> HSV colorspace - let cmin = Math.min(Math.min(rgb[0],rgb[1]),rgb[2]); - let cmax = Math.max(Math.max(rgb[0],rgb[1]),rgb[2]); - let delta = parseFloat(cmax - cmin); - let hue = 0; - if(delta === 0){ + + let cmin = Math.min(Math.min(rgb[0], rgb[1]), rgb[2]); + let cmax = Math.max(Math.max(rgb[0], rgb[1]), rgb[2]); + let delta = parseFloat(cmax - cmin); + let hue = 0; + if (delta === 0) { hue = 0; - } else if( cmax === rgb[0]){ - hue = 60*(((rgb[1]-rgb[2])/delta)); - } else if( cmax === rgb[1]){ - hue = 60*(((rgb[2]-rgb[0])/delta)+2); - } else if( cmax === rgb[2]){ - hue = 60*(((rgb[0]-rgb[1])/delta)+4); - } - if (hue < 0) hue +=360; - let sat = cmax === 0 ? 0 : (delta/cmax)*100; - let value = cmax*100; - - return [hue,sat,value]; -} - - -// [0-360,0-100,0-100] -> [0-255,0-255,0-255] -export function hsvToRgb(hsv){ - let u = 255 * (hsv[2] / 100); - let h = hsv[0]/60; - let s = hsv[1]/100; + } else if (cmax === rgb[0]) { + hue = 60 * (((rgb[1] - rgb[2]) / delta)); + } else if (cmax === rgb[1]) { + hue = 60 * (((rgb[2] - rgb[0]) / delta) + 2); + } else if (cmax === rgb[2]) { + hue = 60 * (((rgb[0] - rgb[1]) / delta) + 4); + } + if (hue < 0) hue += 360; + let sat = cmax === 0 ? 0 : (delta / cmax) * 100; + let value = cmax * 100; + + return [hue, sat, value]; +} + +/** + * Convert from HSV to RGB + * [0-360,0-100,0-100] -> [0-255,0-255,0-255] + * @param hsv + * @returns {*[]} + */ + +export function hsvToRgb(hsv) { + let u = 255 * (hsv[2] / 100); + let h = hsv[0] / 60; + let s = hsv[1] / 100; let i = Math.floor(h); - if(i < 0) i = 0; - let f = i%2 ? h-i : 1-(h-i); + if (i < 0) i = 0; + let f = i % 2 ? h - i : 1 - (h - i); let m = u * (1 - s); let n = u * (1 - s * f); switch (i) { case 6: - case 0: return [u,n,m]; - case 1: return [n,u,m]; - case 2: return [m,u,n]; - case 3: return [m,n,u]; - case 4: return [n,m,u]; - case 5: return [u,m,n]; - } + case 0: + return [u, n, m]; + case 1: + return [n, u, m]; + case 2: + return [m, u, n]; + case 3: + return [m, n, u]; + case 4: + return [n, m, u]; + case 5: + return [u, m, n]; + } } diff --git a/src/ui/menus/Feature.js b/src/ui/menus/Feature.js index b3fd3605..5d33d3ab 100644 --- a/src/ui/menus/Feature.js +++ b/src/ui/menus/Feature.js @@ -1,79 +1,116 @@ /** - * Feature - * A mithril component for displaying feature information. - */ + * Feature + * A mithril component for displaying feature information. + */ import m from 'mithril'; import PubSub from 'pubsub-js'; -import {featureUpdate, reset} from '../../topics'; +import {featureUpdate} from '../../topics'; import {mix} from '../../../mixwith.js/src/mixwith'; -import {Menu} from './Menus'; +import {Menu} from './Menu'; import {RegisterComponentMixin} from '../RegisterComponentMixin'; -export class FeatureMenu extends mix(Menu).with(RegisterComponentMixin){ +export class FeatureMenu extends mix(Menu).with(RegisterComponentMixin) { - oninit(vnode){ + /** + * + * @param vnode + */ + + oninit(vnode) { super.oninit(vnode); this.tagList = vnode.attrs.info.parent.parent.model.tags.sort(); this.settings = vnode.attrs.info.parent.parent.model.qtlGroups[vnode.attrs.order]; this.groupTags = vnode.attrs.info.tags; - this.selected = { name: this.settings.filter, index: this.tagList.indexOf(this.settings.filter)}; + this.selected = {name: this.settings.filter, index: this.tagList.indexOf(this.settings.filter)}; } - + /** * mithril component render method + * @param vnode + * @returns {*} */ + view(vnode) { - let info = vnode.attrs.info || {}; + //let info = vnode.attrs.info || {}; + //let order = vnode.attrs.order || 0; let bounds = vnode.attrs.bounds || {}; - let order = vnode.attrs.order || 0; let modal = this; modal.rootNode = vnode; - + return m('div', { - class: 'feature-menu', - style: `position:absolute; left: 0px; top: 0px; width:${bounds.width}px;height:${bounds.height}px`, - onclick: function(){console.log('what',this);} - },[this._dropDown(modal), this._applyButton(modal), this._closeButton(modal)]); + class: 'feature-menu', + style: `position:absolute; left: 0px; top: 0px; width:${bounds.width}px;height:${bounds.height}px`, + onclick: function () { + console.log('what', this); + } + }, [this._dropDown(modal), this._applyButton(modal), this._closeButton(modal)]); } - - _applyButton(modal){ - return m('button',{ - onclick: function(){ - console.log('what what', modal.settings); - modal.settings.filter = modal.selected.name; - modal.groupTags[0] = modal.selected.name; - PubSub.publish(featureUpdate, null); - modal.rootNode.dom.remove(modal.rootNode); - } - },'Apply Selection'); + + /** + * + * @param modal + * @returns {*} + * @private + */ + + _applyButton(modal) { + return m('button', { + onclick: function () { + console.log('what what', modal.settings); + modal.settings.filter = modal.selected.name; + modal.groupTags[0] = modal.selected.name; + PubSub.publish(featureUpdate, null); + modal.rootNode.dom.remove(modal.rootNode); + } + }, 'Apply Selection'); } - _closeButton(modal){ - return m('button',{ - onclick: function(){ - modal.rootNode.dom.remove(modal.rootNode); - } - },'Close'); + /** + * + * @param modal + * @returns {*} + * @private + */ + + _closeButton(modal) { + return m('button', { + onclick: function () { + modal.rootNode.dom.remove(modal.rootNode); + } + }, 'Close'); } - _dropDown(modal){ + /** + * + * @param modal + * @returns {*} + * @private + */ + + _dropDown(modal) { console.log('what inner', modal, modal.rootNode); let selector = this; - return m('select',{ - selectedIndex : selector.selected.index, - onchange: function(e){ - console.log("selected on drop",this,e); + return m('select', { + selectedIndex: selector.selected.index, + onchange: function (e) { + console.log('selected on drop', this, e); selector.selected.name = e.target.value; selector.selected.index = modal.tagList.indexOf(e.target.value); - }},[modal.tagList.map(tag => { + } + }, [modal.tagList.map(tag => { return m('option', tag); - }) - ]) + }) + ]); } - handleGesture(){ - // prevent interacting with div from propegating events - return true; - } + /** + * + * @returns {boolean} + */ + + handleGesture() { + // prevent interacting with div from propagating events + return true; + } } diff --git a/src/ui/menus/FeatureMenu.js b/src/ui/menus/FeatureMenu.js index 564ffef1..c2294275 100644 --- a/src/ui/menus/FeatureMenu.js +++ b/src/ui/menus/FeatureMenu.js @@ -8,9 +8,14 @@ import PubSub from 'pubsub-js'; import {featureUpdate} from '../../topics'; import {ColorPicker} from './ColorPicker'; - export class FeatureMenu { - constructor(data,order){ + /** + * + * @param data + * @param order + */ + + constructor(data, order) { // Setup modal position based on current placement of the actual map // layout viewport. keeps things self-contained when embedding. let viewport = document.getElementById('cmap-menu-viewport'); @@ -22,242 +27,389 @@ export class FeatureMenu { viewport.style.left = `${layoutBounds.left}px`; viewport.style.width = '95%'; viewport.style.height = `${layoutBounds.height}px`; - // Setup track and subtrack data - let model = data.parent.parent.model; - let tagList = model.tags.sort(); - let settings = {}; - let trackGroups = []; - let defaultSettings = model.qtlGroups && model.qtlGroups[order] != undefined ? {filters:model.qtlGroups[order].filters.slice(0),trackColor:model.qtlGroups[order].trackColor.slice(0)} : undefined; - if(order == undefined){ - order = model.qtlGroups ? model.qtlGroups.length : 0; - } - - if(!model.qtlGroups || model.qtlGroups[0] === undefined){ - order = 0; - settings = {filters:[tagList[0]],trackColor:['red']}; - trackGroups[0]= settings; - model.qtlGroups = []; - } else { - trackGroups = model.qtlGroups.slice(0); - if(!trackGroups[order]){ - trackGroups[order] = {filters:[tagList[0]],trackColor:['red']}; - } - settings = trackGroups[order]; + let model = data.model || data.component.model; + let tagList = model.tags.sort(); + let settings = model.tracks[order] ? data.config : model.config.qtl; + if(typeof settings.fillColor === 'string'){settings.fillColor = [settings.fillColor];} + let trackGroups = []; + let filters = settings.filters ? settings.filters.slice() : [tagList[0].slice()]; + let fillColor = settings.fillColor ? settings.fillColor.slice() : ['aqua']; + if(typeof fillColor === 'string'){fillColor = [fillColor];} + + + if(!settings.filters){ + settings.filters = filters; + } + if(!settings.fillColor){ + settings.fillColor = fillColor; } - - let selected = settings.filters.map( (item) => { - return { - name: item, - index: tagList.indexOf(item) - }; + if(!settings.title){ + settings.title = filters[0]; + } + + let defaultSettings = { + filters : settings.filters.slice(), + fillColor : settings.fillColor.slice(), + title : settings.title.slice() + }; + + settings.position = data.position; + + let selected = filters.map((item) => { + return { + name: item, + index: tagList.indexOf(item) + }; }); let trackConfig = { model: model, tagList: tagList, - settings: settings, - selected: selected, - trackGroups:trackGroups - }; + settings: settings, + selected: selected, + trackGroups: trackGroups + }; //Attach components to viewport, in general these are the close button (x in top - //right), the acutal modal contents, and the apply/close/delete button bar + //right), the actual modal contents, and the apply/close/delete button bar let controls = [ - m(_applyButton,{qtl:model.qtlGroups,track:trackGroups,order:order,reset:defaultSettings,newData:selected}), - m(_cancelButton,{qtl:model.qtlGroups,order:order,reset:defaultSettings,newData:selected}) - ]; - - if(model.qtlGroups[order] != undefined){ - controls.push(m(_removeButton,{qtl:model.qtlGroups,order:order,reset:defaultSettings,newData:selected})); + m(_applyButton, { + model:model, + config : settings, + order: order, + bioMapIndex : model.component.bioMapIndex, + reset: defaultSettings, + newData : selected + }), + m(_cancelButton, {model: model, config: settings, order: order, reset: defaultSettings}) + ]; + + if (order < model.tracks.length) { + controls.push(m(_removeButton, { + order:order, + model:model, + bioMapIndex : model.component.bioMapIndex, + })); } - - // Buld menu mithril component, then mount + + // Build menu mithril component, then mount let modalDiv = { - oncreate: function (vnode){ + oncreate: function (vnode) { vnode.dom.mithrilComponent = this; // Without this and handleGesture, clicks in modal will pass through to the underlying view }, - view: function(vnode){ - return m('div',{style:'height:100%; width:100%'},[ - m(CloseButton,{qtl:model.qtlGroups,order:order,reset:defaultSettings,newData:selected}), - m(TrackMenu,{info:trackConfig,count:0}), - m('div',{style:'text-align:center'},controls) - ] - ) + view: function () { + return m('div', {style: 'height:100%; width:100%'}, [ + m(CloseButton, {model: model, config: settings, order: order, reset: defaultSettings}), + m(TitleBox, {settings: settings}), + m(TrackMenu, {info: trackConfig, count: 0}), + m('div', {style: 'text-align:center'}, controls) + ] + ); }, - handleGesture: function(){ + handleGesture: function () { return true; } - } + }; m.mount(document.getElementById('cmap-menu-viewport'), modalDiv); } } +/** + * + * @type {{view: _removeButton.view}} + * @private + */ + export let _removeButton = { - view: function(vnode){ - return m('button',{ - onclick: - ()=>{ - vnode.attrs.qtl.splice(vnode.attrs.order,1); - PubSub.publish(featureUpdate,null); - m.redraw(); + + /** + * + * @param vnode + * @returns {*} + */ + + view: function (vnode) { + return m('button', { + onclick: + () => { + vnode.attrs.model.tracks.splice(vnode.attrs.order, 1); + PubSub.publish(featureUpdate, {mapIndex: vnode.attrs.bioMapIndex}); + m.redraw(); closeModal(); }, - style:'background:red' - },'Remove Track'); + style: 'background:red' + }, 'Remove Track'); } }; +export let TitleBox = { + /** + * + * @param vnode + * @returns {*} + */ + oninit: function (vnode) { + vnode.state.value = vnode.attrs.settings.title; + }, + view: function (vnode) { + return m('div',{},'Track title: ', + m('input[type=text].title-input', { + style: 'display:block; width:10%;', + defaultValue : vnode.attrs.settings.title, + oninput: m.withAttr('value', function (value) { + try { + vnode.attrs.settings.title = value; + } catch (e) { + // expect this to fail silently, as most typing will not actually give + // a proper hex triplet/sextet + } + }) + }) + ); + } +}; + + +/** + * + * @type {{view: _cancelButton.view}} + * @private + */ + export let _cancelButton = { - view: function(vnode){ - return m('button',{ - onclick: - ()=>{ - if(vnode.attrs.qtl && vnode.attrs.qtl[vnode.attrs.order] != undefined){ - vnode.attrs.qtl[vnode.attrs.order] = vnode.attrs.reset; + + /** + * + * @param vnode + * @returns {*} + */ + + view: function (vnode) { + return m('button', { + onclick: + () => { + vnode.attrs.config.fillColor = vnode.attrs.reset.fillColor; + vnode.attrs.config.filters = vnode.attrs.reset.filters; + vnode.attrs.config.title = vnode.attrs.reset.title; + if(vnode.attrs.order < vnode.attrs.model.tracks.length) { + vnode.attrs.model.tracks[vnode.attrs.order] = vnode.attrs.config; } closeModal(); } - },'Close'); + }, 'Cancel'); } }; +/** + * + * @type {{view: _applyButton.view}} + * @private + */ + export let _applyButton = { - view: function(vnode){ - return m('button',{ - onclick: function(){ - let order = vnode.attrs.order; - let filters = vnode.attrs.newData.map( selected => { - return selected.name; - }); - let colors = vnode.attrs.track[order].trackColor; - vnode.attrs.qtl[order] = {filters: filters.slice(0), trackColor:colors.slice(0)}; - PubSub.publish(featureUpdate,null); - m.redraw(); - closeModal(); - } - },'Apply Selection'); + + /** + * + * @param vnode + * @returns {*} + */ + + view: function (vnode) { + return m('button', { + onclick: function () { + vnode.attrs.config.filters = vnode.attrs.newData.map(data => {return data.name;}); + vnode.attrs.model.tracks[vnode.attrs.order] = vnode.attrs.config; + PubSub.publish(featureUpdate, {mapIndex: vnode.attrs.bioMapIndex}); + m.redraw(); + closeModal(); + } + }, 'Apply Selection'); } }; -/* +/** * Div with simple close X + * @type {{view: CloseButton.view}} */ -export let CloseButton = { - view: function(vnode){ + +export let CloseButton = { + + /** + * + * @param vnode + * @returns {*} + */ + + view: function (vnode) { return m('div', - { style:'text-align:right;', - onclick: - ()=>{ - if(vnode.attrs.qtl && vnode.attrs.qtl[vnode.attrs.order] != undefined){ - vnode.attrs.qtl[vnode.attrs.order] = vnode.attrs.reset; + { + style: 'text-align:right;', + onclick: + () => { + vnode.attrs.config.fillColor = vnode.attrs.reset.fillColor; + vnode.attrs.config.filters = vnode.attrs.reset.filters; + vnode.attrs.config.title = vnode.attrs.reset.title; + if(vnode.attrs.order < vnode.attrs.model.tracks.length) { + vnode.attrs.model.tracks[vnode.attrs.order] = vnode.attrs.config; + } + closeModal(); } - closeModal(); - } - },'X'); + }, 'X'); } }; /* * Mithril component * Div that contains the dropdowns and components for selecting track options + * @type {{oninit: TrackMenu.oninit, view: TrackMenu.view}} */ + export let TrackMenu = { - oninit: function (vnode){ + + /** + * + * @param vnode + */ + + oninit: function (vnode) { vnode.state = vnode.attrs; - vnode.state.hidden = []; - vnode.state.picker = []; + vnode.state.hidden = []; + vnode.state.picker = []; }, - view: function(vnode){ - let selected = vnode.state.info.selected; - let settings = vnode.state.info.settings; - this.count = 0; - - let dropdows = selected.map( (item,order)=>{ - if(settings.trackColor[order] == undefined){ - settings.trackColor[order] = settings.trackColor.slice(0,1); + + /** + * + * @param vnode + * @returns {*} + */ + + view: function (vnode) { + let selected = vnode.state.info.selected; + let settings = vnode.state.info.settings; + this.count = 0; + + + let dropdowns = selected.map((item, order) => { + if (settings.fillColor[order] === undefined) { + settings.fillColor[order] = settings.fillColor.slice(0, 1); + } + if (!vnode.state.hidden[order]) { + vnode.state.hidden[order] = 'none'; + } + if (!vnode.state.picker[order]) { + vnode.state.picker[order] = settings.fillColor[order] || 'orange'; + } + + let dropSettings = { + selected: selected, + name: settings.filters[order], + fillColor: settings.fillColor, + tags: vnode.state.info.tagList, + nodeColor: vnode.state.picker + }; + if (selected[order].index === -1) { + selected[order].index = dropSettings.tags.indexOf(dropSettings.name); } - if(!vnode.state.hidden[order]){ - vnode.state.hidden[order] = 'none'; - } - if(!vnode.state.picker[order]){ - vnode.state.picker[order] = settings.trackColor[order] || 'orange'; - } - let dropSettings = { - selected: selected, - name: settings.filters[order], - trackColor: settings.trackColor, - tags: vnode.state.info.tagList, - nodeColor: vnode.state.picker - }; - if(selected[order].index === -1){ - selected[order].index = dropSettings.tags.indexOf(dropSettings.name); - } let controls = [ - m('button',{onclick : () =>{ - selected[selected.length] = {name:vnode.state.info.tagList[0],index:0}; - }},'+') + m('button', { + onclick: () => { + selected[selected.length] = {name: vnode.state.info.tagList[0], index: 0}; + } + }, '+') ]; - if(selected.length > 1){ - controls.push(m('button',{onclick: () => { selected.splice(order,1);}},'-')); + if (selected.length > 1) { + controls.push(m('button', { + onclick: () => { + selected.splice(order, 1); + } + }, '-')); } - controls.push(m('button',{ - onclick: () => { - vnode.state.hidden[order] = vnode.state.hidden[order]==='none'? 'block':'none'; - } - },m('div', - {style:`color:${vnode.state.picker[order]}`} - ,'â– ') - )); - return [m(Dropdown,{settings:dropSettings,order:order,parentDiv:this,hidden:vnode.state.hidden}),controls]; - }); - return m('div#track-select-div',{ - style:'overflow:auto;width:100%;height:80%;' - },dropdows); + controls.push(m('button', { + onclick: () => { + vnode.state.hidden[order] = vnode.state.hidden[order] === 'none' ? 'block' : 'none'; + } + }, m('div', + {style: `color:${vnode.state.picker[order]}`} + , 'â– ') + )); + return [m(Dropdown, { + settings: dropSettings, + order: order, + parentDiv: this, + hidden: vnode.state.hidden + }), controls]; + }); + return m('div#track-select-div', { + style: 'overflow:auto;width:100%;height:80%;' + }, dropdowns); } }; /* * Mithril component * Actual dropdown selector + * @type {{oninit: Dropdown.oninit, onbeforeupdate: Dropdown.onbeforeupdate, view: Dropdown.view}} */ + export let Dropdown = { - oninit: function(vnode){ + + /** + * + * @param vnode + */ + + oninit: function (vnode) { vnode.state = vnode.attrs; }, - onbeforeupdate: function(vnode){ - if(vnode.state.count > vnode.attrs.parentDiv.count){ + /** + * + * @param vnode + */ + + onbeforeupdate: function (vnode) { + if (vnode.state.count > vnode.attrs.parentDiv.count) { vnode.attrs.parentDiv.count = vnode.state.count; } else { vnode.state.count = vnode.attrs.parentDiv.count; } }, - view: function(vnode){ + + /** + * + * @param vnode + * @returns {*} + */ + + view: function (vnode) { let order = vnode.state.order; - let settings = vnode.state.settings; - return m('div',m('select',{ - id:`selector-${order}`, - selectedIndex : settings.selected[order].index, - oninput: (e)=>{ - var selected = e.target.selectedIndex; + let settings = vnode.state.settings; + return m('div', m('select', { + id: `selector-${order}`, + selectedIndex: settings.selected[order].index, + oninput: (e) => { + let selected = e.target.selectedIndex; settings.selected[order].name = settings.tags[selected]; settings.selected[order].index = selected; - } - },[settings.tags.map(tag => { + } + }, [settings.tags.map(tag => { return m('option', tag); - }) - ]), m(ColorPicker,{settings:vnode.state.settings,order:order,hidden:vnode.state.hidden})); - } + }) + ]), m(ColorPicker, {settings: vnode.state.settings, order: order, hidden: vnode.state.hidden})); + } }; -/* + +/** * Function to close the menu-viewport and reshow the * layout viewport + * */ -export function closeModal (){ + +export function closeModal() { //reset cmap-menu-viewport vdom tree to empty state - m.mount(document.getElementById('cmap-menu-viewport'),null); - //explicity set visibility to avoid weird page interaction issues + m.mount(document.getElementById('cmap-menu-viewport'), null); + //explicitly set visibility to avoid weird page interaction issues document.getElementById('cmap-layout-viewport').style.visibility = 'visible'; document.getElementById('cmap-menu-viewport').style.display = 'none'; } diff --git a/src/ui/menus/Menu.js b/src/ui/menus/Menu.js index 450fb044..8fc1ab1e 100644 --- a/src/ui/menus/Menu.js +++ b/src/ui/menus/Menu.js @@ -1,28 +1,31 @@ /** - * LayoutBase - * A Mithril component Base class for Layouts, e.g. HorizontalLayout and - * CircosLayout. - */ + * LayoutBase + * A Mithril component Base class for Layouts, e.g. HorizontalLayout and + * CircosLayout. + */ import {Bounds} from '../../model/Bounds'; -export class Menu { - +export class Menu { // constructor() - prefer do not use in mithril components /** * mithril lifecycle callback + * @param vnode */ + oninit(vnode) { this.appState = vnode.attrs.appState; } /** * mithril lifecycle method + * @param vnode */ + oncreate(vnode) { // save a reference to this component's dom element - + this.el = vnode.dom; vnode.dom.mithrilComponent = this; this.bounds = new Bounds(vnode.dom.getBoundingClientRect()); @@ -30,7 +33,9 @@ export class Menu { /** * mithril lifecycle method + * @param vnode */ + onupdate(vnode) { this.bounds = new Bounds(vnode.dom.getBoundingClientRect()); } diff --git a/src/ui/menus/Popover.js b/src/ui/menus/Popover.js index 75e63f72..db6c97f6 100644 --- a/src/ui/menus/Popover.js +++ b/src/ui/menus/Popover.js @@ -1,110 +1,148 @@ /** - * popover - * A mithril component for displaying feature information. - */ + * popover + * A mithril component for displaying feature information. + */ import m from 'mithril'; import {mix} from '../../../mixwith.js/src/mixwith'; import {Menu} from './Menu'; import {RegisterComponentMixin} from '../RegisterComponentMixin'; -export class Popover extends mix(Menu).with(RegisterComponentMixin){ +export class Popover extends mix(Menu).with(RegisterComponentMixin) { - oninit(vnode){ + /** + * + * @param vnode + */ + + oninit(vnode) { super.oninit(vnode); } - + /** * mithril component render method + * @param vnode + * @returns {*} */ + view(vnode) { let b = vnode.attrs.domBounds || {}; - let info = vnode.attrs.info || {data:[]}; + let info = vnode.attrs.info || {data: []}; return m('div', { - class: 'biomap-info', - style: `left: ${info.left+b.left}px; top: ${info.top+b.top}px; + class: 'biomap-info', + style: `left: ${info.left + b.left}px; top: ${info.top + b.top}px; display: ${info.display};`, - },this._generateInner(info.data)); + }, this._generateInner(info.data)); } + /** + * + * @param data + * @returns {*} + * @private + */ - _generateInner(data){ - if(!data) return; + _generateInner(data) { + if (!data) return; let popover = data.map(item => { - let start = m('div', 'start: '+ item.model.coordinates.start); - let stop = m('div', 'stop: '+ item.model.coordinates.stop); - let tags = item.model.tags.length > 0 && typeof item.model.tags[0] != 'undefined' ? m('div','tags: ',item.model.tags.join('\n')) : []; - let aliases = item.model.aliases.length > 0 && typeof item.model.aliases[0] != 'undefined' ? m('div','aliases: ',item.model.aliases.join('\n')) : []; - let links = item.model.source.linkouts.length > 0 ? - m('div', {id:`links-div-${item.model.name}`}, - item.model.source.linkouts.filter(l => (! l.isLinkingService) && item.model.typeLinkedBy(l) ).map( - l => {return m('div', {}, m('a', {'target' : '_blank', 'href' : l.url.replace(/\${item.id}/, item.model.name)}, l.text));} - ).concat( - item.model.source.linkouts.some(l => {return l.isLinkingService && item.model.typeHasLinkouts;}) ? - (item.model.links == undefined ? m('img[src=images/ajax-loader.gif]') : item.model.links.map(l => {return m('div',{}, m('a', {target:'_blank', href:l.href}, l.text));})) - : [] - ) - ) : []; - - return [m(this._buttonTest(item.model),{targetId:item.model.name}), - m('div',{class:'biomap-info-data', id:`biomap-info-${item.model.name}`, style: 'display: none;'},[start,stop,tags, aliases, links]) + let start = m('div', 'start: ' + item.model.coordinates.start); + let stop = m('div', 'stop: ' + item.model.coordinates.stop); + let tags = item.model.tags.length > 0 && typeof item.model.tags[0] !== 'undefined' ? m('div', 'tags: ', item.model.tags.join('\n')) : []; + let aliases = item.model.aliases.length > 0 && typeof item.model.aliases[0] !== 'undefined' ? m('div', 'aliases: ', item.model.aliases.join('\n')) : []; + let links = item.model.source.linkouts.length > 0 ? + m('div', {id: `links-div-${item.model.name}`}, + item.model.source.linkouts.filter(l => (!l.isLinkingService) && item.model.typeLinkedBy(l)).map( + l => { + return m('div', {}, m('a', { + 'target': '_blank', + 'href': l.url.replace(/\${item.id}/, item.model.name) + }, l.text)); + } + ).concat( + item.model.source.linkouts.some(l => { + return l.isLinkingService && item.model.typeHasLinkouts; + }) ? + (item.model.links === undefined ? m('img[src=images/ajax-loader.gif]') : item.model.links.map(l => { + return m('div', {}, m('a', {target: '_blank', href: l.href}, l.text)); + })) + : [] + ) + ) : []; + + return [m(this._buttonTest(item.model), {targetId: item.model.name}), + m('div', { + class: 'biomap-info-data', + id: `biomap-info-${item.model.name}`, + style: 'display: none;' + }, [start, stop, tags, aliases, links]) ]; }); - - return m('div',{},popover); + + return m('div', {}, popover); } - _buttonTest(feature){ - var Links = { - fetch: function() { - var url; - return feature.source.linkouts.filter(l => l.isLinkingService && feature.tags.includes(l.featuretype)).map(l => { - url = l.url; - url = url.replace(/\${item\.id}/, feature.name); - return m.request({ - method: 'GET', - url: url, - }) - .then(function(result) { - feature.links = result; - }); - }); - } - }; + /** + * + * @param feature + * @returns {{view: view}} + * @private + */ + _buttonTest(feature) { + let Links = { + fetch: function () { + let url; + return feature.source.linkouts.filter(l => l.isLinkingService && feature.tags.includes(l.featuretype)).map(l => { + url = l.url; + url = url.replace(/\${item\.id}/, feature.name); + return m.request({ + method: 'GET', + url: url, + }) + .then(function (result) { + feature.links = result; + }); + }); + } + }; - return{ - view: function(vnode){ + return { + view: function (vnode) { let targetName = `biomap-info-${vnode.attrs.targetId}`; - return m('div', { - class:'biomap-info-name', - onclick: function() { - let target = document.getElementById(targetName); - target.style.display = target.style.display == 'none' ? 'block' : 'none'; - if (feature.links == undefined) { - if (feature.source.linkouts.some(l => {return l.isLinkingService && feature.typeHasLinkouts;})) { - let p = Links.fetch(); - if (p != undefined) { - p[0].then(vnode.redraw); - } - } - else { - feature.links = []; - vnode.redraw; + return m('div', { + class: 'biomap-info-name', + onclick: function () { + let target = document.getElementById(targetName); + target.style.display = target.style.display === 'none' ? 'block' : 'none'; + if (feature.links === undefined) { + if (feature.source.linkouts.some(l => { + return l.isLinkingService && feature.typeHasLinkouts; + })) { + let p = Links.fetch(); + if (p !== undefined) { + p[0].then(vnode.redraw); } } + else { + feature.links = []; + vnode.redraw(); + } } - }, vnode.attrs.targetId); + } + }, vnode.attrs.targetId); } }; } + /** + * + * @returns {boolean} + */ - - - handleGesture(){ - // prevent interacting with div from propegating events - return true; - } + handleGesture() { + // prevent interacting with div from propagating events + console.log('popover gesture!'); + return true; + } } diff --git a/src/ui/tools/AddMapButton.js b/src/ui/tools/AddMapButton.js index 68bf9f58..f0522040 100644 --- a/src/ui/tools/AddMapButton.js +++ b/src/ui/tools/AddMapButton.js @@ -8,8 +8,11 @@ export class AddMapButton { // constructor() - prefer do not use in mithril components /** - * mithril render callback - */ + * mithril render callback + * @param vnode + * @return {*} + */ + view(vnode) { const attrs = { onclick: vnode.attrs.onclick diff --git a/src/ui/tools/ConfigurationButton.js b/src/ui/tools/ConfigurationButton.js new file mode 100644 index 00000000..055d3fd9 --- /dev/null +++ b/src/ui/tools/ConfigurationButton.js @@ -0,0 +1,24 @@ +/** + * A mithril component of Add Map button + */ +import m from 'mithril'; + +export class ConfigurationButton { + + // constructor() - prefer do not use in mithril components + + /** + * mithril render callback + * @param vnode + * @returns {*} + */ + view(vnode) { + const attrs = { + onclick: vnode.attrs.onclick + }; + return m('button', attrs, [ + m('i.material-icons', 'mode_edit'), + 'Configuration' + ]); + } +} diff --git a/src/ui/tools/ConfigurationDialog.js b/src/ui/tools/ConfigurationDialog.js new file mode 100644 index 00000000..c3df926a --- /dev/null +++ b/src/ui/tools/ConfigurationDialog.js @@ -0,0 +1,131 @@ +/** + * A mithril component for configuration import/export + */ +import m from 'mithril'; +import {featureUpdate} from '../../topics'; +import PubSub from 'pubsub-js'; + +export class ConfigurationDialog { + + // constructor() - prefer do not use in mithril components + + /** + * mithril lifecycle method + */ + /** + * + * @param vnode + */ + + oninit(vnode) { + this.model = vnode.attrs.model; + let cd = {}; + this.model.bioMaps.forEach(bioMap => { + cd[bioMap.name] = { + config: bioMap.config, + name: bioMap.name, + qtlGroups: bioMap.qtlGroups, + source: bioMap.source.id + }; + }); + + ConfigData.base = JSON.stringify(cd, null, 2); + ConfigData.updated = JSON.stringify(cd, null, 2); + + this.onDismiss = vnode.attrs.onDismiss; + this.selection = null; + } + + /** + * event handler for cancel button. + * @param evt + * @private + */ + + _onCancel(evt) { + evt.preventDefault(); + this.onDismiss(evt); + } + + /** + * event handler for use new configuration button + * @param evt + * @private + */ + + _onUpdated(evt) { + let newConfig = JSON.parse(ConfigData.updated); + let finalConfig = []; + this.model.allMaps.forEach(map => { + for (let name in newConfig) { + if (newConfig.hasOwnProperty(name) && name === map.name && newConfig[name].source === map.source.id) { + console.log() + let item = map; + item.config = newConfig[name].config; + if(newConfig[name].config.tracks) { + let tracks = JSON.parse(JSON.stringify(newConfig[name].config.tracks)); + delete newConfig[name].config.tracks; + item.tracks = tracks; + } + finalConfig.push(item); + } + } + }); + this.model.bioMaps = finalConfig; + for (let i = 0; i < finalConfig.length; i++) { + PubSub.publish(featureUpdate, {mapIndex: i}); + } + this.onDismiss(evt); + } + + /** + * mithril component render callback. + * @returns {*} + */ + + view() { + //const allMaps = this.model.allMaps || []; + return m('div.cmap-map-addition-dialog', [ + m('h5', 'Configuration Details'), + m('form', [ + m('textarea', { + style: 'width:50%;height:600%', + value: ConfigData.updated, + onchange: function (e) { + e.preventDefault(); + ConfigData.updated = String(e.currentTarget.value); + } + } + ) + ]), + m('button', { + class: 'button', + onclick: evt => this._onUpdated(evt) + }, [ + m('i.material-icons', 'mode_edit'), + 'Use new configuration' + ] + ), + m('button.button', {onclick: evt => this._onCancel(evt)}, [ + m('i.material-icons', 'cancel'), + 'Cancel' + ]) + ]); + } +} + +/** + * + * @type {{base: string, updated: string, setBase: ConfigData.setBase, setUpdated: ConfigData.setUpdated}} + */ + +let ConfigData = { + base: '', + updated: '', + setBase: function (value) { + ConfigData.base = value; + }, + setUpdated: function (value) { + ConfigData.updated = value; + } +}; diff --git a/src/ui/tools/FilterButton.js b/src/ui/tools/FilterButton.js index 7b24f410..755dc178 100644 --- a/src/ui/tools/FilterButton.js +++ b/src/ui/tools/FilterButton.js @@ -3,26 +3,28 @@ */ import m from 'mithril'; -export class FilterButton { +export class FilterButton { // constructor() - prefer do not use in mithril components /** - * mithril render callback - */ + * mithril render callback + * @returns {*} + */ + view() { const attrs = { onclick: evt => this._onClick(evt) }; - return m('button', attrs , [ + return m('button', attrs, [ m('i.material-icons', 'filter_list'), 'Filter' ]); } /** - * button event handler - */ + * button event handler + */ _onClick() { } } diff --git a/src/ui/tools/LayoutPicker.js b/src/ui/tools/LayoutPicker.js index cef95cd5..c56241bd 100644 --- a/src/ui/tools/LayoutPicker.js +++ b/src/ui/tools/LayoutPicker.js @@ -9,57 +9,62 @@ import {layout} from '../../topics'; import {HorizontalLayout} from '../../ui/layout/HorizontalLayout'; import {CircosLayout} from '../../ui/layout/CircosLayout'; - -export class LayoutPicker { +export class LayoutPicker { // constructor() - prefer do not use in mithril components /** * mithril lifecycle method + * @param vnode */ + oninit(vnode) { this.appState = vnode.attrs.appState; } /** * mithril component render method + * @returns {*} */ + view() { return m('fieldset', [ - m('legend', 'layout:'), - m('label', { for: 'horizontal-radio'}, [ - m('input', { - type: 'radio', - name: 'layout', - value: HorizontalLayout, - id: 'horizontal-radio', - checked: this.appState.tools.layout === HorizontalLayout, - onchange: e => this.onchange(e) - }), - 'horizontal' - ]), - m('label', { for: 'circos-radio'}, [ - m('input', { - type: 'radio', - name: 'layout', - value: CircosLayout, - id: 'circos-radio', - checked: this.appState.tools.layout === CircosLayout, - onchange: e => this.onchange(e) - }), - 'circos' - ]) - ] + m('legend', 'layout:'), + m('label', {for: 'horizontal-radio'}, [ + m('input', { + type: 'radio', + name: 'layout', + value: HorizontalLayout, + id: 'horizontal-radio', + checked: this.appState.tools.layout === HorizontalLayout, + onchange: e => this.onchange(e) + }), + 'horizontal' + ]), + m('label', {for: 'circos-radio'}, [ + m('input', { + type: 'radio', + name: 'layout', + value: CircosLayout, + id: 'circos-radio', + checked: this.appState.tools.layout === CircosLayout, + onchange: e => this.onchange(e) + }), + 'circos' + ]) + ] ); } /** * mithril event handler + * @param e */ + onchange(e) { let l = e.target.value; this.appState.layout = l; e.redraw = false; - PubSub.publish(layout, { evt: e, layout: l }); + PubSub.publish(layout, {evt: e, layout: l}); } } diff --git a/src/ui/tools/MapAdditionDialog.js b/src/ui/tools/MapAdditionDialog.js index 21bd81d0..bcb739d7 100644 --- a/src/ui/tools/MapAdditionDialog.js +++ b/src/ui/tools/MapAdditionDialog.js @@ -9,7 +9,9 @@ export class MapAdditionDialog { /** * mithril lifecycle method + * @param vnode */ + oninit(vnode) { this.model = vnode.attrs.model; this.onDismiss = vnode.attrs.onDismiss; @@ -18,7 +20,10 @@ export class MapAdditionDialog { /** * event handler for cancel button. + * @param evt + * @private */ + _onCancel(evt) { evt.preventDefault(); this.onDismiss(evt); @@ -26,7 +31,10 @@ export class MapAdditionDialog { /** * event handler for add-on-right button + * @param evt + * @private */ + _onAddRight(evt) { const i = this.model.bioMaps.length; this.model.addMap(this.selection, i); @@ -36,7 +44,10 @@ export class MapAdditionDialog { /** * event handler for add-on-left button + * @param evt + * @private */ + _onAddLeft(evt) { this.model.addMap(this.selection, 0); evt.preventDefault(); @@ -45,15 +56,21 @@ export class MapAdditionDialog { /** * event handler for radio button change. + * @param evt + * @param map + * @private */ + _onSelection(evt, map) { evt.preventDefault(); this.selection = map; } /** - * mithril component render callback. - */ + * mithril component render callback. + * @returns {*} + */ + view() { const allMaps = this.model.allMaps || []; return m('div.cmap-map-addition-dialog', [ @@ -61,16 +78,16 @@ export class MapAdditionDialog { m('form', [ m('table.u-full-width', [ m('thead', - m('tr', [ m('th', 'Data Source'), m('th', 'Available Maps') ]) + m('tr', [m('th', 'Data Source'), m('th', 'Available Maps')]) ), m('tbody', - this.model.sources.map( source => { + this.model.sources.map(source => { return m('tr', [ m('td', source.id), - m('td', allMaps.filter( map => { - return (map.source === source && - this.model.bioMaps.indexOf(map) === -1); - }).map( map => { + m('td', allMaps.filter(map => { + return (map.source === source && + this.model.bioMaps.indexOf(map) === -1); + }).map(map => { return m('label', [ m('input[type="radio"]', { name: `maps4${source.id}`, @@ -88,7 +105,7 @@ export class MapAdditionDialog { ]) ]), m('button', { - disabled: this.selection ? false : true, + disabled: !this.selection, class: this.selection ? 'button-primary' : 'button', onclick: evt => this._onAddLeft(evt) }, [ @@ -96,8 +113,8 @@ export class MapAdditionDialog { 'Add Map On Left' ] ), - m('button.button', { - disabled: this.selection ? false : true, + m('button.button', { + disabled: !this.selection, class: this.selection ? 'button-primary' : 'button', onclick: evt => this._onAddRight(evt) }, [ @@ -105,7 +122,7 @@ export class MapAdditionDialog { 'Add Map On Right' ] ), - m('button.button', { onclick: evt => this._onCancel(evt) }, [ + m('button.button', {onclick: evt => this._onCancel(evt)}, [ m('i.material-icons', 'cancel'), 'Cancel' ]) diff --git a/src/ui/tools/MapRemovalDialog.js b/src/ui/tools/MapRemovalDialog.js index c5a8ebc4..21521843 100644 --- a/src/ui/tools/MapRemovalDialog.js +++ b/src/ui/tools/MapRemovalDialog.js @@ -11,7 +11,9 @@ export class MapRemovalDialog { /** * mithril lifecycle method + * @param vnode */ + oninit(vnode) { this.model = vnode.attrs.model; this.onDismiss = vnode.attrs.onDismiss; @@ -20,20 +22,25 @@ export class MapRemovalDialog { /** * event handler for cancel button + * @param evt + * @private */ - onCancel(evt) { + + _onCancel(evt) { evt.preventDefault(); this.onDismiss(evt); } /** * event handler for remove button + * @param evt + * @private */ - onRemove(evt) { - const filtered = this.model.bioMaps.filter( bioMap => { + + _onRemove(evt) { + this.model.bioMaps = this.model.bioMaps.filter(bioMap => { return this.selection.indexOf(bioMap) === -1; }); - this.model.bioMaps = filtered; PubSub.publish(mapRemoved, this.selection); evt.preventDefault(); this.onDismiss(evt); @@ -41,10 +48,13 @@ export class MapRemovalDialog { /** * event handler for checkbox + * @param bioMap + * @private */ - onToggleSelection(bioMap) { + + _onToggleSelection(bioMap) { const i = this.selection.indexOf(bioMap); - if(i === -1) { + if (i === -1) { this.selection.push(bioMap); } else { @@ -53,33 +63,35 @@ export class MapRemovalDialog { } /** - * mithril render callback - */ + * mithril render callback + * @returns {*} + */ + view() { const haveSelection = this.selection.length > 0; const plural = this.selection.length > 1; return m('div.cmap-map-removal-dialog', [ m('h5', plural ? 'Remove Maps' : 'Remove Map'), m('form', [ - this.model.bioMaps.map( bioMap => { + this.model.bioMaps.map(bioMap => { return m('label.cmap-map-name', [ m('input[type="checkbox"]', { checked: this.selection.indexOf(bioMap) !== -1, - onclick: () => this.onToggleSelection(bioMap) + onclick: () => this._onToggleSelection(bioMap) }), - m('span.label-body', bioMap.uniqueName ) + m('span.label-body', bioMap.uniqueName) ]); }), m('button', { class: haveSelection ? 'button-primary' : 'button', - disabled: ! haveSelection, + disabled: !haveSelection, autocomplete: 'off', // firefox workaround for disabled state - onclick: evt => this.onRemove(evt) + onclick: evt => this._onRemove(evt) }, [ m('i.material-icons', 'remove_circle_outline'), 'Remove Selected' ]), - m('button.button', { onclick: evt => this.onCancel(evt) }, [ + m('button.button', {onclick: evt => this._onCancel(evt)}, [ m('i.material-icons', 'cancel'), 'Cancel' ]) diff --git a/src/ui/tools/RemoveMapButton.js b/src/ui/tools/RemoveMapButton.js index ccb99b07..cfdea54b 100644 --- a/src/ui/tools/RemoveMapButton.js +++ b/src/ui/tools/RemoveMapButton.js @@ -3,13 +3,16 @@ */ import m from 'mithril'; -export class RemoveMapButton { +export class RemoveMapButton { // constructor() - prefer do not use in mithril components /** - * mithril render callback - */ + * mithril render callback + * @param vnode + * @returns {*} + */ + view(vnode) { const attrs = { onclick: vnode.attrs.onclick diff --git a/src/ui/tools/ResetButton.js b/src/ui/tools/ResetButton.js index fab035a1..7fa9f41d 100644 --- a/src/ui/tools/ResetButton.js +++ b/src/ui/tools/ResetButton.js @@ -12,7 +12,9 @@ export class ResetButton { /** * mithril render callback + * @returns {*} */ + view() { return m('button', { onclick: evt => this._onClick(evt) @@ -24,7 +26,10 @@ export class ResetButton { /** * reset button event handler + * @param evt + * @private */ + _onClick(evt) { PubSub.publish(reset, null); // subscribers to the reset topic may m.redraw if they need to; suppress diff --git a/src/ui/tools/Tools.js b/src/ui/tools/Tools.js index edec5443..9542ba51 100644 --- a/src/ui/tools/Tools.js +++ b/src/ui/tools/Tools.js @@ -1,23 +1,28 @@ /** - * A mithril component of the UI tools in a div (toolbar). - */ + * A mithril component of the UI tools in a div (toolbar). + */ import m from 'mithril'; import {ResetButton} from './ResetButton'; import {RemoveMapButton} from './RemoveMapButton'; import {AddMapButton} from './AddMapButton'; +import {ConfigurationButton} from './ConfigurationButton'; +import {UploadButton} from './UploadButton'; //import {FilterButton} from './FilterButton'; import {MapRemovalDialog} from './MapRemovalDialog'; import {MapAdditionDialog} from './MapAdditionDialog'; +import {ConfigurationDialog} from './ConfigurationDialog'; +import {UploadDialog} from './UploadDialog'; - -export class Tools { +export class Tools { // constructor() - prefer do not use in mithril components /** * mithril lifecycle method + * @param vnode */ + oninit(vnode) { this.appState = vnode.attrs.appState; this.currentDialog = vnode.attrs.dialog; @@ -25,7 +30,9 @@ export class Tools { /** * mithril component render method + * @returns {*} */ + view() { return m('div.cmap-tools', [ m('div.cmap-toolbar.cmap-hbox', [ @@ -36,6 +43,12 @@ export class Tools { }), m(RemoveMapButton, { onclick: () => this.currentDialog = MapRemovalDialog + }), + m(ConfigurationButton, { + onclick: () => this.currentDialog = ConfigurationDialog + }), + m(UploadButton, { + onclick: () => this.currentDialog = UploadDialog }) ]), this.currentDialog && m(this.currentDialog, { diff --git a/src/ui/tools/UploadButton.js b/src/ui/tools/UploadButton.js new file mode 100644 index 00000000..bcfabca9 --- /dev/null +++ b/src/ui/tools/UploadButton.js @@ -0,0 +1,25 @@ +/** + * A mithril component of Add Map button + */ +import m from 'mithril'; + +export class UploadButton { + + // constructor() - prefer do not use in mithril components + + /** + * mithril render callback + * @param vnode + * @returns {*} + */ + + view(vnode) { + const attrs = { + onclick: vnode.attrs.onclick + }; + return m('button', attrs, [ + m('i.material-icons', 'input'), + 'Add Data' + ]); + } +} diff --git a/src/ui/tools/UploadDialog.js b/src/ui/tools/UploadDialog.js new file mode 100644 index 00000000..40344cbf --- /dev/null +++ b/src/ui/tools/UploadDialog.js @@ -0,0 +1,212 @@ +/** + * A mithril component for map removal dialog + */ +import m from 'mithril'; +import {DataSourceModel} from '../../model/DataSourceModel'; + +export class UploadDialog { + + // constructor() - prefer do not use in mithril components + + /** + * mithril lifecycle method + * @param vnode + */ + + oninit(vnode) { + this.model = vnode.attrs.model; + this.onDismiss = vnode.attrs.onDismiss; + UploadData.new = false; + UploadData.setName(''); + UploadData.file = ''; + this.selection = null; + } + + /** + * event handler for cancel button. + * @param evt + * @private + */ + + _onCancel(evt) { + evt.preventDefault(); + this.onDismiss(evt); + } + + /** + * event handler for add-on-right button + * @param evt + * @private + */ + + _onAddData(evt) { + let sources = []; + if (!this.selection) { + this.selection = { + id: UploadData.newName, + filters: [], + linkouts: [], + method: 'GET', + url: UploadData.file !== '' ? UploadData.file : UploadData.loc, + config: {}, + parseResult: {data: []} + }; + } + + const oURL = this.selection.url; + this.selection.url = UploadData.file !== '' ? UploadData.file : UploadData.loc; + let cfg = [this.selection]; + + let promises = cfg.map(cfg => { + let dsm = new DataSourceModel(cfg); + sources.push(dsm); + return dsm.load(); + }); + + Promise.all(promises).then(() => { + sources.forEach(src => { + // change names to indicate uploaded data + if (UploadData.new) { + this.model.sources.push(src); + } + src.parseResult.data.forEach(data => data.feature_type_acc = 'Uploaded_' + data.feature_type_acc); + // update parseResults and all maps to reflect new data + this.selection.parseResult.data = this.selection.parseResult.data.concat(src.parseResult.data); + this.model.allMaps = this.model.sources.map(src => Object.values(src.bioMaps)).concatAll(); + // update active view models to show new data + this.model.bioMaps.forEach(activeMap => { + this.model.allMaps.filter(map => { + return ((map.name === activeMap.name && + activeMap.source.id === map.source.id)); + }).forEach(match => { + activeMap.features = match.features; + activeMap.tags = match.tags; + }); + }); + }); + this.selection.url = oURL; + }).catch(err => { + const msg = `While loading data source, ${err}`; + console.error(msg); + console.trace(); + alert(msg); + }); + + evt.preventDefault(); + this.onDismiss(evt); + } + + /** + * event handler for radio button change. + * @param evt + * @param map + * @private + */ + + _onSelection(evt, map) { + evt.preventDefault(); + this.selection = map; + UploadData.toggleNew(this.selection); + } + + /** + * mithril component render callback. + * @returns {*} + */ + + view() { + //const allMaps = this.model.allMaps || []; + return m('div.cmap-map-addition-dialog', [ + m('h5', 'Add Map'), + m('p', 'Currently only one file may be added at a time. If both a URL and a local file are provided, preference will be given to the local file.'), + m('form', [ + m('table.u-full-width', [ + m('thead', [ + m('tr', [m('th', 'URL'), m('th', m('input[type=text]', { + oninput: m.withAttr('value', UploadData.setLoc), + value: UploadData.loc, + style: 'width:60%;' + }))]) + , m('tr', [m('th', 'Local File'), m('th', m('input[type=file]', { + onchange: m.withAttr('files', UploadData.setFile), + file: UploadData.files + }))])] + ), + m('tbody', + m('tr', [ + m('td', 'Target Map Set'), + m('td', [ + m('label', [ + m('input[type="radio"]', { + name: 'maps4new', + checked: UploadData.new, + value: 'newMap', + onchange: (evt) => this._onSelection(evt, null) + }), m('input[type=text]', { + oninput: m.withAttr('value', UploadData.setName), + value: UploadData.newName + }) + ]) + ].concat(this.model.sources.map(map => { + return m('label', [ + m('input[type="radio"]', { + name: `maps4${map.id}`, + checked: this.selection === map, + value: map.id, + onchange: (evt) => this._onSelection(evt, map) + }), + m('span[class="label-body"]', map.id) + ]); + }) + ) + ) + ]) + ) + ]) + ]), + m('button', { + //disabled unless a selection is made, or a new set is selected *and* there is a location or file state) + disabled: !((this.selection || UploadData.new) && (UploadData.loc !== '' || UploadData.file !== '')), + class: this.selection || UploadData.new ? 'button-primary' : 'button', + onclick: evt => this._onAddData(evt) + }, [ + m('i.material-icons', 'input'), + 'Add Data to Map' + ] + ), + m('button.button', {onclick: evt => this._onCancel(evt)}, [ + m('i.material-icons', 'cancel'), + 'Cancel' + ]) + ]); + } +} + +/** + * + * @type {{loc: string, file: string, newName: string, new: boolean, setLoc: UploadData.setLoc, setName: UploadData.setName, setFile: UploadData.setFile, toggleNew: UploadData.toggleNew}} + */ + +let UploadData = { + loc: '', + file: '', + newName: '', + new: false, + setLoc: function (value) { + UploadData.loc = value; + }, + setName: function (value) { + UploadData.newName = value; + }, + setFile: function (files) { + let reader = new FileReader(); + reader.onload = function (e) { + UploadData.file = e.target.result; + }; + reader.readAsDataURL(files[0]); + }, + toggleNew: function (selection) { + UploadData.new = !selection; + } +}; + diff --git a/src/util/CanvasUtil.js b/src/util/CanvasUtil.js index 08c6c940..c04443bd 100644 --- a/src/util/CanvasUtil.js +++ b/src/util/CanvasUtil.js @@ -1,24 +1,46 @@ /** - * Helper functions for calculating canvas points. + * @file Helper functions for calculating canvas points. */ -// Takes a point on a map and translates it pixel coordinates on the current canvas -export function translateScale(point, baseScale, newScale){ - return ((baseScale.stop - baseScale.start)*(point-newScale.start)/(newScale.stop-newScale.start)+baseScale.start) - baseScale.start; +/** + * Takes a point on a map and translates it from the newScale to the baseScale scale + * @param point - Map point in terms of new scale + * @param baseScale - largest and smallest possible values of the scale + * @param newScale - largest and smallest values of the adjusted scale + * @param {boolean} invert - is the scale to be drawn "flipped" + * @returns {number} point converted from location on new scale to location on base scale + */ + +export function translateScale(point, baseScale, newScale, invert) { + let loc = ((baseScale.stop - baseScale.start) * (point - newScale.start) / (newScale.stop - newScale.start) + baseScale.start) - baseScale.start; + if (invert) { + loc = (baseScale.start + baseScale.stop) - loc; + } + return loc; } -// takes an event and translates the event coordinates to canvas coordinates -export function pageToCanvas(evt, canvas){ - function getOffset( el ) { - var _x = 0; - var _y = 0; - while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) { +/** + * Takes an event and translates the event coordinates to canvas coordinates + * here because webkit events vs mozilla events vs ie events don't all provide + * the same data + * + * @param evt - dom event + * @param canvas - target canvas + * @returns {{x: number, y: number}} location translated from page event coordinates to canvas coordinates. + */ + +export function pageToCanvas(evt, canvas) { + function getOffset(el) { + let _x = 0; + let _y = 0; + while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) { _x += el.offsetLeft - el.scrollLeft; _y += el.offsetTop - el.scrollTop; el = el.offsetParent; } - return { top: _y, left: _x }; + return {top: _y, left: _x}; } + let pageOffset = getOffset(canvas); return { 'x': evt.srcEvent.pageX - pageOffset.left, diff --git a/src/util/concatAll.js b/src/util/concatAll.js index 1beac5bd..6d22cbd0 100644 --- a/src/util/concatAll.js +++ b/src/util/concatAll.js @@ -1,9 +1,11 @@ /** - * concatAll() aka flattenDeep(), based on http://reactivex.io/learnrx/ + * @description concatAll() aka flattenDeep(), based on http://reactivex.io/learnrx/ + * @return {array} concatenated array. */ -Array.prototype.concatAll = function() { - var results = []; - this.forEach(function(subArray) { + +Array.prototype.concatAll = function () { + let results = []; + this.forEach(function (subArray) { results.push.apply(results, subArray); }); return results; diff --git a/src/util/isNil.js b/src/util/isNil.js index 3e30ed44..6733075c 100644 --- a/src/util/isNil.js +++ b/src/util/isNil.js @@ -1,6 +1,7 @@ /** - * Helper function for detecting null or undefined. + * @description Helper function for detecting null or undefined. */ + const isNil = (o) => o === null || o === undefined; export {isNil}; diff --git a/test/canvas/geometry/ruler.test.js b/test/canvas/geometry/ruler.test.js index 709ca773..8095f27e 100644 --- a/test/canvas/geometry/ruler.test.js +++ b/test/canvas/geometry/ruler.test.js @@ -2,9 +2,9 @@ import {expect} from 'chai'; import {Bounds} from '../../../src/model/Bounds'; import {Ruler} from '../../../src/canvas/geometry/Ruler'; -describe('Ruler test', function() { +describe('Ruler test', function () { - it('constructor works', function() { + it('constructor works', function () { let bounds = new Bounds({ top: 1, bottom: 11, @@ -14,13 +14,18 @@ describe('Ruler test', function() { height: 10 }); let parent = {}; - let model = { - view:{ - base:{ + let bioMap = { + config: { + rulerColor: 'blue', + rulerWidth: 10, + rulerSpacing: 5 + }, + view: { + base: { start: 0, stop: 100 }, - visible:{ + visible: { start: 0, stop: 100 }, @@ -28,23 +33,23 @@ describe('Ruler test', function() { pixelScaleFactor: 1 } }; - parent.bounds = new Bounds({top:0,left:0,width:20,height:20}); + parent.bounds = new Bounds({top: 0, left: 0, width: 20, height: 20}); parent.backbone = {}; parent.backbone.bounds = bounds; - let ruler = new Ruler({parent,bioMap:model}); + let ruler = new Ruler({parent, bioMap: bioMap}); let rulerBounds = new Bounds({ top: parent.bounds.top, - left: bounds.left -15, + left: bounds.left - 15, width: 10, height: bounds.height, allowSubpixel: false }); expect(ruler.parent).to.equal(parent); - expect(ruler.mapCoordinates).to.equal(model.view); - expect(ruler.pixelScaleFactor).to.equal(model.view.pixelScaleFactor); + expect(ruler.mapCoordinates).to.equal(bioMap.view); + expect(ruler.pixelScaleFactor).to.equal(bioMap.view.pixelScaleFactor); expect(ruler.bounds).to.eql(rulerBounds); }); - it('get visible', function() { + it('get visible', function () { let bounds = new Bounds({ top: 1, bottom: 11, @@ -55,12 +60,17 @@ describe('Ruler test', function() { }); let parent = {}; let model = { - view:{ - base:{ + config: { + rulerColor: 'blue', + rulerWidth: 10, + rulerSpacing: 5 + }, + view: { + base: { start: 0, stop: 100 }, - visible:{ + visible: { start: 0, stop: 100 }, @@ -68,20 +78,19 @@ describe('Ruler test', function() { pixelScaleFactor: 1 } }; - parent.bounds = new Bounds({top:0,left:0,width:20,height:20}); + parent.bounds = new Bounds({top: 0, left: 0, width: 20, height: 20}); parent.backbone = {}; parent.backbone.bounds = bounds; - let ruler = new Ruler({parent,bioMap:model}); + let ruler = new Ruler({parent, bioMap: model}); let rulerBounds = new Bounds({ top: parent.bounds.top, - left: bounds.left -15, + left: bounds.left - 15, width: 10, height: bounds.height, allowSubpixel: false }); - expect(ruler.visible).to.eql({data:ruler}); + expect(ruler.visible).to.eql({data: ruler}); }); - }); diff --git a/test/canvas/layout/BioMap.js b/test/canvas/layout/BioMap.js index 8deee96b..f0a49b5b 100644 --- a/test/canvas/layout/BioMap.js +++ b/test/canvas/layout/BioMap.js @@ -1,63 +1,71 @@ import {expect} from 'chai'; import mq from '../../ui/mithrilQuerySetup'; import {Bounds} from '../../../src/model/Bounds'; -import {BioMap} from '../../../src/canvas/layout/BioMap'; +import {BioMap} from '../../../src/canvas/canvas/BioMap'; -describe('BioMap test', function() { - describe('constructor', function() { - it('should construct a new node', function() { +describe('BioMap test', function () { + describe('constructor', function () { + it('should construct a new node', function () { let gestureRegex = { - pan: new RegExp('^pan'), - pinch: new RegExp('^pinch'), - tap: new RegExp('^tap'), - wheel: new RegExp('^wheel') - }; - let params = baseParams(); - // mock up test constructor to prevent _layout propegation blocking - // tests - let testBioMap = BioMap; - testBioMap.prototype._layout = function(layoutBounds){return true;}; - let node = new testBioMap(params); + pan: new RegExp('^pan'), + pinch: new RegExp('^pinch'), + tap: new RegExp('^tap'), + wheel: new RegExp('^wheel') + }; + let params = baseParams(); + // mock up test constructor to prevent _layout propegation blocking + // tests + let testBioMap = BioMap; + testBioMap.prototype._layout = function (layoutBounds) { + return true; + }; + let node = new testBioMap(params); - expect(node.model.visible).to.eql(params.bioMapModel.coordinates); - expect(node.model.view.base).to.eql(params.bioMapModel.coordinates); - expect(node.model.view.visible).to.eql(params.bioMapModel.coordinates); + expect(node.model.visible).to.eql(params.bioMapModel.coordinates); + expect(node.model.view.base).to.eql(params.bioMapModel.coordinates); + expect(node.model.view.visible).to.eql(params.bioMapModel.coordinates); expect(node.appState).to.equal(params.appState); expect(node.verticalScale).to.equal(0); - expect(node.backbone).to.equal(null); - expect(node.featureMarks).to.eql([]); - expect(node.featureLabels).to.eql([]); + expect(node.backbone).to.equal(null); + expect(node.featureMarks).to.eql([]); + expect(node.featureLabels).to.eql([]); expect(node._gestureRegex).to.eql(gestureRegex); }); }); - describe('custom getters', function() { - describe('get visible', function() { - it('should return visible noded if children have visible', function() { - let p1 = baseParams(); - let testBioMap = BioMap; - testBioMap.prototype._layout = function(layoutBounds){return true;}; - let node = new testBioMap(p1); - node.children[0] = {visible:"visibleTest"}; - expect(node.visible).to.eql(["visibleTest"]); - }); - it('should return nothing if no children are visible', function() { - let p1 = baseParams(); - let testBioMap = BioMap; - testBioMap.prototype._layout = function(layoutBounds){return true;}; - let node = new testBioMap(p1); - expect(node.visible).to.eql([]); - }); - }); - describe('get hitMap', function() { - it('should return rbush tree of visible objects', function() { - let p1 = baseParams(); - let testBioMap = BioMap; - testBioMap.prototype._layout = function(layoutBounds){return true;}; - let node = new testBioMap(p1); - expect(node.hitMap).to.eql(node.locMap); - }); - }); + describe('custom getters', function () { + describe('get visible', function () { + it('should return visible noded if children have visible', function () { + let p1 = baseParams(); + let testBioMap = BioMap; + testBioMap.prototype._layout = function (layoutBounds) { + return true; + }; + let node = new testBioMap(p1); + node.children[0] = {visible: 'visibleTest'}; + expect(node.visible).to.eql(['visibleTest']); + }); + it('should return nothing if no children are visible', function () { + let p1 = baseParams(); + let testBioMap = BioMap; + testBioMap.prototype._layout = function (layoutBounds) { + return true; + }; + let node = new testBioMap(p1); + expect(node.visible).to.eql([]); + }); + }); + describe('get hitMap', function () { + it('should return rbush tree of visible objects', function () { + let p1 = baseParams(); + let testBioMap = BioMap; + testBioMap.prototype._layout = function (layoutBounds) { + return true; + }; + let node = new testBioMap(p1); + expect(node.hitMap).to.eql(node.locMap); + }); + }); }); // // describe('mithril lifecycle events', function() { @@ -156,7 +164,7 @@ describe('BioMap test', function() { // }); // }); // - describe('private methods', function() { + describe('private methods', function () { // describe('_onZoom(evt)', function() { // it('should increase zoom on negative deltaY', function() { // let p1 = baseParams(); @@ -224,55 +232,72 @@ describe('BioMap test', function() { // }); // }); - describe('_onTap(evt)', function() { - it('should be able to calculate a tap event', function() { + describe('_onTap(evt)', function () { + it('should be able to calculate a tap event', function () { let p1 = baseParams(); let testBioMap = BioMap; - testBioMap.prototype._layout = function(layoutBounds){return true;}; - testBioMap.prototype._draw = function(layoutBounds){return true;}; + testBioMap.prototype._layout = function (layoutBounds) { + return true; + }; + testBioMap.prototype._draw = function (layoutBounds) { + return true; + }; let node = new testBioMap(p1); - let evt = {srcEvent:{ pageX: 0, pageY: 0}}; - //let node.canvas = {offsetLeft:0, scrollLeft:0,offsetTop:0,scrollTop:0,offsetParent:null}; - node.backbone = {loadLabelMap : function(){return true;} }; - document.getElementsByClassName = function(){return [];}; + let evt = {srcEvent: {pageX: 0, pageY: 0}}; + //let node.canvas = {offsetLeft:0, scrollLeft:0,offsetTop:0,scrollTop:0,offsetParent:null}; + node.backbone = { + loadLabelMap: function () { + return true; + } + }; + document.getElementsByClassName = function () { + return []; + }; let tap = node._onTap(evt); expect(tap).to.equal(true); }); - }); + }); - describe('_loadHitMap()', function() { - it('should load a new hit map', function() { + describe('_loadHitMap()', function () { + it('should load a new hit map', function () { let p1 = baseParams(); let testBioMap = BioMap; - testBioMap.prototype._layout = function(layoutBounds){return true;}; - testBioMap.prototype._draw = function(layoutBounds){return true;}; + testBioMap.prototype._layout = function (layoutBounds) { + return true; + }; + testBioMap.prototype._draw = function (layoutBounds) { + return true; + }; let node = new testBioMap(p1); - let hit = {minX:1,maxX:1,minY:1,maxY:1,data:'test'} - node.addChild({hitMap:hit}); - node._loadHitMap(); - //let node.canvas = {offsetLeft:0, scrollLeft:0,offsetTop:0,scrollTop:0,offsetParent:null}; + let hit = {minX: 1, maxX: 1, minY: 1, maxY: 1, data: 'test'}; + node.addChild({hitMap: hit}); + node._loadHitMap(); + //let node.canvas = {offsetLeft:0, scrollLeft:0,offsetTop:0,scrollTop:0,offsetParent:null}; expect(node.locMap.all()).to.eql([hit]); }); - }); - }); + }); + }); }); -const baseParams = function() { - let bounds = new Bounds({ +const baseParams = function () { + let bounds = new Bounds({ top: 1, - left: 10, + left: 10, width: 10, height: 10 }); return { bioMapModel: { - coordinates:{ - start: 1, - stop: 100 - } - }, + coordinates: { + start: 1, + stop: 100 + }, + config: { + rulerSteps: 100 + } + }, appState: {}, - layoutBounds: bounds + layoutBounds: bounds }; -} +}; diff --git a/test/canvas/node/sceneGraphNodeBase.test.js b/test/canvas/node/sceneGraphNodeBase.test.js index ccfb8c2d..1af569a3 100644 --- a/test/canvas/node/sceneGraphNodeBase.test.js +++ b/test/canvas/node/sceneGraphNodeBase.test.js @@ -2,9 +2,9 @@ import {expect} from 'chai'; import {Bounds} from '../../../src/model/Bounds'; import {SceneGraphNodeBase} from '../../../src/canvas/node/SceneGraphNodeBase'; -describe('SceneGraphNodeBase test', function() { - describe('constructor', function() { - it('should construct a new node', function() { +describe('SceneGraphNodeBase test', function () { + describe('constructor', function () { + it('should construct a new node', function () { let bounds = new Bounds({ top: 1, bottom: 11, @@ -28,8 +28,8 @@ describe('SceneGraphNodeBase test', function() { }); }); - describe('custom getters', function() { - it('should get children', function() { + describe('custom getters', function () { + it('should get children', function () { let p1 = baseParams('parent'); let p2 = baseParams('child'); let parentNode = new SceneGraphNodeBase(p1); @@ -38,10 +38,10 @@ describe('SceneGraphNodeBase test', function() { expect(parentNode.children.length).to.equal(1); expect(parentNode.children[0]).to.equal(childNode); }); - it('should get bounds', function() { + it('should get bounds', function () { let p1 = baseParams('parent'); let parentNode = new SceneGraphNodeBase(p1); - let b1 = new Bounds ({ + let b1 = new Bounds({ top: 0, bottom: 1, left: 0, @@ -52,20 +52,20 @@ describe('SceneGraphNodeBase test', function() { parentNode._bounds = b1; expect(parentNode.bounds).to.equal(b1); }); - it('should get rotation', function() { + it('should get rotation', function () { let p1 = baseParams('parent'); let parentNode = new SceneGraphNodeBase(p1); parentNode._rotation = 90; expect(parentNode.rotation).to.equal(90); }); - it('should get tags', function() { + it('should get tags', function () { let p1 = baseParams('parent'); let parentNode = new SceneGraphNodeBase(p1); parentNode._tags[0] = 'testTag'; expect(parentNode.tags).to.eql(['testTag']); }); - describe('get globalBounds', function() { - it('should work with existing parent', function() { + describe('get globalBounds', function () { + it('should work with existing parent', function () { let parentNode = parentChildGenerator(); let childNode = parentNode.children[0]; parentNode.children.push(childNode); @@ -77,14 +77,14 @@ describe('SceneGraphNodeBase test', function() { expect(result.left).to.equal(childNode.bounds.left + parentNode.bounds.left); expect(result.right).to.equal(childNode.bounds.right + parentNode.bounds.left); }); - it('should work with no parent', function() { + it('should work with no parent', function () { let p1 = baseParams('testNode'); let parentNode = new SceneGraphNodeBase(p1); expect(parentNode.globalBounds).to.eql(parentNode.bounds); }); }); - it('should get visible from children', function() { + it('should get visible from children', function () { let parentNode = parentChildGenerator(); let childNode = parentNode.children[0]; let visNode = { @@ -99,7 +99,7 @@ describe('SceneGraphNodeBase test', function() { expect(parentNode.visible).to.eql(childNode.locMap.all()); }); - it('should get hitMap from children', function() { + it('should get hitMap from children', function () { let p1 = baseParams('testNode'); let parentNode = new SceneGraphNodeBase(p1); let visNode = { @@ -109,13 +109,13 @@ describe('SceneGraphNodeBase test', function() { maxY: 2, data: 'empty' }; - parentNode.addChild({hitMap:[visNode]}) + parentNode.addChild({hitMap: [visNode]}); expect(parentNode.hitMap).to.eql([visNode]); }); }); - describe('custom setters', function() { - it('should set children', function() { + describe('custom setters', function () { + it('should set children', function () { let p1 = baseParams('testNode'); let p2 = baseParams('childNode'); let parentNode = new SceneGraphNodeBase(p1); @@ -125,10 +125,10 @@ describe('SceneGraphNodeBase test', function() { expect(parentNode.children).to.eql([childNode]); }); - it('should set bounds', function() { + it('should set bounds', function () { let p1 = baseParams('testNode'); let parentNode = new SceneGraphNodeBase(p1); - let b1 = new Bounds ({ + let b1 = new Bounds({ top: 0, bottom: 1, left: 0, @@ -140,26 +140,26 @@ describe('SceneGraphNodeBase test', function() { parentNode.bounds = b1; expect(parentNode.bounds).to.equal(b1); }); - it('should set rotation', function() { + it('should set rotation', function () { let p1 = baseParams('parent'); let parentNode = new SceneGraphNodeBase(p1); expect(parentNode.rotation).to.equal(45); parentNode.rotation = 90; expect(parentNode.rotation).to.equal(90); }); - it('should set tags', function() { + it('should set tags', function () { let p1 = baseParams('parent'); let parentNode = new SceneGraphNodeBase(p1); expect(parentNode.tags).to.eql(['parent']); parentNode.tags[0] = 'testTag'; expect(parentNode.tags).to.eql(['testTag']); }); - + }); - describe('public methods', function() { - describe('translatePointToGlobal({x,y})', function() { - it('should translate given {x,y} point to global coordinates', function() { + describe('public methods', function () { + describe('translatePointToGlobal({x,y})', function () { + it('should translate given {x,y} point to global coordinates', function () { let parentNode = parentChildGenerator(); let childNode = parentNode.children[0]; let point = childNode.translatePointToGlobal({x: 3, y: 8}); @@ -167,19 +167,25 @@ describe('SceneGraphNodeBase test', function() { }); }); - describe('draw(ctx)', function() { - it('should not throw an error when invoked', function() { + describe('draw(ctx)', function () { + it('should not throw an error when invoked', function () { let p1 = baseParams('parent'); let p2 = baseParams('child'); let parentNode = new SceneGraphNodeBase(p1); - let childNode = {draw: function() {return true;}} + let childNode = { + draw: function () { + return true; + } + }; parentNode.addChild(childNode); - expect(function() {parentNode.draw()}).to.not.throw(); + expect(function () { + parentNode.draw(); + }).to.not.throw(); }); }); - describe('removeChild(node)', function() { - it('should remove the passed node from its parent', function() { + describe('removeChild(node)', function () { + it('should remove the passed node from its parent', function () { let p1 = baseParams('parent'); let p2 = baseParams('child'); let parentNode = new SceneGraphNodeBase(p1); @@ -193,60 +199,62 @@ describe('SceneGraphNodeBase test', function() { expect(childNode.parent).to.equal(null); }); - it('should not throw an error is passed node has no parent', function() { + it('should not throw an error is passed node has no parent', function () { let p2 = baseParams('child'); let childNode = new SceneGraphNodeBase(p2); - expect(function() {childNode.removeChild(childNode)}).to.not.throw(); + expect(function () { + childNode.removeChild(childNode); + }).to.not.throw(); expect(childNode.parent).to.equal(null); }); }); - describe('addChild(node))', function() { - it('should add new child to new parent node', function() { + describe('addChild(node))', function () { + it('should add new child to new parent node', function () { let p1 = baseParams('parent'); let p2 = baseParams('child'); let parentNode = new SceneGraphNodeBase(p1); let childNode = new SceneGraphNodeBase(p2); - expect(parentNode.children.length).to.equal(0); - parentNode.addChild(childNode); - - expect(parentNode.children.length).to.equal(1); - expect(parentNode.children[0]).to.equal(childNode); - expect(childNode.parent).to.equal(parentNode); - }); + expect(parentNode.children.length).to.equal(0); + parentNode.addChild(childNode); - it('should not duplicate already existing children', function() { + expect(parentNode.children.length).to.equal(1); + expect(parentNode.children[0]).to.equal(childNode); + expect(childNode.parent).to.equal(parentNode); + }); + + it('should not duplicate already existing children', function () { let p1 = baseParams('parent'); let p2 = baseParams('child'); let parentNode = new SceneGraphNodeBase(p1); let childNode = new SceneGraphNodeBase(p2); - expect(parentNode.children.length).to.equal(0); - parentNode.addChild(childNode); + expect(parentNode.children.length).to.equal(0); + parentNode.addChild(childNode); parentNode.addChild(childNode); - expect(parentNode.children.length).to.equal(1); - expect(parentNode.children[0]).to.equal(childNode); - expect(childNode.parent).to.equal(parentNode); - }); - - it('should transfer child node between two parent nodes', function() { + expect(parentNode.children.length).to.equal(1); + expect(parentNode.children[0]).to.equal(childNode); + expect(childNode.parent).to.equal(parentNode); + }); + + it('should transfer child node between two parent nodes', function () { let p1 = baseParams('parentOriginal'); let p2 = baseParams('parentNew'); let p3 = baseParams('child'); let parentNode = new SceneGraphNodeBase(p1); - let secondParent = new SceneGraphNodeBase(p2); + let secondParent = new SceneGraphNodeBase(p2); let childNode = new SceneGraphNodeBase(p3); - parentNode.addChild(childNode); - secondParent.addChild(childNode); - expect(parentNode.children.length).to.equal(0); - expect(secondParent.children.length).to.equal(1); - expect(secondParent.children[0]).to.equal(childNode); - expect(childNode.parent).to.equal(secondParent); - }); - }); + parentNode.addChild(childNode); + secondParent.addChild(childNode); + expect(parentNode.children.length).to.equal(0); + expect(secondParent.children.length).to.equal(1); + expect(secondParent.children[0]).to.equal(childNode); + expect(childNode.parent).to.equal(secondParent); + }); + }); }); }); -const parentChildGenerator = function() { +const parentChildGenerator = function () { let parentNode = new SceneGraphNodeBase({ parent: null, bounds: new Bounds({ @@ -273,7 +281,7 @@ const parentChildGenerator = function() { return parentNode; }; -const baseParams = function(tag) { +const baseParams = function (tag) { let bounds = new Bounds({ top: 1, bottom: 11, @@ -287,5 +295,5 @@ const baseParams = function(tag) { tags: [tag], rotation: 45, parent: null - } + }; }; diff --git a/test/canvas/node/sceneGraphNodeCanvas.test.js b/test/canvas/node/sceneGraphNodeCanvas.test.js index f58ef588..e4261258 100644 --- a/test/canvas/node/sceneGraphNodeCanvas.test.js +++ b/test/canvas/node/sceneGraphNodeCanvas.test.js @@ -3,19 +3,19 @@ import mq from '../../ui/mithrilQuerySetup'; import {Bounds} from '../../../src/model/Bounds'; import {SceneGraphNodeCanvas} from '../../../src/canvas/node/SceneGraphNodeCanvas'; -describe('SceneGraphNodeCanvas test', function() { - describe('constructor', function() { - it('should construct a new node', function() { +describe('SceneGraphNodeCanvas test', function () { + describe('constructor', function () { + it('should construct a new node', function () { let params = { model: 'model', appState: 'appState' }; let gestureRegex = { - pan: new RegExp('^pan'), - pinch: new RegExp('^pinch'), - tap: new RegExp('^tap'), - wheel: new RegExp('^wheel') - }; + pan: new RegExp('^pan'), + pinch: new RegExp('^pinch'), + tap: new RegExp('^tap'), + wheel: new RegExp('^wheel') + }; let node = new SceneGraphNodeCanvas(params); expect(node.model).to.equal(params.model); expect(node.appState).to.equal(params.appState); @@ -24,151 +24,167 @@ describe('SceneGraphNodeCanvas test', function() { }); }); - describe('custom getters', function() { - describe('get selected', function() { - it('should return true if this canvas is seleted', function() { - let p1 = baseParams(); - let parentNode = new SceneGraphNodeCanvas(p1); - parentNode.appState.selection.bioMaps[0] = parentNode; - expect(parentNode.selected).to.equal(true); - }); - it('should return false if canvas is not selected', function() { - let p1 = baseParams(); - let parentNode = new SceneGraphNodeCanvas(p1); - expect(parentNode.selected).to.equal(false); - }); - }); + describe('custom getters', function () { + describe('get selected', function () { + it('should return true if this canvas is seleted', function () { + let p1 = baseParams(); + let parentNode = new SceneGraphNodeCanvas(p1); + parentNode.appState.selection.bioMaps[0] = parentNode; + expect(parentNode.selected).to.equal(true); + }); + it('should return false if canvas is not selected', function () { + let p1 = baseParams(); + let parentNode = new SceneGraphNodeCanvas(p1); + expect(parentNode.selected).to.equal(false); + }); + }); }); - describe('mithril lifecycle events', function() { - it('should generate approprate output', function() { + describe('mithril lifecycle events', function () { + it('should generate approprate output', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let domBounds = new Bounds({ - top: 1, - bottom: 11, - left: 10, - right: 20, - width: 10, - height: 10 - }); - let out = mq(parentNode,{domBounds: domBounds}); + let domBounds = new Bounds({ + top: 1, + bottom: 11, + left: 10, + right: 20, + width: 10, + height: 10 + }); + let out = mq(parentNode, {domBounds: domBounds}); expect(out.vnode.tag).to.eql(parentNode); - out.should.have('canvas'); - out.should.have('.cmap-canvas'); - out.should.have('.cmap-biomap'); + out.should.have('canvas'); + out.should.have('.cmap-canvas'); + out.should.have('.cmap-biomap'); }); }); - describe('public methods', function() { - describe('draw(ctx)', function() { - it('should return if no context', function() { + describe('public methods', function () { + describe('draw(ctx)', function () { + it('should return if no context', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - expect(function() {parentNode.draw()}).to.not.throw(); + expect(function () { + parentNode.draw(); + }).to.not.throw(); }); - it('should return if no bounds', function() { + it('should return if no bounds', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - parentNode.context2d = { - clearRect : ()=>{return true;}, - save : () => {return true;}, - restore : () => {return true;}, - }; - expect(function() {parentNode.draw()}).to.not.throw(); + parentNode.context2d = { + clearRect: () => { + return true; + }, + save: () => { + return true; + }, + restore: () => { + return true; + }, + }; + expect(function () { + parentNode.draw(); + }).to.not.throw(); }); - it('should propegate if both domBounds and context', function() { + it('should propegate if both domBounds and context', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - parentNode.context2d = { - clearRect : ()=>{return true;}, - save : () => {return true;}, - restore : () => {return true;}, - }; - parentNode.canvas = {width:10, height:10}; - let domBounds = new Bounds({ - top: 1, - left: 10, - width: 10, - height: 10 - }); - parentNode.domBounds = domBounds; - parentNode.bounds = domBounds; - parentNode.draw(); - expect(parentNode.lastDrawnCanvasBounds).to.eql(domBounds); + parentNode.context2d = { + clearRect: () => { + return true; + }, + save: () => { + return true; + }, + restore: () => { + return true; + }, + }; + parentNode.canvas = {width: 10, height: 10}; + let domBounds = new Bounds({ + top: 1, + left: 10, + width: 10, + height: 10 + }); + parentNode.domBounds = domBounds; + parentNode.bounds = domBounds; + parentNode.draw(); + expect(parentNode.lastDrawnCanvasBounds).to.eql(domBounds); }); }); - describe('handleGesture(evt)', function() { - it('should recognise pan', function() { + describe('handleGesture(evt)', function () { + it('should recognise pan', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'pan'}; + let evt = {type: 'pan'}; expect(parentNode.handleGesture(evt)).to.eql(parentNode._onPan(evt)); }); - it('should recognise tap', function() { + it('should recognise tap', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'tap'}; + let evt = {type: 'tap'}; expect(parentNode.handleGesture(evt)).to.eql(parentNode._onTap(evt)); }); - it('should recognise wheel', function() { + it('should recognise wheel', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'wheel'}; + let evt = {type: 'wheel'}; expect(parentNode.handleGesture(evt)).to.eql(parentNode._onZoom(evt)); }); - it('should recognise pinch', function() { + it('should recognise pinch', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'pinch'}; + let evt = {type: 'pinch'}; expect(parentNode.handleGesture(evt)).to.eql(parentNode._onZoom(evt)); }); - it('should ignore invalid event', function() { + it('should ignore invalid event', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'quack'}; + let evt = {type: 'quack'}; expect(parentNode.handleGesture(evt)).to.equal(false); }); - }); - }); + }); + }); - describe('private methods', function() { - describe('_onZoom(evt)', function() { - it('should propegate zoom event', function() { + describe('private methods', function () { + describe('_onZoom(evt)', function () { + it('should propegate zoom event', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'quack'}; + let evt = {type: 'quack'}; expect(parentNode._onZoom(evt)).to.equal(false); }); }); - describe('_onTap(evt)', function() { - it('should not block tap event', function() { + describe('_onTap(evt)', function () { + it('should not block tap event', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'quack'}; + let evt = {type: 'quack'}; expect(parentNode._onTap(evt)).to.equal(false); }); }); - describe('_onPan(evt)', function() { - it('should not block pan event if direction is not provided', function() { + describe('_onPan(evt)', function () { + it('should not block pan event if direction is not provided', function () { let p1 = baseParams(); let parentNode = new SceneGraphNodeCanvas(p1); - let evt = {type:'quack'}; + let evt = {type: 'quack'}; expect(parentNode._onPan(evt)).to.equal(false); }); }); - }); + }); }); -const baseParams = function() { +const baseParams = function () { return { - model: {}, - appState: { selection: { bioMaps:[] } } - } + model: {}, + appState: {selection: {bioMaps: []}} + }; }; diff --git a/test/canvas/node/sceneGraphNodeGroup.test.js b/test/canvas/node/sceneGraphNodeGroup.test.js index 6209072b..74a849d4 100644 --- a/test/canvas/node/sceneGraphNodeGroup.test.js +++ b/test/canvas/node/sceneGraphNodeGroup.test.js @@ -1,10 +1,10 @@ import {expect} from 'chai'; import {Bounds} from '../../../src/model/Bounds'; -import {Group} from '../../../src/canvas/node/SceneGraphNodeGroup'; +import {SceneGraphNodeGroup} from '../../../src/canvas/node/SceneGraphNodeGroup'; -describe('SceneGraphNodeGroup test', function() { - describe('constructor', function() { - it('should create a new group', function() { +describe('SceneGraphNodeGroup test', function () { + describe('constructor', function () { + it('should create a new group', function () { let bounds = new Bounds({ top: 1, bottom: 11, @@ -20,7 +20,7 @@ describe('SceneGraphNodeGroup test', function() { tags: ['test'], rotation: 45 }; - let node = new Group(params); + let node = new SceneGraphNodeGroup(params); expect(node.parent).to.equal(parent); expect(node.bounds).eql(bounds); expect(node.tags).eql(['test']); diff --git a/test/canvas/node/sceneGraphNodeTrack.test.js b/test/canvas/node/sceneGraphNodeTrack.test.js index 1cb6f302..fd62d63a 100644 --- a/test/canvas/node/sceneGraphNodeTrack.test.js +++ b/test/canvas/node/sceneGraphNodeTrack.test.js @@ -2,8 +2,8 @@ import {expect} from 'chai'; import {Bounds} from '../../../src/model/Bounds'; import {SceneGraphNodeTrack} from '../../../src/canvas/node/SceneGraphNodeTrack'; -describe('SceneGraphNodeTrack test', function() { - it('constructor works', function() { +describe('SceneGraphNodeTrack test', function () { + it('constructor works', function () { let bounds = new Bounds({ top: 1, bottom: 11, @@ -19,7 +19,7 @@ describe('SceneGraphNodeTrack test', function() { tags: ['test'], rotation: 45 }; - let node = new SceneGraphNodeTrack (params); + let node = new SceneGraphNodeTrack(params); expect(node.parent).to.equal(parent); expect(node.bounds).eql(bounds); expect(node.tags).eql(['test']); diff --git a/test/model/AppModel.test.js b/test/model/AppModel.test.js index 57110108..db4a2285 100644 --- a/test/model/AppModel.test.js +++ b/test/model/AppModel.test.js @@ -6,9 +6,9 @@ import {AppModel} from '../../src/model/AppModel'; const config = require('../../cmap.json'); -describe('AppModel test', function() { +describe('AppModel test', function () { - it('constructor works', function() { + it('constructor works', function () { const model = new AppModel(); expect(model).to.have.property('sources') .that.is.an('array'); diff --git a/test/model/BioMapModel.test.js b/test/model/BioMapModel.test.js index c6ccb8df..f9a7ee2f 100644 --- a/test/model/BioMapModel.test.js +++ b/test/model/BioMapModel.test.js @@ -2,18 +2,16 @@ import {expect} from 'chai'; import {BioMapModel} from '../../src/model/BioMapModel'; import {DataSourceModel} from '../../src/model/DataSourceModel'; - - let model; const params = { name: 'Pv01', features: [], - coordinates: { start: 42, stop: 142 }, - source: new DataSourceModel( { id: 'test'} ) + coordinates: {start: 42, stop: 142}, + source: new DataSourceModel({id: 'test'}) }; -describe('BioMapModel test', function() { - it('constructor works', function() { +describe('BioMapModel test', function () { + it('constructor works', function () { model = new BioMapModel(params); expect(model).to.have.property('source') .that.is.an('object'); @@ -27,11 +25,11 @@ describe('BioMapModel test', function() { expect(model.coordinates.stop).to.equal(142); }); - it('length getter works', function() { + it('length getter works', function () { expect(model.length).to.equal(100); }); - it('uniqueName getter works', function() { + it('uniqueName getter works', function () { expect(model).to.have.property('uniqueName') .that.is.a('string'); expect(model.uniqueName).to.equal('test/Pv01'); diff --git a/test/model/Bounds.test.js b/test/model/Bounds.test.js index be13c42a..2aacc030 100644 --- a/test/model/Bounds.test.js +++ b/test/model/Bounds.test.js @@ -1,7 +1,7 @@ import {expect, assert} from 'chai'; import {Bounds} from '../../src/model/Bounds'; -describe('Bounds test', function() { +describe('Bounds test', function () { let params = { top: 1, bottom: 11, @@ -11,7 +11,7 @@ describe('Bounds test', function() { height: 10 }; - it('constructor works', function() { + it('constructor works', function () { let b = new Bounds(params); expect(b.top).eql(params.top); expect(b.bottom).eql(params.bottom); @@ -21,7 +21,7 @@ describe('Bounds test', function() { expect(b.height).eql(params.height); }); - it('constructor calculates missing width, height', function() { + it('constructor calculates missing width, height', function () { let missingParams = Object.assign({}, params); delete missingParams.width; delete missingParams.height; @@ -30,7 +30,7 @@ describe('Bounds test', function() { expect(b.height).to.equal(10); }); - it('constructor calculates missing bottom, right', function() { + it('constructor calculates missing bottom, right', function () { let missingParams = Object.assign({}, params); delete missingParams.bottom; delete missingParams.right; @@ -39,73 +39,73 @@ describe('Bounds test', function() { expect(b.right).to.equal(20); }); - it('ignores x and y properties from DOMRect', function() { - let paramsWithExtras = Object.assign({ x: -1, y: -1 }, params); + it('ignores x and y properties from DOMRect', function () { + let paramsWithExtras = Object.assign({x: -1, y: -1}, params); let b = new Bounds(paramsWithExtras); expect(b.x).eql(undefined); expect(b.y).eql(undefined); }); - it('equals()', function() { + it('equals()', function () { let b1 = new Bounds(params); let b2 = new Bounds(params); assert(Bounds.equals(b1, b2)); assert(b1.equals(b2)); params.width = 7; b2 = new Bounds(params); - assert(! b1.equals(b2)); + assert(!b1.equals(b2)); }); - it('equals() rounds to pixel', function() { - let paramsWithExtras = Object.assign({ width: params.width + 0.1}, params); + it('equals() rounds to pixel', function () { + let paramsWithExtras = Object.assign({width: params.width + 0.1}, params); let b1 = new Bounds(paramsWithExtras); let b2 = new Bounds(params); assert(Bounds.equals(b1, b2)); assert(b1.equals(b2)); }); - it('equals() handles nils', function() { + it('equals() handles nils', function () { let b = new Bounds(params); - [null, undefined].forEach( nil => { - assert(! Bounds.equals(nil, b)); - assert(! Bounds.equals(b, nil)); - assert(! Bounds.equals(nil, nil)); + [null, undefined].forEach(nil => { + assert(!Bounds.equals(nil, b)); + assert(!Bounds.equals(b, nil)); + assert(!Bounds.equals(nil, nil)); }); }); - it('emptyArea()', function() { + it('emptyArea()', function () { let b = new Bounds({top: 10, left: 10, width: 0, height: 0}); assert(b.isEmptyArea); b = new Bounds({top: 10, left: 10, width: 100, height: 90}); - assert(! b.isEmptyArea); + assert(!b.isEmptyArea); b = new Bounds({top: 10, left: 10, width: 100, height: 0}); assert(b.isEmptyArea); }); - it('area()', function() { + it('area()', function () { let b = new Bounds({top: 10, left: 10, width: 100, height: 90}); expect(b.area).to.equal(9000); }); - it('areaEquals()', function() { + it('areaEquals()', function () { let b = new Bounds({top: 10, left: 10, width: 10, height: 2}); let bp = new Bounds({top: 10, left: 10, width: 5, height: 4}); assert(Bounds.areaEquals(b, bp)); expect(b.areaEquals(bp)); }); - it('areaEquals() rounds to pixel', function() { + it('areaEquals() rounds to pixel', function () { let b = new Bounds({top: 10, left: 10, width: 10.5, height: 2}); let bp = new Bounds({top: 10, left: 10, width: 10, height: 2}); expect(b.areaEquals(bp)); }); - it('areaEquals() handles nils', function() { + it('areaEquals() handles nils', function () { let b = new Bounds(params); - [null, undefined].forEach( nil => { - assert(! Bounds.areaEquals(nil, b)); - assert(! Bounds.areaEquals(b, nil)); - assert(! Bounds.areaEquals(nil, nil)); + [null, undefined].forEach(nil => { + assert(!Bounds.areaEquals(nil, b)); + assert(!Bounds.areaEquals(b, nil)); + assert(!Bounds.areaEquals(nil, nil)); }); }); }); diff --git a/test/model/DataSourceModel.test.js b/test/model/DataSourceModel.test.js index 0f4311f9..2da2be48 100644 --- a/test/model/DataSourceModel.test.js +++ b/test/model/DataSourceModel.test.js @@ -3,10 +3,10 @@ import {DataSourceModel} from '../../src/model/DataSourceModel'; const config = require('../../cmap.json'); -describe('DataSourceModel test', function() { +describe('DataSourceModel test', function () { - it('constructor works', function() { - config.sources.forEach( params => { + it('constructor works', function () { + config.sources.forEach(params => { const model = new DataSourceModel(params); expect(model).to.have.property('method') .that.is.a('string'); @@ -21,7 +21,7 @@ describe('DataSourceModel test', function() { // TODO: test the load() function which will cause an http fetch. - it('deserialize works', function() { + it('deserialize works', function () { const model = new DataSourceModel(config.sources[0]); model.deserialize(data); expect(model).to.have.property('parseResult') @@ -30,12 +30,12 @@ describe('DataSourceModel test', function() { expect(model.parseResult.data).to.have.lengthOf(9); }); - it('includeRecord equals', function() { + it('includeRecord equals', function () { let model; config.sources[1].filters = [{ - column : 'feature_type', - operator : 'equals', - value : 'gene_test' + column: 'feature_type', + operator: 'equals', + value: 'gene_test' }]; model = new DataSourceModel(config.sources[1]); model.deserialize(data); @@ -43,13 +43,13 @@ describe('DataSourceModel test', function() { expect(model.parseResult.data).to.have.lengthOf(1); }); - it('includeRecord not', function() { + it('includeRecord not', function () { let model; config.sources[1].filters = [{ - column : 'feature_type', + column: 'feature_type', not: true, - operator : 'equals', - value : 'gene_test' + operator: 'equals', + value: 'gene_test' }]; model = new DataSourceModel(config.sources[1]); model.deserialize(data); @@ -57,12 +57,12 @@ describe('DataSourceModel test', function() { expect(model.parseResult.data).to.have.lengthOf(8); }); - it('includeRecord regex', function() { + it('includeRecord regex', function () { let model; config.sources[1].filters = [{ - column : 'feature_type', - operator : 'regex', - value : '^g.+' + column: 'feature_type', + operator: 'regex', + value: '^g.+' }]; model = new DataSourceModel(config.sources[1]); model.deserialize(data); @@ -70,16 +70,16 @@ describe('DataSourceModel test', function() { expect(model.parseResult.data).to.have.lengthOf(8); }); - it('includeRecord multiple filters', function() { + it('includeRecord multiple filters', function () { let model; config.sources[1].filters = [{ - column : 'map_name', - operator : 'equals', - value : 'Pv01' + column: 'map_name', + operator: 'equals', + value: 'Pv01' }, { - column : 'feature_type', - operator : 'regex', - value : '_test$' + column: 'feature_type', + operator: 'regex', + value: '_test$' }]; model = new DataSourceModel(config.sources[1]); model.deserialize(data); @@ -89,13 +89,13 @@ describe('DataSourceModel test', function() { }); const data = -"map_name map_start map_stop feature_start feature_stop feature_name feature_type\n" + -"Pv01 0 69.6073 13.3417 13.3469 phavu.Phvul.001G073700 gene\n" + -"Pv01 0 69.6073 1.33705 1.34065 phavu.Phvul.001G011400 gene\n" + -"Pv01 0 69.6073 13.4007 13.4024 phavu.Phvul.001G073800 gene\n" + -"Pv01 0 69.6073 1.34167 1.34511 phavu.Phvul.001G011500 gene\n" + -"Pv01 0 69.6073 13.4293 13.4313 phavu.Phvul.001G073900 gene\n" + -"Pv01 0 69.6073 1.34743 1.35232 phavu.Phvul.001G011600 gene\n" + -"Pv01 0 69.6073 13.4815 13.5003 phavu.Phvul.001G074000 gene\n" + -"Pv01 0 69.6073 13.5095 13.5121 phavu.Phvul.001G074100 gene_test\n" + -"Pv01 0 69.6073 13.5127 13.52 phavu.Phvul.001G074200 xyz_test\n"; + 'map_name map_start map_stop feature_start feature_stop feature_name feature_type\n' + + 'Pv01 0 69.6073 13.3417 13.3469 phavu.Phvul.001G073700 gene\n' + + 'Pv01 0 69.6073 1.33705 1.34065 phavu.Phvul.001G011400 gene\n' + + 'Pv01 0 69.6073 13.4007 13.4024 phavu.Phvul.001G073800 gene\n' + + 'Pv01 0 69.6073 1.34167 1.34511 phavu.Phvul.001G011500 gene\n' + + 'Pv01 0 69.6073 13.4293 13.4313 phavu.Phvul.001G073900 gene\n' + + 'Pv01 0 69.6073 1.34743 1.35232 phavu.Phvul.001G011600 gene\n' + + 'Pv01 0 69.6073 13.4815 13.5003 phavu.Phvul.001G074000 gene\n' + + 'Pv01 0 69.6073 13.5095 13.5121 phavu.Phvul.001G074100 gene_test\n' + + 'Pv01 0 69.6073 13.5127 13.52 phavu.Phvul.001G074200 xyz_test\n'; diff --git a/test/model/Feature.test.js b/test/model/Feature.test.js index cf39b3a0..8ce0db66 100644 --- a/test/model/Feature.test.js +++ b/test/model/Feature.test.js @@ -1,61 +1,62 @@ import {expect} from 'chai'; import {Feature, featuresInCommon} from '../../src/model/Feature'; -describe('Feature test', function() { +describe('Feature test', function () { let params = { - name: 'test feature', - tags: ['hilite', 'etc'], - aliases: ['foo', 'feature 123'], + source :{}, coordinates: { start: 10, stop: 10 - } + }, + name: 'test feature', + tags: ['hilite', 'etc'], + aliases: ['foo', 'feature 123'], }; - it('constructor works', function() { + it('constructor works', function () { let f = new Feature(params); expect(f).eql(params); }); - it('length()', function() { + it('length()', function () { let f = new Feature(params); expect(f.length).to.equal(0); let p1 = Object.assign(params, { aliases: [], - coordinates: { start: 100, stop: 142 } + coordinates: {start: 100, stop: 142} }); f = new Feature(p1); expect(f.length).to.equal(42); }); - it('featuresInCommon()', function() { + it('featuresInCommon()', function () { let i, features1 = [], features2 = []; for (i = 1; i <= 10; i++) { let name = `feature ${i}`; - let p1 = Object.assign(params, { name }); + let p1 = Object.assign(params, {name}); features1.push(new Feature(p1)); } for (i = 8; i <= 15; i++) { let name = `feature ${i}`; - let p1 = Object.assign(params, { name }); + let p1 = Object.assign(params, {name}); features2.push(new Feature(p1)); } let res = featuresInCommon(features1, features2); expect(res.length).to.equal(3); }); - it('featuresInCommon() with aliases', function() { + it('featuresInCommon() with aliases', function () { let i, features1 = [], features2 = []; for (i = 1; i <= 10; i++) { let name = `feature ${i}`; let aliases = [`foo ${i}`, `bar ${i}`]; - let p = Object.assign(params, { name, aliases }); + let p = Object.assign(params, {name, aliases}); features1.push(new Feature(p)); } for (i = 8; i <= 15; i++) { let name = `misnamed feature xxx${i}`; let aliases = [`foo ${i}`, `bling ${i}`]; - let p = Object.assign(params, { name, aliases }); + let p = Object.assign(params, {name, aliases}); features2.push(new Feature(p)); } let res = featuresInCommon(features1, features2); diff --git a/test/ui/UI_test.js b/test/ui/UI_test.js index f9ac4864..2a59ce1e 100644 --- a/test/ui/UI_test.js +++ b/test/ui/UI_test.js @@ -5,6 +5,7 @@ describe('UI component', function() { it('should generate appropriate output', function() { let component = new UI(); let out = mq(component); - out.should.have('div.cmap-layout.cmap-vbox > div.cmap-layout-viewport.cmap-hbox'); + out.should.have('div.cmap-layout.cmap-vbox'); + out.should.have('div.cmap-layout-viewport.cmap-hbox'); }); }); diff --git a/test/ui/cmap.test.js b/test/ui/cmap.test.js index cbe1c7bb..58dc636a 100644 --- a/test/ui/cmap.test.js +++ b/test/ui/cmap.test.js @@ -2,8 +2,8 @@ import './mithrilQuerySetup'; import {CMAP} from '../../src/ui/CMAP'; import {assert} from 'chai'; -describe('CMAP class', function() { - it('constructor works', function() { +describe('CMAP class', function () { + it('constructor works', function () { let cmap = new CMAP(); assert(cmap); }); diff --git a/test/ui/elementsFromPoint_test.js b/test/ui/elementsFromPoint_test.js index be84a296..a7b5e30c 100644 --- a/test/ui/elementsFromPoint_test.js +++ b/test/ui/elementsFromPoint_test.js @@ -1,8 +1,8 @@ import {assert} from 'chai'; import '../../src/polyfill/index'; -describe('elementsFromPoint polyfill', function() { - it('function exists document.elementsFromPoint()', function() { +describe('elementsFromPoint polyfill', function () { + it('function exists document.elementsFromPoint()', function () { assert(document.elementsFromPoint); }); }); diff --git a/test/ui/mithrilQuerySetup.js b/test/ui/mithrilQuerySetup.js index f904e27f..cb66527a 100644 --- a/test/ui/mithrilQuerySetup.js +++ b/test/ui/mithrilQuerySetup.js @@ -2,6 +2,6 @@ global.window = Object.assign( require('mithril/test-utils/domMock.js')(), require('mithril/test-utils/pushStateMock')(), ); -const mock = require( 'mithril/test-utils/browserMock' )(global); +const mock = require('mithril/test-utils/browserMock')(global); global.document = mock.document; export default require('mithril-query'); diff --git a/test/ui/tools/Reset_test.js b/test/ui/tools/Reset_test.js index 838c4067..8d6e6d37 100644 --- a/test/ui/tools/Reset_test.js +++ b/test/ui/tools/Reset_test.js @@ -7,9 +7,9 @@ import PubSub from 'pubsub-js'; import {ResetButton} from '../../../src/ui/tools/ResetButton'; import {reset as resetTopic} from '../../../src/topics'; -describe('Reset button', function() { +describe('Reset button', function () { - it('should generate appropriate output', function() { + it('should generate appropriate output', function () { const component = new ResetButton(); const out = mq(component); out.should.have('button'); @@ -17,9 +17,9 @@ describe('Reset button', function() { out.should.contain('Reset'); }); - it('should publish a PubSub reset event', function() { + it('should publish a PubSub reset event', function () { // eslint-disable-next-line no-unused-vars - const p = new Promise( (resolve, reject) => { + const p = new Promise((resolve, reject) => { const component = new ResetButton(); const out = mq(component); PubSub.subscribe(resetTopic, resolve); diff --git a/test/util/concatAll.test.js b/test/util/concatAll.test.js index 1fac5596..e508827a 100644 --- a/test/util/concatAll.test.js +++ b/test/util/concatAll.test.js @@ -1,14 +1,14 @@ import {assert, expect} from 'chai'; import '../../src/util/concatAll'; -describe('concatAll test', function() { - it('ok', function() { +describe('concatAll test', function () { + it('ok', function () { assert(Array.prototype.concatAll); const input = [ - [1,2,3], - ['foo','bar','bla'], + [1, 2, 3], + ['foo', 'bar', 'bla'], ]; - const expected = [ 1, 2, 3, 'foo', 'bar', 'bla' ]; + const expected = [1, 2, 3, 'foo', 'bar', 'bla']; const output = input.concatAll(); expect(output).to.eql(expected); }); diff --git a/test/util/isNil.test.js b/test/util/isNil.test.js index 7e038a34..4c2d0b7e 100644 --- a/test/util/isNil.test.js +++ b/test/util/isNil.test.js @@ -1,13 +1,13 @@ import {assert} from 'chai'; import {isNil} from '../../src/util/isNil'; -describe('isNil test', function() { - it('ok', function() { - assert(! isNil(0)); - assert(! isNil('')); - assert(! isNil('foo')); - assert(! isNil(123)); - assert(! isNil({})); +describe('isNil test', function () { + it('ok', function () { + assert(!isNil(0)); + assert(!isNil('')); + assert(!isNil('foo')); + assert(!isNil(123)); + assert(!isNil({})); assert(isNil(null)); assert(isNil(undefined)); });