Saturday, December 27, 2014

Simple page to download file in PHP

Below is the code snippet in PHP which download a file from the server.

<php?
// http headers for zip downloads
$localfilename='myfileOnServer.zip';
$filename='myfilenameClient.zip';
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($localfilename));
ob_end_flush();
@readfile($localfilename);
?>

The above codes download the file named "myFileOnServer.zip" to the client with default save file "myfilenameClient.zip".

The above download may not work if the file "myFileOnServer.zip" is larger than the default settings in php.ini (PHP 4) or php5.ini (PHP 5). To solve this download the php.ini and php5.ini from your server and add the following lines:

Upload_max_filesize  = 1500M
Max_input_time  = 1000
Memory_limit = 640M
Max_execution_time = 1800
Post_max_size = 2000M


Monday, December 22, 2014

Bootstrap: Display a network graph within a tab page of a tab control

Recently I was working with bootstrap and needed to display a network graph within a bootstrap tab control. I decided to use vis.js which is easy to use for creating a graph network. Initially the problem looked simple as all that i need to do is to create tab control using bootstrap and create a graph network in one of its tab pages. Below is the original implementation:

<html>
<head>
<link href="./css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="./css/bootstrap.additional.css" rel="stylesheet" type="text/css" />
<link href="./css/vis.css" rel="stylesheet" type="text/css">
<script src="./jslib/jquery-1.8.3.min.js"></script>
<script src="./jslib/bootstrap.min.js"></script>
<script src="./jslib/vis.js"></script>
<script>
$(function(){
   $("#tabs").tab();
   updateNetwork("myNetwork");
});

function updateNetwork(networkElementId) {     
    // create some nodes
    var nodes = [
                {id:0,"labelHidden":"Myriel","group":1},
                {id:1,"labelHidden":"Napoleon","group":1},
                {id:2,"labelHidden":"Mlle.Baptistine","group":1},
                {id:3,"labelHidden":"Mme.Magloire","group":1},
                {id:4,"labelHidden":"CountessdeLo","group":1},
                {id:5,"labelHidden":"Geborand","group":1},
                {id:6,"labelHidden":"Champtercier","group":1},
                {id:7,"labelHidden":"Cravatte","group":1},
                {id:8,"labelHidden":"Count","group":1},
                {id:9,"labelHidden":"OldMan","group":1},
                {id:10,"labelHidden":"Labarre","group":2},
                {id:11,"labelHidden":"Valjean","group":2},
                {id:12,"labelHidden":"Marguerite","group":3},
                {id:13,"labelHidden":"Mme.deR","group":2},
                {id:14,"labelHidden":"Isabeau","group":2},
                {id:15,"labelHidden":"Gervais","group":2},
                {id:16,"labelHidden":"Tholomyes","group":3},
                {id:17,"labelHidden":"Listolier","group":3},
                {id:18,"labelHidden":"Fameuil","group":3},
                {id:19,"labelHidden":"Blacheville","group":3},
                {id:20,"labelHidden":"Favourite","group":3},
                {id:21,"labelHidden":"Dahlia","group":3},
                {id:22,"labelHidden":"Zephine","group":3},
                {id:23,"labelHidden":"Fantine","group":3},
                {id:24,"labelHidden":"Mme.Thenardier","group":4},
                {id:25,"labelHidden":"Thenardier","group":4},
                {id:26,"labelHidden":"Cosette","group":5},
                {id:27,"labelHidden":"Javert","group":4},
                {id:28,"labelHidden":"Fauchelevent","group":0},
                {id:29,"labelHidden":"Bamatabois","group":2},
                {id:30,"labelHidden":"Perpetue","group":3},
                {id:31,"labelHidden":"Simplice","group":2},
                {id:32,"labelHidden":"Scaufflaire","group":2},
                {id:33,"labelHidden":"Woman1","group":2},
                {id:34,"labelHidden":"Judge","group":2},
                {id:35,"labelHidden":"Champmathieu","group":2},
                {id:36,"labelHidden":"Brevet","group":2},
                {id:37,"labelHidden":"Chenildieu","group":2},
                {id:38,"labelHidden":"Cochepaille","group":2},
                {id:39,"labelHidden":"Pontmercy","group":4},
                {id:40,"labelHidden":"Boulatruelle","group":6},
                {id:41,"labelHidden":"Eponine","group":4},
                {id:42,"labelHidden":"Anzelma","group":4},
                {id:43,"labelHidden":"Woman2","group":5},
                {id:44,"labelHidden":"MotherInnocent","group":0},
                {id:45,"labelHidden":"Gribier","group":0},
                {id:46,"labelHidden":"Jondrette","group":7},
                {id:47,"labelHidden":"Mme.Burgon","group":7},
                {id:48,"labelHidden":"Gavroche","group":8},
                {id:49,"labelHidden":"Gillenormand","group":5},
                {id:50,"labelHidden":"Magnon","group":5},
                {id:51,"labelHidden":"Mlle.Gillenormand","group":5},
                {id:52,"labelHidden":"Mme.Pontmercy","group":5},
                {id:53,"labelHidden":"Mlle.Vaubois","group":5},
                {id:54,"labelHidden":"Lt.Gillenormand","group":5},
                {id:55,"labelHidden":"Marius","group":8},
                {id:56,"labelHidden":"BaronessT","group":5},
                {id:57,"labelHidden":"Mabeuf","group":8},
                {id:58,"labelHidden":"Enjolras","group":8},
                {id:59,"labelHidden":"Combeferre","group":8},
                {id:60,"labelHidden":"Prouvaire","group":8},
                {id:61,"labelHidden":"Feuilly","group":8},
                {id:62,"labelHidden":"Courfeyrac","group":8},
                {id:63,"labelHidden":"Bahorel","group":8},
                {id:64,"labelHidden":"Bossuet","group":8},
                {id:65,"labelHidden":"Joly","group":8},
                {id:66,"labelHidden":"Grantaire","group":8},
                {id:67,"labelHidden":"MotherPlutarch","group":9},
                {id:68,"labelHidden":"Gueulemer","group":4},
                {id:69,"labelHidden":"Babet","group":4},
                {id:70,"labelHidden":"Claquesous","group":4},
                {id:71,"labelHidden":"Montparnasse","group":4},
                {id:72,"labelHidden":"Toussaint","group":5},
                {id:73,"labelHidden":"Child1","group":10},
                {id:74,"labelHidden":"Child2","group":10},
                {id:75,"labelHidden":"Brujon","group":4},
                {id:76,"labelHidden":"Mme.Hucheloup","group":8}
            ];

    // create some edges
    var edges = [
        {"from":1,"to":0},
        {"from":2,"to":0},
        {"from":3,"to":0},
        {"from":3,"to":2},
        {"from":4,"to":0},
        {"from":5,"to":0},
        {"from":6,"to":0},
        {"from":7,"to":0},
        {"from":8,"to":0},
        {"from":9,"to":0},
        {"from":11,"to":10},
        {"from":11,"to":3},
        {"from":11,"to":2},
        {"from":11,"to":0},
        {"from":12,"to":11},
        {"from":13,"to":11},
        {"from":14,"to":11},
        {"from":15,"to":11},
        {"from":17,"to":16},
        {"from":18,"to":16},
        {"from":18,"to":17},
        {"from":19,"to":16},
        {"from":19,"to":17},
        {"from":19,"to":18},
        {"from":20,"to":16},
        {"from":20,"to":17},
        {"from":20,"to":18},
        {"from":20,"to":19},
        {"from":21,"to":16},
        {"from":21,"to":17},
        {"from":21,"to":18},
        {"from":21,"to":19},
        {"from":21,"to":20},
        {"from":22,"to":16},
        {"from":22,"to":17},
        {"from":22,"to":18},
        {"from":22,"to":19},
        {"from":22,"to":20},
        {"from":22,"to":21},
        {"from":23,"to":16},
        {"from":23,"to":17},
        {"from":23,"to":18},
        {"from":23,"to":19},
        {"from":23,"to":20},
        {"from":23,"to":21},
        {"from":23,"to":22},
        {"from":23,"to":12},
        {"from":23,"to":11},
        {"from":24,"to":23},
        {"from":24,"to":11},
        {"from":25,"to":24},
        {"from":25,"to":23},
        {"from":25,"to":11},
        {"from":26,"to":24},
        {"from":26,"to":11},
        {"from":26,"to":16},
        {"from":26,"to":25},
        {"from":27,"to":11},
        {"from":27,"to":23},
        {"from":27,"to":25},
        {"from":27,"to":24},
        {"from":27,"to":26},
        {"from":28,"to":11},
        {"from":28,"to":27},
        {"from":29,"to":23},
        {"from":29,"to":27},
        {"from":29,"to":11},
        {"from":30,"to":23},
        {"from":31,"to":30},
        {"from":31,"to":11},
        {"from":31,"to":23},
        {"from":31,"to":27},
        {"from":32,"to":11},
        {"from":33,"to":11},
        {"from":33,"to":27},
        {"from":34,"to":11},
        {"from":34,"to":29},
        {"from":35,"to":11},
        {"from":35,"to":34},
        {"from":35,"to":29},
        {"from":36,"to":34},
        {"from":36,"to":35},
        {"from":36,"to":11},
        {"from":36,"to":29},
        {"from":37,"to":34},
        {"from":37,"to":35},
        {"from":37,"to":36},
        {"from":37,"to":11},
        {"from":37,"to":29},
        {"from":38,"to":34},
        {"from":38,"to":35},
        {"from":38,"to":36},
        {"from":38,"to":37},
        {"from":38,"to":11},
        {"from":38,"to":29},
        {"from":39,"to":25},
        {"from":40,"to":25},
        {"from":41,"to":24},
        {"from":41,"to":25},
        {"from":42,"to":41},
        {"from":42,"to":25},
        {"from":42,"to":24},
        {"from":43,"to":11},
        {"from":43,"to":26},
        {"from":43,"to":27},
        {"from":44,"to":28},
        {"from":44,"to":11},
        {"from":45,"to":28},
        {"from":47,"to":46},
        {"from":48,"to":47},
        {"from":48,"to":25},
        {"from":48,"to":27},
        {"from":48,"to":11},
        {"from":49,"to":26},
        {"from":49,"to":11},
        {"from":50,"to":49},
        {"from":50,"to":24},
        {"from":51,"to":49},
        {"from":51,"to":26},
        {"from":51,"to":11},
        {"from":52,"to":51},
        {"from":52,"to":39},
        {"from":53,"to":51},
        {"from":54,"to":51},
        {"from":54,"to":49},
        {"from":54,"to":26},
        {"from":55,"to":51},
        {"from":55,"to":49},
        {"from":55,"to":39},
        {"from":55,"to":54},
        {"from":55,"to":26},
        {"from":55,"to":11},
        {"from":55,"to":16},
        {"from":55,"to":25},
        {"from":55,"to":41},
        {"from":55,"to":48},
        {"from":56,"to":49},
        {"from":56,"to":55},
        {"from":57,"to":55},
        {"from":57,"to":41},
        {"from":57,"to":48},
        {"from":58,"to":55},
        {"from":58,"to":48},
        {"from":58,"to":27},
        {"from":58,"to":57},
        {"from":58,"to":11},
        {"from":59,"to":58},
        {"from":59,"to":55},
        {"from":59,"to":48},
        {"from":59,"to":57},
        {"from":60,"to":48},
        {"from":60,"to":58},
        {"from":60,"to":59},
        {"from":61,"to":48},
        {"from":61,"to":58},
        {"from":61,"to":60},
        {"from":61,"to":59},
        {"from":61,"to":57},
        {"from":61,"to":55},
        {"from":62,"to":55},
        {"from":62,"to":58},
        {"from":62,"to":59},
        {"from":62,"to":48},
        {"from":62,"to":57},
        {"from":62,"to":41},
        {"from":62,"to":61},
        {"from":62,"to":60},
        {"from":63,"to":59},
        {"from":63,"to":48},
        {"from":63,"to":62},
        {"from":63,"to":57},
        {"from":63,"to":58},
        {"from":63,"to":61},
        {"from":63,"to":60},
        {"from":63,"to":55},
        {"from":64,"to":55},
        {"from":64,"to":62},
        {"from":64,"to":48},
        {"from":64,"to":63},
        {"from":64,"to":58},
        {"from":64,"to":61},
        {"from":64,"to":60},
        {"from":64,"to":59},
        {"from":64,"to":57},
        {"from":64,"to":11},
        {"from":65,"to":63},
        {"from":65,"to":64},
        {"from":65,"to":48},
        {"from":65,"to":62},
        {"from":65,"to":58},
        {"from":65,"to":61},
        {"from":65,"to":60},
        {"from":65,"to":59},
        {"from":65,"to":57},
        {"from":65,"to":55},
        {"from":66,"to":64},
        {"from":66,"to":58},
        {"from":66,"to":59},
        {"from":66,"to":62},
        {"from":66,"to":65},
        {"from":66,"to":48},
        {"from":66,"to":63},
        {"from":66,"to":61},
        {"from":66,"to":60},
        {"from":67,"to":57},
        {"from":68,"to":25},
        {"from":68,"to":11},
        {"from":68,"to":24},
        {"from":68,"to":27},
        {"from":68,"to":48},
        {"from":68,"to":41},
        {"from":69,"to":25},
        {"from":69,"to":68},
        {"from":69,"to":11},
        {"from":69,"to":24},
        {"from":69,"to":27},
        {"from":69,"to":48},
        {"from":69,"to":41},
        {"from":70,"to":25},
        {"from":70,"to":69},
        {"from":70,"to":68},
        {"from":70,"to":11},
        {"from":70,"to":24},
        {"from":70,"to":27},
        {"from":70,"to":41},
        {"from":70,"to":58},
        {"from":71,"to":27},
        {"from":71,"to":69},
        {"from":71,"to":68},
        {"from":71,"to":70},
        {"from":71,"to":11},
        {"from":71,"to":48},
        {"from":71,"to":41},
        {"from":71,"to":25},
        {"from":72,"to":26},
        {"from":72,"to":27},
        {"from":72,"to":11},
        {"from":73,"to":48},
        {"from":74,"to":48},
        {"from":74,"to":73},
        {"from":75,"to":69},
        {"from":75,"to":68},
        {"from":75,"to":25},
        {"from":75,"to":48},
        {"from":75,"to":41},
        {"from":75,"to":70},
        {"from":75,"to":71},
        {"from":76,"to":64},
        {"from":76,"to":65},
        {"from":76,"to":66},
        {"from":76,"to":63},
        {"from":76,"to":62},
        {"from":76,"to":48},
        {"from":76,"to":58}
    ];

    // create a network
    var container = document.getElementById(networkElementId);
    var data = {
        nodes: nodes,
        edges: edges
    };
    var options = {nodes: {shape:'circle'},stabilize: false};
    var network = new vis.Network(container, data, options);
}
</script>
</head>
<body>

