Pages

What is in a Name?

It was a spark of inspiration to try my hand at literal translation in Beijing in April 2009.


We were trying to get from Point A to Point B by foot, seeing as it was the most direct route compared to taking the underground. So there's this short straight section of road (multiplied by a few times for a few turns) which should bring us happily to dinner at a modern shopping mall. However, as with most things in China, don't forget the scale of things! 2 centimeters on a map work out to a lot more steps and sweat in a sprawling metropolis like Beijing. Not that we didn't know, having trod through the Temple of Heaven grounds the day before, but here we are, too far gone to turn back to Point A, but still a long and hungry distance from Point B. But I digress...

In the turn of a corner, we were at 正义道. All at once, we were greeted by a pretty promenade under an intricate canopy of almost bare branches. After all, spring has yet time to coax the life and colours out of these graceful trees. We were informed that this was a park. That's nice, and hey, isn't that a modern toilet smack in the centre? Solar powered, no less, and in spiffy silver. Strangely enough, without the space age looking toilet, the park would have reminded me of a similar promenade in Boston, MA, reclaimed from marshland.


That hunch was a hint to the history of the area. The guidebook tells us that this was formerly an embassy strip. Looking at this building, you'd probably expect it to be in some Western country, well, not China in any case. But here it is, next to a futuristic looking public toilet, on 正义道 in Beijing. I realise what an accurate snapshot that was of Beijing now in all its transition and transformation, but that's not the point of this blog today :-)


正义道 literally means path of righteousness. Besides sounding a happy resonance of Psalm 23:3, it evoked some irony for an area that housed diplomats of a bygone era, all jostling for a foothold in the Middle Kingdom. Today, the place is a quiet park, a convenient connector between two main roads. Quaint statues dot the promenade. Here a sweeper girl, there a musician. Quite a different flavour from earlier times, but still, no less, 正义道.


I haven't told you about 望京, the City of Hope yet! The usual tourist wouldn't detour here, but this is a pleasant northeastern suburb with a sizeable Korean community and modern amenities. My Korean friends have no problems getting ingredients from home here, the City of Hope.

Beijing Cherry Blossom Festival 2009

A little heart wish was fulfilled recently at YuYuanTan Park in Beijing.

To set eyes on transient beauty, lay hold of fleeting spring, feel time brush langorously past as lithe willows sway, enjoy the translucent canopy of paper thin blooms overhead... flitting, flying, falling so perfectly, none out of place.

This is the Cherry Blossom Festival 2009 in Beijing. Weeks of anticipation turned into overwhelmed sight and a gentle heart's sigh.

It was on the first Saturday of April 2009 that I became a privileged partaker of these beauteous blooms, a guest ushered into the magical alcove of millions of flowerheads, all singing the same springsong in the breeze.

No matter that hundreds of local denizens were there; on the contrary they added to the authenticity of the experience. No notion of genteel Japanese hanami here either; it was crowds of all ages, families and friends out in full force, children running, tumbling, crying. It was a private and public enjoyment all at once.

An outsider like me finds it most strange and a little amusing that ladies donning contemporary garb would wear wreaths of plastic cherry blossoms in their hair, not just in the park but onto the public bus outside. A singular sighting I dismissed as a lone, high-spirited young lass but wandering deeper and deeper into the garden, more and more such lasses appeared before me, as in a vision!

The girls were real all right. They were in the flush of exuberance, chatting happily with their friends or special someones, colourful crowns bobbing in the sea of heads. Here are some of them


Then, there are the stars of the show, the gracious hosts who forebear the multitude of feet on their turf.

We are told that there are 2000 cherry blossom trees of 20 varieties, so this is only
the slightest sampling of the show...


This is the handmaiden to the cherry blossoms, called YingChunHua (literally translated as "welcome-spring flower"):

It was a hive of activity in the park: Tiny tots admiring each other all decked out... Enterprising Chinese selling all sorts of artificial flowers to the spring-inebriated crowds... In child-centric China, sweet sellers never go out of business. Simple pleasures in life... bubbles and bunnies. More simple pleasures in life... the humble shuttlecock! That's the outdoor spirit... spot the 'gung ho' baby.

