DotNetCurry Logo

Biological Modeling with AngularJS

Posted by: Gil Fink , on 4/27/2016, in Category AngularJS
Views: 19233
Abstract: A small use case of how to create a visualization of some biology data using AngularJS

A few weeks ago I returned back from AngularConnect conference in London. I had the pleasure of speaking about my experience of building a genome viewer for Genome Compiler using both AngularJS and SVG.

In this article, I will show you a small use case of how to create a visualization of some biology data using AngularJS. While I won’t show you how I created Genome Compiler genome viewer, you will get a taste of how you can create your own visualization. The article will explain how to read a biology formatted file using HTML5 File API, how to use AngularPlasmid library to create a simple visualization and how to build the application using AngularJS.

This article is published from the DNC Magazine for .NET & JavaScript Developers. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions

Disclaimer: this article assumes that you have AngularJS knowledge and won’t cover the framework building blocks.

EditorialNote: You can learn more about AngularJS using our tutorials at http://www.dotnetcurry.com/tutorials/angularjs . You can learn more about AngularJS and SVG using Gil’s previous article http://www.dotnetcurry.com/angularjs/1213/create-graphics-using-svg-angularjs

Biology 101

Before I start to explain how the application works and how I created it, you will need a small ramp up about some biology concepts that we will use in the application: the FASTA format and plasmids.

FASTA Format Definitions

In the article we will read FASTA files. The FASTA format is a text-based format that represents a biological sequence. The first line in a FASTA file includes the sequence description. All the other lines in the FASTA file hold the sequence data which is represented in alphabet characters. For example, the following snippet shows a simple FASTA file:

>HSBGPG Human gene for bone gla protein (BGP)
GGCAGATTCCCCCTAGACCCGCCCGCACCATGGTCAGGCATGCCCCTCCTCATCGCTGGGCACAGCCCAGAGGGTATAAACAGTGCTGGAGGCTGGCGGGGCAGGCCAGCTGAGTCCTGAGCAGCAGCCCAGCGCAGCCACCGAGACACCATGAGAGCCCTCACACTCCTCGCCCTATTGGCCCTGGCCGCACTTTGCATCGCTGGCCAGGCAGGTGAGTGCCCCCACCTCCCCTCAGGCCGCATTGCAGTGGGGGCTGAGAGGAGGAAGCACCATGGCCCACCTCTTCTCACCCCTTTGGCTGGCAGTCCCTTTGCAGTCTAACCACCTTGTTGCAGGCTCAATCCATTTGCCCCAGCTCTGCCCTTGCAGAGGGAGAGGAGGGAAGAGCAAGCTGCCCGAGACGCAGGGGAAGGAGGATGAGGGCCCTGGGGATGAGCTGGGGTGAACCAGGCTCCCTTTCCTTTGCAGGTGCGAAGCCCAGCGGTGCAGAGTCCAGCAAAGGTGCAGGTATGAGGATGGACCTGATGGGTTCCTGGACCCTCCCCTCTCACCCTGGTCCCTCAGTCTCATTCCCCCACTCCTGCCACCTCCTGTCTGGCCATCAGGAAGGCCAGCCTGCTCCCCACCTGATCCTCCCAAACCCAGAGCCACCTGATGCCTGCCCCTCTGCTCCACAGCCTTTGTGTCCAAGCAGGAGGGCAGCGAGGTAGTGAAGAGACCCAGGCGCTACCTGTATCAATGGCTGGGGTGAGAGAAAAGGCAGAGCTGGGCCAAGGCCCTGCCTCTCCGGGATGGTCTGTGGGGGAGCTGCAGCAGGGAGTGGCCTCTCTGGGTTGTGGTGGGGGTACAGGCAGCCTGCCCTGGTGGGCACCCTGGAGCCCCATGTGTAGGGAGAGGAGGGATGGGCATTTTGCACGGGGGCTGATGCCACCACGTCGGGTGTCTCAGAGCCCCAGTCCCCTACCCGGATCCCCTGGAGCCCAGGAGGGAGGTGTGTGAGCTCAATCCGGACTGTGACGAGTTGGCTGACCACATCGGCTTTCAGGAGGCCTATCGGCGCTTCTACGGCCCGGTCTAGGGTGTCGCTCTGCTGGCCTGGCCGGCAACCCCAGTTCTGCTCCTCTCCAGGCACCCTTCTTTCCTCTTCCCCTTGCCCTTGCCCTGACCTCCCAGCCCTATGGATGTGGGGTCCCCATCATCCCAGCTGCTCCCAAATAAACTCCAGAAG