<div role="tabpanel">

  <!-- Nav tabs -->
  <ul id="tabs" class="nav nav-tabs" role="tablist">
    <li role="presentation" class="active"><a href="#tab1" aria-controls="tab1" role="tab" data-toggle="tab">Tab1</a></li>
    <li role="presentation"><a href="#tab2" aria-controls="tab2" role="tab" data-toggle="tab">Tab2</a></li>
    <li role="presentation"><a href="#tab3" aria-controls="tab3" role="tab" data-toggle="tab">Tab3</a></li>
  </ul>

<div class="tab-content">
   <div role="tabpanel" class="tab-pane active" id="tab1">
      This is tab 1 (Active by default)
   </div>

   <div role="tabpanel" class="tab-pane active" id="tab2">
       <div id="myNetwork" style = "height:500px; margin-top:10px;"></div>
   </div>
  
   <div role="tabpanel" class="tab-pane active" id="tab3">
     This is tab 3
   </div>
</div>

</div>

</body>
</html>

Well the above code does not work as the network is not displayed in the tab page. The cause seems to be that the vis.js requires a visible <div> to draw the network. So finally i settled for a solution in which the network is created in the "myNetwork" <div> element when the tab page "tab2" is shown. Below is the working implementation.
<html>
<head>
<link href="./css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="./css/bootstrap.additional.css" rel="stylesheet" type="text/css" />
<link href="./css/vis.css" rel="stylesheet" type="text/css">
<script src="./jslib/jquery-1.8.3.min.js"></script>
<script src="./jslib/bootstrap.min.js"></script>
<script src="./jslib/vis.js"></script>
<script>
$(function(){
   $("#tabs").tab();
    $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
    var target = $(e.target).attr("href");
    if(target=="#tab2")
    {
     updateNetwork("myNetwork");
    }
   });
   
});

