-1

I am trying to draw connected lines using canvas from data in a MySql table. The table (gene_dna_segments) contains the length of each line segment and the segment name.

The desired output is a continuous straight horizontal line comprised of each of the segments. Each segment also needs to have the segment name showing above the corresponding segment as shown in the image below:

enter image description here

+----------------------+--------------------+------------------+--------------------+
| gene_dna_segments_pk | gene_expression_fk | dna_segment_name | dna_segment_length |
+----------------------+--------------------+------------------+--------------------+
|                    1 |                 11 | Exon 1           |                 50 |
|                    2 |                 11 | Intron 1         |                 75 |
|                    3 |                 11 | Exon 2           |                 20 |
|                    4 |                 11 | Intron 2         |                 90 |
+----------------------+--------------------+------------------+--------------------+

Query (old fashioned no PDO...):

$query_dna = "SELECT * FROM gene_dna_segments WHERE gene_expression_fk = '11'";
$result_dna = mysql_query($query_dna, $connection) or die(mysql_error());

Display:

 <canvas id="canvas" width="800" height="500"></canvas>
    <script type="text/javascript">
    var canvas = document.getElementById('canvas');
    var context = canvas.getContext('2d');
    <?php
    while($row_dna = mysql_fetch_assoc($result_dna)) {

   echo "context.beginPath();context.moveTo(100, 100);context.lineTo(" . $row_dna['dna_segment_length'] . ", 100);context.lineWidth = 12;context.strokeStyle = '#009543';context.stroke();context.font = 'bold 12pt Calibri';
    context.fillStyle = '#009543';
    context.fillText('" . $row_dna['dna_segment_name'] . "', 180, 90);";
    }
    ?>
    </script>

Now, the line segments and text as defined in the table are drawn OK, but on top of each other as the context.moveTo(100, 100) is the same for each row that is output in the while loop. How should this be handled so that the segments are drawn as a continuous line?

Possibly the easiest thing is to add a start point column to the table and calculate the start points for each segment based on the previous segment's length...I've opened a seperate question on that possibility Calculating new array values based on another numeric array

Note that 'normal' code to draw the lines is like the following code, note that the moveto parts start at the end of the previous line. I need to do the same in the loop...

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

context.beginPath();
context.moveTo(100, 100);
context.lineTo(150, 100);
context.lineWidth = 12;
context.strokeStyle = '#009543';
context.stroke();

context.font = 'bold 11pt Calibri';
context.fillStyle = '#009543';
context.fillText('Exon 1', 105, 90);

context.beginPath();
context.moveTo(150, 100);
context.lineTo(225, 100);
context.lineWidth = 12;
context.strokeStyle = '#e97300';
context.stroke();

context.font = 'bold 11pt Calibri';
context.fillStyle = '#e97300';
context.fillText('Intron 1', 165, 90);

context.beginPath();
context.moveTo(225, 100);
context.lineTo(275, 100);
context.lineWidth = 12;
context.strokeStyle = '#009543';
context.stroke();  

context.font = 'bold 11pt Calibri';
context.fillStyle = '#009543';
context.fillText('Exon 2', 230, 90);

context.beginPath();
context.moveTo(275, 100);
context.lineTo(375, 100);
context.lineWidth = 12;
context.strokeStyle = '#e97300';
context.stroke();  

context.font = 'bold 11pt Calibri';
context.fillStyle = '#e97300';
context.fillText('Intron 2', 300, 90);
Community
  • 1
  • 1
IlludiumPu36
  • 4,196
  • 10
  • 61
  • 100

1 Answers1

1

[ Updated based on new info from questioner ]

What you're describing is a horizontally stacked bar-chart

Demo: http://jsfiddle.net/m1erickson/RzMWS/

Your barchart is fairly straightforward.

Small problem:

We need to display the segment_name label above each bar. In a perfect world, we could use the segment_length for the bar width. But in reality, sometimes the resulting bar width will be too narrow to fit the text label above.