As the sun began to set and activity dropped a notch, it wasn't clear whether the multitudes were there to gaze at cherry blossoms with single-hearted devotion or to exult in the presence of spring with soaring spirits. But surely the beauteous blossoms would have no quarrel with the latter. Cherry blossoms and spring, why, they're the most natural bedfellows!











JavaFX and Google Maps

Recently, I have taken a very keen interest in JavaFX. The declarative nature of the language and its powerful data binding capabilities, coupled with the fact that there are so many existing Java programs will accelerate its adoption very quickly.

My exploration with JavaFX has been very fruitful. It is really easy to pick up and to be productive with it in a matter of hours. When I started to learn how Google Maps can be used with JavaFX, I found very little useful information in the Web. Googling the keywords "JavaFX Google Maps" was less than satisfactory. Then, I stumbled upon Google Maps can be used statically without Javascript. For example, the following map on Eiffel Tower is displayed using a static html link. Notice the latitude and longitude are specified in the URL.
http://maps.google.com/staticmap?center=48.8531,2.3691&markers=48.8531,2.3691,rede&zoom=14&size=320x240

Paris Eiffel Tower
It then dawned upon me how easy it is to incorporate Google Maps into JavaFX, all we need are the JavaFX Image and ImageView objects. For instance, the following codes will display the above map in a JavaFX application.
def stage = Stage {
title : "JavaFX Google Maps Demonstration"
scene: Scene {
 width: 500
 height: 400
 content: [
   ImageView {
     image: Image {
       url: "http://maps.google.com/staticmap?center=48.8531,2.3691&markers=48.8531,2.3691,rede&zoom=12&size=320x240"
     }
   }
 ]
}
}
Next, I discovered the use of Google Geocoding web service. Given an address, you can get the latitude and longitude of that address. For example, to find the latitude and longitude of New York USA, enter the following url.
http://maps.google.com/maps/geo?q=NEW+YORK+USA&output=xml
The Google Geocoding web service will return the following XML. Notice the latitude and longitude of New York, USA are returned in the coordinates element in line 25.

  
    NEW YORK USA
    
      200
      geocode
    
    
      
New York, NY, USA
US USA NY New York -73.9869510,40.7560540,0

Hence, using static Google Maps and GeoCoding web service, I ventured to develop this demonstration application using the latest JavaFX 1.2. A screen shot of the application is given here.


This simple application allows the user to enter an address. If Google recognises it, the map of the address will be shown. The user can also pan the map by dragging the mouse or zoom in and out of the map using the slider. The application comprises three files Main.fx, GMapUtils.fx and GmapGeoCoding.fx.

Click on the Launch button to try the application.



The full source codes of these three files are given as follows.
// Main.fx
package jfxgm;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Cursor;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.control.TextBox;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;

var lng:Number = 103.8476410;
var lat:Number = 1.3717300;
var zoom:Integer = bind javafx.util.Math.round(sZoom.value);

bound function getMapImage(lt, lg, zm):Image {
  var mapurl:String = "http://maps.google.com/staticmap?center={lt},{lg}&markers={lt},{lg},reda&zoom={zm}&size=480x320";
  var map:Image = Image {
    url: mapurl;
  }
return map;
}

function setLatLng(lt:Number, lg:Number):Void {
  lat = lt;
  lng = lg;
}

var mapview:ImageView = ImageView {
  layoutX:10 layoutY:75;
  image: bind getMapImage(lat, lng, zoom);
  cursor : Cursor.MOVE;
  var anchorx:Number;
  var anchory:Number;
  onMousePressed: function( e: MouseEvent ):Void {
    anchorx = e.x;
    anchory = e.y;
  }
  onMouseReleased: function( e: MouseEvent ):Void {
    var diffx = anchorx - e.x;
    var diffy = anchory - e.y;
    lat = GMapUtils.adjustLatByPixels(lat, diffy, zoom);
    lng = GMapUtils.adjustLngByPixels(lng, diffx, zoom);
  }
}

def lblAddress = Label {
  layoutX:10 layoutY:15;
  text: "Address :"
}

def txtAddress = TextBox {
  layoutX:70 layoutY:10;
  text: ""
  columns: 28
  selectOnFocus: true
}