function updateNetwork(networkElementId) {     
    // create some nodes
    var nodes = [
                {id:0,"labelHidden":"Myriel","group":1},
                {id:1,"labelHidden":"Napoleon","group":1},
                {id:2,"labelHidden":"Mlle.Baptistine","group":1},
                {id:3,"labelHidden":"Mme.Magloire","group":1},
                {id:4,"labelHidden":"CountessdeLo","group":1},
                {id:5,"labelHidden":"Geborand","group":1},
                {id:6,"labelHidden":"Champtercier","group":1},
                {id:7,"labelHidden":"Cravatte","group":1},
                {id:8,"labelHidden":"Count","group":1},
                {id:9,"labelHidden":"OldMan","group":1},
                {id:10,"labelHidden":"Labarre","group":2},
                {id:11,"labelHidden":"Valjean","group":2},
                {id:12,"labelHidden":"Marguerite","group":3},
                {id:13,"labelHidden":"Mme.deR","group":2},
                {id:14,"labelHidden":"Isabeau","group":2},
                {id:15,"labelHidden":"Gervais","group":2},
                {id:16,"labelHidden":"Tholomyes","group":3},
                {id:17,"labelHidden":"Listolier","group":3},
                {id:18,"labelHidden":"Fameuil","group":3},
                {id:19,"labelHidden":"Blacheville","group":3},
                {id:20,"labelHidden":"Favourite","group":3},
                {id:21,"labelHidden":"Dahlia","group":3},
                {id:22,"labelHidden":"Zephine","group":3},
                {id:23,"labelHidden":"Fantine","group":3},
                {id:24,"labelHidden":"Mme.Thenardier","group":4},
                {id:25,"labelHidden":"Thenardier","group":4},
                {id:26,"labelHidden":"Cosette","group":5},
                {id:27,"labelHidden":"Javert","group":4},
                {id:28,"labelHidden":"Fauchelevent","group":0},
                {id:29,"labelHidden":"Bamatabois","group":2},
                {id:30,"labelHidden":"Perpetue","group":3},
                {id:31,"labelHidden":"Simplice","group":2},
                {id:32,"labelHidden":"Scaufflaire","group":2},
                {id:33,"labelHidden":"Woman1","group":2},
                {id:34,"labelHidden":"Judge","group":2},
                {id:35,"labelHidden":"Champmathieu","group":2},
                {id:36,"labelHidden":"Brevet","group":2},
                {id:37,"labelHidden":"Chenildieu","group":2},
                {id:38,"labelHidden":"Cochepaille","group":2},
                {id:39,"labelHidden":"Pontmercy","group":4},
                {id:40,"labelHidden":"Boulatruelle","group":6},
                {id:41,"labelHidden":"Eponine","group":4},
                {id:42,"labelHidden":"Anzelma","group":4},
                {id:43,"labelHidden":"Woman2","group":5},
                {id:44,"labelHidden":"MotherInnocent","group":0},
                {id:45,"labelHidden":"Gribier","group":0},
                {id:46,"labelHidden":"Jondrette","group":7},
                {id:47,"labelHidden":"Mme.Burgon","group":7},
                {id:48,"labelHidden":"Gavroche","group":8},
                {id:49,"labelHidden":"Gillenormand","group":5},
                {id:50,"labelHidden":"Magnon","group":5},
                {id:51,"labelHidden":"Mlle.Gillenormand","group":5},
                {id:52,"labelHidden":"Mme.Pontmercy","group":5},
                {id:53,"labelHidden":"Mlle.Vaubois","group":5},
                {id:54,"labelHidden":"Lt.Gillenormand","group":5},
                {id:55,"labelHidden":"Marius","group":8},
                {id:56,"labelHidden":"BaronessT","group":5},
                {id:57,"labelHidden":"Mabeuf","group":8},
                {id:58,"labelHidden":"Enjolras","group":8},
                {id:59,"labelHidden":"Combeferre","group":8},
                {id:60,"labelHidden":"Prouvaire","group":8},
                {id:61,"labelHidden":"Feuilly","group":8},
                {id:62,"labelHidden":"Courfeyrac","group":8},
                {id:63,"labelHidden":"Bahorel","group":8},
                {id:64,"labelHidden":"Bossuet","group":8},
                {id:65,"labelHidden":"Joly","group":8},
                {id:66,"labelHidden":"Grantaire","group":8},
                {id:67,"labelHidden":"MotherPlutarch","group":9},
                {id:68,"labelHidden":"Gueulemer","group":4},
                {id:69,"labelHidden":"Babet","group":4},
                {id:70,"labelHidden":"Claquesous","group":4},
                {id:71,"labelHidden":"Montparnasse","group":4},
                {id:72,"labelHidden":"Toussaint","group":5},
                {id:73,"labelHidden":"Child1","group":10},
                {id:74,"labelHidden":"Child2","group":10},
                {id:75,"labelHidden":"Brujon","group":4},
                {id:76,"labelHidden":"Mme.Hucheloup","group":8}
            ];

    // create some edges
    var edges = [
        {"from":1,"to":0},
        {"from":2,"to":0},
        {"from":3,"to":0},
        {"from":3,"to":2},
        {"from":4,"to":0},
        {"from":5,"to":0},
        {"from":6,"to":0},
        {"from":7,"to":0},
        {"from":8,"to":0},
        {"from":9,"to":0},
        {"from":11,"to":10},
        {"from":11,"to":3},
        {"from":11,"to":2},
        {"from":11,"to":0},
        {"from":12,"to":11},
        {"from":13,"to":11},
        {"from":14,"to":11},
        {"from":15,"to":11},
        {"from":17,"to":16},
        {"from":18,"to":16},
        {"from":18,"to":17},
        {"from":19,"to":16},
        {"from":19,"to":17},
        {"from":19,"to":18},
        {"from":20,"to":16},
        {"from":20,"to":17},
        {"from":20,"to":18},
        {"from":20,"to":19},
        {"from":21,"to":16},
        {"from":21,"to":17},
        {"from":21,"to":18},
        {"from":21,"to":19},
        {"from":21,"to":20},
        {"from":22,"to":16},
        {"from":22,"to":17},
        {"from":22,"to":18},
        {"from":22,"to":19},
        {"from":22,"to":20},
        {"from":22,"to":21},
        {"from":23,"to":16},
        {"from":23,"to":17},
        {"from":23,"to":18},
        {"from":23,"to":19},
        {"from":23,"to":20},
        {"from":23,"to":21},
        {"from":23,"to":22},
        {"from":23,"to":12},
        {"from":23,"to":11},
        {"from":24,"to":23},
        {"from":24,"to":11},
        {"from":25,"to":24},
        {"from":25,"to":23},
        {"from":25,"to":11},
        {"from":26,"to":24},
        {"from":26,"to":11},
        {"from":26,"to":16},
        {"from":26,"to":25},
        {"from":27,"to":11},
        {"from":27,"to":23},
        {"from":27,"to":25},
        {"from":27,"to":24},
        {"from":27,"to":26},
        {"from":28,"to":11},
        {"from":28,"to":27},
        {"from":29,"to":23},
        {"from":29,"to":27},
        {"from":29,"to":11},
        {"from":30,"to":23},
        {"from":31,"to":30},
        {"from":31,"to":11},
        {"from":31,"to":23},
        {"from":31,"to":27},
        {"from":32,"to":11},
        {"from":33,"to":11},
        {"from":33,"to":27},
        {"from":34,"to":11},
        {"from":34,"to":29},
        {"from":35,"to":11},
        {"from":35,"to":34},
        {"from":35,"to":29},
        {"from":36,"to":34},
        {"from":36,"to":35},
        {"from":36,"to":11},
        {"from":36,"to":29},
        {"from":37,"to":34},
        {"from":37,"to":35},
        {"from":37,"to":36},
        {"from":37,"to":11},
        {"from":37,"to":29},
        {"from":38,"to":34},
        {"from":38,"to":35},
        {"from":38,"to":36},
        {"from":38,"to":37},
        {"from":38,"to":11},
        {"from":38,"to":29},
        {"from":39,"to":25},
        {"from":40,"to":25},
        {"from":41,"to":24},
        {"from":41,"to":25},
        {"from":42,"to":41},
        {"from":42,"to":25},
        {"from":42,"to":24},
        {"from":43,"to":11},
        {"from":43,"to":26},
        {"from":43,"to":27},
        {"from":44,"to":28},
        {"from":44,"to":11},
        {"from":45,"to":28},
        {"from":47,"to":46},
        {"from":48,"to":47},
        {"from":48,"to":25},
        {"from":48,"to":27},
        {"from":48,"to":11},
        {"from":49,"to":26},
        {"from":49,"to":11},
        {"from":50,"to":49},
        {"from":50,"to":24},
        {"from":51,"to":49},
        {"from":51,"to":26},
        {"from":51,"to":11},
        {"from":52,"to":51},
        {"from":52,"to":39},
        {"from":53,"to":51},
        {"from":54,"to":51},
        {"from":54,"to":49},
        {"from":54,"to":26},
        {"from":55,"to":51},
        {"from":55,"to":49},
        {"from":55,"to":39},
        {"from":55,"to":54},
        {"from":55,"to":26},
        {"from":55,"to":11},
        {"from":55,"to":16},
        {"from":55,"to":25},
        {"from":55,"to":41},
        {"from":55,"to":48},
        {"from":56,"to":49},
        {"from":56,"to":55},
        {"from":57,"to":55},
        {"from":57,"to":41},
        {"from":57,"to":48},
        {"from":58,"to":55},
        {"from":58,"to":48},
        {"from":58,"to":27},
        {"from":58,"to":57},
        {"from":58,"to":11},
        {"from":59,"to":58},
        {"from":59,"to":55},
        {"from":59,"to":48},
        {"from":59,"to":57},
        {"from":60,"to":48},
        {"from":60,"to":58},
        {"from":60,"to":59},
        {"from":61,"to":48},
        {"from":61,"to":58},
        {"from":61,"to":60},
        {"from":61,"to":59},
        {"from":61,"to":57},
        {"from":61,"to":55},
        {"from":62,"to":55},
        {"from":62,"to":58},
        {"from":62,"to":59},
        {"from":62,"to":48},
        {"from":62,"to":57},
        {"from":62,"to":41},
        {"from":62,"to":61},
        {"from":62,"to":60},
        {"from":63,"to":59},
        {"from":63,"to":48},
        {"from":63,"to":62},
        {"from":63,"to":57},
        {"from":63,"to":58},
        {"from":63,"to":61},
        {"from":63,"to":60},
        {"from":63,"to":55},
        {"from":64,"to":55},
        {"from":64,"to":62},
        {"from":64,"to":48},
        {"from":64,"to":63},
        {"from":64,"to":58},
        {"from":64,"to":61},
        {"from":64,"to":60},
        {"from":64,"to":59},
        {"from":64,"to":57},
        {"from":64,"to":11},
        {"from":65,"to":63},
        {"from":65,"to":64},
        {"from":65,"to":48},
        {"from":65,"to":62},
        {"from":65,"to":58},
        {"from":65,"to":61},
        {"from":65,"to":60},
        {"from":65,"to":59},
        {"from":65,"to":57},
        {"from":65,"to":55},
        {"from":66,"to":64},
        {"from":66,"to":58},
        {"from":66,"to":59},
        {"from":66,"to":62},
        {"from":66,"to":65},
        {"from":66,"to":48},
        {"from":66,"to":63},
        {"from":66,"to":61},
        {"from":66,"to":60},
        {"from":67,"to":57},
        {"from":68,"to":25},
        {"from":68,"to":11},
        {"from":68,"to":24},
        {"from":68,"to":27},
        {"from":68,"to":48},
        {"from":68,"to":41},
        {"from":69,"to":25},
        {"from":69,"to":68},
        {"from":69,"to":11},
        {"from":69,"to":24},
        {"from":69,"to":27},
        {"from":69,"to":48},
        {"from":69,"to":41},
        {"from":70,"to":25},
        {"from":70,"to":69},
        {"from":70,"to":68},
        {"from":70,"to":11},
        {"from":70,"to":24},
        {"from":70,"to":27},
        {"from":70,"to":41},
        {"from":70,"to":58},
        {"from":71,"to":27},
        {"from":71,"to":69},
        {"from":71,"to":68},
        {"from":71,"to":70},
        {"from":71,"to":11},
        {"from":71,"to":48},
        {"from":71,"to":41},
        {"from":71,"to":25},
        {"from":72,"to":26},
        {"from":72,"to":27},
        {"from":72,"to":11},
        {"from":73,"to":48},
        {"from":74,"to":48},
        {"from":74,"to":73},
        {"from":75,"to":69},
        {"from":75,"to":68},
        {"from":75,"to":25},
        {"from":75,"to":48},
        {"from":75,"to":41},
        {"from":75,"to":70},
        {"from":75,"to":71},
        {"from":76,"to":64},
        {"from":76,"to":65},
        {"from":76,"to":66},
        {"from":76,"to":63},
        {"from":76,"to":62},
        {"from":76,"to":48},
        {"from":76,"to":58}
    ];

    // create a network
    var container = document.getElementById(networkElementId);
    var data = {
        nodes: nodes,
        edges: edges
    };
    var options = {nodes: {shape:'circle'},stabilize: false};
    var network = new vis.Network(container, data, options);
}
</script>
</head>
<body>

<div role="tabpanel">

  <!-- Nav tabs -->
  <ul id="tabs" class="nav nav-tabs" role="tablist">
    <li role="presentation" class="active"><a href="#tab1" aria-controls="tab1" role="tab" data-toggle="tab">Tab1</a></li>
    <li role="presentation"><a href="#tab2" aria-controls="tab2" role="tab" data-toggle="tab">Tab2</a></li>
    <li role="presentation"><a href="#tab3" aria-controls="tab3" role="tab" data-toggle="tab">Tab3</a></li>
  </ul>

