2010/06/25

CSS3 3D Transformations

Here's a nice, concise snippet to demonstrate CSS3 3D transformations, from Superted's Toy Story iAd Navigation post:


items[i].style.webkitTransform = matrix.rotate(newAngle, 0, 0).translate(0, 0, 380);


Want to create a "wheel" of images? Rotate each one by its position around the wheel, so it will face the center of the wheel. Then shove it back (along z) to the edge of the wheel.

2010/06/23

IE 9 + Canvas

From twitter:

jeresig IE 9 shipping with Canvas! http://j.mp/cTyuQg


Huzzah!

Hover on Multi-touch Devices

Technical Note TN2262: Preparing Your Web Content for iPad:

"For example, a mouse pointer can hover over a webpage element and trigger an event; a finger on a Multi-Touch screen cannot."


This is certainly true for multi-touch devices available now, but I wouldn't be surprised to see, someday, touch-sensitive devices which are also proximity-sensitive.

Update 2010/07/08: Others are thinking about implications of hover on multi-touch devices: http://trentwalton.com/2010/07/05/non-hover/ via http://news.ycombinator.com/item?id=1497108.

Update 2010/08/14: A recent Ars Technica article, exploring the similarities between the iPad and ST:TNG's PADD, contained a relevant snippet:
"Still, what new frontiers are out there for interacting with computing devices? Michael Okuda believes that removing the touch requirement will bring new advances in gesture-based control. "Once you don't have to physically touch the screen," he told Ars, "I think yet another window is going to open up.""

Stephen Wolfram on Alan Turing

Here's a part of the "suicide" story with which I was not familiar.

Wolfram|Alpha Blog : Happy Birthday, Alan Turing:

"When one first hears that Alan Turing died by eating an apple impregnated with cyanide one assumes it must have been intentional suicide. But when one later discovers that he was quite a tinkerer, had recently made cyanide for the purpose of electroplating spoons, kept chemicals alongside his food, and was rather a messy individual, the picture becomes a lot less clear."

2010/05/25

Growl - The iPad and immersive computing - O'Reilly Radar

This made me chuckle.

The iPad and immersive computing - O'Reilly Radar:

"I love how focused I am using an iPad, versus working on a laptop. New mail isn't constantly arriving; tweets aren't Growling into view; I don't even have an RSS reader installed."


When Growl first appeared I couldn't imagine why a sane Mac user would want it. How could you make fun of Windows and its incessant, work-stopping notification balloons, and want the same sort of behavior on your Mac? Better to see an alert when something goes wrong than to be interrupted by a balloon whenever something works as expected.

Of course, I long ago installed Growl on my desktop :)

It's not (necessarily) as intrusive as the old Windows notification balloons: you can control which apps generate Growl notifications. As with eye-grating page layouts in early desktop-publishing, or blinking text in early web pages, there's always the danger that you'll turn on all notifications because you can.

I do like the ease with which you can create custom Growl notifications for your own scripts/applications. T.ex. when I create a new timestamp in my VoodooPad worklog, Growl pops up a summary of times by task for the day. Were I not so lazy, I could ask it to notify me when long-running distributed compute jobs have finished; etc.

Anyway, it was funny to read the observations on O'Reilly Radar, written almost as though Growl were part of a standard OS X install.

2010/05/11

Sony's getting back in the game?

NEX-3 and NEX-5 coming in July, and an interchangeable lens camcorder in the fall (Sneak Peek: New Sony Camcorder in Development).

I'm not sure what the camcorder will offer over and above the NEX cameras, but to a person living in the bright environs of northern New Mexico that optional optical viewfinder on the digicams is really appealing. SDHC/SDXC memory card support and automatic HDR look nice, too.

2010/04/28

You have mail?

Recently, when opening new Terminal sessions in Mac OS X, I've started seeing a familiar old message before the first shell prompt.

Last login: Wed Apr 28 06:27:11 on ttys006
You have mail.
mymac:~ my_username$


I assumed some system utility had delivered a notification via /usr/bin/mail. What did the message say?
$ mail
mail: /var/mail/my_username: Is a directory


Indeed, /bin/ls showed an empty my_username directory in /var/mail.

Weird.

In any case I deleted /var/mail/my_username/, and the notifications no longer appear in new Terminal sessions.

2010/04/25

A life well lived

Tomi Pierce must have been an amazing person.

via Tim O'Reilly.

2010/04/06

Eric Margolis

Via Tim O'Reilly, a tribute to Mikhail Gorbachev.

Eric Margolis:

"Gorbachev did not plan to destroy the Soviet Union but to reform and revitalize it. But by refusing to hold it together by force, he brought about its doom.
Gorbachev did the world a huge favor."