As you can see, the description line (first line) starts with a greater-than (>) symbol which is part of the format. The greater-than symbol might be followed by an identifier and after the identifier there will be a description. Both the identifier and description are optional. In the example above, there is only a description.

Now that we know a little bit about the FASTA format, we can build a small AngularJS service that will parse a FASTA string. In the implementation, I assume that there is only a description and no identifier. So as an exercise, try to implement the whole parser by yourself after reading the article. The following code snippet is the service code:

(function () {
    'use strict';
    angular.
        module("biologyDemo").
        factory("fastaParserService", fastaParserService);

    function fastaParserService() {
        function readSequence(fastaText) {
            var splittedStrings  = fastaText.split('\n'),
                result = {},
                i = 1;
            result.name = splittedStrings[0].substr(1, splittedStrings[0].length - 1);
            result.sequence = '';
            for (; i < splittedStrings.length; i++) {
                result.sequence += splittedStrings[i];
            }
            return result;
        }
        return {
            readSequence: readSequence
        };
    }
}());

In the service code, there is only one function, the readSequence function. This function accepts a FASTA string and then parses it and outputs a JavaScript object that includes two properties: a name and the sequence. As you can see, the code is very simple. I use the split function to split all the lines in the file. Then, I take the first line, remove the greater-than character and put the output in the returned object name property. The last thing I do is to iterate on all the other lines and concat them to the sequence property.

Plasmids

The second biology concept that you have to know is what are plasmids. A plasmid is a small DNA molecule represented as a circle with some annotations. You can think about it as a graph that represents some DNA of a flu virus or some body cell. For example, the following figure is a plasmid with some annotations:

plasmid

Figure 1: Plasmid

I won’t get into further explanations more than what I have already given, since this includes a lot of biology stuff that is not relevant to the article content. You may visit Wikipedia or, search on the internet to know more about plasmids. All you need to understand is that we will probably use a graphics model such as SVG or Canvas to create a plasmid visualization. In the application I will use a library called AngularPlasmid in order to create our FASTA sequence representation. Now that we know a little bit of biology, it is time to return back to HTML5 and JavaScript coding.

Reading FASTA Files Using HTML5 File API

In the application that we are going to create, we will receive a FASTA file as an input, which we will read and parse. In the previous section, we already created the FASTA format parser and in this section you will learn how to read a file using the HTML5 File API.

The HTML5 File API includes a small set of objects that enables you to read files. That doesn’t mean that you can just create file readers and read the entire user file system. Browsers restrict the option to read file for security reasons so you will need the user to interact with the application and explicitly allow you to read specific files. You have two points of interactions: drag and drop or file input types. Each of those options will enable you to get a pointer to a file/s to be able to read it.

In the application I’m building, I use a file input type and in the following code snippet you can see the file input type definitions that I use:

<input id="fastaFileInput" type="file" file-reader fasta-content="vm.fastaFileContent" accept=".fasta" />

As you can see in the snippet, in the file input type, I use a file-reader attribute which indicates that I have created an AngularJS directive which I’ll explain later on. I also use a second attribute that is called fasta-content which is going to be a model holder in the application. Once the file is read, the fastaFileContent will hold the FASTA file content and we will be able to parse it.

It is not enough to just add a file input type to our HTML in order to read the file. In the directive that we will create, you will use a FileReader. The FileReader object enables you to read the file. From the file input type you get a file pointer to a file object that includes some file metadata and the file content. Using a FileReader you will be able to read the file and then get its content. The FileReader object exposes 4 different read functions that you can use to read a file:

1. readAsText – reads the file as string.

2. readAsDataURL – reads the file and returns a URL representing the file's data.