<div class="tab-content">
   <div role="tabpanel" class="tab-pane active" id="tab1">
      This is tab 1 (Active by default)
   </div>

   <div role="tabpanel" class="tab-pane active" id="tab2">
       <div id="myNetwork" style = "height:500px; margin-top:10px;"></div>
   </div>
  
   <div role="tabpanel" class="tab-pane active" id="tab3">
     This is tab 3
   </div>
</div>

</div>

</body>
</html>



Bootstrap: Display a map in a tab page within a tab control

Recently i was working with bootstrap and i needed to display a map within a tab page of a tab control. The task look simple enough, below is the html and javascript codes:

<html>
<head>
<link href="./css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="./css/bootstrap.additional.css" rel="stylesheet" type="text/css" />
<script src="./jslib/jquery-1.8.3.min.js"></script>
<script src="./jslib/bootstrap.min.js"></script>
<script src="./jslib/OpenLayers.js"></script>
<script>
$(function(){
   $("#tabs").tab();
   var lat = 1.3000;
   var lon = 103.8000;
   updateMap("myMap", lon, lat);
});

function updateMap(mapElementId, lon, lat) {     
    var layer = new OpenLayers.Layer.OSM();

    map = new OpenLayers.Map(mapElementId);
    map.addLayer(layer);
    
    var epsg4326 =  new OpenLayers.Projection("EPSG:4326");
    var projectTo = map.getProjectionObject(); 
    var lonLat = new OpenLayers.LonLat(lon, lat).transform(epsg4326, projectTo);
    var zoom = 10;
    var popup = null;
    
    map.setCenter (lonLat, zoom);
}
</script>
</head>
<body>

<div role="tabpanel">

  <!-- Nav tabs -->
  <ul id="tabs" class="nav nav-tabs" role="tablist">
    <li role="presentation" class="active"><a href="#tab1" aria-controls="tab1" role="tab" data-toggle="tab">Tab1</a></li>
    <li role="presentation"><a href="#tab2" aria-controls="tab2" role="tab" data-toggle="tab">Tab2</a></li>
    <li role="presentation"><a href="#tab3" aria-controls="tab3" role="tab" data-toggle="tab">Tab3</a></li>
  </ul>

<div class="tab-content">
   <div role="tabpanel" class="tab-pane active" id="tab1">
      This is tab 1 (Active by default)
   </div>

   <div role="tabpanel" class="tab-pane active" id="tab2">
      <div id="myMap" style="width:440px; height:300px; vertical-align: middle; margin-top: 10px; margin-bottom: 10px">
   </div>
  
   <div role="tabpanel" class="tab-pane active" id="tab3">
     This is tab 3
   </div>
</div>

</div>

</body>
</html>

I expected it to work, but somehow the map fails to show up. After some digging, it seems that the map fails to realize it height in the tab page if the tab page is not initially active. The solution is to add  style="height:100%" to the  <div class="tab-content"> so that it becomes like  <div class="tab-content" style="height:100%">.

Saturday, December 20, 2014

Maven: missing artifact org.eclipse.equinox.app.jar

Sometimes if one sees the errors such as "missing artifact org.eclipse.equinox.app.jar..." in Eclipse or Maven, it is probably they did not set the repository right in their pom file. Below is the repositories which can be added to the pom file that might resolve the error:

<repositories>
<repository>
<id>clojars</id>
<url>http://clojars.org/repo</url>
</repository>
<repository>
<id>mvnCentral</id>
<url>http://repo1.maven.org/maven2</url>
</repository>
</repositories>

Friday, December 19, 2014

Javascript: Convert date string to timestamp

Below is the codesnippet to convert date string to a timestamp:

var dateString = $('#dateText').val(); // in format yyyy-MM-dd HH:mm:ss
var timeStamp = convertDateStringToTimeStamp(dateString);

function convertDateStringToTimeStamp(dateString)
{
 
 var date = parseDate(dateString);
 return date.getTime();
}
 
function parseDate(dateString)
{
    var dateParts = dateString.split(' ');
    var timeParts = dateParts[1].split(':');
    
    var dateParts = dateParts[0].split('-');

    var date = new Date(dateParts[0], parseInt(dateParts[1], 10) - 1, dateParts[2], timeParts[0], timeParts[1], timeParts[2]);
    
    return date;
}

Thursday, December 18, 2014

C3: Create a Pie Chart with specific width and height

Below is the code snippet to create a pie chart with specific with and height, using C3 data visualization library:

<div id="pieChart"></div>

<script>
var chart = c3.generate({
bindto : '#pieChart',
size: {
width: 300,
height: 300
},
   data: {
       columns: [
['data1' : 45],
['data2' : 56],
['data3' : 20]
],
       type : 'pie'
   }
});
</script>

Wednesday, December 17, 2014

C3: Formatting for yyyy-MM-dd HH:mm:ss for the timeseries

This simple step in C3 cracked my head a bit, therefore i like to write it down for future reference. Basically, I have a piece of json data containing the following two fields:

  • date
  • rate
I like the x axis of a C3 time series chart to display the date values in the json, the format of the date values is as follows:

yyyy-MM-dd HH:mm:ss

My original javascript looks like the following:

<link href="./c3.min.css" rel="stylesheet" type="text/css">
<script src="./d3.min.js"></script>
<script src="./c3.min.js"></script>
<script src="./d3.tip.v0.6.3.js"></script>
<script src="./jquery-1.8.3.min.js"></script>

<script type="text/javascript">
 $(function(){
$.ajax({
            url        : "jsonData.php",
            type       : "GET",
            data       : post_data,
            dataType : "json",
            success    : function(returned_data)
            {
                console.log(returned_data);
               
                var column_date = returned_data.date;
                var column_val = returned_data.sample;
               
                console.log(column_date);
                console.log(column_val);
               
                column_date.unshift('date');
                column_val.unshift('sample');
               
                var chart = c3.generate({
                    data: {
                        x : 'date',
                        columns : [
                            column_date,
                            column_val
                        ]
                    },
                    bindto: '#timeSeriesChart',
                    axis: {
                        x: {
                            type: 'timeseries',
                        }
                    }                });
            }
        });

});
</script>

After trying out various configuration that do not work, i finally stumbled on one solution that works, which is to add a "xFormat : '%Y-%m-%d %H:%M:%S'", that is as follows:

$(function(){
$.ajax({
            url        : "jsonData.php",
            type       : "GET",
            data       : post_data,
            dataType : "json",
            success    : function(returned_data)
            {
                console.log(returned_data);
               
                var column_date = returned_data.date;
                var column_val = returned_data.sample;
               
                console.log(column_date);
                console.log(column_val);
               
                column_date.unshift('date');
                column_val.unshift('sample');
               
                var chart = c3.generate({
                    data: {
                        x : 'date',
                        xFormat : '%Y-%m-%d %H:%M:%S',
                        columns : [
                            column_date,
                            column_val
                        ]
                    },
                    bindto: '#timeSeriesChart',
                    axis: {
                        x: {
                            type: 'timeseries',
                        }
                    }                });
            }
        });

}); 

Another thing to note is that the settings of x axis tick label can be done by adding the following to the "axis" element (if the time value is too long, you can add a "rotate: 45" to the "tick" below):

 tick: {
        format: '%H:%M:%S'
 }


for example:

 
$(function(){
$.ajax({
            url        : "jsonData.php",
            type       : "GET",
            data       : post_data,
            dataType : "json",
            success    : function(returned_data)
            {
                console.log(returned_data);
               
                var column_date = returned_data.date;
                var column_val = returned_data.sample;
               
                console.log(column_date);
                console.log(column_val);
               
                column_date.unshift('date');
                column_val.unshift('sample');
               
                var chart = c3.generate({
                    data: {
                        x : 'date',
                        xFormat : '%Y-%m-%d %H:%M:%S',
                        columns : [
                            column_date,
                            column_val
                        ]
                    },
                    bindto: '#timeSeriesChart',
                    axis: {
                        x: {
                            type: 'timeseries',


 tick: {
        format: '%H:%M:%S'
 }


                        }
                    }                });
            }
        });

}); 

Trident: What is the batch size for KafkaSpout?

Today i encountered a problem in which i need to know what is the batch size for a TransactionalTridentKafkaSpout.

The default output field by KafkaSpout is "str", which can be of variable length. By some simple thought, it is reasonable that kafka spout will set the batch size dynamically based on the actual sizes of the "str" messages it retrieves in a batch. In other words, i suspect that the batch size may be determined by the following pseudo codes:

batchSize = 0;

totalBytesConsumed=0;


while(totalBytesConsumed < maxBytesConsumed)
{
    totalBytesConsumed+= getMessageSize("str");
    batchSize ++;

}

After quickly read through trident-kafka's source codes on TransactionalTridentKafkaSpout and trace down to the following classes and their methods:

TransactionalTridentKafkaSpout.getEmitter(...)
TridentKafkaEmitter.fetchMessages(...)
KafkaUtils.fetchMessages(...)

by the builder.addFetch(...) line in "KafkaUtils.fetchMessages(...)", it looks like the batch size is determine by a variable defined in KafkaConfig.fetchSizeBytes, which is defaulted to 1024 * 1024.

I also noticed another variable KafkaConfig.bufferSizeBytes, which is used by the SimpleConsumer class, this is also defaulted to 1024 * 1024.

Therefore i suspect that the batch size of the kafka spout depends on both Math.Min(KafkaConfig.bufferSizeBytes, kafkaConfig.fetchSizeBytes).

After some googling, I noticed for  handling huge data in Kafka, the following settings have been used:

kafkaConfig.bufferSizeBytes = 1024 * 1024 * 4;
kafkaConfig.fetchSizeBytes = 1024 * 1024 * 4;

Tuesday, December 16, 2014

Tomcat: modules/classes auto-reload and published in Tomcat Server when changes made to the web project

When using the spring tool suite, you will have the ability to auto-reload modules and auto-publish the updated project in tomcat server whenever you made changes to the codes in your web project. To enable this feature, double-click your Tomcat server instance in the "Servers" tab in the IDE, and do the following steps:

1. Check the option "Modules auto-reload by default" under the "Server Options" section (or check the "Enable Java-agent reload behavior" option under the "Application reload behavior section", depending on your IDE)

2. Select the radio option "Automatically publish when resources change" under the "Publish" section

Now restart your Tomcat server, and whenever you made changes to your code, your changes will be immediately reflected on the changes in the web pages.

