Articles in the web category

tinymap.net - now with embedded maps

tinymap.net just got another thing ticked off its to-do list, as it now supports embedding maps in an iframe on your website.

Coming up with an interface to do this was a bit of a pain, but you can see it in action here (or click on the "embed this map" link on every public map). I think there's still update bugs with the Google map inside a re-sizable panel, and every browser wants to layout the panels slightly differently.

But, all going well, you should be able to see a small embedded map just below!

Simple accordion style menu with YUI

I couldn't find a simple reference for the popular "accordion" style fold out menus with the YUI animation libraries (maybe because it is so easy!). Only traps are setting the overflow properties of the fold-out div, and the various DOM height elements.

<script src="http://yui.yahooapis.com/2.2.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script src="http://yui.yahooapis.com/2.2.0/build/animation/animation-min.js"></script>

<script type="text/javascript">

function swap_div(div_name) {
 var div = document.getElementById(div_name);
 var to_height = (div.offsetHeight == 0) ? div.scrollHeight : 0;
 var from_height = (div.offsetHeight == 0) ? 0 : div.scrollHeight;
 var ease_type = (from_height == 0) ? YAHOO.util.Easing.easeOut : YAHOO.util.Easing.easeIn;
 var new_status = (from_height == 0) ? "collapse" : "expand";
 var anim = new YAHOO.util.Anim(div_name, { height: {to: to_height, from: from_height} }, 0.5, ease_type);

 anim.animate();

 document.getElementById(div_name+"_status").innerHTML = new_status;
}

</script>

<div id="status">
  <a id="expand_me_status" href="#" onclick="javascript:swap_div('expand_me');return false">expand</a>
</div>

<div id="expand_me" style="overflow:hidden; height: 0px; background:#eee">
  <p>Hello, this is some text to be expanded out!</p>
</div>

And here's the example in action

expand

Hello, this is some text to be expanded out!

A more advanced version might use the callbacks to regulate the swapping of the expand link, but with short swipe times (as one would want from a UI perspective) this isn't really an issue.

In related news, tinymap.net has had a bit of an overhaul (and includes expanding menus).

tinymap again

tinymap.net appears to have found a small niche, and is getting a lot more hits than I expected. Over a few nights it has gained

  • Passwords for viewing/deleting
  • Changable waypoint color line
  • Export to KML
  • YUI served directly from Yahoo (hopefully faster)
  • Images served directly from Amazon S3 (hopefully faster)
  • Hopefully a slowly improving UI

It is kind of strange and cool that the whole thing hangs together with a mash-up of Google, Yahoo and Amazon services. I considered using Amazon S3 to actually store the map database, but it isn't big enough yet, and I think latency would be an issue.

I have sitemaps that point to KML for public maps, and I was hoping that by pinging Google with the address they would index the KML with the page, and Adsense would serve more relevant, localised ads. So far, despite my pings, Google has not gone for any of the sitemaps -- I think you need to have them under your "webmaster tools" or something. If any Googlers are listening integrating Adsense with KML is going to be a killer app.

Some highlight links

I still have some more ideas, but I don't want to clutter it and loose the KISS approach I desired from the start. It sure is a lot of fun, even if Javascript is the most un-debuggable language ever invented.

tinymap.net

Recently I've had some fun building tinymap.net which is best described as tinyurl for Google maps. You scribble on the map, and then you get a short URL to it. For example, to see my average trip to work go to http://www.tinymap.net/MkJVFx77KSX/.

I of course did no market research or planning and simply started coding, and have since found a few other similar (and fairly cool) sites such as Quikmaps and wayfaring. My site certainly has them beat in the KISS stakes, although it does involve a mashup, some AJAX and has been tagged a lot on del.icio.us so I'm claiming it is Web 2.0!

Easy geotagging

I love the idea of image metadata, but don't like the idea that if I invest the time in adding it to my photos and Flickr disappears then I've lost it all.

I wrote a Python wrapper for libiptcdata (coming to Debian if the NEW queue ever moves) and some tools to add titles, keywords and comments to IPTC data (embedded in the JPEG), which fixes one part of my problem.

Although one day I might have a camera that has a built-in GPS, until that day the next best thing is Google Maps. Since they added Australian street data, I can pretty much pin-point wherever I was in a matter of seconds with a place name search. Using their API I hacked up a latitude and longitude finder page which can search by address, and pops up information in a format suitable for pasting directly to the exiv2 command line to embed the location in the EXIF data. You can also click anywhere to add a mark and then click on it to get its location information.

The only problem was that EXIF expects locations with degrees, minutes and seconds rather than the more modern decimal points, which makes ye feel a bit like a scurvy old sea dog, ahhrrr.

Flickr can also apparently take geographic information from tags, if you paste them in and know the secret page to import it. However, when uploading with EXIF GPS data I found that it placed things on the map straight away and doesn't pollute your existing tags. Yahoo maps are however, at this point, unfortunately a fairly mediocre imitation of Google maps.

