Friday, January 25, 2008

Moving status checking stuff to a managed bean?

It is a time consuming procedure for javascript now to check whether each station has a status change on the chosen date or within the last 30 days before that date, because javascript is not efficient enough. So I am thinking about moving this procedure into a jsf managed bean, and call a method like "getColorsForStations" on it when we need to recolor the stations.

The problem is that managed bean can only be used by binding to some jsf tag of a html control. We can't call the methods of a managed bean directly from javascript. So the following tricks might be needed:
a. The method may need five parameters: the current view scope of the map (min latitude, max latitude, min longitude, max longitude), and the chosen date. So we need five invisible control tags to save the value of these parameters;
b. We need an invisible commandButton or something like that to map its onClick action to the call to this method. In this way we'll be able to call the method by invoking "btn.click()", after setting up the parameter values.
c. We need an invisible control to store the result. The result should be simple, like a string on {0, 1, 2, 3,4, 5}, where each number denotes a different color.

After calling the method, we can decide the color for every station by analyzing the resulted string.

The miscoloring problem caused by time zone difference

We got this miscoloring problem several days ago that for some specific dates, some stations are colored as red on some machines, but yellow on others.

The reason for this is relevant to the way we record the status change dates in javascript code. The dates were previously stored as a millisecond count of the local time since 1970-01-01-0:0:0:000.
Since it is only the date that matters to our application, the specific time of all dates are set to 12:0:0:000.
The initial dates are setup at the server side by analyzing the xml file containing status change information of all stations, so the time used is the local time of the machine where the web server is running. Then when a user chooses a date from the portal, a time according to the chosen date is generated with client side javascript and used for coloring the stations. The color of each station is decided by comparing this time with each stored time that has been initialized at the server side.
As a result, if the client and server have different time zone configuration, the two millisecond time counts for the same date will be different, and this will lead to miscoloring of the station.

To solve this problem, we just use standard UTC time for generating both the initialized status change time and the dynamic date time chosen by portal user. Moreover, since only the date matters, we use day count instead of milliseconds since 1970-01-01.

Wednesday, January 2, 2008

Manipulate xml file in javascript

Consider the following sample samp.xml file:

/*
< xml>
< station>
< id>aacc< /id>
< lat>33.124< /lat>
< long>110.223< /long>

< station>
< id>bbcc< /id>
< lat>32.124< /lat>
< long>110.289< /long>
< /station>
< /xml>
*/

We can use java codes to analyze the xml files in jsp, e.g., with SAXReader. But when mixed with javascripts, this will generate ugly html source codes. For instance, if there are hundreds of stations in this xml, and we wanna create one google map marker for every station, we might write the following codes in jsp:
/*
< % for (int i=0; i < count_station; i++) { % >
marker[idx++] = new GMarker('<%=station.element("id").getText()%>', ...);
< % } % >
*/

And this will result in hundreds of lines like "marker[idx++] = new GMarker('aacc', ...);" in the html generated from this jsp. On the other hand, if we can manipulate xml files directly with javascript, codes will be much more elegant. To be honest, I always think jsp is ugly. If we really want the web interface to do more things, we'd better come back to the time of ActiveX object of applications, where codes of the objects are compiled and execute much faster...

To avoid such ugly codes, we can manipulate xml files just in javascript. The specific object to use depends on the type of browser:
function xmlMicoxLoader(url){
//by Micox: micoxjcg@yahoo.com.br.
//http://elmicoxcodes.blogspot.com
if(window.XMLHttpRequest){
var Loader = new XMLHttpRequest();
//assyncronous mode to load before the 'return' line
Loader.open("GET", url ,false);
Loader.send(null);
return Loader.responseXML;
}else if(window.ActiveXObject){
var Loader = new ActiveXObject("Msxml2.DOMDocument.3.0");
//assyncronous mode to load before the 'return' line
Loader.async = false;
Loader.load(url);
return Loader;
}
}
var changeXml = xmlMicoxLoader("/samp.xml");
var xmlNode = changeXml.childNodes[0];

Then we can treat this xmlNode as the root node of a tree structure corresponding to the structure of the xml elements, and reference the elements at different layers from different layer of child nodes:
for (var i=2; i < xmlChildCount; i++, saIdx++) {
stationNode = xmlNode.childNodes[i];
var id = stationNode.childNodes[0].firstChild.nodeValue;
....
}

Note that for pretty-formatted xml files, the dented tabs, spaces and line-changes are treated as a special kind of "text" nodes--in fact, even in "ugly-formatted" xml files where no spaces or line-changes we still find these "text" nodes--so unless we are very sure about the layout order of "text" nodes and xml-element nodes that are really useful for us, we should always check the type of nodes before access their child nodes, or node values. For example, for traversing the whole tree structure:

function xmlMicoxTree(xmlNode,ident){
//by Micox: micoxjcg@yahoo.com.br
var treeTxt=""; //var to content temp
for(var i=0;i < xmlNode.childNodes.length;i++){//each child node
if(xmlNode.childNodes[i].nodeType == 1){//no white spaces
//node name
treeTxt = treeTxt + ident + xmlNode.childNodes[i].nodeName + ": "
if(xmlNode.childNodes[i].childNodes.length==0){
//no children. Get nodeValue
treeTxt = treeTxt + xmlNode.childNodes[i].nodeValue
for(var z=0;z < xmlNode.childNodes[i].attributes.length;z++){
var atrib = xmlNode.childNodes[i].attributes[z];
treeTxt = treeTxt + " (" + atrib.nodeName + " = " + atrib.nodeValue + ")";
}
treeTxt = treeTxt + "
\n";
}else if(xmlNode.childNodes[i].childNodes.length>0){
//children. get first child
treeTxt = treeTxt + xmlNode.childNodes[i].firstChild.nodeValue;
for(var z=0;z < xmlNode.childNodes[i].attributes.length;z++){
var atrib = xmlNode.childNodes[i].attributes[z];
treeTxt = treeTxt + " (" + atrib.nodeName + " = " + atrib.nodeValue + ")";
}
//recursive to child of children
treeTxt = treeTxt + "
\n" + xmlMicoxTree(xmlNode.childNodes[i],ident + "> > ");
}
}
}
return treeTxt;
}

Except for the problem of "text" nodes, performance is also an important drawback of using javascript to manipulate xml files, especially when the file is large and there is a big number of nodes to process. For example, when employed for analyzing the result xml file of daily rdahmm, because there are too many stations to process, the loading time of the page becomes unacceptable. As a result we came back to the java code solution, which is much faster, but produces ugly codes, and results in a large html file. The loading time mainly depends on the execution of jsp codes when the network connectivity is good, which is the normal case, so this makes the portlet response more quickly.

Plotting problem and portlet performance

The present plotting of rdahmm uses lines to plot, which leaves out some outliers if these points have a different status from that of the days before and after their dates, as well as covers some time periods of missing data if the statuses before and after the periods are the same. To solve this problem, we plot with points, in which case all outliers will be plotted, and time periods with missing data are just left as sections with no points.

The portlet is having a very long loading time for analyzing the status change xml file, and creating the markers. One solution for this is just to create necessary resources for markers in the current range of the map, and build the rest on the fly when users move around in the map.