2010/03/31

Chrome to Include Flash -- Engadget

Chrome brings Flash Player into the fold, trains it to kill iPads? -- Engadget

As of about a year ago I thought Google didn't really care for Flash. It had accessibility problems and was opaque to search engines.

Apparently Adobe have addressed these issues.

2010/02/22

Mercurial, Migration Assistant, and dotfiles

I recently upgraded my iMac. Migration Assistant moved all of my files to the new machine without issue -- or so it seemed.

I had created Mercurial repositories in a couple of virtualenv environments, to track changes locally.[1] I didn't notice that Mercurial had put each virtual environment's .Python file under revision control.

Shortly after completing the migration I made changes in one of these virtual environments. A quick 'hg status' before committing, and...


$ hg status
abort: data/.Python.i@13b27e856c38: no match found!


WTF?

After much investigation it appears that the following has happened.

  1. Mercurial represented the .Python link in its .hg/store/data directory as ._Python.i
    1. The leading underscore appears to be Mercurial's way of noting that the 'P' should be capitalized.

  2. I think Migration Assistant uses ditto to copy files.
    1. ditto saw the leading '._' and concluded this was an orphaned resource file.[2] So it didn't copy the file.

  3. Mercurial knew that it was supposed to have a .hg/store/data/._Python.i file; when it couldn't find it, it decided the repository was corrupted.



Luckily the problem cropped up before I traded in the old machine, so I was able to copy across the missing files manually.

In my experiments, the problem manifested only when the capitalized dotfile was a symbolic link, not when it was a regular data file.

Well... the above write-up contains several unproven assertions, e.g. about the conditions under which Mercurial will create a '._' filename. I'm not really sure whether this is a bug or merely a caveat regarding an obscure corner condition. For now, the easiest workaround is:

Don't Track virtualenv .Python Files With Mercurial.

[Update 2010/02/22: Someone has already filed this as a Mercurial bug.]



[1] (Mercurial makes a great filesystem "undo" facility, useful even for directory trees which you never intend to share with anyone else.)