3. readAsBinaryString – reads the file and returns raw binary data representing the file content.

4. readAsArrayBuffer – reads the file and returns an ArrayBuffer that represents the file’s content.

The FileReader reads the file asynchronously and therefore includes some event handlers such as onerror, onload or onloadstart. Once the file was read, the file’s content will be stored in the FileReader result property.

Now that we know about the FileReader object, it is time to create two new AngularJS objects. The first object will be the file-reader directive that will enable us to interact with the file input type. The following code snippet shows you the directive code:

(function () {
    'use strict';
    angular.
        module("biologyDemo").
        directive("fileReader", fileReader);
    fileReader.$inject = ['fileReaderService'];
    function fileReader(reader) {
        function link(scope, element) {
            element.bind("change", function (evt) {
                var file = evt.target.files[0];
                if (file && file.name.indexOf('.fasta') > 0) {
                    reader.readFile(file).then(function (data) {
                        scope.fastaContent = data;
                    });
                } else {
                    alert('Please insert a fasta format file!');
                }
            });
        }
        return {
            scope: {
                fastaContent: "="
            },
            restrict: 'A',
            link: link
        };
    }
}());

The directive wires an event handler to the file input type change event. When we get a new file, we validate it is a FASTA file and then use a service to read the file content and put it in the fastaContent scope variable.

The second AngularJS object that we will create is the fileReaderService that will be responsible to read the file. The reason I separated this functionality into a directive and a service is the idea of separating DOM interactions and logic code. The following code shows you how the service will look like:

(function () {
    'use strict';
    angular.
        module("biologyDemo").
        factory("fileReaderService", fileReaderService);
    fileReaderService.$inject = ['$q'];
    function fileReaderService($q) {
        function readFile(file) {
            var deferred = $q.defer(),
                reader = new FileReader();
            reader.onload = function (loaded) {
                deferred.resolve(loaded.target.result);
            }
            reader.readAsText(file);
            return deferred.promise;
        }
        return {
            readFile: readFile
        };
    }
}());

The code of the service is straight forward. Since the FileReader API is asynchronous, we will use a promise to wrap the code. The promise is created using the AngularJS $q service and is resolved only when the FileReader finished the reading of the file.

Note: If you don’t know what promises are, I suggest reading “Using Promises in Node.js Applications” article to get some information about the subject.

 

Creating a Plasmid Using AngularPlasmid

After we have read the file and parsed it, we will need a way to visualize our sequence so let’s get started to know AngularPlasmid. AngularPlasmid is a DNA plasmid visualization component that uses AngularJS and SVG underneath. You can get started with the library on its website: http://angularplasmid.vixis.com/index.php. Once you have download the library and referenced it in your web page, you will need to load its module into AngularJS. The following code shows you how to create the biologyDemo module and load AngularPlasmid module:

(function () {
    var app = angular.module('biologyDemo', ['angularplasmid']);
}());

After you have added AngularPlasmid module, you can start creating visualizations using the directives it includes. The main directive is the plasmid element directive which is the drawing space that all the features will be attached to. Once you have the surface, you would probably add a plasmid track. A track is the circular representation that holds other features for the plasmid. You will use the plasmidtrack element directive in order to do that. A track can have marking scales which can be created using the trackscale element directive. A track can also hold track markers to mark some features in the DNA sequence. You will use the trackmarker element directive. For example, the following code snippet shows how to create a simple plasmid created with the AngularPlasmid directives:

<plasmid sequencelength='1000'>
    <plasmidtrack radius='50'>
        <tracklabel text='Demo'></tracklabel>
        <trackscale interval='100' showlabels='1'></trackscale>
        <trackmarker start='212' end='345'></trackmarker>
        <trackmarker start='530' end='650'>
            <markerlabel text='Ecol'></markerlabel>
        </trackmarker>
        <trackmarker start='677' end='820'></trackmarker>
    </plasmidtrack>
</plasmid>

This is just a static representation of a plasmid with scales and markers. The output of running the code will look like the following:

angularjs-plasmid-example

Figure 2: AngularPlasmid Example

Note: You can explore the entire AngularPlasmid API in the library’s web site at: http://angularplasmid.vixis.com/api.php.