def lblLat = Label {
layoutX:39 layoutY:45;
text: "Lat :"
}
def txtLatitude = TextBox {
layoutX:70 layoutY:40;
text: bind (lat.toString());
columns: 8
editable: false;
}
def lblLng = Label {
layoutX:175 layoutY:45;
text: "Lng :"
}
def txtLongitude = TextBox {
layoutX:210 layoutY:40;
text: bind (lng.toString());
columns: 8
editable: false;
}

def btnUpdateMap = Button {
layoutX:320 layoutY:10;
text: "Get Latitude and Longitude"
action: function() {
  var addr:String = txtAddress.text;
  GMapGeoCoding.getLatLng(addr, setLatLng);
}
}

def lblLevel = Label {
layoutX:310 layoutY:45;
text: "Level :"
}

def sZoom:Slider = Slider {
layoutX:355 layoutY:45;
min: 1
max: 20
value:15
vertical: false
}

def stage = Stage {
title : "JavaFX Google Maps Demonstration"
scene: Scene {
  width: 500
  height: 400
  content: [
    lblAddress, txtAddress,
    lblLat, txtLatitude,
    lblLng, txtLongitude,
    btnUpdateMap,
    lblLevel,sZoom,
    mapview,
  ]
}
}
// GMapUtils.fx
package jfxgm;

import javafx.util.Math.*;

def GMAPOFFSET = 268435456;
def RADIUS = GMAPOFFSET / PI;
def GMAPMAXZOOM = 21;

function lng2x(lng):Integer {
  return round(GMAPOFFSET + RADIUS * lng * PI / 180);      
}

function lat2y(lat) {
return round(GMAPOFFSET -
               RADIUS *
               log((1 + sin(lat * PI / 180)) /
               (1 - sin(lat * PI / 180))) / 2);
}

function x2Lng(x:Number):Number {
return ((round(x) - GMAPOFFSET) / RADIUS) * 180/ PI;
}

function y2Lat(y:Number) {
return (PI / 2 - 2 * atan(exp((round(y) - GMAPOFFSET) / RADIUS))) * 180 / PI;
}

public function adjustLngByPixels(lng:Number, delta:Number, zoom:Number):Number {
return x2Lng(lng2x(lng) + (delta * pow(2, (GMAPMAXZOOM - zoom))));
}

public function adjustLatByPixels(lat:Number, delta:Number, zoom:Number):Number {
return y2Lat(lat2y(lat) + (delta * pow(2, (GMAPMAXZOOM - zoom))));
}
// GMapGeoCoding.fx
package jfxgm;

import javafx.io.http.*;
import javafx.data.pull.PullParser;
import javafx.data.pull.Event;
import javafx.io.http.URLConverter;

public function getLatLng(address:String, setLatLng:function(lat:Number, lng:Number)):Void {
  def getRequest: HttpRequest = HttpRequest {
    var addr = URLConverter{}.encodeString(address);
    location: "http://maps.google.com/maps/geo?q={addr}&output=xml";
    onInput: function(is: java.io.InputStream) {
    def parser = PullParser {
      documentType: PullParser.XML;
      input: is;
      onEvent: function(event: Event) {
        if (event.type == PullParser.END_ELEMENT) {
          if (event.qname.name == "code" and event.text == "602") {
            setLatLng(0.0, 0.0);
          }
          else if (event.qname.name == "coordinates") {
            var pts = event.text.split(",");
            setLatLng(java.lang.Float.parseFloat(pts[1]),
                      java.lang.Float.parseFloat(pts[0]));
          }
        }
      }
    }
    parser.parse();
    parser.input.close();
  }
}
getRequest.start();
}

Java Compile and Run in Notepad++

Notepad++ is an excellent text editor. It supports syntax highlighting of many programming languages besides Java. Unlike Eclipse and Netbeans, it is light-weight and it can be launched from Windows Explorer easily by right-clicking on the file you wish to edit.

The following steps describe how you can extend the capabilities of Notepad++ to include compiling and executing Java programs from Notepad++ itself.

First, create a file called JavaCompileRun.bat. You can do this by right-clicking on your Windows' Desktop and then click New and Text Document. Then, key in "JavaCompileRun.bat" as the new file name. Right-click on this file and click Edit. Put in the following codes and save the file.
cd /d "%1"
javac %2
if errorlevel 1 goto finish
java %3
:finish
pause
Next, copy the file JavaCompileRun.bat to your Notepad++ program folder, usually at C:\Program Files\Notepad++.