[2] OS X still supports something ike resource forks. In tarballs and other non-OS Extended filesystems, resource forks are represented as dot-files with a leading underscore. (See Norman Walsh's blog for more info.) For example, the resource fork for a file named 'foo.txt' might be '._foo.txt'.

2010/02/11

Who will buy the lottery tickets?

EOF - The Google Exposure | Linux Journal:

" The 2008 study found half of all clicks come from lower-income young adults."

2010/01/24

U.S. enables Chinese hacking of Google - CNN.com

U.S. enables Chinese hacking of Google - CNN.com:

"In the aftermath of Google's announcement, some members of Congress are reviving a bill banning U.S. tech companies from working with governments that digitally spy on their citizens. Presumably, those legislators don't understand that their own government is on the list."


From llimllib on Twitter via brettsky.

2009/11/12

TextMate, Emacs and META indent-region

[cross-posted from the Desert Moon blog.]

I haven't used GNU Emacs very much since switching to TextMate in 2005. One Emacs feature which I really miss in TextMate is indent-region. It lets you take an entire region of code, whatever its language, whatever its mix of tabs and spaces and indentation widths, and re-format it using your preferred indentation style.

But wait! Emacs has a batch mode, and you can drive it from TextMate. Many thanks to Gragusa's Things for showing the way.

The post on Gragusa's Things is specific to R code, but I'm more interested in re-formatting C and C++ code. Here's my first cut at a general TextMate Bundle to re-format code regardless of the source language:

#!/usr/local/bin/python2.6
"""
Use Emacs to re-indent regions of the current buffer.
Inspired by
http://gragusa.wordpress.com/2007/11/11/textmate-emacs-like-indentation-for-r-files/
"""
import tempfile
import os
import sys
import subprocess

# Use the same filename extension so Emacs will know which
# mode to use.
ext = os.path.splitext(os.environ["TM_FILEPATH"])[-1]
outf = tempfile.NamedTemporaryFile(suffix=ext, delete=False)
pathname = outf.name

outf.write(os.environ["TM_SELECTED_TEXT"])
outf.close()

args = [
"emacs", "-batch", pathname,
# Assume no emacs-startup.el
"--eval", "(setq indent-tabs-mode nil)",
"--eval", '(c-set-style "java")',
"--eval", "(setq c-basic-offset 4)",
"--eval", "(indent-region (point-min) (point-max) nil)",
"-f", "save-buffer"]
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
if p.returncode:
print(err)

inf = open(pathname, "r")
sys.stdout.write(inf.read())
inf.close()

os.remove(pathname)


NB:
  1. Due to the use of the delete=False keyword argument to tempfile.NamedTemporaryFile, this command bundle requires Python 2.6+.
  2. TextMate on OS X 10.5 won't, by default, have /usr/local/bin in its path; hence the pathetic shebang.


Anyway, install this as a new TextMate command bundle, assign a Key Equivalent such as ⌘-Shift-R, and enjoy.

2009/11/03

Creating an 'hg ignore' extension

[cross-posted from the Desert Moon blog.]

I often wish Mercurial had an 'hg ignore' command similar to 'bzr ignore'. Turns out it's pretty easy to add one:


#!/usr/bin/env python
"""Ignore pathnames and patterns"""

import os

def ignore(ui, repo, *pathnames):
"""Ignore the given pathnames and patterns."""
outf = open(os.path.join(repo.root, ".hgignore"), "a")
for p in pathnames:
outf.write(p + "\n")
outf.close()
return

cmdtable = {
'ignore': (ignore, [], "hg ignore pathname [pathname]"),
}


To use this, save it to a file such as ${HOME}/platform/independent/lib/hg/ignore.py. Then add the extension to your ${HOME}/.hgrc:
[extensions]
~/platform/independent/lib/hg/ignore.py

2009/09/24

Running TileCache within a Django Application

Punchline

Here is how to serve TileCache tile images from within a Django application.


from TileCache.Service import Service

_service = Service(...)

def get_tile(request):
global _service

format, image = _service.dispatchRequest(
request.GET, request.path, request.method,
request.get_host())
result = HttpResponse(str(image), mimetype=format)
return result


Scenario

You're building a low-traffic Django-based GIS application, and you need to serve your own map layers. You're using TileCache to improve your application's performance. But installation and configuration are hassles.

  • All of your servers must run with the right user and group IDs, so the Django app can expire the tile cache when necessary.
  • Your Django app needs to understand the structure of the tile cache, so it can remove the correct tile images when the underlying data changes.
  • Etc.

standalone_tilecache.png


This would all be much easier if you could serve TileCache requests from within your Django application. They're both Python-based; why not?

django_plus_tilecache.png


The TileCache code base includes sample code that shows how to run TileCache as a CGI or a FastCGI service. I couldn't find any sample code for running TileCache within a Django application, but it was easy to convert the cgiHandler code for use with Django's HttpRequest objects.

Installation Prerequisites

In order for TileCache to generate its own tiles, instead of delegating to a separate mapserver instance, you must already have compiled and installed mapserver's Python mapscript bindings. For instructions on compiling the bindings see the mapscript/python/README file in the mapserver source distribution.

Configuring TileCache


import os
thisdir = os.path.abspath(os.path.dirname(__file__))
def relpath(p):
return os.path.abspath(os.path.join(thisdir, p))

from TileCache.Service import Service
import TileCache.Layers.MapServer as MS

# Create the service 'singleton'.
_mapfile = relpath("../mapserv/data/mapfile.map")

_service = Service(
_cache, # See "Cache Invalidation", below
{
"basic": MS.MapServer(
"basic", _mapfile, layers="basic", debug=False),
}
)


Handling Tile Requests

This is the sweet part. It's derived from the cgiHandler() example in the TileCache source code, but Django's HttpRequest class makes the implementation very simple:


def get_tile(request):
global _service

format, image = _service.dispatchRequest(
request.GET, request.path, request.method,
request.get_host())
result = HttpResponse(str(image), mimetype=format)
return result


What About Feature Info Requests?

I don't know much about the required web API of a WMS server, but it appears as if the same URL must serve both tiles and feature info requests; the type of request is determined by the Request querystring parameter.

Django's dispatch system is based on URL pathnames; I'm not aware of any way to dispatch based on query string parameters. So you'll need to either configure your web server (e.g. Apache) to rewrite WMS requests to distinct URLs provided by your Django app, or you'll need to do some dispatch within your Django app.

Suppose you opt for the latter. Then your urls.py might look something like this:

...
url(r'^wms/$', 'world.views.wms', name='wms'),
...

and in world/views.py you might have this:

def wms(request):
if request.GET.get("request") == "GetFeatureInfo":
return get_feature_info(request)
return get_tile(request)

Cache Invalidation

For my web app, several of the tile layers are derived from a Django model which is updated via the admin interface. Whenever the model changes, the tile cache for the corresponding layer(s) needs to be invalidated, so the images can be regenerated.

The TileCache Cache interface doesn't provide for invalidation. Since I'm using a filesystem-based cache, I subclassed TileCache.Caches.Disk to create a Disk cache which does support invalidation.

import shutil
from TileCache.Caches.Disk import Disk

class InvalidatingDisk(Disk):
"""A Disk cache which can invalidate its contents,
layer by layer."""
def invalidate(self, layerName=None):
if self.basedir:
pathname = self.basedir
if layerName is not None:
pathname = os.path.join(self.basedir,
layerName)
shutil.rmtree(pathname, ignore_errors=True)

2009/09/22

Introducing Google Chrome Frame

Introducing Google Chrome Frame:

"With Google Chrome Frame, developers can now take advantage of the latest open web technologies, even in Internet Explorer. From a faster Javascript engine, to support for current web technologies like HTML5's offline capabilities and <canvas>, to modern CSS/Layout handling, Google Chrome Frame enables these features within IE with no additional coding or testing for different browser versions.
To start using Google Chrome Frame, all developers need to do is to add a single tag:


<meta equiv="X-UA-Compatible" content="chrome=1">
"


I guess that's good news. Makes you wonder why the target audience wouldn't just install Google Chrome. But I suppose this lets people continue to use IE while using modern web facilites on sites which require them.

2009/09/14

TR: China Wind Energy Potential, HVDC

(Just taking notes, trying to understand what HVDC is, what it has to do with variable power sources such as wind, and why it makes buried transmission lines convenient.)

Technology Review: China's Potent Wind Potential:

"The major grid upgrades already under way in China are making extensive use of continental-scale high-voltage direct-current (HVDC) lines, which remain the stuff of supergrid blueprints in Europe and the United States. 'They are leading the world in implementing long-distance transmission schemes,' says Bjarne Andersen, director of U.K.-based consultancy Andersen Power Electronic Solutions and an expert in the ultra-efficient HVDC technology."


Technology Review: Europe Backs Supergrids:
"This summer [2008], for example, a negotiator appointed by the EC convinced France to accept a new transmission connection with Spain, breaking a 15-year impasse over expanding power exchanges between the countries. Use of high-voltage DC (HVDC) technology will enable planners to bury the new line and thereby overcome local opposition to conventional overhead AC transmission lines."


Wikipedia explains how HVDC can have low power losses:
"Power in a circuit is proportional to the current, but the power lost as heat in the wires is proportional to the square of the current. However, power is also proportional to voltage, so for a given power level, higher voltage can be traded off for lower current. Thus, the higher the voltage, the lower the power loss."
"The advantage of HVDC is the ability to transmit large amounts of power over long distances with lower capital costs and with lower losses than AC."


Lots of interesting stuff in the Wikipedia article. This excerpt seems to explain the connection between HVDC and variable power sources:
"Because HVDC allows power transmission between unsynchronised AC distribution systems, it can help increase system stability, by preventing cascading failures from propagating from one part of a wider power transmission grid to another. Changes in load that would cause portions of an AC network to become unsynchronized and separate would not similarly affect a DC link, and the power flow through the DC link would tend to stabilize the AC network. The magnitude and direction of power flow through a DC link can be directly commanded, and changed as needed to support the AC networks at either end of the DC link. This has caused many power system operators to contemplate wider use of HVDC technology for its stability benefits alone."


2009/08/17

ActionScript: TileList, deleting, scrolling backwards

I have a Flex app which shows users a TiledList of chemical structure depictions. Since it can be a large list, it's lazy-loaded from the server as the user scrolls through the list.

Users can delete items from the list, with undo. In order to do this with reasonable performance, once the app has received confirmation from the server that an item has been deleted, it clears out the single deleted item from its local lazy-list.

To undo the deletion locally, the Flex app fills the correct lazy-list entry with an ItemPendingError; that error gets thrown as soon as the TileList tries to retrieve the item.

All of this works okay when the row containing the undeleted item is already visible. On the other hand, if the user has scrolled away from the row where the undeleted item will reappear, then when (s)he scrolls back the TileList simply empties out that item and all of the successive items in the row. Ugly!

ugly_repaint.png


Workaround

When the item is undeleted, immediately try to retrieve it via getItemAt(itemIndex). Catch the resulting ItemPendingError and register an ItemResponder. When the ItemResponder's result or fault method is called, tell the TileList to invalidateList(). If the undeleted item actually contains a value, the TileList will repaint correctly -- no more unsightly gaps.


import mx.collections.errors.ItemPendingError;
import mx.collections.ItemResponder;
[...]
try {
structures.getItemAt(offset);
} catch (e:ItemPendingError) {
e.addResponder(
new ItemResponder(
function(result:Object, token:Object = null):void {
tilelist.invalidateList();
},
function(error:Object, token:Object = null):void {
tilelist.invalidateList();
}));
}