Create the Plasmid Dynamically

One challenge that you will most likely face is how to create the plasmid dynamically according to the sequence we read. The solution for this problem is to generate the entire plasmid elements using regular JavaScript and then use the $compile service to compile the elements and give them a scope. The next code snippet shows you a directive that will generate a plasmid when a data:ready event is published:

(function () {
    'use strict';
    angular.
        module("biologyDemo").
        directive("ngPlasmid", ngPlasmidDirective);
    ngPlasmidDirective.$inject = ['$compile'];
    function ngPlasmidDirective($compile) {
        function link(scope, element) {
            scope.$on('data:ready', init);
            function init(e, data) {
                draw(data);
            }
            function draw(data) {
                element.append(createPlasmid(data));
                $compile(element.contents())(scope);
            }
            function createPlasmid(data) {
                var path = document.createElement('plasmid');
                path.id = 'p1';
                path.setAttribute('sequencelength', data.sequence.length);
                path.setAttribute('sequence', data.sequence);
                path.setAttribute('plasmidheight', 400);
                path.setAttribute('plasmidwidth', 400);
                path.appendChild(createPlasmidTrack('t1', data));
                return path;
            }
            function addTrackLabel(path, data) {
                var elm = document.createElement('tracklabel');
                elm.setAttribute('text', data.name);
                elm.setAttribute('labelclass', 'tracklabel');
                elm.setAttribute('vadjust', '-12');
                path.appendChild(elm);
            }
            function addTrackScales(path) {
                var elm = document.createElement('trackscale');
                elm.setAttribute('interval', '150');
                elm.setAttribute('ticksize', 'stroke:#000000;stroke-width:1px;');
                elm.setAttribute('style', '100');
                elm.setAttribute('vadjust', '10');
                elm.setAttribute('showlabels', '1');
                elm.setAttribute('direction', 'out');
                elm.setAttribute('labelclass', 'trackscale-label');
                path.appendChild(elm);
            }
            function createPlasmidTrack(id, data) {
                var elm = document.createElement('plasmidtrack');
                elm.setAttribute('id', id);
                elm.setAttribute('radius', '133');
                elm.setAttribute('width', '3');
                elm.setAttribute('fill', '#d5d6db');

                addTrackLabel(elm, data);
                addTrackScales(elm);
                return elm;
            }
        }
       return {
           restrict: 'E',
           replace: true,
           scope: {
           },
           link: link
       };
    }
}());

The interesting function in the directive is the draw() function. In the draw function, I append the plasmid I generate using the given data to the directive’s element. Then, I compile the element content and attach to it the directive scope. That will do the magic and create the data binding for us.

Now that we know how to generate a plasmid representation, it is time to combine all the things that we learned so far and create a whole application based around it.

The Full Example – Biological Modeling with AngularJS

In the previous sections, you already saw some of the components that we will use to create the application. Now all we need to do is to join all the parts together. We will start with the main web page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Biology with Angular</title>
    <link href="styles/main.css" rel="stylesheet"/>
</head>
<body>
    
http://app/vendor/angular/angular.min.js http://app/vendor/angularplasmid/angularplasmid.min.js http://app/app.js http://app/biology/services/fastaParserService.js http://app/biology/services/restrictionSiteService.js http://app/common/services/fileReaderService.js http://app/common/directives/fileReaderDirective.js http://app/common/controllers/demoController.js http://app/common/directives/ngDemoDirective.js http://app/common/directives/ngPlasmidDirective.js </body> </html>

Nothing interesting happens here and the only thing to notice is the usage of ng-biology-demo directive. Here is the directive code:

(function () {
    'use strict';
    angular.
        module("biologyDemo").
        directive("ngBiologyDemo", ngBiologyDemo);
    ngBiologyDemo.$inject = [];
    function ngBiologyDemo() {
        return {
            restrict: 'E',
            templateUrl: 'app/common/templates/demoTemplate.html',
            controller: 'demoController',
            controllerAs: 'vm'
        };
    }
}());

Again, the directive itself isn’t interesting but it has a controller called demoController and a template that we load. The template code looks like this:

Select a Fasta File:
 
</div>

And the controller code looks like this:

(function () {
    'use strict';
    angular.
        module("biologyDemo").
        controller("demoController", demoController);
    demoController.$inject = ['$scope', 'fastaParserService'];
    function demoController($scope, parser) {
        var vm = this;
        function init() {
            vm.fastaFileContent = '';
            $scope.$watch(function () {
                return vm.fastaFileContent;
            }, function(changed) {
                if (!changed) {
                    return;
                }
                $scope.$broadcast('data:ready', parser.readSequence(vm.fastaFileContent));
            });
        }
        init();
    }
}());

In the controller, we watch the fastaFileContent property. The property will only change when the file is read by the file-reader directive change function. Once the content changes, we parse it and raise the data:ready event to notify the plasmid directive that its data is ready to use. Then, the plasmid directive will generate our plasmid.

Since the generated plasmid won’t have markers, I decided to add a service that will find the occurrences of some sub sequence and we will use it in the plasmid directive to add a marker. The following code snippet shows you how the service looks like:

(function () {
    'use strict';
    angular.
        module("biologyDemo").
        factory("bioService", bioService);
    function bioService() {
        function findMatches(sequence, pattern) {
            var result = [],
                indexFound = sequence.indexOf(pattern);
            while (indexFound > -1) {
                result.push({
                    start: indexFound,
                    end: indexFound + pattern.length
                });
                indexFound = sequence.indexOf(pattern, indexFound + 1);
            }
            return result;
        }
        return {
            findMatches: findMatches
        };
    }
}());

The service exposes a function called findMatches. The function is responsible to find matches in the sequence and return them as an array of start and end points. In the plasmid directive, you will add a new function called createTrackMarkers and for simplicity we will use a constant pattern with it:

function createTrackMarkers(elm, data) {
    var pattern = 'CTGCAG',
        matches = bio.findMatches(data.sequence, pattern),
        i = 0,
        marker,
        markerLabel;
    for (; i < matches.length; i++ ) {
        marker = document.createElement('trackmarker');
        marker.setAttribute('start', matches[i].start);
        marker.setAttribute('end', matches[i].end);
        marker.setAttribute('class', 'track-marker');
        marker.setAttribute('wadjust', '10');
        marker.setAttribute('vadjust', '-4');
        marker.setAttribute('markerstyle', 'stroke: yellow;fill: blue;');
        markerLabel = document.createElement('markerlabel');
        markerLabel.setAttribute('class', 'markerlabel-inside');
        markerLabel.setAttribute('text', pattern);
        markerLabel.setAttribute('type', 'path');
        markerLabel.setAttribute('vadjust', '15');
        marker.appendChild(markerLabel);
        elm.appendChild(marker);
    }
}

Note: In real world applications, the pattern will be dynamic and will probably arrive from a user or from another service.

In the createPlasmidTrack, add a call to the createTrackMarkers function before you return the plasmidtrack element and of course add an injection to the bioService in the plasmid directive. Now you can run the demo. If you read a FASTA file such as the file content showed in “FASTA Format Definitions” section, you will get the following output:

application-running

Figure 3: The Application Running

This article is available on YouTube: https://youtu.be/dPTcdkcrvCY.

Summary

This article is probably not your ordinary coding article. We explored a few concepts in AngularJS and JavaScript but also some biology concepts as well. My hope is that you will take this small application I built and try to understand how it is structured and implemented.

All in all, the creation of biology models in JavaScript is very challenging. My hope is that you got some hints of how to create interesting things that you can play with and maybe use it as a starting point to build your own biology oriented application.

Download the entire source code from GitHub at
bit.ly/dncm22-angularjs-plasmid

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Gil Fink is a web development expert, ASP.Net/IIS Micrsoft MVP and the founder of sparXys. He conducts lectures and workshops for individuals and enterprises who want to specialize in infrastructure and web development. He is also co-author of several Microsoft Official Courses (MOCs) and training kits, co-author of “Pro Single Page Application Development” book (Apress) and the founder of Front-End.IL Meetup. You can get more information about Gil in his website gilfink.net


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!