<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-36158856</id><updated>2012-01-21T08:58:14.815-08:00</updated><category term='Python'/><category term='Google Local'/><category term='AJAX'/><category term='S60'/><category term='JSON'/><title type='text'>Weekend Projects of a Geek</title><subtitle type='html'>I am a mobile application enthusiast. Most of the projects you will see here are mobile based projects and the rest have at least something to do with mobile phones. Though the processing power on a mobile handset is respectable, human interface limitations and the power-consumption consciousness leave many features left desired for. In my projects, I try to bridge the gap between the potential of the handset and the applications on it...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>10</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-36158856.post-6917870549902328796</id><published>2009-08-08T13:08:00.001-07:00</published><updated>2009-08-08T13:21:43.735-07:00</updated><title type='text'>Testing Windows Live Writer</title><content type='html'>&lt;p&gt;Started using Windows 7 on a netbook (my first one) this week. The first impression of a netbook is that ‘I wish this would fit in my pocket – It has everything I want in a netbook’. However, Windows 7 *seems* a bit heavy on a device with lower end parameters.&lt;/p&gt;  &lt;p&gt;While discovering various new things under the hood, I stumbled upon this – the program I am using to compose this blog entry – Windows Live Writer. This is an old program – but new to me :)&lt;/p&gt;  &lt;p&gt;This wonderful program allows me to WYSIWIG-compose blogs while offline and post them when I please. I can preview the blog entry while offline (once I have downloaded the whole blog of course). &lt;/p&gt;  &lt;p&gt;All in all, I am quite impressed with this little program. Hopefully, the next blog entry will be about extending this program to add funky things. Now, it already provides adding photo albums (illustrated below with pics of my darling daughter), etc. It will be a challenge to top that. A challenge fit for a geek… &lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:66721397-FF69-4ca6-AEC4-17E6B3208830:827b6908-fa40-465a-87d7-ffa882dae6aa" class="wlWriterEditableSmartContent"&gt;&lt;a style="border:0px" href="http://cid-5252bbf84c630511.skydrive.live.com/redir.aspx?page=browse&amp;amp;resid=5252BBF84C630511!147&amp;amp;ct=photos"&gt;&lt;img style="border:0px" alt="View Vinita" src="http://lh3.ggpht.com/_7oZgM_rIxgw/Sn3bQ1UyTdI/AAAAAAAAAGU/IcytgIp0XmM/InlineRepresentationd2deb429-8643-4750-817d-c4d1ed9d5156.jpg?imgmax=800" /&gt;&lt;/a&gt;&lt;div style="width:400px;text-align:right;" &gt;&lt;a href="http://cid-5252bbf84c630511.skydrive.live.com/redir.aspx?page=browse&amp;amp;resid=5252BBF84C630511!147&amp;amp;ct=photos"&gt;View Full Album&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-6917870549902328796?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/6917870549902328796/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=6917870549902328796' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/6917870549902328796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/6917870549902328796'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2009/08/testing-windows-live-writer.html' title='Testing Windows Live Writer'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_7oZgM_rIxgw/Sn3bQ1UyTdI/AAAAAAAAAGU/IcytgIp0XmM/s72-c/InlineRepresentationd2deb429-8643-4750-817d-c4d1ed9d5156.jpg?imgmax=800' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-523744760581957997</id><published>2007-01-04T21:29:00.000-08:00</published><updated>2007-01-04T21:41:43.671-08:00</updated><title type='text'>Online Stock Quotes - Using Python S60</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/_7oZgM_rIxgw/RZ3lBYpjAhI/AAAAAAAAAAM/r9vaZqBszFk/s1600-h/nok.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_7oZgM_rIxgw/RZ3lBYpjAhI/AAAAAAAAAAM/r9vaZqBszFk/s320/nok.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5016417371919942162" /&gt;&lt;/a&gt;&lt;br /&gt;I recently came across an article about a service provided by the Yahoo! finance website, which return details about stock quotes in &lt;i&gt;comma separated value&lt;/i&gt;s (CSVs). Many folks have used this service in their programs to display (almost) real time stock data. Of course, I had to make my phone do the same :) As we will see, Python makes it look easy - no, Python makes it look GOOD!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Background&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Yahoo! finance service &lt;span style="font-family:courier new;"&gt;http://quote.yahoo.com/d/quotes.csv&lt;/span&gt; is a very simple yet powerful service. The user can control the fields in the stock quote data through a format string. The return data contains stock data fields in CSV format. The fields and their order is based on the format string. &lt;a href="http://www.codeproject.com/netcf/WNKPocket.asp"&gt;This&lt;/a&gt; is another example of the usage of this service.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Cache Management&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The application on the phone should minimize the network traffic. However, care should be taken that the data is updated as frequently as possible to keep the real-time nature of the data. To achieve this, there is a very rudimentary cache management technique implemented in the module. The cache management checks how old the stock data is and if it is older than a certain expiry. The network traffic is generated only if the data is expired.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Chart&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This module also allows downloading the chart for a given symbol. The chart is downloaded from yet another Yahoo! finance service &lt;span style="font-family:courier new;"&gt;http://ichart.finance.yahoo.com/t&lt;/span&gt; The response of this service is a intra-day chart for the latest day. If a chart exists, the response is of type &lt;span style="font-family:courier new;"&gt;image/png&lt;/span&gt;. This can be used as a test. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The following code snippet is from the &lt;span style="font-family:courier new;"&gt;quotes.py&lt;/span&gt; module. This piece works well on 3rd edition phones:&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #eee"&gt;&lt;span style="font-family:courier new;"&gt;# These times should be set to minimize the network traffic&lt;br /&gt;QUOTEEXPIRY = 60 # 1 Minute&lt;br /&gt;CHARTEXPIRY = 300 # 5 Minutes&lt;br /&gt;CACHEDIR = 'c:\\cache\\'&lt;br /&gt;&lt;br /&gt;# If the file does not exist or if the creation time of the &lt;br /&gt;# existing file is older than expiry, then return True&lt;br /&gt;def is_expired(filename, expiry):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;expired = False&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not os.path.exists(filename):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;expired = True&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;lastmod = os.stat(filename)[ST_MTIME]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currtime = int(time.time())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if currtime &gt; lastmod + expiry:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;expired = True&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return expired&lt;br /&gt;&lt;br /&gt;# This function downloads the chart for a given symbol&lt;br /&gt;# If the symbol does not have a chart, the function returns&lt;br /&gt;# False, else returns True&lt;br /&gt;def get_chart(symbol):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval = True&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;filename = CACHEDIR+symbol.upper()+'chart.png'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if is_expired(filename, CHARTEXPIRY):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;name, status = urllib.urlretrieve('http://ichart.finance.yahoo.com/t?s='+symbol.upper(), filename)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if status.type != 'image/png':&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retval = False&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;os.remove(filename)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return retval&lt;br /&gt;&lt;br /&gt;# Get the CSV from yahoo server and return parsed values&lt;br /&gt;# Format is known and hence the cryptic retval parsing&lt;br /&gt;def get_quote(symbol):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;filename = CACHEDIR+symbol.upper()+'quote.txt'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if is_expired(filename, QUOTEEXPIRY):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;urllib.urlretrieve('http://quote.yahoo.com/d/quotes.csv?s='+symbol.upper()+'&amp;d=t&amp;f=sl1d1t1c1ohgvj1pp2xwenr', filename)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f = file(filename)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data = f.read()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f.close()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;names = ['sym', 'last', 'date', 'curtime', 'change', 'open', 'high', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'low', 'vol', 'mcap', 'close', 'pctchg', 'exchange', 'annrange', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'earnings', 'name', 'peratio']&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;values = map(lambda s: s.strip('"'), data.split('\n')[0].split(','))&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return dict(zip(names, values))&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;The meaning of the format string can be found from the correspondance between the string and the &lt;span style="font-family:courier new;"&gt;names&lt;/span&gt; array.&lt;br /&gt;&lt;br /&gt;The complete implementation is located &lt;a href="http://www.cs.uh.edu/~nikhilv/PyS60/quotes.zip"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This log is yet another example of power and elegance of Python. The way &lt;span style="font-family:courier new;"&gt;zip()&lt;/span&gt; function simplifies creation of a very usable dictionary out of an array.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-523744760581957997?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/523744760581957997/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=523744760581957997' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/523744760581957997'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/523744760581957997'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2007/01/online-stock-quotes-using-python-s60.html' title='Online Stock Quotes - Using Python S60'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_7oZgM_rIxgw/RZ3lBYpjAhI/AAAAAAAAAAM/r9vaZqBszFk/s72-c/nok.png' height='72' width='72'/><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-7448454402254988540</id><published>2006-11-23T00:05:00.000-08:00</published><updated>2006-11-23T00:10:42.073-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><category scheme='http://www.blogger.com/atom/ns#' term='AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Local'/><category scheme='http://www.blogger.com/atom/ns#' term='S60'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Google Local's AJAX Data Objects Demystified</title><content type='html'>JSON is slowly, but steadily, becoming an Internet standard. Refer to the &lt;a href="http://www.ietf.org/rfc/rfc4627.txt"&gt;RFC 4627&lt;/a&gt;. Not too long ago, Google Local server changed its AJAX data object format from XML to JSON-like notation. In this log, we will see how we tweak the Google Local data object to suit our JSON parser (a.k.a. &lt;span style="font-family:courier new;"&gt;eval()&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Background&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;JSON standard requires the name in the name value pair to be a string enclosed in quotation marks. The Google Local server sends AJAX data object in a JSON-like format in which the name is not enclosed in quotation marks. For example:&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #eee"&gt;&lt;span style="font-family:courier new;"&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;title: "from: ... to: 15939 ... - Google Maps",&lt;br /&gt;&amp;nbsp;&amp;nbsp;vartitle: "",&lt;br /&gt;&amp;nbsp;&amp;nbsp;url: "/maps?saddr=...&amp;daddr=...&amp;ie=UTF8",&lt;br /&gt;&amp;nbsp;&amp;nbsp;urlViewport: false,&lt;br /&gt;&amp;nbsp;&amp;nbsp;form: &lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;selected: "d",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q: "from: ... to: ..."&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.&lt;br /&gt;}&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This disparity between actual data object and the standard can easily be eliminated by enclosing all the names in quotes. The best way to achieve this is to use regular expression. A regular expression can easily find a name by searching for a alphanumeric word before colon. Of course, care must be taken not to confuse words before colons within a string value. The following code snippet shows how to achieve this:&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #eee"&gt;&lt;span style="font-family:courier new;"&gt;# Javascript constants not known to python&lt;br /&gt;false = False&lt;br /&gt;true = True&lt;br /&gt;null = None&lt;br /&gt;&lt;br /&gt;# Helper RegEx based function to enclose names in quotes&lt;br /&gt;# (which is required in JSON) - Google server does not put&lt;br /&gt;# quote around names&lt;br /&gt;# E.g. {title: "From: Home To: Office"} is converted into&lt;br /&gt;# &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {"title": "From: Home To: Office"}&lt;br /&gt;import re&lt;br /&gt;pat = re.compile(r"""&lt;br /&gt;&amp;nbsp;(?P&amp;lt;name&amp;gt;\w+)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# Name of JSON pair&lt;br /&gt;&amp;nbsp;\s*&amp;nbsp;:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# Whitespace, and a colon&lt;br /&gt;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# Or&lt;br /&gt;&amp;nbsp;(".*?")&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# Enclosed in double quotes&lt;br /&gt;""", re.VERBOSE)&lt;br /&gt;def subfunc(match):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# If pattern was found enclosed in double quotes,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# do not substitute&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if match.group(2):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return match.group(2)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return '"'+match.group(1)+'"'+':'&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This "pre-processed" data object is ready to be consumed by our JSON parser to give us all the information we need from the Google Local server.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This little log just goes on to show that with Python (even on an S60 handset) we can get the information we need in the format we want with minimal effort. I wasted much time thinking about parsing techniques before I stumbled across this simple solution. And there is unmistakable elegance in simplicity, don't you think?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-7448454402254988540?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/7448454402254988540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=7448454402254988540' title='245 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/7448454402254988540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/7448454402254988540'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/11/google-locals-ajax-data-objects.html' title='Google Local&apos;s AJAX Data Objects Demystified'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>245</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-4976796303337993671</id><published>2006-11-22T10:25:00.000-08:00</published><updated>2006-11-22T10:30:01.699-08:00</updated><title type='text'>JSON Parser Using Python</title><content type='html'>&lt;a href="http://photos1.blogger.com/blogger2/7560/4413/1600/blog7.gif"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger2/7560/4413/320/blog7.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. More information about JSON can be found at &lt;a href="http://www.json.org/"&gt;http://www.json.org/&lt;/a&gt;. Yahoo's Douglas Crockford developed this format and within a short period, many web based services have adopted JSON as the primary data interchange format. In this log, we will look at one such service.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Background&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There are a bunch of JSON parsers out there, such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://undefined.org/python/#simplejson"&gt;simplejson&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://sourceforge.net/projects/json-py/"&gt;json-py&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;An example JSON document appears as follows:&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #eee"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;"Image": {&lt;br /&gt;&amp;nbsp;&amp;nbsp;"Width":  800,&lt;br /&gt;&amp;nbsp;&amp;nbsp;"Height": 600,&lt;br /&gt;&amp;nbsp;&amp;nbsp;"Title":  "View from 15th Floor",&lt;br /&gt;&amp;nbsp;&amp;nbsp;"Thumbnail": {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Url":    "http://www.example.com/image/481989943",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Height": 125,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Width":  "100"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp;"IDs": [116, 943, 234, 38793]&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;For a Python developer, a JSON document is nothing but a dictionary with each element being either an element, array or another dictionary. We can use this fact to our advantage, by simply using the &lt;span style="font-family:courier new;"&gt;eval()&lt;/span&gt; function provided by Python to not only parse this document, but to create a data structure in one statement!&lt;br /&gt;&lt;br /&gt;The following code snippet shows how to use the &lt;span style="font-family:courier new;"&gt;eval()&lt;/span&gt; function to parse JSON object. This snippet is based on &lt;a href="http://developer.yahoo.com/python/python-json.html"&gt;http://developer.yahoo.com/python/python-json.html&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #eee"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# If needed set the proxy address&lt;br /&gt;import os&lt;br /&gt;os.environ['http_proxy'] = 'http://[proxy_host]:[proxy_port]/'&lt;br /&gt;import urllib&lt;br /&gt;APP_ID = 'YahooDemo' # Change this to your API key&lt;br /&gt;SEARCH_BASE = 'http://api.search.yahoo.com/WebSearchService/V1/webSearch'&lt;br /&gt;&lt;br /&gt;class YahooSearchError(Exception):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass&lt;br /&gt;&lt;br /&gt;def search(query, results=20, start=1, **kwargs):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;kwargs.update({&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'appid': APP_ID,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'query': query,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'results': results,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'start': start,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'output': 'json'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url = SEARCH_BASE + '?' + urllib.urlencode(kwargs)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f = urllib.urlopen(url)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;buff = f.read().replace('\\/', '/')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;f.close()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result = eval(buff)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if 'Error' in result:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# An error occurred; raise an exception&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise YahooSearchError, result['Error']&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return result['ResultSet']&lt;br /&gt;&lt;br /&gt;info = search('json python')&lt;br /&gt;results = info['Result']&lt;br /&gt;for result in results:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print result['Title'], result['Url']&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Caveats&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;"&gt;eval()&lt;/span&gt; function is not safe from programmer errors. Putting a &lt;span style="font-family:courier new;"&gt;try...catch&lt;/span&gt; around the function call fixes that problem. But there is another sinister problem with &lt;span style="font-family:courier new;"&gt;eval()&lt;/span&gt;, not unlike &lt;span style="font-family:courier new;"&gt;SQL&lt;/span&gt;. The &lt;span style="font-family:courier new;"&gt;eval()&lt;/span&gt; function will not distinguish between a JSON object and a malicious statement. Consider the following:&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #eee"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;import os&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;eval("os.remove('something_very_important')")&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Care should be taken to use eval when the string to be evaluated is from a trusted source. I consider Yahoo! or Google servers trusted.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This article briefly touches upon using the &lt;span style="font-family:courier new;"&gt;eval()&lt;/span&gt; function to understand JSON documents. This will be further elaborated when we will look at Google Local client-server interactions later. I hope you find this trick useful...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-4976796303337993671?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/4976796303337993671/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=4976796303337993671' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/4976796303337993671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/4976796303337993671'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/11/json-parser-using-python.html' title='JSON Parser Using Python'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-116392666267076590</id><published>2006-11-19T00:55:00.000-08:00</published><updated>2006-11-19T00:57:42.680-08:00</updated><title type='text'>Virtual Earth Maps on the Go</title><content type='html'>For a map client, it is necessary to get the maps for a given location. Most GPS navigation systems store the maps of the entire nation at all zoom levels in their memory. On handsets, this might not be the best strategy. We know that Virtual Earth server has all the maps required for a map client, just an HTTP request away. All we need to know is how to get them.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Background&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In the last log, we checked out some of the geocoding services provided by virtual earth and used them in our Python on S60 programs. In this one, we will see how to download the maps of the location of your choice. Our primary goal here is to find a mapping function from latitude, longitude, zoom to a URL to download the map. Once we have a mapping function, Python will take care of all the basic functionality to give us a fully functional map client without having to store large amount of data.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;How Virtual Earth Stores Maps&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Virtual Earth map system, like many map systems (e.g. Google Maps) uses &lt;a href="http://en.wikipedia.org/wiki/Mercator_projection"&gt;&lt;i&gt;Mercator&lt;/i&gt; projection&lt;/a&gt; to fit Earth's curved suface onto a flat sheet (screen in present case). The &lt;i&gt;Mercator&lt;/i&gt; projection as described in the Wikipedia article is a mathematical scheme that converts latitude into an y value and longitude into x. These x and y values are then used very easily to store and retrieve maps from the server. When I started putting together a map client for Virtual Earth, I thought that once this mapping function is implemented, half my job will be done. But that wasn't to be as Virtual Earth server stores quad-tree encoded x, y values (a la Google Maps satellite images server).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Mapping Function&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The mapping function to map the latitude, longitude, zoom to map URL is as follows:&lt;br /&gt;&lt;br /&gt;url = &lt;i&gt;f&lt;/i&gt;(latitude, longitude, zoom)&lt;br /&gt;&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-TOP: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-LEFT: 5px; BORDER-BOTTOM: #ccc 1px solid; PADDING-BOTTOM: 5px; background-color:#eee;" &gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;import math  &lt;br /&gt;earthRadius = 6378137 &lt;br /&gt;earthCircum = earthRadius * 2.0 * math.pi &lt;br /&gt;earthHalfCirc = earthCircum / 2.0  &lt;br /&gt;&lt;br /&gt;def LongitudeToXAtZoom(lon, zl):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;arc = earthCircum / ((1 &amp;lt;&amp;lt; zl) * 256) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;metersX = earthRadius * DegToRad(lon) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return int(round((metersX + earthHalfCirc) / arc))  &lt;br /&gt;&lt;br /&gt;def LatitudeToYAtZoom(lat, zl): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;arc = earthCircum / ((1 &amp;lt;&amp;lt; zl) * 256) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sinLat = math.sin(DegToRad(lat)) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;metersY = earthRadius / 2 * math.log((1.0 + sinLat) / (1.0 - sinLat)) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return int(round((earthHalfCirc - metersY) / arc))  &lt;br /&gt;&lt;br /&gt;def DegToRad(d): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return d * math.pi / 180.0  &lt;br /&gt;&lt;br /&gt;def RadToDeg(r): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return r * 180.0 / math.pi  &lt;br /&gt;&lt;br /&gt;def TileToQuadKey(tx, ty, zl): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;quad = '' &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in range(zl, 0, -1): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mask = 1 &amp;lt;&amp;lt; (i - 1) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cell = 0 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (tx &amp;amp; mask) != 0: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cell = cell + 1 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (ty &amp;amp; mask) != 0: &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cell = cell + 2 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;quad = quad + str(cell) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return quad  &lt;br /&gt;&lt;br /&gt;def GetTileSpecs(lat, lon, zoom): &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;tx = LongitudeToXAtZoom(lon, zoom) / 256 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cx = LongitudeToXAtZoom(lon, zoom) % 256 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ty = LatitudeToYAtZoom(lat, zoom) / 256 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cy = LatitudeToYAtZoom(lat, zoom) % 256 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;server = ((tx &amp;amp; 1) + ((ty &amp;amp; 1) &lt;&lt; 1)) % 4 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q = TileToQuadKey(tx, ty, zoom) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;filename = 'r%s.png' % q &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;url = 'http://r%d.ortho.tiles.virtualearth.net/tiles/%s?g=22' % (server, filename) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return (filename, url, cx, cy)  &lt;br /&gt;&lt;br /&gt;lon = -117.068092 &lt;br /&gt;lat = 32.9913528 &lt;br /&gt;zoom = 17  &lt;br /&gt;print GetTileSpecs(lat, lon, zoom)&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This Python code snippet is another exmaple of how easily we can implement mapping client in Python. Adding Python for S60 wrapper around this to have a map client on the phone. I have done some ground work to get you started.  Avaiable at:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.cs.uh.edu/~nikhilv/PyS60/velocation.py"&gt;velocation.py&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.cs.uh.edu/~nikhilv/PyS60/vemaps.py"&gt;vemaps.py&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Hopefully, this log will get you excited, not just about Python, but about mapping techniques as well. It is very interesting to learn how map service providers implement their services. I hope you can build upon the available code to add address search and direction finding using the routines in velocation.py. Happy mapping...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-116392666267076590?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/116392666267076590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=116392666267076590' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116392666267076590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116392666267076590'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/11/virtual-earth-maps-on-go.html' title='Virtual Earth Maps on the Go'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-116391868048674664</id><published>2006-11-18T22:32:00.000-08:00</published><updated>2006-11-18T22:52:42.993-08:00</updated><title type='text'>Virtual Earth Geocoding Services - Python Style</title><content type='html'>In this log, I will take you through using Python to utilize the Virtual Earth geocoding services on your PyS60 handset.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Background&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Geocoding is the analysis technique of geo-demographic data such as ZIP codes, counties, regions, etc. These techniques include, among others, translating a postal address into a geographical coordinates. Web portals such as Google Maps and Virtual Earth allow the users to do this through their portals. What if you had power to do this from a handset, which might not be able to open these JavaScript heavy web portals?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Virtual Earth Services&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Virtual Earth is a Microsoft product that provides web bases map services amongst other to the users. You can find a US postal address on the map, find directions between 2 points, locate yourself on the map (e.g. based on your IP address) and lot more. These services are consumed by the code-behind running on the website and are invisible to the user. But a simple network packet watcher shows the following:&lt;br /&gt;&lt;br /&gt;Find Address:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;http://local.live.com/search.ashx?b=[address]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Locate Me:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;http://local.live.com/WiFiIPService/locate.ashx&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Find Directions:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;http://local.live.com/directions.ashx?start=[address1]&amp;end=[address2]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The responses are in form of C# code. E.g. when searching for an address, you can expect:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/*1.3.0515*/&lt;br /&gt;SetViewport(32.9500497939651,-117.086802489646,32.9307362060349,-117.117483510354);&lt;br /&gt;VE_Scratchpad.AddLocation('12278 Scripps Summit Dr, San Diego, CA 92131-3697, United States', 32.940393, -117.102143, '');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Python Solution&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Of course, you can use all the frameworks available to achieve what we are set out to do. Our solution is quite elegant as you will see in some time. We make use of the fact that C# syntax resembles that of Python when it comes to arrays. If we take out comments, function names, and class names, we have tuple of arrays of tuples (see the example above).&lt;br /&gt;&lt;br /&gt;The solution looks as follows:&lt;br /&gt;&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-LEFT: 5px; PADDING-BOTTOM: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #ccc 1px solid; BACKGROUND-COLOR: #eee"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# If needed set the proxy address&lt;br /&gt;import os&lt;br /&gt;os.environ['http_proxy'] = 'http://[proxy_host]:[proxy_port]/'&lt;br /&gt;&lt;br /&gt;# Definations for eval()&lt;br /&gt;false = 0&lt;br /&gt;true = 1&lt;br /&gt;&lt;br /&gt;import urllib&lt;br /&gt;&lt;br /&gt;# To remove the comments and non-py elements in the response buffer&lt;br /&gt;import re&lt;br /&gt;&lt;br /&gt;comment_pat = re.compile('(/\*.*?\*/)(".*?")', re.S)&lt;br /&gt;def subfunc(match):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;if match.group(2):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return match.group(2)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return ''&lt;br /&gt;&lt;br /&gt;nonpy_pat = re.compile('(new [\w\s,.]+)(".*?")', re.S)&lt;br /&gt;&lt;br /&gt;def getAddress(address):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;encaddress = urllib.urlencode({'b': address})&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;url = 'http://local.live.com/search.ashx?%s' % encaddress&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;sock = urllib.urlopen(url)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp = comment_pat.sub(subfunc, sock.read())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;sock.close()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;lines = resp.split(';')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;for instruction in lines:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if instruction.find('VE_Scratchpad.AddLocation') &gt; -1:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;locationstr = instruction.replace('VE_Scratchpad.AddLocation', '')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;(infostr, lat, lon, pad) = eval(locationstr)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;return (infostr, lat, lon)&lt;br /&gt;&lt;br /&gt;def locateMe():&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;url = 'http://local.live.com/WiFiIPService/locate.ashx'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;sock = urllib.urlopen(url)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp = comment_pat.sub(subfunc, sock.read())&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;sock.close()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;locationstr = resp.replace('SetAutoLocateViewport', '')[:-1]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;(lat, lon, zoomradius, unknown, message) = eval(locationstr)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;return (lat, lon, zoomradius)&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;result = getAddress('12278 scripps summit dr san diego ca')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;print result&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;result = locateMe()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;print result&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This example shows how simply Python eval() allows parsing complex text. I have left few issues to be addressed, such as ambigous address, etc. Moreover, you can explore the directions as well. Hope you enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-116391868048674664?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/116391868048674664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=116391868048674664' title='23 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116391868048674664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116391868048674664'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/11/virtual-earth-geocoding-services.html' title='Virtual Earth Geocoding Services - Python Style'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>23</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-116366459985685308</id><published>2006-11-15T21:56:00.000-08:00</published><updated>2006-11-16T00:20:26.460-08:00</updated><title type='text'>Text To Speech Using Python for S60</title><content type='html'>Recently, I came across a very interesting post to symbianexample.com by Artem &lt;a href="http://symbianexample.com/texttospeech"&gt;http://symbianexample.com/texttospeech&lt;/a&gt;. This article publishes a 'hidden' S60 feature - An MMF plugin that is able to play text!! This plugin apparently exists only for S60 2.8+ phones. I have not been able to confirm that.&lt;br /&gt;&lt;br /&gt;Imagine your program synthesizing any text... Who would not like their programs to speak intelligently to the user? I for one would for sure!!&lt;br /&gt;&lt;br /&gt;Now, I am a die-hard PyS60 fan... I jumped on this opportunity to enable PyS60 scripts with TTS. The result was a Python extension, &lt;strong&gt;_ttsplayer.pyd&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;_ttsplayer.pyd&lt;/strong&gt; is a C++ Python extension DLL. This DLL wraps the &lt;span style="font-family:courier new;"&gt;CTtsPlayer&lt;/span&gt; class provided by Artem in his original post. The extension provides only one method:&lt;br /&gt;&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-TOP: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-LEFT: 5px; BORDER-BOTTOM: #ccc 1px solid; PADDING-BOTTOM: 5px; background-color:#eee;" &gt;&lt;span style="font-family:courier new;"&gt;playtext(text) # text is unicode&lt;/span&gt; &lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The usage of this module is very simple:&lt;br /&gt;&lt;br /&gt;&lt;div style="BORDER-RIGHT: #ccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #ccc 1px solid; PADDING-TOP: 5px; BORDER-LEFT: #ccc 1px solid; PADDING-LEFT: 5px; BORDER-BOTTOM: #ccc 1px solid; PADDING-BOTTOM: 5px; background-color:#eee;" &gt;&lt;span style="font-family:courier new;"&gt;import ttsplayer&lt;br /&gt;ttsplayer.playtext(u'Hello Mr. Anderson')&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;As per Artem's article, this MMF plugin synthesizes the speech using &lt;em&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/em&gt;peaker &lt;em&gt;&lt;strong&gt;IND&lt;/strong&gt;&lt;/em&gt;ependent (&lt;strong&gt;&lt;em&gt;SIND&lt;/em&gt;&lt;/strong&gt;) framework. As you will find out when you start using this module, the quality of the generated speech is poor. Although, in more recent S60 devices (such as N75 - cool phone! oh sorry, cool computer!) we see evidence of a High Quality TTS framework. I am not sure whether &lt;strong&gt;&lt;em&gt;SIND&lt;/em&gt;&lt;/strong&gt; framework will eventually merge with this High Quality TTS framework. I can only hope that it does!&lt;br /&gt;&lt;br /&gt;The package is published here: &lt;a href="http://www2.cs.uh.edu/~nikhilv/PyS60/ttsplayer.zip"&gt;http://www2.cs.uh.edu/~nikhilv/PyS60/ttsplayer.zip&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;The package contains the following components:&lt;ul&gt;&lt;li&gt;Source package - &lt;span style="font-family:courier new;"&gt;CTtsPlayer&lt;/span&gt; class and Python module wrapper, &lt;span style="font-family:courier new;"&gt;MMP&lt;/span&gt; file, &lt;span style="font-family:courier new;"&gt;PKG&lt;/span&gt; file, &lt;span style="font-family:courier new;"&gt;bld.inf&lt;/span&gt; file. These file allow you to create PYD extension module using your favorite S60 SDK.&lt;/li&gt;&lt;li&gt;Python module - This module wraps the C++ extension DLL loading etc.&lt;/li&gt;&lt;li&gt;Example python script - To show usage in a PyS60 script.&lt;/li&gt;&lt;li&gt;Unsigned SIS file S60 3.0 - You can sign this SIS using your favorite certificate and install it if you do not want to go through the trouble of compilation.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I hope you like this little extension module. There are countless uses for this module. One can imagine a PyS60 script that gets directions from one location to another (see &lt;a href="http://blog.360.yahoo.com/blog-TVkkFrk_cq24bxa1kcXVZeB0?bid=12&amp;yy=2006&amp;amp;mm=8"&gt;Rantings of a Snake Charmer IV&lt;/a&gt;) and dictates the direction steps to the user using TTS!&lt;br /&gt;&lt;br /&gt;Thanks to Artem for finding this hidden feature in S60!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-116366459985685308?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/116366459985685308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=116366459985685308' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116366459985685308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116366459985685308'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/11/text-to-speech-using-python-for-s60.html' title='Text To Speech Using Python for S60'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-116140309513442902</id><published>2006-10-20T20:38:00.000-07:00</published><updated>2006-10-21T01:43:32.633-07:00</updated><title type='text'>Rants of a Snake Charmer - III</title><content type='html'>&lt;a href="http://photos1.blogger.com/blogger/6620/4035/1600/r3.jpg"&gt;&lt;img style="FLOAT: right; MARGIN: 0px 0px 10px 10px; CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/6620/4035/320/r3.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:180%;"&gt;XmlParser&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;This is a minimal XML parser that does what it is supposed to. The implementation is based on regular expressions. It is adapted from REX by Robert D. Camron. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Background&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;When Google Maps used to send the data objects needed by their AJAX framework in plain XML, I needed an XML parser to get location/directions data from the Google Maps server. In my earlier blog, I mentioned that PyS60 can seamlessly take modules from the desktop Python. But there are certain exceptions to that and the XML framework in Python happens to be one of them. I tried improting Python XML framework to PyS60 without any success and hence needed to find my own solution. As mentioned earlier, this XML Parser implementation is based on REX. I found REX when I was searching for the best approach to my solution. Robert Cameron has a set of regular expressions that can be used - very easily and effectively - to parse XML content. The following copyright notice and the license appear in the code.&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;# Robert D. Cameron "REX: XML Shallow Parsing with Regular Expressions",&lt;br /&gt;# Technical Report TR 1998-17, School of Computing Science, Simon Fraser&lt;br /&gt;# University, November, 1998.&lt;br /&gt;# Copyright (c) 1998, Robert D. Cameron.&lt;br /&gt;# The following code may be freely used and distributed provided that&lt;br /&gt;# this copyright and citation notice remains intact and that modifications&lt;br /&gt;# or additions are clearly identified.&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The regular expressions for parsing the XML file are not modified in any way. Only the language in which the RE are implemented is changed to Python. The RE support is included in the PyS60 package.&lt;/p&gt;&lt;p&gt;The technical report can be found &lt;a href="http://www.cs.sfu.ca/~cameron/REX.html"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Usage&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.cs.uh.edu/~nikhilv/PyS60/XmlParser.py"&gt;XmlParser.py&lt;/a&gt;: This module can be used to&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Parse an XML file &lt;li&gt;Parse an XML string&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;XMLNode Class&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;addProperty(property, value)&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Adds a property and its value to the node. The property is added as an entry to the "properties" dictionary&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;addChild(tag, node)&lt;/font&gt; &lt;br /&gt;&lt;br /&gt;Adds a child to the current node that can be accessed by the "tag". If there is more than one child to the current node by the same "tag", the children are added as an array in the order each child is encountered&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;setContent&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Sets content of the current node. If the current node already contains content, then this content is appended to it&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;properties&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;A dictionary containing properties and their values of the current node&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;childnodes&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;A dictionary containing arrays containing children indexed by their tags&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;content&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;The actual content inside the tags&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;XMLParser Class&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;parseXMLFile(file)&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Parses an XML file&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;parseXML(xmlBuffer)&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Parses XML buffer passed as a string&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;getElementsByTagName(tag)&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Traverses the minimal DOM tree PREorder and returns the array containing node(s) having "tag" name&lt;br /&gt;&lt;br /&gt;&lt;font face="Courier New, Courier, mono"&gt;root&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Holds the root of the DOM tree&lt;br /&gt;&lt;p&gt;The user can decide to disregard some tags (not include them in the DOM tree) by adding them to the following array:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Unsupported tags (HTML formatting tags for displaying info)&lt;br /&gt;unSupportedTags = ['b', 'i', 'u']&lt;/pre&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Example Usage&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Example XML file:&lt;/p&gt;&lt;pre&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;page&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;mumbai india&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;query&amp;gt;mumbai india&amp;lt;/query&amp;gt;&lt;br /&gt;    &amp;lt;request&amp;gt;&lt;br /&gt;        &amp;lt;url&amp;gt;http://maps.google.com/maps?q=mumbai+india&amp;amp;amp;num=10&amp;lt;/url&amp;gt;&lt;br /&gt;        &amp;lt;query&amp;gt;mumbai india&amp;lt;/query&amp;gt;&lt;br /&gt;    &amp;lt;/request&amp;gt;&lt;br /&gt;    &amp;lt;center lat="18.959999" lng="72.819999"/&amp;gt;&lt;br /&gt;    &amp;lt;span lat="0.089989" lng="0.095151"/&amp;gt;&lt;br /&gt;    &amp;lt;overlay panelStyle="/maps?file=gp&amp;amp;amp;hl=en"&amp;gt;&lt;br /&gt;        &amp;lt;location infoStyle="/maps?file=gi&amp;amp;amp;hl=en" id="A"&amp;gt;&lt;br /&gt;            &amp;lt;point lat="18.959999" lng="72.819999"/&amp;gt;&lt;br /&gt;            &amp;lt;icon class="noicon"/&amp;gt;&lt;br /&gt;            &amp;lt;info&amp;gt;&lt;br /&gt;                &amp;lt;address&amp;gt;&lt;br /&gt;                    &amp;lt;line&amp;gt;Bombay&amp;lt;/line&amp;gt;&lt;br /&gt;                    &amp;lt;line&amp;gt;India&amp;lt;/line&amp;gt;&lt;br /&gt;                &amp;lt;/address&amp;gt;&lt;br /&gt;            &amp;lt;/info&amp;gt;&lt;br /&gt;        &amp;lt;/location&amp;gt;&lt;br /&gt;    &amp;lt;/overlay&amp;gt;&lt;br /&gt;&amp;lt;/page&amp;gt;&lt;/pre&gt;&lt;p&gt;To find out the "lat" and "lng" properties of "point" element:&lt;/p&gt;&lt;pre&gt;...&lt;br /&gt;    locxml = ... Above XML ...&lt;br /&gt;    parser = XMLParser()&lt;br /&gt;    parser.parseXML(locxml)&lt;br /&gt;    pointNode = parser.getElementsByTagName('point')&lt;br /&gt;    if pointNode is None:&lt;br /&gt;        appuifw.note(u'Address not found', 'error')&lt;br /&gt;    else:&lt;br /&gt;        addressNode = parser.getElementsByTagName('address')&lt;br /&gt;        lines = []&lt;br /&gt;        if addressNode is not None:&lt;br /&gt;            lineNodes = addressNode.childnodes['line']&lt;br /&gt;            for node in lineNodes:&lt;br /&gt;                lines.append(node.content)&lt;br /&gt;        lat = float(pointNode.properties['lat'])&lt;br /&gt;        lng = float(pointNode.properties['lng'])&lt;br /&gt;...&lt;/pre&gt;&lt;p&gt;&lt;span style="font-family:Georgia, Helvetica;font-size:130%;"&gt;Summary&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:Georgia;"&gt;This little module is an evidence that with minimal effort, complex things can be done with the help of PyS60 on your handset. &lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-116140309513442902?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/116140309513442902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=116140309513442902' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116140309513442902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116140309513442902'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/10/rants-of-snake-charmer-iii.html' title='Rants of a Snake Charmer - III'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-116115157647399833</id><published>2006-10-17T22:50:00.000-07:00</published><updated>2006-10-17T23:45:35.250-07:00</updated><title type='text'>Rants of a Snake Charmer - II</title><content type='html'>&lt;a href="http://photos1.blogger.com/blogger/6620/4035/1600/r2.0.jpg"&gt;&lt;img style="FLOAT: right; MARGIN: 0px 0px 10px 10px; CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/6620/4035/320/r2.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:180%;"&gt;Traffic App&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Background&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;This is a small application that displays the realtime traffic data to the user. I was inspired by a nifty little sample program in the BREW SDK that allowed the San Diego county traffic data available on &lt;a href="http://www.dot.ca.gov/dist11/d11tmc/sdmap/showmap.html"&gt;CalTrans&lt;/a&gt; website to be viewed in a specialized client on the handset. Of course, the BREW SDK being as complicated as it is, this solution is easier on the eyes.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Things You Need&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The PyS60 is an essential subset of Python. All the must-have features of Python are packaged with it. What this also means is most platform independent features of Python can be simply added to PyS60. The task of porting the programs that rely on these features becomes quite straightforward. For the Traffic App, you will need the following modules from your local Python installation:&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;formatter.py &lt;li&gt;htmlentitydefs.py &lt;li&gt;HTMLLIB.PY &lt;li&gt;markupbase.py &lt;li&gt;SGMLLIB.PY&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Once you have copied these modules to the &lt;em&gt;libs&lt;/em&gt; folder (or &lt;em&gt;resource&lt;/em&gt; folder on S60 3rd ed.), we are ready to put the Traffic App in place.&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Network Data&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The data needed for this particular application is requested from the folowing URL:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;North Bound I-15: &lt;a href="http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/nb15.php"&gt;http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/nb15.php&lt;/a&gt; &lt;/li&gt;&lt;li&gt;South Bound I-15: &lt;a href="http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/sb15.php"&gt;http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/sb15.php&lt;/a&gt; &lt;/li&gt;&lt;li&gt;North Bound I-5: &lt;a href="http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/nb5.php"&gt;http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/nb5.php&lt;/a&gt; &lt;/li&gt;&lt;li&gt;South Bound I-5: &lt;a href="http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/sb5.php"&gt;http://www.dot.ca.gov/dist11/d11tmc/sdmap/speeds/sb5.php&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This data is the following format:&lt;/p&gt;&lt;pre&gt;&amp;lt;HTML&amp;gt;&lt;br /&gt;    &amp;lt;HEAD&amp;gt;....&lt;br /&gt;    &amp;lt;/HEAD&amp;gt;&lt;br /&gt;    &amp;lt;BODY&amp;gt;...&lt;br /&gt;        &amp;lt;TABLE&amp;gt;&amp;lt;CAPTION&amp;gt;[Highway Name]--[Date]--[Time]&amp;lt;/CAPTION&amp;gt;&lt;br /&gt;            &amp;lt;TR&amp;gt;&amp;lt;TD class=head&amp;gt;LOCATION&amp;lt;/TD&amp;gt;&amp;lt;TD class=head&amp;gt;SPEED&amp;lt;BR&amp;gt;&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&lt;br /&gt;            &amp;lt;TR&amp;gt;&amp;lt;TD&amp;gt;[Exit 1]&amp;lt;/TD&amp;gt;&amp;lt;TD class=green&amp;gt;58 mph&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&lt;br /&gt;            &amp;lt;TR&amp;gt;&amp;lt;TD&amp;gt;[Exit 2]&amp;lt;/TD&amp;gt;&amp;lt;TD class=green&amp;gt;59 mph&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&lt;br /&gt;            &amp;lt;TR&amp;gt;&amp;lt;TD&amp;gt;[Exit 3]&amp;lt;/TD&amp;gt;&amp;lt;TD class=yellow&amp;gt;37 mph&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&lt;br /&gt;            &amp;lt;TR&amp;gt;&amp;lt;TD&amp;gt;[Exit 4]&amp;lt;/TD&amp;gt;&amp;lt;TD class=red&amp;gt;18 mph&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&lt;br /&gt;            &amp;lt;TR&amp;gt;&amp;lt;TD&amp;gt;[Exit 5]&amp;lt;/TD&amp;gt;&amp;lt;TD class=menuitem&amp;gt;no data&amp;lt;/TD&amp;gt;&amp;lt;/TR&amp;gt;&amp;lt;TR&amp;gt;&lt;br /&gt;        &amp;lt;/TABLE&amp;gt;&lt;br /&gt;    &amp;lt;/BODY&amp;gt;&lt;br /&gt;&amp;lt;/HTML&amp;gt;&lt;/pre&gt;&lt;span style="font-family:Georgia, Helvetica;"&gt;What we need for a bare minimum Traffic App is an HTTP client and a parser that understands the predefined format mentioned above. Since Python and thus PyS60 readily provide the HTTP parser and HTML parser framework, all we need is a simple UI, a traffic parser, and we are done.&lt;/span&gt; &lt;p&gt;&lt;span style="font-family:Courier New, Courier, mono;"&gt;&lt;span style="font-family:Georgia;font-size:130%;"&gt;Code&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:Courier New, Courier, mono;"&gt;&lt;span style="font-family:Georgia;"&gt;The following files are in the codebase:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:Courier New, Courier, mono;"&gt;&lt;span style="font-family:Georgia;"&gt;traffic.py (Module to parse the response from the traffic data server)&lt;/span&gt;&lt;/span&gt; &lt;li&gt;&lt;span style="font-family:Courier New, Courier, mono;"&gt;&lt;span style="font-family:Georgia;"&gt;trafficapp.py (The application)&lt;/span&gt;&lt;/span&gt; &lt;li&gt;&lt;span style="font-family:Courier New, Courier, mono;"&gt;&lt;span style="font-family:Georgia;"&gt;speeds.mbm (Icons)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:Courier New, Courier, mono;"&gt;&lt;span style="font-family:Georgia;"&gt;The code can be found &lt;a href="http://www.cs.uh.edu/~nikhilv/PyS60/trafficapp.zip"&gt;here&lt;/a&gt;. The snapshots are on top of the blog.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:130%;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I hope you enjoyed this little application. I would recommend that you take the following points home:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Talking in HTTP with PyS60 &lt;/li&gt;&lt;li&gt;HTML parsing with PyS60 &lt;li&gt;Using icons in a ListBox&lt;span style="font-family:Courier New, Courier, mono;"&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-116115157647399833?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/116115157647399833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=116115157647399833' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116115157647399833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116115157647399833'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/10/rants-of-snake-charmer-ii.html' title='Rants of a Snake Charmer - II'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-36158856.post-116106083800578592</id><published>2006-10-16T21:47:00.000-07:00</published><updated>2006-10-17T12:42:57.913-07:00</updated><title type='text'>Rants of a Snake Charmer - I</title><content type='html'>&lt;a href="http://photos1.blogger.com/blogger/6620/4035/1600/r1.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/6620/4035/400/r1.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;I have been fiddling a lot with Python ever since &lt;a href="http://opensource.nokia.com/projects/pythonfors60/index.html"&gt;Python for Series 60&lt;/a&gt; was launched. Python on a phone gives power to the end-user (who also wears the shoes usually worn by a developer) to make the phone do fantastic things.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font size=4&gt;Python&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Python is scripting language in the league of languages such as Perl. Perl is a universal measure for one's 'geekiness'. Although I would like to be remembered as a geek, I cannot help but prefer Python for its elegance (as compared to Perl) and as for Perl as a 'geekiness' measure, I cannot call myself a geek! :( &lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;font size=4&gt;Python for Series 60 (Or PyS60)&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Good folks at Nokia Research Center started this project in Dec 2004 and I was right on their tail. Armed with only a Nokia 3650 - a very old Series 60 1.2 product, I started learning the Python language; and became - the Snake Charmer!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The platform has evolved considerably in the past year and a half. Although this platform is good for RAD type prototype development, PyS60 community has produced highly functional and some complex programs.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In this series, I will publish my applications - small and large. These include:&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Real-time Traffic Monitor for San Diego, CA freeways &lt;/li&gt;&lt;br /&gt;&lt;li&gt;XML Parser &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Virtual Earth Maps API&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;I hope you will enjoy these rants of a Snake Charmer...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/36158856-116106083800578592?l=geekyprojects.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://geekyprojects.blogspot.com/feeds/116106083800578592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=36158856&amp;postID=116106083800578592' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116106083800578592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/36158856/posts/default/116106083800578592'/><link rel='alternate' type='text/html' href='http://geekyprojects.blogspot.com/2006/10/rants-of-snake-charmer-i.html' title='Rants of a Snake Charmer - I'/><author><name>Nikhil Vajramushti</name><uri>http://www.blogger.com/profile/10313400305704420758</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www2.cs.uh.edu/~nikhilv/img/nikhil.jpg'/></author><thr:total>0</thr:total></entry></feed>