Open Notepad++. Select the Run menu and click on Run.... A dialog box will appears. Key in the following command.
$(NPP_DIRECTORY)\JavaCompileRun.bat "$(CURRENT_DIRECTORY)" "$(FILE_NAME)" "$(NAME_PART)"
After keying in the above command, the dialog box should look something like this...


Click on the Save... button. In the Name: text box, key in "Java Compile and Save" as shown in the figure below.



Click OK and then click on the Run! button. Your Java program should compile and run if there is no error.

Now to compile the current Java program, make sure you have saved the file. Then, in the Run menu, click Java Compile and Run as shown.


A Command Prompt window will appear and the results of your Java program will be displayed.


There is however one serious limitation with this method. Only Java programs with default package will compile and run.

App_Code Folder in ASP .NET 3.5

In Visual Studio Professional 2008, you can still create the App_Code folder by right-clicking on the Web Project and selecting Add, then Add Folder. Rename the new folder to App_Code.

Contrary to some recommendations on the Web that you should not use the App_Code folder because you cannot place common Web UI codes or classes in this folder, you can do so by setting Build Action of each class to Compile. Suppose this step is not done, the classes defined in this folder will not be visible to your other codes. This explains why people recommended against the use of App_Code folder in VS 2008.

Suppose you have a class called WebCommon.cs in this folder. All you have to do is to right-click on this file and select Properties. A properties window will appear. Set Build Action to Compile. Viola! The class can be accessed exactly like what you have seen in VS 2005.

Using IronPython in ASP .NET VS 2008

Download Microsoft ASP.NET Futures (July 2007) from Microsoft and follow the instructions for installation closely in the download page especially you are installing it on Windows Vista.

The installation should work for Visual Studio 2005, Visual Web Developer 2005 Express Edition, Visual Studio 2008 or Visual Web Developer 2008 Express Edition. Start up Visual Studio, create a new website. You should see IronPython in the Language drop down box.

Storing Passwords in MS SQL

In MySQL, storing users' passwords is easily done by the function SHA1. Assuming a table usertable exists with two columns userid and passwrd.
INSERT INTO usertable(userid, passwrd) 
VALUES('johnlim', SHA1('SECRET'));

To retrieve the user's record,
SELECT * FROM usertable
 WHERE userid = 'johnlim'
   AND passwrd = SHA1('SECRET');

Is there an equivalent in MS SQL Server? Yes! Recently, MS SQL Server 2005 has nicely built-in support for hashing and the function is called HASHBYTES. This function takes in two string parameters. The first determines the algorithm used to provide the hash. Possible values for the algorithm are MD2, MD4, MD5, SHA and SHA1. The second takes in the value to be hashed.

Hence the equivalent SQL statements for MS SQL are
INSERT INTO usertable(userid, passwrd) 
VALUES('johnlim', HASHBYTES('SHA1', 'SECRET'));

SELECT * FROM usertable
 WHERE userid = 'johnlim'
   AND passwrd = HASHBYTES('SHA1', 'SECRET');

The only difference is that the passwrd column in MySQL is VARCHAR while in MS SQL is VARBINARY.

Changing Template in Blogger

I had been a casual blogger until of late when I am seriously exploring how Blogger works. One of the things which I sought to do was to change the template to something more professional. I have tried many templates without success. The cryptic errors reported by Blogger on various occasions were bX-si9ejx, bX-aoj9qb, bX-hq2u5m and etc.

Many other bloggers also experienced these problems and the solution commonly suggested was to delete all browser cookies and temporary files, and then upload the template again. This solution didn't work for me. I also tried changing browsers from FireFox to IE to Chrome and even Opera.

After many hours of research and experimentation, I've finally understood the problem. I came to the understanding that the template not only defines the skin and the layout of the blog, it also stores information about the widgets which I've created. The definition for each created widget is stored in this template. And along with this definition, Blogger also automatically assigns an id with each created widget. By the way, for the uninitiated, widgets are page elements which make up a blog. These are your blog archives, labels, feeds, links and etc. Basically, they are different sections of your blog.