Note that if you do not have the options as stated in step 1 because of the IDE you are using does not package with this feature. The alternative step will be to

1.1 Download the spring-loaded from

https://github.com/spring-projects/spring-loaded

 and store the downloaded jar file in your file system somewhere (said, /home/chen/Documents/Works/spring-reloaded/springloaded-1.2.1.RELEASE.jar)

1.2 Double-click the Tomcat server instance in your IDE's "Servers" tab and click the link "Open Launch Configuration" in the "Server Configuration" page.

1.3 In the "Edit Configuration" dialog popup, select the "Arguments" tab, and add the following to the end of the "VM arguments" field text:

-javaagent:/home/chen/Documents/Works/spring-reloaded/springloaded-1.2.1.RELEASE.jar -noverify

After the above step, following the step 2 as mentioned above and restart the server after that.


Monday, December 15, 2014

Setup OpenFire Spark client on Windows 8.1 64 bit

This post documented some problems that i encountered when trying to setup OpenFire's Spark client on Windows 8.1 64 bit OS, as well as some work-around solution.

Install Spark Client

First of all, you probably won't be able to install the Spark client, the install4j used by Spark client for installation seems to not work with Windows 8 settings. If you double-click to launch it, you will probabily see the installer terminates after first appearing.

To solve this, right click the installer and select "Properties", in the "Properties" page, set the compatability option to Windows 7 or lower, and click "OK", Now double-click the installer and try to install again.

Run Spark Client

Again even if you have jre 1.7 or 1.8 installed and system variable path properly setup (including the JAVA_HOME and Path), you may not be able to launch it, the app will complain the "EXE4J_JAVA_HOME" is not properly setup. Even if you at the "EXE4J_JAVA_HOME" to the environment system variables (by mirroring the JAVA_HOME), the same error dialog simply refuses to go away.

To solve this, go to the installation folder ("e.g. C:\\Program Files (x86)\Spark") and double-click the startup.jar file in the "lib" folder

Maven: generics are not supported in -source 1.3

Today I was integrating two projects, and encountered a very intriguing bug whenever i try to compile and run the Maven project using either Eclipse's "Maven Build" or maven's "mvn clean compile" commands. Initially i was using maven2 and the bug complained that "maven-compiler-plugin" plugin not found in the POM file. I upgraded to maven 3.13. The original error message disappeared, but i got some new error messages:

[ERROR] (use -source 5 or higher to enable for-each loops)
[ERROR] /.../PAClassifierJsonDecoder.java:[53,7] error: generics are not supported in -source 1.3
[ERROR]
[ERROR] (use -source 5 or higher to enable generics)
[ERROR] /.../drools/RulesEngineActions.java:[112,37] error: generics are not supported in -source 1.3
[ERROR]
[ERROR] (use -source 5 or higher to enable generics)
[ERROR] /.../KafkaSpout.java:[3,7] error: static import declarations are not supported in -source 1.3
[ERROR]
[ERROR] (use -source 5 or higher to enable static import declarations)
[ERROR] /.../KafkaSpout.java:[53,29] error: generics are not supported in -source 1.3
[ERROR]
[ERROR] (use -source 5 or higher to enable generics)
[ERROR] /.../KafkaSpout.java:[193,5] error: annotations are not supported in -source 1.3
[ERROR]

After some googling, it appears that maven by default configured to have source set as 1.3 (on Ubuntu at least), so if the project has use anotation such as "@Override" or generic, it will complain as these are not supported on Java 1.3. The remedy is to include the "maven-compiler-plugin" plugin. This is intriguing as i looked through the merged project's POM, and it already has included the "maven-compiler-plugin" plugin in the build section, which should have worked.

After a more detailed scrutiny and compare with examples on the web, it was noticed that the "maven-compiler-plugin" written in the POM file has one additional line "<groupId>org.apache.maven</groupId>" as follows:

<plugin>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.1</version>
   <configuration>
      <source>1.7</source>
      <target>1.7</target>
   </configuration>
</plugin>       

Therefore i decided to remove the line "<groupId>org.apache.maven</groupId>". and it work this time after rerunning the maven command

Sunday, December 14, 2014

XMPP: IM using OpenFire and Smack

While searching for some useful tutorials on how to do IM using OpenFire and Smack, I found there is no a satisfactory one-stop basic tutorial to get one easily started on creating an IM application, therefore i decided to write something about it after some experiments. This post will show how to setup OpenFire for instant messaging application and send notification via XMPP using Smack.

Setup Droplet

As OpenFire needs public IP address in order to send and receive messages, to this end, a droplet is created at digitalocean. Follow the following link to create droplet at digitalocean:

https://www.digitalocean.com/community/tutorials/how-to-create-your-first-digitalocean-droplet-virtual-server

Suppose the droplet that we created has the following settings:

hostname: droplet.demo.com
IP: 1.1.1.1
port: 9090
user: root

Setup OpenFire on Droplet

The following describes how to set up openfire on droplet:

https://www.digitalocean.com/community/tutorials/how-to-install-openfire-xmpp-server-on-a-debian-or-ubuntu-vps

Below I describe my process of setting up openfire on a droplet which may be a bit different. Suppose you have set up your droplet at root@1.1.1.1. Run the following command to login to root@1.1.1.1:

> ssh root@1.1.1.1

Once login to root@1.1.1.1, run the following command to download the latest openfire:

> wget -O openfire_3.9.3_all.deb http://www.igniterealtime.org/downloadServlet?filename=openfire/openfire_3.9.3_all.deb

Once downloaded, run the following commands to update the environment:

> sudo apt-get update
> sudo apt-get install openjdk-7-jre

The above commands install the openjdk-7 on the droplet. Now run the following command to install openfire:

> dpkg --install openfire-3.9.3_all.deb

Open the web browser and navigate to http://1.1.1.1:9090/ to configure the openfire. After the installation the files can be found in "/usr/share/openfire"

After the openfire is installed, one can check whether it is running by the following command:

> sudo ps -ef | grep openfire

To stop openfire at root@1.1.1.1, run the following command after login:

> /etc/init.d/openfire stop

To start openfire at root@1.1.1.1, run the following command after login to root@1.1.1.1

> /etc/init.d/openfire start

Lets visits 1.1.1.1:9090 to add to users account (non-admin) to the Users/Groups:

  • demo
  • reader
Install Spark IM Client to receive IM
Download and install Spark IM Client from the following link:


After the installation, we will login the Spark IM client using the "reader" account.

Smack as OpenFire Client to send IM

Now we will develop the java code which allow the "demo" account to send IM to the "reader" account (which we have login Spark IM client in the previous step). To this end, the Smack library is used to connect to the openfire server running in the droplet (which is at 1.1.1.1:5222). Lets create a Maven project and add the following dependencies:

<dependency>
<groupId>jivesoftware</groupId>
<artifactId>smack</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>jivesoftware</groupId>
<artifactId>smackx</artifactId>
<version>3.1.0</version>
</dependency>

Now in the main class java file App.java, add the following implementation:

package com.memeanalytics.openfire_client;

import java.util.Calendar;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Type;

public class App 
{
    public static void main( String[] args )
    {
     sendMessage();
    }
    