Therefore, we must preprocess the data and determine an appropriate scale factor for the bars. By scaling the bars proportionally, each bar will remain proportional to their segment_length and will also be wide enough to fit the segment_name label.

Here is heavily commented code.

Of course, you will read from your database fetch instead of reading the test data in the rows[] array.

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    // get references to the canvas

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    ctx.font = 'bold 11pt Calibri';


    // define the color of the bar for each segment_name

    var colorCodes={
        Exon1:"green",
        Intron1:"orange",
        Exon2:"green",
        Intron2:"orange"
    };


    // create some test data

    var rows=[];
    rows.push({dna_segment_name:"Exon 1",dna_segment_length:50});
    rows.push({dna_segment_name:"Intron 1",dna_segment_length:75});
    rows.push({dna_segment_name:"Exon 2",dna_segment_length:20});
    rows.push({dna_segment_name:"Intron 2",dna_segment_length:90});

    // some variables
    // segments[]: pre-processed data will be saved into segments[]
    // scaleFactor: scales every bar if any text label fails to fit
    // padding: allow minimum padding between text labels

    var segments=[];
    var scaleFactor=1;
    var padding=5;

    // pre-process

    // Of course, you will be reading from your database Fetch
    // instead of this test data in rows[]

    for(var i=0;i<rows.length;i++){

        var $row_dna=rows[i];

        // make variables for the segment_name & segment_length
        // being read from the data fetch

        var name=$row_dna['dna_segment_name'];
        var length=$row_dna['dna_segment_length'];

        // lookup the color for this segment_name

        var color=colorCodes[name.replace(/\s+/g, '')];

        // rescale the bars if any text won't fit

        var textWidth=ctx.measureText(name).width+padding;
        var textRatio=textWidth/length;
        if (textRatio>scaleFactor){ scaleFactor=textRatio; }

        // save the pre-processed info in a javascript object
        // for later processing

        segments.push({
            name:name,
            length:length*scaleFactor,
            color:color
        });

    }

    // draw the stacked bar-chart 
    // based on the preprocessed JS objects in segments[]

    var accumLength=0;
    var y=100;  // the Y-coordinate of the barchart

    for(var i=0;i<segments.length;i++){

        // load the object for the current bar

        var segment=segments[i];

        // set the bar color

        ctx.fillStyle=segment.color;

        // draw the bar

        ctx.fillRect(accumLength,y,segment.length,12);

        // draw the text label

        ctx.fillText(segment.name,accumLength,y-10);

        // accumulate where the next bar will begin

        accumLength+=segment.length;
    }


}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=400 height=300></canvas>
</body>
</html>
markE
  • 102,905
  • 11
  • 164
  • 176
  • The line is horizontal, so the increment needs to happen on the x axis? Anyway linePosition as the starting point (moveto) will always be the same with the above code. See an alternative question at http://stackoverflow.com/questions/20186913/php-calculating-start-point-of-line-segments-based-on-previous-segments-length – IlludiumPu36 Nov 25 '13 at 07:34
  • I think I understand your question now: How to layout a data grid with headings+data+gridlines. If not, let me know and I'll check back tomorrow. – markE Nov 25 '13 at 08:23
  • I'm not sure whether you still understand the question (though I do appreciate your help!). I have edited the OP and attached an image showing the sort of result that I'm after. Note that the lengths in the database refer to line lengths, not text. – IlludiumPu36 Nov 26 '13 at 01:40
  • I've edited the other question so it's a whole lot clearer (I hope) http://stackoverflow.com/questions/20186913/php-calculating-start-point-of-line-segments-based-on-previous-segments-length – IlludiumPu36 Nov 26 '13 at 02:04
  • Ok, thanks for the new description (much clearer now)! What you want is a horizontally stacked bar chart with labels above. I have changed my answer to describe how to draw your chart on canvas. Good luck with your project--Cheers! :) – markE Nov 26 '13 at 05:52
  • Thanks for this. I have actually solved the horizontal start point locations in my other question, but I'll look at utilising your code to align the text above the bars. Thanks for your help with this! – IlludiumPu36 Nov 26 '13 at 06:21