For each template downloaded from popular sites such as Our BLOGGER Template, the template also consists of pre-defined widgets . As mentioned above, each widget is defined by its id. Most of the time unfortunately, the ids of these widgets clash with your existing widgets. In another words, they have the same name. This is where the problem lies!

To overcome this problem, before you upload the template, you should resolve these name conflicts. Common names of widgets are blog1, feed1, label1, etc. You probably need to rename all these other names, (such as blog111, feed111 and label111 etc) in order to avoid the problem altogether.

Take for example the Professional Template downloaded from http://www.ourblogtemplates.com/2008/11/blogger-template-professional-template.html.
These are the lines in the template which should be changed. You can open this file using WordPad and search for "widget id". You need not rename every instance. Only those instances that have name conflicts with your existing widgets need to be renamed. But for simplicity, just rename every widget id.
<b:widget id='Header1' locked='true' title='The Professional Template (Header)' type='Header'>
<b:widget id='Blog1' locked='true' title='Blog Posts' type='Blog'>
<b:widget id='Profile1' locked='false' title='About Me' type='Profile'>
<b:widget id='Label2' locked='false' title='Labels' type='Label'>
<b:widget id='Image1' locked='false' title='' type='Image'>
<b:widget id='Text2' locked='false' title='About This Blog' type='Text'>
<b:widget id='Text3' locked='false' title='Lorem Ipsum' type='Text'>
<b:widget id='Text4' locked='false' title='Lorem Ipsum' type='Text'>
<b:widget id='Text5' locked='false' title='Lorem' type='Text'>
<b:widget id='LinkList1' locked='true' title='Linkbar' type='LinkList'>
<b:widget id='LinkList2' locked='false' title='Links' type='LinkList'>
<b:widget id='BlogArchive1' locked='false' title='Blog Archive' type='BlogArchive'>
<b:widget id='Feed1' locked='false' title='Our Blogger Templates' type='Feed'>
The following lines show the renamed ids. E.g. Header1 → Header111.
<b:widget id='Header111' locked='true' title='The Professional Template (Header)' type='Header'>
<b:widget id='Blog111' locked='true' title='Blog Posts' type='Blog'>
<b:widget id='Profile111' locked='false' title='About Me' type='Profile'>
<b:widget id='Label222' locked='false' title='Labels' type='Label'>
<b:widget id='Image111' locked='false' title='' type='Image'>
<b:widget id='Text222' locked='false' title='About This Blog' type='Text'>
<b:widget id='Text333' locked='false' title='Lorem Ipsum' type='Text'>
<b:widget id='Text444' locked='false' title='Lorem Ipsum' type='Text'>
<b:widget id='Text555' locked='false' title='Lorem' type='Text'>
<b:widget id='LinkList111' locked='true' title='Linkbar' type='LinkList'>
<b:widget id='LinkList222' locked='false' title='Links' type='LinkList'>
<b:widget id='BlogArchive111' locked='false' title='Blog Archive' type='BlogArchive'>
<b:widget id='Feed111' locked='false' title='Our Blogger Templates' type='Feed'>
Have a productive time changing your blogger templates!

Dim Sum Trolley

A warm welcome to our dim sum trolley!

Here we hope to share with you choice pickings from our travels near and far. It is our humble wish that these bite-sized nuggets tantalize, inspire, delight or stoke a sense of wonder in the reader.


A post-script for our non-Chinese visitors who may wonder about "dim sum", these Chinese words are transliterated as "a little heart" or "touch the heart". Dim sum are the favourite snacks of many Chinese, especially Cantonese, coming in all shapes and form, weird and wonderful. We like the Chinese words used for this array of delicious snacks -- a little heart. After all, more than food, it's about heart.

Setting Default Folder in Windows Explorer

When Windows Explorer is started, the Documents folder of the current user will be selected. Suppose you want it to select another folder, say the Work folder in D:, can this be done easily? Yes!

Right click on the Windows Explorer icon and click Properties. In the Target textbox, you will see "%SystemRoot%\explorer.exe". Replace this with
%SystemRoot%\explorer.exe /n, /e, /select, D:\Work

The above tip works for Windows XP, Vista and Seven.