Yay Web 2.0!

Django setup notes

This might be helpful if you're new to Django and figuring out how to develop and deploy it effectively.

I tend to keep my projects together in a subversion repository, which means I like everything in one place. I develop locally, and then deployment consists of svn update on the remote server. I generally lay things out like below.

/var/www/project
|-- db
|-- django
|   `-- project
|       |-- __init__.py
|       |-- application
|       |   |-- __init__.py
|       |   |-- models.py
|       |   |-- views.py
|       |-- manage.py
|       |-- settings.py
|       |-- urls.py
|-- media
|   |-- admin
|   |   `-- media -> /usr/share/python-support/python-django/django/contrib/admin/media/
|   |-- images
|   |   `-- something.png
|   |-- blah.css
`-- www
    |-- base.html
    |-- application
        |-- something.html
  • db stores a SQLite DB -- generally enough for me but of course you can use a "real" database. Because my projects are small, a database backup then consists of a svn ci. You need to make sure the web server has permissions to this. As a hack I link /var/www/project/db/dbfile.db into my local development tree, which generally isn't under /var/www/. This makes it easier to deploy.
  • django holds generated stuff, views, models, etc. Run django-admin.py in this directory. Apps live underneath it too.
  • media stores static media. I use the Debian packages, so link into the default store for the admin media files, and setup ADMIN_MEDIA_PREFIX in settings.py to be /media/admin/.
  • www holds the HTML templates.

I then setup Apache with a snippet like

DocumentRoot /var/www/project

<Location "/">
         SetHandler python-program
             PythonHandler django.core.handlers.modpython
             PythonPath "['/var/www/project/django'] + sys.path"
             SetEnv DJANGO_SETTINGS_MODULE project.settings
             PythonDebug On
</Location>

Alias /media /var/www/project/media
<Location "/media">
         SetHandler none
</Location>
<Directory "/var/www/project/media">
          AllowOverride none
              Order allow,deny
              Allow from all
              Options FollowSymLinks Indexes
</Directory>

This lets Apache serve up the static files, which it obviously does well. The only other trick is getting the Django server to serve up the static stuff when I'm developing with it locally. I add the following to the bottom of urls.py

if "GATEWAY_INTERFACE" not in os.environ:
    urlpatterns += patterns('',
            (r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/path/to/local/dev/project/media/'}),
                )

This makes sure it only serves when it is not running under mod_python.

IE, YUI and rendering

If you're using the Yahoo! UI Library and Internet Explorer (particularly IE7) and it randomly bails out with "operation aborted", chances are you are calling some object's render method before IE has had a chance to build the DOM (of course, every other browser seems to work fine). More annoyingly, this seems to be a race condition, so you might not always catch it.

Google suggests many things, but the only sure-fire solution appears to be waiting until the load event before trying to call render. Unfortunately, none of the sample code does this.

So, for example, to render a menu bar you would firstly create a function within your private namespace to do it, e.g.

// create a new namespace
YAHOO.namespace("mynamespace");

// create a function in our namespace to render the menu bar
YAHOO.mynamespace.onMenuBarAvailable = function() {
  var oMenuBar = new YAHOO.widget.MenuBar("render-to-this-div", { width:"100%"});
  oMenuBar.render();
}

Then register this to be run on load, e.g.

// Initialize and render the menu bar when it is available in the DOM
YAHOO.util.Event.addListener(window, "load",  YAHOO.mynamespace.onMenuBarAvailable);

This means your menu bar, if written with ul,li elements might flash up as a list before it is re-drawn as the menu-bar you expect; but this is better than the browser crashing.

Creating webforms quickly

I from time to time want to create a quick static web form to run where PHP etc isn't involved. The best way I've found to do this is with the HTML_QuickForm PEAR package.

To install it on Debian, just install the php-pear package and run sudo pear install HTML_Common HTML_QuickForm and then you can easily create really nifty forms.

For example, the comments form below was created with the following input.

<?php

require_once 'HTML/QuickForm.php';

$form = new HTML_QuickForm('comments_form');

$form->addElement('header', null, 'Add a comment');

$form->addElement('text', 'author', 'Name', array('size' => 50, 'maxlength' => 50));
$form->addRule('author', 'Please enter a name', 'required', null, 'client');

$form->addElement('text', 'email', 'Email', array('size' => 50, 'maxlength' => 50));
$form->addRule('email', 'Please enter an email', 'required', null, 'client');

$form->addElement('text', 'url', 'Website', array('size' => 50, 'maxlength' => 50));

$form->addElement('textarea', 'body', 'Comment:', array('rows'=>10,'cols'=>50));
$form->addRule('body', 'Please enter a comment', 'required', null, 'client');

$form->addElement('submit', null, 'Submit');

$form->display();

?>

It automagically does client side javascript to do first level validation and makes the form look quite nice. You can just run php file.php on the command line to scrape out the plain HTML.