    private static void sendMessage()
    {
     String hostname =  "1.1.1.1";
     int clientPort = 5222;
        ConnectionConfiguration config=new ConnectionConfiguration(hostname, clientPort);
        XMPPConnection conn=new XMPPConnection(config);
        
        String username="demo";
        String password="[demo_password]";
        
        String to_username = "reader";
        String to = to_username + "@droplet.demo.com";
        
        try {
         conn.connect();
         conn.login(username, password);
   
  listContacts(conn);
   
  sendMessage(conn, to, "This is some message! The time is "+(Calendar.getInstance().getTime()));
   
  if(!hasContact(conn, to))
  {
   addFriend(conn, to, "reader");
  }
   
   
   
  conn.disconnect();
   
   
 } catch (XMPPException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
    }
    
    private static void sendMessage(XMPPConnection conn, String to, String message_body)
    {
     Message message = new Message(to, Type.normal);
 message.setBody(message_body);
 conn.sendPacket(message);
 System.out.println("Message sent!");
    }
    
    private static boolean hasContact(XMPPConnection conn, String to)
    {
     RosterEntry entry = conn.getRoster().getEntry(to);
     return entry != null;
    }
    
    private static void addFriend(XMPPConnection conn, String to, String to_nickname) throws XMPPException
    {
     String[] groups=new String[] { "Friends" };
  
 conn.getRoster().createEntry(to, to_nickname, groups);
 System.out.println("Invitation sent!");
    }
    
    private static void listContacts(XMPPConnection conn)
    {
     for(RosterEntry entry : conn.getRoster().getEntries())
     {
      String userId = entry.getUser();
      String username = entry.getName();
      System.out.println(username + " >> " + userId);
     }
    }
    
   
}

The above implementation should be quite well self-explainable. The code start with opening an XMPP connection to 1.1.1.1:5222 which runs the OpenFire XMPP server at DigitalOcean's Droplet, and end by closing the connection.

After connection, the java code login using the demo's account, next it invokes the listContacts() method which list down all the contacts in demo's contact list. and sends a message to "reader" account. Lastly it checks whether "reader" is in demo's contact list, if not, then it sends an invitation to "reader".


Eclipse: Java was started but returned exit code=13

Recently I have an update from Oracle on my Windows 7's Java jre. After i suddenly was no longer able to launch my Eclipse or Spring Tool Suit application. It always throw the following error when i try to launch the Eclipse application.

Java was started but returned exit code=13
C:\ProgramData\Oracle\Java\javapath\javaw.exe
-Dosgi.requiredJavaVersion=1.6
-Xms40m
-Xms768m
-XX:MaxPermSize=256m
-Dorg.eclipse.swt.browser.IEVersion=10001
-jar
D:\STS\sts-3.6.3.RELEASE\\plugins/org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar
-os win32
-ws win32
-arch x86_64
-showsplash
-launcher D:\STS\sts-3.6.3.RELEASE\STS.exe
-name STS
--launcher.library
...

I was in a bit of shock initially, but realized that somehow, the update may have messed up my default java jre path, as the error indicating it is using javaw.exe from "C:\ProgramData\Oracle\Java\javapath;" and i don't remember that i set include java's bin path as this directory. Therefore, I run the following commands:

> java -version
> javac -version

True enough they refers to very different version, next I want to find out which version of java.exe i am currently using and from which directory, and i run the following command:

> for %i in (java.exe) do @echo.   %~$PATH:i

The print out indicates the current java.exe being used is from "C:\ProgramData\Oracle\Java\javapath;", I then remove the "C:\ProgramData\Oracle\Java\javapath;" from the $PATH system environment variable.

Further mess up was noticed when i rerun the command "for %i in (java.exe) do @echo.   %~PATH:i" and some other app's java.exe shows up. This is finally fixed when I move my JAVA_HOME/bin directory to be the first entry in the PATH system environment variable. This finally fix my problem.


Saturday, December 13, 2014

Eclipse: The method ? of type ? must override a superclass method

Recent I import and git project into Spring Tool Suit and Eclipse, it comes up with the following annoying errors such as :

"The method prepare(Map, TopologyContext, OutputCollector) of type AnalyticBolt must override a superclass method"

My imported project is  littered with all these errors. After some search online, it turns out that these are caused by the compliance of eclipse to Java 1.5, which does not recognize @Overrride attribute.

I was able to correct this by:

1. Right-click the project and select "Properties"
2. "Java Compiler" in the "Properties" page and uncheck "Use compliance from execution environment 'J2SE-1.5' on the 'Java Build Path'
3. Set the 'Compiler compliance level:" to 1.7
4. Click "OK".


ElasticSearch: Filtered Query using JEST

While it is possible to query ElasticSearch using httpclient or es node, it is not as effective as JEST. This post explains the basics of using JEST for filtered query against ElasticSearch. To start, create Maven project and add the following dependencies into the pom file:

<dependency>
    <groupId>io.searchbox</groupId>
    <artifactId>jest</artifactId>
    <version>0.1.3</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.6.1</version>
</dependency>
<dependency>
    <groupId>com.googlecode.json-simple</groupId>
    <artifactId>json-simple</artifactId>
    <version>1.1.1</version>
</dependency>

Suppose the elastic search stores indexed document with the following example mapping:

{ "name" : "xxx",
  "age" : 11,
"address" : "xxxxx"}

Let's suppose the elasticsearch is running at 192.168.2.2:9200 and the indexed documents are stored under http://192.168.2.2:9200/myindex/mytype.

Let's create a simple java class representing this index document:

public class SearchResultTuple {
 public String address;
 public String name;
 public int age;
}


We want to retrieves 20 records of the indexed documents from the elasticsearch matching using the following query:

{"from": 0, "size" : 20,
"sort" : {
   "age" : { "order" : "asc" }
},
"query" : {
  "filtered" : {
     "query" : {
        "match" : { "name" : "James" }
     },

    "filter" : {
       "range" : { "age" : { "lte" : 10, "gte" : 20 } }
     }
   }
  }
}


The java implementation to execute the above filtered query using JEST on ElasticSearch is shown below:

import io.searchbox.client.JestClient;
import io.searchbox.client.JestClientFactory;
import io.searchbox.client.config.HttpClientConfig;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import io.searchbox.core.SearchResult.Hit;

import java.io.IOException;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;

import org.json.simple.JSONObject;

public class App 
{
    public static void main( String[] args ) 
    {
        JestClient client = openClient();
        
        JSONObject json=new JSONObject();
     
        json.put("from", 0);
        json.put("size", 20);
     
        JSONObject sortJson=new JSONObject();
        json.put("sort", sortJson);
     
        JSONObject sortDateJson=new JSONObject();
        sortJson.put("age", sortDateJson);
        sortDateJson.put("order", "asc");
     
        JSONObject queryJson=new JSONObject();
        json.put("query", queryJson);
        
        JSONObject filteredJson=new JSONObject();
        queryJson.put("filtered", filteredJson);
 

        JSONObject queryMatchJson=new JSONObject();
        filteredJson.put("query", queryMatchJson);
 
        JSONObject matchJson=new JSONObject();
        queryMatchJson.put("match", matchJson);
 
        matchJson.put("name", "James");
 
        JSONObject filterJson=new JSONObject();
        filteredJson.put("filter", filterJson);
  
        JSONObject rangeJson=new JSONObject();
        filterJson.put("range", rangeJson);

        JSONObject dateJson = new JSONObject();

        rangeJson.put("age", dateJson);

        dateJson.put("gte", 20);
        dateJson.put("lte", 10);
     
        String jsonString = json.toJSONString();
     
        Search search = (Search) new Search.Builder(jsonString)
        .addIndex("myindex")
        .addType("mytype")
        .build();

        try {
           SearchResult result = client.execute(search);
           //System.out.println(result.getJsonString());
           List<Hit<ElasticSearchResultTuple, Void>> hits = result.getHits(SearchResultTuple.class);
           //System.out.println(hits.size());
          for(Hit<SearchResultTuple, Void> hit : hits)
          {
            SearchResultTuple hitTuple = hit.source;
            int age = hitTuple.age;
            String name = hitTuple.name;
            String address =hitTuple.address;
          }
        } catch (Exception e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
        }
     
       
        client.shutdownClient();
    }
    
    private static JestClient openClient()
    {
     HttpClientConfig clientConfig = new HttpClientConfig.Builder("http://192.168.2.2:9200")
          .multiThreaded(true).build();
     JestClientFactory factory = new JestClientFactory();
  
     factory.setHttpClientConfig(clientConfig);
     JestClient jestClient = factory.getObject();
  
     return jestClient;
    }
}




Winforms: Read and display Arabic characters from a text file

Recently I was working on a program which i need to read in and display a text file containing arabic characters. The problem gave me quite a bit of a challenge initially, therefore i decide to share the lesson that i learned while figuring out how to do that.

Read the text file containing Arabic characters

The essence lies on how you sent the encoding in your StreamReader object which reads the text file content. Firstly let me list out what will not work:

1. Default encoding like "StreamReader reader = new StreamReader(filepath)" will not work, you will get messed up text displayed when it comes to arabic characters.
2. Unicode encoding like "StreamReader reader = new StreamReader(filepath, System.Text.Encoding.Unicode)" will not work, your program will hang possibly.
3. UTF Encoding like "StreamReader reader = new StreamReader(filepath, System.Text.Encoding.UTF8)" will not work, you will get messed up arabic characters. Sames goes for UTF32, UTF7

Now below is what will work:

StreamReader reader = new StreamReader(filepath, System.Text.Encoding.GetEncoding("Arabic")

Display the text file containing arabic characters in a Winform control

Ok at this point, you may get it to work perfectly. but if you still cannot display text correctly, one solution is to apply a "Arial Unicode MS" font to your Winform controls. Below is the instruction on how to do this:

1. Download a copy of the "Arial Unicode MS" (google it), and embed into your program's resource file (copy and paste it directly in the "Resources" tab after your open your project's property page). Let's say the name of the embeded resource is "Arial_Unicode_MS".
2. Create a singleton class FontManager, which will be used to apply the "Arial_Unicode_MS" to the Winform. the codes of the FontManager is shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Text;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;

namespace Lab001
{
    public class FontManager
    {
        private static FontManager mInstance = null;
        private static object mSyncObj = new object();

        public static FontManager Instance
        {
            get
            {
                if (mInstance == null)
                {
                    lock (mSyncObj)
                    {
                        mInstance = new FontManager();
                    }
                }
                return mInstance;
            }
        }

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]

        private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont,
           IntPtr pdv, [System.Runtime.InteropServices.In] ref uint pcFonts);

        private PrivateFontCollection MYpfc = new PrivateFontCollection();

        private FontManager()
        {

            try
            {
                unsafe
                {
                    fixed (byte* pFontData = Properties.Resources.Arial_Unicode_MS)
                    {
                        uint dummy = 0;
                        MYpfc.AddMemoryFont((IntPtr)pFontData, Properties.Resources.Arial_Unicode_MS.Length);
                        AddFontMemResourceEx((IntPtr)pFontData, (uint)Properties.Resources.Arial_Unicode_MS.Length, IntPtr.Zero, ref dummy);
                    }
                }
            }
            catch
            {

            }
        }

        public FontFamily UnicodeFontFamily
        {
            get
            {
                return MYpfc.Families[0];
            }
        }

        public FontFamily DefaultFontFamily
        {
            get
            {
                return UnicodeFontFamily;
            }
        }


        public void ApplyDefaultFont(Control control)
        {
            control.Font = new Font(DefaultFontFamily, control.Font.Size, control.Font.Style);
        }

        public void ApplyDefaultFont(Form frm)
        {
            foreach (Control c in frm.Controls)
            {
                ApplyDefaultFont(c);
            }
        }
    }
}

3. Now in your Winform's contructor, add in the following line immidiately after the "InitializeComponent();" line:

FontManager.Instance.ApplyDefaultFont(this);

4. In your project property page, click the "Build" tab and check the "Allow unsafe code" option.

Now build and run your application, you should be able to read and display the arabic characters in your text files.

Friday, December 12, 2014

Storm: java.lang.NoClassDefFoundError: org/apache/curator/RetryPolicy

Recently I was working on a Trident topology with Trident-ML which uses nathan marz's storm-kafka which pushes the results from the Trident topology to be read by another storm topology. While the program works perfectly in local cluster testing. When it was deployed in a storm cluster, the following error showed up from nowhere:

java.lang.NoClassDefFoundError: org/apache/curator/RetryPolicy

The error got me stuck for more than half an hour, before i figured out a workable solution. It seems that i am using there is a version compatibility issues with the version of storm, trident-ml, and the storm-kafka. Originally i had been using the following dependencies in the pom file:

<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>0.9.2-incubating</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>net.wurstmeister.storm</groupId>
<artifactId>storm-kafka-plus-0.8</artifactId>
<version>0.4.0</version>
</dependency>

<dependency>
<groupId>com.github.pmerienne</groupId>
<artifactId>trident-ml</artifactId>
<version>0.0.4</version>
</dependency>

The error was gone, after I changed the storm-kafka dependency from storm-kafka-plus-0.8 to the following:

<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-kafka</artifactId>
<version>0.9.2-incubating</version>
</dependency>

I figured the updated dependency solves the curator framework dependency in the storm-kafka's pom file. Note that if you have a pom-assembly.xml, remember to include the following:

<include>org.apache.storm:*</include>



Thursday, December 11, 2014

Ubuntu: copy file from local computer to another computer.

To copy a file (e.g. /var/app.jar) from the local computer to the "/tmp" folder of a remote computer (said root@192.168.2.2), run the following command:

> scp /var/app.jar root@192.168.2.2:/tmp

Storm: Debugging topology running in storm cluster

Usually debugging is much easier when running storm topology in a local cluster, especially when it comes to command line output (e.g. "System.out.println()"). However, once the storm topology is deployed in a storm cluster. the command line output will longer be available. In such case, the logs created by storm becomes very useful as your command line output will be stored there. To this end, suppose you configure to have the storm logs stored in /var/storm-logs directory. and you want to view and continuously print out of command line just like what "System.out.println()" did in a local cluster. And suppose the logs is stored in a file named worker-6666.log. The easiest way to do this is to run the following commands:

> cd /var/storm-log
> tail -f worker-6666.log

The "tail -f" command will print out the last ten lines of the worker-6666.log continuously,

Thursday, December 4, 2014

Unity: Collision Detection

Collision detection in Unity is extremely simple. For each game object that the player character will collide with, add a collider (e.g., by selecting Component->Physics->Box Collider), next enable the "Is Trigger" property in the game object's "Box Collider" section. We can set a tag to these game objects so that they can recognized by their tag, e.g. a tag like "enemy". Now in the MonoBehavior C# script attached to the player character, add the following method:

void OnTriggerEnter(Collider hitCollider)
{
 if("enemy" == hitCollider.tag)
 {
  GameObject enemy=hitCollider.gameObject;
  this.Treasures.Add(hitCollider.GetComponent<Treasure>());
  Destroy(enemy);
 }
}

The method above destroy any "enemy" the player character collides with and steal its Treasure item.

Unity: Horizontal Flow Layout

The following code snippet uses the GUILayout to conveniently make a horizontal flow layout that contains 10 labels.

void OnGUI()
{
  GUILayout.BeginArea(new Rect(0, 0, Screen.width / 4, 32));
  GUILayout.BeginHorizontal();

  for(int i=0; i < 10; ++i)
  {
 GUILayout.Label(i.ToString());
  }

  GUILayout.EndHorizontal();
  GUILayout.EndArea();
}


Unity: Implement a Timer

This post shows how to create a timer in Unity similar to the one used in WinForms or WPF. To create a timer in a Unity game, create a C# script and attached to a empty game object, The content of the C# looks like the following:

using UnityEngine;
using System.Collections;

public class Timer : MonoBehaviour {
 public float timerInterval=1.0f; //timer interval in seconds

 // Call this when the scene is loaded before the Start()
 private void Awake()
 {
  StartCoroutine (TimerCoroutine ());
 }

 private IEnumerator TimerCoroutine()
 {
  while(true)
  {
   Timer_OnTicked();
   yield return new WaitForSeconds(timerInterval);
  }
 }

 private void Timer_OnTicked()
 {
  //TODO: put your implementation here
 }

 // Use this for initialization
 void Start () {
 
 }
 
 // Update is called once per frame
 void Update () {
 
 }
}

As can be seen above, the implementation starts the timer when the Unity scene containing the game object is loaded (i.e. in the Awake() method), this is done via the StartCoroutine() method which starts the co routine TimerCoroutine(). The TimerCoroutine() contains an infinite loop which updates based on the timerInterval. The actual update is to be implemented inside the Timer_OnTicked() handler.

Unity: Mouse Event Handling

To handle mouse event on a game object, we can write a C# script attached to the game object, in which the MonoBehavior derived class implements the following event handlers:

void OnMouseEnter(): handler triggered when the mouse moves over the game object
void OnMouseExit(): handler triggered when the mouse moves outside the game object
void OnMouseDown(): handler triggered when the mouse button is down on the game object
void OnMouseUp(): handler triggered when the mouse button is up on the game object

The above methods allow the game object to identify when the mouse is over it, or when the mouse is pressed and released over it, and behave accordingly (e.g., by change to highlight color, etc). Remember to apply a Component->Physics->Box Collider on the game object otherwise the above events will not be fired.

There are a set of methods from the Input class which returns a flag indicating whether a mouse button is pressed, for example:

Input.GetMouseButton(0) : return true if the left mouse button is pressed down
Input.GetMouseButton(1) : return true if the right mouse button is pressed down

There are also a set of methods from the Input class which returns the distance moves by the mouse, for example:

float Input.GetAxis("Mouse X") : return distance on screen for a the mouse moves left or right (positive or negative, respectively)
float Input.GetAxis("Mouse Y") : return distance on screen for a the mouse moves upward (positive) or downward (negative)
Vector3 Input.mousePosition: return the screen coordinate of the mouse position
float Input.GetAxis("Mouse ScrollWheel"): return the extent of mouse wheel scrolling


Wednesday, December 3, 2014

Unity: MiniMap

This post shows an example of a simple implementation for minimap in Unity game. The complete source of the project can be downloaded from:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Minimap.zip

Firstly, we want to make sure that the player character's 3D model will not be rendered in the minimap, therefore we need to exclude the layer to which the player character belong to. The easiest way is to create a new layer and set the player character to be on that layer. Let's name that layer "No Map" layer)

Next, create a camera in the "Hierarchy" view by selecting "Create->Camera". Name the camera "MiniMap Camera", and set the following properties in its "Inspector" view:

1. Clear Flags: Depth Only
2. Culling Mask: Mixed (by excluding "No Map" layer in selected layers so that the player character 3D model will not be rendered in the minimap)
3. Projection: Orthographic
4. Depth: 1

Next, create a C# script named "MiniMapGenerator.cs" and attached it to the "MiniMap Camera" game object in the "Hierarchy" view. Update its content as follows:

using UnityEngine;
using System.Collections;

public class MiniMapGenerator : MonoBehaviour {

 public float camHeight=10.0f;
 public float camDistance=10.0f;

 public GameObject target; 

 public Texture2D marker;

 public bool freezeRotation=true;

 // Use this for initialization
 void Start () {
  Vector3 angles = transform.eulerAngles;
  angles.x = 90;
  angles.y = target.transform.transform.eulerAngles.y;
  transform.eulerAngles = angles;
  Draw ();
 }
 
 // Update is called once per frame
 void Update () {
  transform.position = new Vector3 (target.transform.position.x, 
                                 target.transform.position.y + camHeight,
                                 target.transform.position.z);

  camera.orthographicSize = camDistance;

  if(freezeRotation)
  {
   Vector3 angles=transform.eulerAngles;
   angles.y=target.transform.transform.eulerAngles.y;
   transform.eulerAngles=angles;
  }


  Draw ();
 }

 void Draw()
 {
  float minimap_width=Screen.width * 0.3f;
  float minimap_height=Screen.height * 0.3f;
  float xOffset = 10.0f;
  float yOffset = 10.0f;
  
  float minimap_left = Screen.width - minimap_width - xOffset;
  float minimap_bottom = Screen.height -  minimap_height - yOffset;
  
  camera.pixelRect = new Rect (minimap_left, minimap_bottom, minimap_width, minimap_height);
 }

 void OnGUI()
 {
  if(marker != null)
  {
   Vector3 markerPos = camera.WorldToViewportPoint(target.transform.position);
   float x = (camera.pixelRect.xMin+camera.pixelRect.xMax) * markerPos.x;
   float y = Screen.height - (camera.pixelRect.yMin + camera.pixelRect.yMax) * markerPos.y;
   GUI.DrawTexture(new Rect(x-marker.width * 0.5f, y-marker.height * 0.5f, marker.width, marker.height), marker, ScaleMode.StretchToFill);
  }
 }
}

The code sets the position and orientation of the "MiniMap Camera" to follow the "target" game object (we will set the "target" game object to the player character later) in the Update() method. The Draw() method determines the location and size of the minimap frame on the game screen.

Since we already exclude the player character 3D model from showing up in the minimap camera (i.e., the culling mask settings earlier), we need to add a marker to the minimap frame to indicate the location of the player character in the minimap. The OnGUI() use the DrawTexture on the marker texture to display a marker representing the player character.

Once the code for the "MiniMapGenerator.cs" is completed and attached to the "MiniMap Camera", we need to assign "target" and "marker" to it. With the "MiniMap Camera" selected in the "Hierarchy" view, drag the player character from "Hierarchy" view to the "target" attribute of the "MiniMapGenerator.cs" in the "Inspector" view. Do the same by dragging a texture for the marker to the "marker" attribute of the component. That is it.

Unity: Spherical Camera

This post discuss a simple implementation of a spherical camera in Unity Game. The complete project source can be downloaded from:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Spherical-Camera.zip

 The spherical camera has the following functionality:

1. When the user presses down the SHIFT+UP, the camera will zoom in to its focus point (i.e. a target point). The camera will zoom out while looking at the target point when user presses down the SHIFT+DOWN keys

2. When the user presses down the LEFT or RIGHT arrow key, the camera will rotate horizontally around the target point.

3. When the user presses down the UP or DOWN arrow key, the camera will rotate vertically around the target point over a range of minimum and maximum angles.

4. When the user hold down the right mouse button and drag on the terrain simultaneously, the target point will move in the same direction, and the camera move accordingly following the direction of the mouse drag motion.

To implement this, first lets create an empty game object named "target", which will act as the the focus/target point of the spherical camera (to make it more visible, a spherical game object is attached as a child to this "target" game object).

Next create a C# script and name it "SphericalCamera.cs". Update its content as shown below:

using UnityEngine;
using System.Collections;

public class SphericalCamera : MonoBehaviour {
 private float initialFOV;

 public float minZoomLimit=0.1f;
 public float maxZoomLimit=10;

 public int distance=100;
 public GameObject target = null;

 private float xRotate;
 private float yRotate;

 public float xRotateSpeed=3.0f;
 public float yRotateSpeed=1.5f;

 public float maxYRotate=90.0f;
 public float minYRotate=0.0f;

 public float xMoveSpeed=100.0f;
 public float zMoveSpeed=100.0f;

 // Use this for initialization
 void Start () {
  Vector3 angles = transform.eulerAngles;
  xRotate = angles.x;
  yRotate = angles.y;

  initialFOV = camera.fieldOfView;

  transform.position = new Vector3 (0, 0, -distance) + target.transform.position;
 }
 
 // LateUpdate is called once at the end of a frame
 void LateUpdate () {
  if(Input.GetKey(KeyCode.RightShift) || Input.GetKey(KeyCode.LeftShift))
  {
   float zoom = camera.fieldOfView - Input.GetAxis ("Vertical") * yRotateSpeed;
   if(zoom >= initialFOV / maxZoomLimit && zoom <= initialFOV / minZoomLimit)
   {
    camera.fieldOfView -= Input.GetAxis ("Vertical") * yRotateSpeed;
   }
  }
  else
  {
   xRotate += Input.GetAxis ("Horizontal") * xRotateSpeed;
   yRotate += Input.GetAxis ("Vertical") * yRotateSpeed;

   if(yRotate > 360) yRotate-=360;
   if(yRotate < -360) yRotate+=360;

   yRotate=Mathf.Clamp(yRotate, minYRotate, maxYRotate);
  }

  if(Input.GetMouseButton(1))
  {
   float target_x = Input.GetAxis ("Mouse X") * xMoveSpeed;
   float target_z = Input.GetAxis ("Mouse Y") * zMoveSpeed;
   Vector3 movement = transform.transformDirection(new Vector3(target_x, 0, target_z));
   movement.y = 0.0f;

   
   target.transform.Translate(movement);
  }

  var rotation = Quaternion.Euler (yRotate, xRotate, 0);
  var position = rotation * (new Vector3 (0, 0, -distance)) + target.transform.position;

  transform.rotation = rotation;
  transform.position = position;
 }
}


Now attach the "SphericalCamera.cs" to the "Main Camera" game object. Next with the "Main Camera" selected in the "Hierarchy" view, drag the "target" game object from the "Hierarchy" view to drop it in the "target" attribute of the "SphericalCamera.cs" in the "Inspector" view of "Main Camera". That is it.

The zooming is performed when the SHIFT key is pressed together with the UP or DOWN arrow key (i.e. KeyCode.LeftShift / KeyCode.RightShift), the change is the zooming is calculated as

Input.GetAxis("Vertical") * yRotateSpeed;

The code is fairly easy to understand once the following methods are understood:

  • Input.GetAxis("Vertical") returns the duration / extent to which the UP or DOWN arrow key is pressed.
  • Input.GetAxis("Horizontal") return the duration / extent to which the LEFT or RIGHT arrow key is pressed
  • Input.GetAxis("Mouse X") return the duration / extent to which the mouse is moved in direction x
  • Input.GetAxis("Mouse Y") return the duration / extent to which the mouse is moved in the direction y
  • Input.GetMouseButton(1) return the boolean value indicating whether the right mouse button has been pressed down.



Tuesday, December 2, 2014

Unity: Switch among cameras

Below is the link to a simple demo of how to switch among different cameras in a Unity game:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Switch-Camera.zip

Firstly, create two more cameras by selecting "Create->Camera" in the "Hierarchy" panel, name them cam1 and cam2. Change their transform position and rotation in the "Inspector" panel, so that they point to different locations in the scene. Now we have 3 cameras: Main Camera (created by default), cam1, and cam2.

Now create a C# script "CameraSwitch.cs" and update its content as follows:

using UnityEngine;
using System.Collections;

public class CameraSwitch : MonoBehaviour {
 public string[] shortcuts;
 public Camera[] cameras;

 // Use this for initialization
 void Start () {

 }
 
 // Update is called once per frame
 void Update () {
  for(int i=0; i < shortcuts.Length; ++i)
  {
   if(Input.GetKeyUp (shortcuts[i]))
   {
    SwitchCamera(i);
   }
  }
 }

 void SwitchCamera(int index)
 {
  for (int i=0; i < cameras.Length; ++i) 
  {
   if(i==index)
   {
    cameras[i].GetComponent<AudioListener>().enabled=true;
    cameras[i].camera.enabled=true;
   }
   else
   {
    cameras[i].GetComponent<AudioListener>().enabled=false;
    cameras[i].camera.enabled=false;
   }
  }
 }
}

Now create an empty game object named "Switchboard" by selecting "Game Object->Create Empty" in the menu. Attach the "CameraSwitch.cs" to it.

Select cam1 in the "Hierarchy" panel and uncheck its "camera" and "Audio Listener" in the "Inspector" panel. Do the same for cam2. This will make the "Main Camera" visible by default when the game is launched.

Select the "Switchboard" game object in the "Hierarchy" panel, and set the size of "shortcuts" and "cameras" in the "CameraSwitch" to 3 in the "Inspector" panel. Now enter "1", "2", "3" in the "shortcuts" array elements and drag the 3 camera objects from the "Hierarchy" panel into the "cameras" array elements in the "Inspector" panel. That is it. When the game is run, by press one of the keys: "1", "2" or "3", the camera will be switched to one of them.

The code is self-explained, whenever, a user press one of the "shortcuts" keys, only the corresponding camera will be enabled while the other two are disabled.

Unity: Camera ZoomIn and ZoomOut

Below is the link to a simple piece of codes which shows how to make the camera in Unity to zoom in and zoom out:

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.ZoomIn-ZoomOut.zip

Firstly create a C# Script named "TelescopicView" and update its codes as shown below:

using UnityEngine;
using System.Collections;

public class TelescopicView : MonoBehaviour {
 public int zoomInSpeed=100;
 public int zoomOutSpeed=100;
 public int zoomLevel=2;
 private float initFOV;

 // Use this for initialization
 void Start () {
  initFOV = Camera.main.fieldOfView;
 }
 
 // Update is called once per frame
 void Update () {
  if(Input.GetKey(KeyCode.Mouse0))
  {
   ZoomIn();
  }
  else
  {
   ZoomOut();
  }
 }

 void ZoomIn()
 {
  if(Mathf.Abs(Camera.main.fieldOfView - initFOV / zoomLevel) < 0.5f)
  {
   Camera.main.fieldOfView=initFOV / zoomLevel;
  }
  else if(Camera.main.fieldOfView-(Time.deltaTime * zoomInSpeed) >= initFOV / zoomLevel)
  {
   Camera.main.fieldOfView -= Time.deltaTime * zoomInSpeed;
  }
 }

 void ZoomOut()
 {
  if(Mathf.Abs(Camera.main.fieldOfView - initFOV) < 0.5f)
  {
   Camera.main.fieldOfView=initFOV;
  }
  else if(Camera.main.fieldOfView + (Time.deltaTime * zoomOutSpeed) <= initFOV)
  {
   Camera.main.fieldOfView += Time.deltaTime * zoomOutSpeed;
  }
 }
}

Now attach the "TelescopicView" script to the camera object in the "Hierarchy" panel (e.g., the "Main Camera"). Also with the "Main Camera" selected in the "Hierarchy" panel, select "Component->Camera Control->Camera Look" to add the Camera Look component to the "Main Camera". That's it.

The codes are self-explained, basically the techniques rely on changing the field of view of the camera by decreasing it (zoom in) or increasing it (zoom out). The zoom in happens when the user holds down the left mouse (i.e. KeyCode.Mouse0).

Unity: Screen Capture in Game

Below is the link to a simple piece of exercise which shows how to do a screen capture in a Unity game.

https://dl.dropboxusercontent.com/u/113201788/Unity3D/Camera.Screen-Capture.zip

Firstly, create a C# script "ScreenTexture.cs" and update its content as follows:

using UnityEngine;
using System.Collections;

public class ScreenTexture : MonoBehaviour {
 public int photoWidth = 50;
 public int photoHeight = 50;
 public int thumbScale=75;
 private int frameWidth;
 private int frameHeight;
 private int screenWidth;
 private int screenHeight;
 public int borderWidth=2;
 public Color borderColor = Color.white;
 private Texture2D texture;
 private Texture2D border;
 private bool shoot = false;

 // Use this for initialization
 void Start () {
  screenWidth = Screen.width;
  screenHeight = Screen.height;
  frameWidth = Mathf.RoundToInt(screenWidth * 0.01f * photoWidth);
  frameHeight = Mathf.RoundToInt(screenHeight * 0.01f * photoHeight);

  texture = new Texture2D (frameWidth, frameHeight, TextureFormat.RGB24, false);
  border = new Texture2D (1, 1, TextureFormat.ARGB32, false);
  border.SetPixel (0, 0, borderColor);
  border.Apply ();
 }
 
 // Update is called once per frame
 void Update () {
  if (Input.GetKeyUp (KeyCode.Mouse0)) 
  {
   StartCoroutine(ScreenCapture());
  }
 }

 IEnumerator ScreenCapture()
 {
  yield return new WaitForEndOfFrame();
  texture.ReadPixels(new Rect(
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   frameWidth,
   frameHeight), 0, 0);
  texture.Apply();
  shoot=true;
 }

 void OnGUI()
 {
  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   frameWidth,
   borderWidth), border, ScaleMode.StretchToFill);

  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f + frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   borderWidth,
   frameHeight), border, ScaleMode.StretchToFill);

  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f + frameHeight * 0.5f,
   frameWidth,
   borderWidth), border, ScaleMode.StretchToFill);

  GUI.DrawTexture (new Rect (
   screenWidth * 0.5f - frameWidth * 0.5f,
   screenHeight * 0.5f - frameHeight * 0.5f,
   borderWidth,
   frameHeight), border, ScaleMode.StretchToFill);

  if (shoot) 
  {
   GUI.DrawTexture(new Rect(10, 10, frameWidth * 0.01f * thumbScale, frameHeight * 0.01f * thumbScale), texture, ScaleMode.StretchToFill);
  }
 }
}

Now attach the script to the "Main Camera" in the "Hierarchy" panel in your unity project. Also with the "Main Camera" selected in the "Hierarchy" panel, select "Component->Camera Control->Camera Look" to add the Camera Look component to the "Main Camera". That's it.

The script works as follows. When user click the left mouse, the ScreenCapture() method is runned in a separate thread. In the ScreenCapture method, the texture object is updated (this texture object can be thought of as a in-memory image which holds the screen capture, the actual capturing is done by Texture2D.ReadsPixel() method), and the shoot flag is set to true. In the OnGUI, a frame is drawn at the center of the game screen indicating the area which will be capture, and if the shoot flag is true, then the texture (which now contains the screen capture) will be rendered.