<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/topics-files/atom2xhtml.xsl" type="text/xsl"?>
<!-- This is a 512 byte XML comment that one must put into XML Atom feeds
such that browsers like Firefox 2.0 and IE7 will obey the XSL stylesheet.
Everybody hates overbearing browsers.
This is a 512 byte XML comment that one must put into XML Atom feeds
such that browsers like Firefox 2.0 and IE7 will obey the XSL stylesheet.
Everybody hates overbearing browsers.
This is a 512 byte XML comment that one must put into XML Atom feeds
such that browsers like Firefox 2.0 and IE7 will obey the XSL stylesheet.
Everybody hates overbearing browsers.
This is a 512 byte XML comment that one must put into XML Atom feeds
such that browsers like Firefox 2.0 and IE7 will obey the XSL stylesheet.
Everybody hates overbearing browsers.
This is a 512 byte XML comment that one must put into XML Atom feeds
such that browsers like Firefox 2.0 and IE7 will obey the XSL stylesheet.
Everybody hates overbearing browsers.
This is a 512 byte XML comment that one must put into XML Atom feeds
such that browsers like Firefox 2.0 and IE7 will obey the XSL stylesheet.
Everybody hates overbearing browsers. -->
<feed xmlns="http://www.w3.org/2005/Atom"
><title
>Blog@Case Topics: projects</title
><link rel="self" href="http://blog.case.edu/topics/projects"
 /><id
>http://blog.case.edu/topics/projects</id
><category term="projects" label="projects"
 /><link rel="related" href="http://blog.case.edu/topics/programming" title="programming"
 /><link rel="related" href="http://blog.case.edu/topics/python" title="python"
 /><link rel="related" href="http://blog.case.edu/topics/google" title="google"
 /><link rel="related" href="http://blog.case.edu/topics/case" title="case"
 /><link rel="related" href="http://blog.case.edu/topics/summer%20of%20code" title="summer of code"
 /><link rel="related" href="http://blog.case.edu/topics/kde" title="kde"
 /><link rel="related" href="http://blog.case.edu/topics/knoware" title="knoware"
 /><link rel="related" href="http://blog.case.edu/topics/google%20maps" title="google maps"
 /><link rel="related" href="http://blog.case.edu/topics/javascript" title="javascript"
 /><link rel="related" href="http://blog.case.edu/topics/pagoda" title="pagoda"
 /><link rel="related" href="http://blog.case.edu/topics/geopy" title="geopy"
 /><contributor
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></contributor
><contributor
><name
>Erin Wolverton</name
><email
>erin.wolverton@case.edu</email
><uri
>http://blog.case.edu/cereal</uri
></contributor
><updated
>2007-09-26T04:07:35Z</updated
><entry
><title
>And on a non-irate note...</title
><link href="http://blog.case.edu/cereal/2009/05/20/and_on_a_nonirate_note"
 /><id
>http://blog.case.edu/cereal/2009/05/20/and_on_a_nonirate_note</id
><published
>2009-05-21T03:41:39Z</published
><updated
>2009-05-21T03:44:05Z</updated
><category term="goals" label="goals"
 /><category term="projects" label="projects"
 /><category term="summer movie watch" label="summer movie watch"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>I just discovered that my Summer Movie Watch website was still password-protected. It's been taken care of, and everybody's free to view it. 
<a href="http://filer.case.edu/elw49/summermoviewatch/">Enjoy</a>!</div
></content
><author
><name
>Erin Wolverton</name
><email
>erin.wolverton@case.edu</email
><uri
>http://blog.case.edu/cereal</uri
></author
></entry
><entry
><title
>Summer project!</title
><link href="http://blog.case.edu/cereal/2009/05/19/summer_project"
 /><id
>http://blog.case.edu/cereal/2009/05/19/summer_project</id
><published
>2009-05-19T08:20:56Z</published
><updated
>2009-05-21T03:45:10Z</updated
><category term="american film institute" label="american film institute"
 /><category term="entertainment weekly" label="entertainment weekly"
 /><category term="goals" label="goals"
 /><category term="movies" label="movies"
 /><category term="oscars" label="oscars"
 /><category term="projects" label="projects"
 /><category term="saving private ryan" label="saving private ryan"
 /><category term="schindler's list" label="schindler's list"
 /><category term="shakespeare in love" label="shakespeare in love"
 /><category term="stagecoach" label="stagecoach"
 /><category term="summer movie watch" label="summer movie watch"
 /><category term="terminator 2" label="terminator 2"
 /><category term="unforgiven" label="unforgiven"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>
<img alt="movie reel.jpg" src="http://blog.case.edu/cereal/2009/05/19/movie%20reel.jpg" width="120" height="150" /> As people who know me know, I am a huge follower of award shows, and of &#226;&#8364;&#339;best of&#226;&#8364; lists. I love to see stuff ranked, and to see quality get celebrated (or even debated: I&#226;&#8364;&#8482;ve argued with a good many people over the years about 
<a href="http://www.ew.com/ew/gallery/0,,20007870_20164474_20250883_8,00.html">whether 
<em>Shakespeare in Love</em> should have bested 
<em>Saving Private Ryan</em> for Best Picture at the Oscars back in &#226;&#8364;&#8482;98</a>). Award shows and &#226;&#8364;&#339;best of&#226;&#8364; lists are great guides for someone who wants to actively seek out movies with great reputations. One of the best movie list-makers is the 
<a href="http://www.afi.com/tvevents/100years/movies.aspx">American Film Institute</a>. They release a new list pretty much every year (they&#226;&#8364;&#8482;ve done 100 Best Comedies, 100 Best Characters, etc.) and they have two 100 Best American Films lists, the original from 1998, and then a revision in 2007. The difference between the two lists is 23 films, some of which were movies that people thought had been overlooked, and some of which first appeared after 1998 (for example, 
<em>
<a href="http://www.imdb.com/title/tt0120815/">Saving Private Ryan</a>
</em>, mentioned above). 
<em>Entertainment Weekly</em> also has a 100 Best list, but they do not compete with the AFI; the 
<em>Entertainment Weekly</em> list is &#226;&#8364;&#339;new classics,&#226;&#8364; all films originating in the 25 years between 1983 and 2008. I am a notorious goal-setter and list-maker, and these kinds of lists indulge both of those attributes (or flaws, depending on how you run your life). So, the first in a series of goals I&#226;&#8364;&#8482;ll be releasing out into cyberspace (check back on my birthday for more) is this: see 91 specific movies, the ones missing from those three lists, and thus become master of three &#226;&#8364;&#339;best of&#226;&#8364; lists. Before I began the project, my record was as follows:
<blockquote>AFI 1998: seen 58, not seen 42
<br />AFI 2007: seen 54, not seen 46
<br />EW: seen 61, not seen 39</blockquote>With overlap (
<em>
<a href="http://www.imdb.com/title/tt0108052/">Schindler&#226;&#8364;&#8482;s List</a>
</em> and 
<em>
<a href="http://www.imdb.com/title/tt0105695/">Unforgiven</a>
</em> are two movies I haven&#226;&#8364;&#8482;t seen, which both appear on all three lists; some other movies appear on two) the number of movies I need to watch to lay waste to these lists is 91. I created 
<a href="https://filer.case.edu/elw49/summermoviewatch/">a website</a> where I&#226;&#8364;&#8482;m tracking my progress; I've linked it on the sidebar as well. I&#226;&#8364;&#8482;m off to a pretty good start, having seen five new movies since my summer vacation began. Am I serious about this? Well, I watched 
<em>
<a href="http://www.imdb.com/title/tt0031971/">Stagecoach</a>
</em> last week, and 
<em>
<a href="http://www.imdb.com/title/tt0103064/">Terminator 2</a>
</em>. That's serious!</div
></content
><author
><name
>Erin Wolverton</name
><email
>erin.wolverton@case.edu</email
><uri
>http://blog.case.edu/cereal</uri
></author
></entry
><entry
><title
>September Projects</title
><link href="http://blog.case.edu/bmb12/2007/09/september_projects"
 /><id
>http://blog.case.edu/bmb12/2007/09/september_projects</id
><published
>2007-09-26T03:45:29Z</published
><updated
>2007-09-26T04:07:35Z</updated
><category term="Pagoda" label="Pagoda"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><category term="geopy" label="geopy"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>I'm in the situation 
<a href="http://blog.ianbicking.org">Ian Bicking</a> was in not long ago&#8212;I'm really tired of this blog design and software and it's making me not want to post any of the entries I have pending. This blog will soon redirect to something better. 
<a href="http://pagodacms.org/">Pagoda</a> should have a Developer Preview in October. Check out 
<a href="http://exogen.case.edu/clepy_pagoda/">my presentation</a> from the September meeting of the 
<a href="http://clepy.org/">Cleveland Python interest group</a>. Remember how Ian and I spent months thinking up hundreds of names for our company? We are now incorporated as 
<a href="http://unstoppablerocket.com">Unstoppable Rocket</a>&#8212;one of the first names that was suggested. If you've been following the 
<a href="http://groups.google.com/group/geopy">geopy list</a>, you've heard about the new release coming out. It should make things much more flexible and extendable, and fix all the issues from the past year or so. geopy 0.99 will be out this week. The geopy update is also getting me back into the 
<a href="http://exogen.case.edu/crime/recent/">campus crime map</a> and my Case geocoder service, which is going to be really smart. Updates there soon. I started a new project called Revisionist, which is like 
<a href="http://blog.case.edu/bmb12/2007/06/pagoda_revisions">Pagoda's revision model</a> except generalized and using SQLAlchemy 0.4. I'm hoping other people will be interested in using and improving such a project. With the right helpers it should make revisioning complex models really easy. If anyone has any neat suggestions for what Gary or I should talk about at the October Clepy meeting, let me know.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Project and apartment updates</title
><link href="http://blog.case.edu/bmb12/2007/05/project_and_apartment_updates"
 /><id
>http://blog.case.edu/bmb12/2007/05/project_and_apartment_updates</id
><published
>2007-05-04T06:49:21Z</published
><updated
>2007-05-04T08:02:02Z</updated
><category term="Cleveland" label="Cleveland"
 /><category term="Pagoda" label="Pagoda"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>Some interesting bits from the past few weeks... Next Tuesday I'll be having lunch with Mike Cermak, webmaster for the 
<a href="http://www.gcrta.org">Greater Cleveland Regional Transit Authority</a>. In 
<a href="http://blog.case.edu/bmb12/2007/04/quicker_cleveland_rta_schedule_with_django">my previous entry</a> I mentioned my 
<a href="http://exogen.case.edu/rta/">RTA Schedule</a> project which has been gaining popularity. There were only a few routes listed on there when I posted it, and the list has been growing as people have been using the route adder. Mike wants to work together to come up with ideas and improvements that will encourage projects like mine&#8212;a very cool response, and beneficial to RTA users as well. I'm looking forward to it! Remember 
<a href="http://blog.case.edu/bmb12/2007/04/multiple_sites_one_python_pagoda_import_tricks">those wacky import tricks I posted about</a> to get multiple database engines working nicely in 
<a href="http://www.pagodacms.org">Pagoda</a>? After coming up with that, Ian dug around to figure out what changes would be necessary to not have to do that. He narrowed it down to one single line of code in TurboGears! In 
<a href="http://svn.turbogears.org/branches/1.0/turbogears/database.py">
<tt>turbogears.database</tt>
</a>:
<pre>
<code>def create_session():
    "Creates a session with the appropriate engine"
    return sqlalchemy.create_session(bind_to=get_engine())</code>
</pre>That 
<tt>bind_to</tt> argument is totally unnecessary when using 
<tt>DynamicMetaData</tt>! Changing that to just use SQLAlchemy's 
<tt>create_session</tt> without arguments makes multiple database engines possible without any black magic. Unfortunately, we didn't notice 
<a href="http://www.blueskyonmars.com/2007/05/02/turbogears-102-released/">TurboGears 1.0.2</a> about to be released and didn't start any discussion about changing this in time. For now we use this little monkeypatch:
<pre>
<code>session_context = turbogears.database.session.context
session_context.registry.createfunc = sqlalchemy.create_session</code>
</pre>So I think it works more like 
<a href="http://cheeseshop.python.org/pypi/Alchemyware">Alchemyware</a> now, except we don't have to write models any differently and the engines are cached. The metadata is simply pointed to the appropriate engine in each thread. Speaking of 
<a href="http://www.pagodacms.org">Pagoda</a>, we're still at least a couple weeks away from a beta release. We're currently writing glue for all the little bits and pieces we've created over the past couple months. We've satisfied many of our original goals and learned more about (and sometimes changed) others. I'll share more about these satisfied and modified goals later. Pagoda's third contributor, 
<a href="http://www.cshesse.com">Chris</a>, moved back home to start hunting for jobs in the California area. Good luck, Chris! Chris is a fine electrical engineer and programmer and you should hire him. This was his plan since starting to help with Pagoda, so it doesn't really affect our development schedule. After receiving practically no feedback from the release of 
<a href="http://code.google.com/p/dmath/">dmath</a>, there has been a small surge of interest recently, with a couple contributions, so there will likely be a new release. I put up a 
<a href="http://cheeseshop.python.org/pypi/dmath">new egg</a> of the old version on the Cheese Shop after learning that the Python 2.5 version was busted. 
<a href="http://exogen.case.edu/projects/geopy/">geopy</a> continues to receive patches; recently the most-requested improvement was contributed by 
<a href="http://latteier.com/">Amos Latteier</a> and that is the removal of 
<tt>print</tt> chatter in favor of logging. I'll get 0.94 out this weekend with that and other improvements. Since Chris moved out, our friend Greg moved in with me and Sara. Greg went to school for art and likes to paint and draw, and might even prove his cooking talents at culinary school next semester. I'll be helping him make a website for his comics, which are very funny, but I can't decide if it's because I know Greg and imagine him coming up with them, which itself makes me laugh. You'll be the judge soon enough... There are two more new, smaller residents of our apartment as well... one's a 14-inch 
<a href="http://en.wikipedia.org/wiki/Oscar_%28fish%29">Oscar cichlid</a> and the other's a 15-inch 
<a href="http://en.wikipedia.org/wiki/Plecostomus">Plecostomus</a>. They're friendly and big! Now I have fantasies about getting them a bigger aquarium with all manner of luxuries. I picked them up from someone who's graduating and they came with their 45-gallon home and necessities for free! I'll post some pictures of these guys soon.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Multiple sites, one Python: Pagoda import tricks</title
><link href="http://blog.case.edu/bmb12/2007/04/multiple_sites_one_python_pagoda_import_tricks"
 /><id
>http://blog.case.edu/bmb12/2007/04/multiple_sites_one_python_pagoda_import_tricks</id
><published
>2007-04-17T23:57:24Z</published
><updated
>2007-04-18T00:30:43Z</updated
><category term="Pagoda" label="Pagoda"
 /><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>One of our early goals when designing 
<a href="http://www.pagodacms.org">Pagoda</a> was to allow a single Pagoda instance to support multiple sites. This was due to the way memory works for web servers running on Python and TurboGears. How exactly this adds up depends on your threading and web server configuration (mod_python), but traditionally hosting multiple sites means running at least one Python instance per site, each costing 10-20 MB. The more modules each instance loads, the higher the memory usage, and since Pagoda sites will likely use a bunch of modules, that adds up. The most limiting factor in many hosting services is the amount of memory your account is allowed to consume. Obviously if each Pagoda site is large and running custom code, it might be a good idea to run each in its own Python instance, so one site can't bring down all the others. But the common case, we think, is a bunch of moderately sized sites using just the built-in page management tools. So we devised some ways to allow multiple sites to run from one TurboGears project... The first and simplest plan involved a database model, where pages and other table rows point to whichever site they belong to. You probably already know why this is a bad idea. First of all, every single table in the database needed to have a site_id column, since nothing would be shared between sites. Unique things like usernames would need their constraints modified to only be unique per-site. That got old pretty fast. Secondly was security. How could we ensure that every piece of code touching the database, even the eventual third-party plugins, would use the correct site in their queries so as not to mess with the others? And finally, having each site's contents in one massive database would not be very convenient if the site owners wanted backups of their portion of the database. So we started looking at multi-database solutions, and quickly realized we were pretty much on our own for what we wanted to do. We don't just want some models in one database, and other models in a difference database; we want the same models in every database. Every site needs a pages table, for example. Since we're mapping tables with 
<a href="http://www.sqlalchemy.org">SQLAlchemy</a>, and each mapper is bound to 
<a href="http://www.sqlalchemy.org/docs/metadata.html">metadata</a>, an engine, and a session, it seems that we'd need to run the table and mapper definitions once per site; each time, the engine would point to the appropriate site's. And now the big trick: how do we do this without modifying any model code, so that plugin writers don't have to learn any silly new details, and without doing a bunch of extra work every time a controller needs to use a model? If our controllers import 
<tt>pagoda.models.pages</tt>, how will it know to get the 
<tt>Page</tt> class bound to the current site's engine, and not another site's? We looked to 
<a href="http://www.cherrypy.org">CherryPy</a> for inspiration. In a TurboGears controller, importing 
<tt>cherrypy.request</tt> and 
<tt>cherrypy.response</tt> will make the current thread's request and response objects available. How do these objects magically belong to the appropriate thread? They simply use a class called 
<tt>ThreadLocalProxy</tt>. As the name suggests, 
<tt>cherrypy.request</tt> and 
<tt>cherrypy.response</tt> are proxy objects that determine the current thread and point object access to the correct 
<tt>request</tt> and 
<tt>response</tt> instances. Similarly, we want something like 
<tt>SiteLocalProxy</tt>, which will make model classes available that are magically bound to the correct site's engine. Using 
<tt>ThreadLocalProxy</tt> as inspiration, we made a clever little object called 
<tt>site</tt>. When anything is imported from 
<tt>pagoda.site</tt>, it will rebind 
<tt>turbogears.database.metadata</tt> and 
<tt>turbogears.database.session</tt> after updating 
<tt>sqlalchemy.dburi</tt> in the config to point to the current site's. Then the requested module is imported and cached for next time (so the models aren't reinitialized every time). No model code was changed at all! The only necessary modification was importing from 
<tt>pagoda.site.models</tt> instead of 
<tt>pagoda.models</tt> in our controllers. Our first implementation looked very much like 
<tt>ThreadLocalProxy</tt>, but it made our import statements look funny since 
<tt>site</tt> wasn't a real module. So we started investigating the 
<tt>imp</tt>, 
<tt>ihooks</tt>, and 
<tt>imputils</tt> modules, eventually leading us to 
<a href="http://www.python.org/dev/peps/pep-0302/">PEP 302</a>. With help from 
<a href="http://peak.telecommunity.com/DevCenter/Importing">Importing</a> (to reduce the amount of code necessary), we now have a special pseudo-module called 
<tt>site</tt>, and Pagoda modules imported from that will take the current request's site into account instead of just being imported once for the entire process. Before writing up this entry, I came across 
<a href="http://cheeseshop.python.org/pypi/Alchemyware">Alchemyware</a>. At first it looked promising for what we want to do, but as far as I can tell it requires modifying the way you write models and reinstantiating them on every request. Also, I don't understand how the mapped class can be "shared by everyone" if it's being mapped to multiple databases. Anyway, after cleaning up our proof-of-concept I'll share the code behind our import trickery in case anyone is trying to do something similar, but mostly just because such tricks are interesting. In case you forgot, we missed the end-of-March deadline we set for our demo, due in part to being burned out after PyCon. We're shooting for the end of April now.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Pagoda CMS Notes</title
><link href="http://blog.case.edu/bmb12/2007/03/pagoda_cms_notes"
 /><id
>http://blog.case.edu/bmb12/2007/03/pagoda_cms_notes</id
><published
>2007-03-03T01:11:09Z</published
><updated
>2007-03-03T17:06:02Z</updated
><category term="Pagoda" label="Pagoda"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>
<div style="text-align: center">
<img alt="pagoda_logo.png" src="http://blog.case.edu/bmb12/2007/03/02/pagoda_logo.png" width="176" height="269" />
</div>If you were at 
<a href="http://us.pycon.org/TX2007/HomePage">PyCon 2007</a> or read 
<a href="http://blog.extracheese.org">Gary's blog</a> or read the 
<a href="http://groups.google.com/group/turbogears">TurboGears mailing list</a>, you may have seen 
<a href="http://www.pagodacms.org">Pagoda CMS</a> mentioned. Pagoda is an open source content management system I've been working on with 
<a href="http://blug.cshesse.com/">Chris</a> and 
<a href="http://www.iancharnas.com">Ian</a>. It's built on 
<a href="http://www.turbogears.org">TurboGears</a> and is focused on being simple yet extensible. We put up an 
<a href="http://www.pagodacms.org/screencast/swf/index.html">introductory screencast</a> at 
<a href="http://www.pagodacms.org">pagodacms.org</a> that we hurriedly made the night before PyCon. We've tried a bunch of content management systems, both open source and commercial, and developed for small shops, big corporations, government organizations, and of course 
<a href="http://eecs.case.edu">Case itself</a>. There are features that are consistently implemented poorly, hard to understand, or simply missing. Pagoda is a result of the observations we've made of how content management systems are really used in a production setting. These are just a few of the notes and design goals we've been using along the way.
<h3>Don't overengineer it</h3>Somewhere along the line someone decided that if you're going to make a content management system, you have to build everything on top of a dozen layers of abstraction. Some pretend that there's no difference between static page content (like a blog entry) and dynamically generated content (like a news feed). Some pretend that building complex workflows that are exactly suited to the way your organization is structured is a common need (we've found that people already have real-life workflows and rarely do they want this duplicated in a CMS). Experience has shown us that such complexity is rarely needed. We don't try to fit every feature into a "plugin" structure or an "actions" framework. We've streamlined the features based on our experience, and hopefully kept it fun to hack on (when you do need something extra) by avoiding meaningless abstractions.
<h3>Do one thing really, really well</h3>A lot of content management systems try to do everything involved in running a web site. Database management, email management, form design, you name it. We don't want a content management system that takes over every aspect of making a web site. We've made conscious decisions to leave a lot of features out. In addition to the above (which can all be found in Zope + Plone, for example), we've spent a lot of time deciding how far certain features should reach and what should be left up to the webmaster. One example is theme switching. When you're first building a web site, being able to download prepackaged themes might be nice. But for production sites, this simply does not happen. Imagine the 
<a href="http://www.cmnh.org">Cleveland Museum of Natural History</a> or a 
<a href="http://eecs.case.edu">university department</a> downloading new themes and swapping them out. Not gonna happen. Instead this is limiting, because prepackaged themes require predetermined markup. As a result, most 
<a href="http://www.plone.org">Plone</a> sites look the same and are structured the same way. They have the little tree on the left and those tiny tabs and a logo above that. And then you're scared to modify too much CSS because there's a bunch already dedicated to making those tabs pixel-perfect. We don't have a default theme or even default markup. Markup and design are meant for programmers and web designers, let's not pretend otherwise.
<h3>Use simple terminology</h3>As Jeffrey Veen mentioned in 
<a href="http://www.adaptivepath.com/publications/essays/archives/000365.php">Making A Better CMS</a>, 
<strong>stop it with the jargon already!</strong> "Mambots", "archetypes", "portlets", and I'll admit it, I'm not even a fan of the term "widgets". We've tried to use understandable terminology throughout Pagoda and not extend failed analogies. One example where we created a feature and spent some effort on choosing a name is Placeholders. This is a feature that we've actually needed on production sites but haven't found in other content management systems. The idea is that there is text that appears on multiple pages 
<em>within the content</em>, and it would be nice to only have to change in one place so we don't have to hunt down every page in the future. Phone numbers, store hours, admission prices, and press contact information are some examples. These aren't template variables because they have nothing to do with templates (to the user) and aren't arbitrary Python objects, and they're not code snippets because they have nothing to do with code. They're simply content placeholders. Here's the mockup we used while implementing this feature:
<div style="text-align: center;">
<img alt="placeholders.png" src="http://blog.case.edu/bmb12/2007/03/02/placeholders.png" width="574" height="588" />
</div>
<h3>Borrow features that work</h3>We've had a lot of inspiration along the way and used it to solve real problems. For example, if you need to have a downloadable file on your web site, a lot of content management systems will force you to ask "where do I put this?" and once you've decided on a place, require you to find your way there in the filesystem. We decided on pages having Attachments. Most downloads are associated with a particular page, so just upload them to that page and that will determine their location. We used 
<a href="http://www.campfirenow.com/tour/">37signal's Campfire</a> for inspiration, where people can upload files to the room they're in and they appear as attachments.
<h3>Reduce the number of clicks</h3>We're lucky enough to have started developing after AJAX became popular. The "Web 2.0" buzzword might be annoying, but this is really something we can use to make content management quicker and easier. Navigation and messing around with page options won't require dozens of clicks and page reloads anymore. Instead of having to retrofit our software to take advantage of AJAX, we can design with it in mind.
<h3>Built a content management system, not a new framework</h3>Similar to doing one thing really well, we're not building a web framework. That's what TurboGears is for. People can still use their existing TurboGears controllers, models, and templates. We're using 
<a href="http://www.sqlalchemy.org">SQLAlchemy</a> for Pagoda's models and 
<a href="http://genshi.edgewall.org">Genshi</a> for the templates. To install Pagoda for your existing TurboGears project, you'll just have to subclass from PagodaController instead of the default RootController, so Pagoda can dispatch requests to the appropriate page. So hopefully it sounds like an interesting project. We're still hacking on the core and hope to release a demo before the end of March, when we'll also invite people to help out and find weak spots. We have some mailing lists on Google Groups for discussion: 
<a href="http://groups.google.com/group/pagoda-talk">pagoda-talk</a> (general discussion), 
<a href="http://groups.google.com/group/pagoda-coders">pagoda-coders</a> (core development), and 
<a href="http://groups.google.com/group/pagoda-announcements">pagoda-announcements</a> (for releases and other notices). For the first few releases we'll also make announcements on the TurboGears list.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Japan Loves Sucks/Rocks</title
><link href="http://blog.case.edu/bmb12/2007/03/japan_loves_sucksrocks"
 /><id
>http://blog.case.edu/bmb12/2007/03/japan_loves_sucksrocks</id
><published
>2007-03-02T20:24:45Z</published
><updated
>2007-03-02T20:38:41Z</updated
><category term="Projects" label="Projects"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>Last night, 
<a href="http://blog.extracheese.org">Gary</a> and I were in Jennings making some improvements to 
<a href="http://www.sucks-rocks.com">sucks/rocks</a>. We switched to 
<a href="http://pythonpaste.org/">Paste's HTTP server</a> so that queries to the search API wouldn't block the whole server (which was single-threaded). This was a big problem because after running out of both Yahoo and Google queries, we had to fall back to 
<a href="http://msdn2.microsoft.com/en-us/library/bb251794.aspx">Microsoft Live's</a>, which is the slowest to respond by far, and has an annoyingly small index. While doing that and other minor changes, Gary noticed the server log start going crazy. "Holy crap! Someone's sending tons of traffic our way!" I asked him to check the referrer. "I just hope it's not &#8212; shit, it's Slashdot! Japanese Slashdot!" We checked 
<a href="http://slashdot.jp/article.pl?sid=07/03/02/0552234">slashdot.jp</a> and sure enough, it was the latest story. We went into panic mode as our CPU usage on 
<a href="http://www.webfaction.com/">WebFaction</a> grew to 50% as we served multiple requests per second. We were able to make a few changes and get it back down around 6%, and Paste's HTTP server kept us online the whole time! We also noticed a bunch of other Japanese sites in our logs, including 
<a href="http://headlines.yahoo.co.jp/hl">headlines.yahoo.co.jp</a> , 
<a href="http://www.100shiki.com/">100shiki.com</a>, and 
<a href="http://japan.internet.com">japan.internet.com</a>. Japanese people love sucks/rocks! Our Google Analytics graph for this morning is below. 
<img alt="traffic-friday.png" src="http://blog.case.edu/bmb12/2007/03/02/traffic-friday.png" width="350" height="225" /> 
<img alt="geo-location.png" src="http://blog.case.edu/bmb12/2007/03/02/geo-location.png" width="410" height="136" /> While Slashdot Japan was busy hammering us, we added a Latest searches box where you can watch incoming searches scroll by. We had been doing this before by watching the server log, and figured others would enjoy it too. This was flying by with all the traffic last night. (You can see below how crappy Microsoft's index is &#8212; they don't even have any results for xbox360, with or without the space!) 
<img alt="latest.png" src="http://blog.case.edu/bmb12/2007/03/02/latest.png" width="384" height="306" /> I'm not sure how many more features such a silly little project can handle, but Gary seems to have started working on a new (unrelated) project.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>sucks/rocks: Does it suck?</title
><link href="http://blog.case.edu/bmb12/2007/02/sucksrocks_does_it_suck"
 /><id
>http://blog.case.edu/bmb12/2007/02/sucksrocks_does_it_suck</id
><published
>2007-02-28T07:08:13Z</published
><updated
>2007-02-28T08:00:44Z</updated
><category term="Projects" label="Projects"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>A few nights ago 
<a href="http://blog.extracheese.org/">Gary</a> and I were in 
<a href="http://www.eecs.case.edu/tech/labs/unix">JCC</a> trying to think of a quick project to make together. Someone suggested a web app that uses Gary's algorithm for evaluating software and companies: Google for "software/company sucks" and "software/company rocks" and compare the number of results. The result is 
<a href="http://www.sucks-rocks.com">sucks-rocks.com</a>, which we've been refining for the past few days. This might sound a lot like 
<a href="http://googlefight.com/">Google Fight</a> until you realize that Google Fight's results are totally meaningless in the context of which term is better. sucks/rocks actually compares the number of pages praising something and the number of pages griping about it, which is a much better metric. Several phrases are used for both positive and negative queries. I showed this to some people at the Django sprint at PyCon after someone brought up 
<a href="http://cod.quisition.com/">Cats or Dogs</a>, which is even more interesting. We included some examples for 
<a href="http://www.sucks-rocks.com/rate/bzr/darcs/git/mercurial/subversion/sourcesafe/bitkeeper/cvs">version control systems</a>, 
<a href="http://www.sucks-rocks.com/rate/windows/linux/os+x/freebsd">operating systems</a>, 
<a href="http://www.sucks-rocks.com/rate/comcast/bellsouth/ameritech/verizon/time+warner/earthlink/speakeasy">broadband providers</a>, 
<a href="http://www.sucks-rocks.com/rate/sprint/at%26t/verizon/cellular+one/nextel/alltel/t-mobile">cell phone providers</a>, and 
<a href="http://www.sucks-rocks.com/rate/java/lisp/scheme/c/haskell/c%2B%2B/python/erlang/ruby">programming languages</a>. We're actually using Yahoo's Search API instead of Google's, since Google deprecated theirs and it only allows 1,000 queries per day. The results aren't much different. If you find anything interesting, post the link (using "Link to this") in a comment. (Note: There are a couple bugs that are fixed but still need to be pushed to the live site.)</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>CAS 1.0 Authentication for Django, Part 2</title
><link href="http://blog.case.edu/bmb12/2006/12/cas_for_django_part_2"
 /><id
>http://blog.case.edu/bmb12/2006/12/cas_for_django_part_2</id
><published
>2006-12-01T18:27:12Z</published
><updated
>2006-12-01T18:39:00Z</updated
><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>
<p>After using 
<a href="http://blog.case.edu/bmb12/2006/11/simple_cas_for_django">my Django CAS authentication module</a> for a while, I decided to make a couple improvements.</p>
<p>The biggest improvement is that instead of modifying code in the CAS module itself to set your CAS address and do things like custom User field population, all this stuff can now be configured in your settings file.</p>
<p>Another improvement is that CAS authentication now works for the 
<a href="http://www.djangoproject.com/documentation/tutorial2/">bundled admin interface</a>. Since the administration interface does not account for an authentication backend that doesn't know the user's password, this makes the login form useless. The CAS module will now intercept requests to the administration interface and do the proper authentication routine if necessary, never showing the login form (which doesn't make sense for CAS). Intercepting requests, you ask? Yes, that means the CAS module is now 
<a href="http://www.djangoproject.com/documentation/middleware/">middleware</a>. Actually it's middleware, a couple views, and an 
<a href="http://www.djangoproject.com/documentation/authentication/#other-authentication-sources">authentication backend</a>.</p>
<p>So here's how to use it now...</p>
<p>Get 
<a href="http://exogen.case.edu/cas_middleware.tar.gz">cas_middleware.tar.gz</a>.</p>
<p>Extract it in 
<code>django/contrib/</code>. The code will be located at 
<code>django/contrib/cas/</code>. Is this a valid place to install 
<em>third-party</em> middleware? It's not really clear. Just do it anyway.</p>
<p>Now add it to the middleware and authentication backends in your settings. Make sure you also have the authentication middleware installed. Here's what mine looks like:</p>
<pre>
<code>MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.cas.middleware.CASMiddleware',
    'django.middleware.doc.XViewMiddleware',
)

AUTHENTICATION_BACKENDS = (
    'django.contrib.cas.backend.CASBackend',
)</code>
</pre>
<p>You can now configure the CAS module in the same settings file. Here are the possible options, most of which can be safely ignored:</p>
<ul>
<li>
<code>CAS_SERVICE_URL</code>: This is the only setting you must explicitly define. Set it to the base URL of your CAS source.</li>
<li>
<code>CAS_POPULATE_USER</code>: A callable or the location of a callable. When a user logs in and is missing name and email attributes in the database, this will be called with their User model instance. Default is None (do nothing).</li>
<li>
<code>CAS_ADMIN_PREFIX</code>: The URL prefix of the Django administration site. If undefined, the CAS middleware will just check the view being rendered to see if it lives in 
<code>django.contrib.admin.views</code>. The method is a little evil, but it works.</li>
<li>
<code>CAS_LOGIN_URL</code>: The URL where you bound 
<code>django.contrib.cas.views.login</code>. If undefined, assume 
<code>/accounts/login/</code>.</li>
<li>
<code>CAS_LOGOUT_URL</code>: The URL where you bound 
<code>django.contrib.cas.views.logout</code>. If undefined, assume 
<code>/accounts/logout/</code>.</li>
<li>
<code>CAS_REDIRECT_URL</code>: Where to send a user after logging in or out if there is no referrer and no 
<code>next</code> page set. Default is 
<code>/</code>.</li>
<li>
<code>CAS_REDIRECT_FIELD_NAME</code>: The name of the GET parameter in which to store the page URL to send the user to after logging in. Default is 
<code>next</code>.</li>
</ul>
<p>Need an example? Here's what my CAS settings look like:</p>
<pre>
<code>CAS_SERVICE_URL = 'https://login.case.edu/cas/'
CAS_POPULATE_USER = 'present.utils.populate_user'
</code>
</pre>
<p>And the callable that lives at 
<code>present.utils.populate_user</code> (notice this code lives in my project instead of tinkering with the CAS module) looks like this:</p>
<pre>
<code>def populate_user(user):
    try:
        ldap = LDAP()
        person = ldap.filter_one_by(uid=user.username)
    except:
        if not user.email:
            user.email = "%s@case.edu" % user.username
    else:
        # If it succeeds, update their User entry
        user.email = person.mail[0]
        user.first_name = fix_case(person.givenName[0])
        user.last_name = fix_case(person.sn[0])
</code>
</pre>
<p>(
<code>LDAP</code> and 
<code>fix_case</code> also live in my 
<code>utils</code> module).</p>
<p>Finally, make sure your project knows how to log users in and out by adding these to your URLconf:</p>
<pre>
<code>(r'^accounts/login/$', 'django.contrib.cas.views.login'),
(r'^accounts/logout/$', 'django.contrib.cas.views.logout'),</code>
</pre>
<p>Users should now be able to log into your site, and staff into the administration interface, using CAS 1.0.</p>
</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Talk: Geocoding at Case</title
><link href="http://blog.case.edu/bmb12/2006/11/talk_geocoding_at_case"
 /><id
>http://blog.case.edu/bmb12/2006/11/talk_geocoding_at_case</id
><published
>2006-11-27T20:44:38Z</published
><updated
>2006-11-27T21:10:57Z</updated
><category term="Case" label="Case"
 /><category term="Projects" label="Projects"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>Tomorrow (November 28th at 6 PM) is ACM's Nerd Cultural Dinner at Case. The dinner is located in Thwing Ballroom and you should probably reserve a ticket by e-mailing 
<a href="mailto:acm-officers@case.edu">acm-officers@case.edu</a>. After dinner is served, there will be several presentations. The first (at 6:30 PM) is by yours truly and is about 
<strong>Geocoding at Case</strong>. They gave me 15 minutes to talk, and I'll be following this approximate outline: 
<strong>Geocoding at Case</strong>
<ul>
<li>What is geocoding?</li>
<li>Why is it useful?</li>
<li>Using the Case geocoder web service</li>
<li>A simple example</li>
<li>Development details
<ul>
<li>Location finding: Case Wiki, Google Maps, ...</li>
<li>Location parsing: Matching buildings, areas, addresses, ...</li>
</ul></li>
<li>How you can contribute to its development and improvement</li>
</ul>Hope to see you there!</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Simple CAS 1.0 Authentication for Django</title
><link href="http://blog.case.edu/bmb12/2006/11/simple_cas_for_django"
 /><id
>http://blog.case.edu/bmb12/2006/11/simple_cas_for_django</id
><published
>2006-11-27T20:18:13Z</published
><updated
>2006-11-27T21:12:30Z</updated
><category term="Case" label="Case"
 /><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>Back when I expressed interest in making the 
<a href="http://blog.case.edu/bmb12/2006/10/a_webbased_presentation_system_for_case">web presentation bounty based solely on client-side code</a>, Simon (bounty master and 
<a href="http://filer.case.edu">Filer</a> admin) expressed his wish to keep the two services decoupled (so I shouldn't rely on Filer for slideshow storage). While I still want to have a save-to-Filer feature, I decided that I should just go ahead and get the web presentation system up and running before worrying about a client-side-only version. So I started a 
<a href="http://www.djangoproject.com">Django</a> project. Anyway, the result is that I got 
<a href="http://wiki.case.edu/CAS">CAS</a> 1.0 working alongside the Django authentication system, which means I can take advantage of built-in features like 
<a href="http://www.djangoproject.com/documentation/authentication/">permissions and messages</a> with CAS-authenticated users. If anyone else is interested in using CAS authentication with Django, you can 
<a href="http://exogen.case.edu/django_cas.tar.gz">download the code I'm using</a>. Here's a brief usage guide:
<ul>
<li>Set 
<code>SERVICE_URL</code> in 
<code>cas/__init__.py</code> to the location of your CAS service. For example, Case's is 
<code>https://login.case.edu/cas/</code>.</li>
<li>Set 
<code>DEFAULT_REDIRECT_URL</code> in 
<code>cas/__init__.py</code>. Normally the user will be sent back to their 
<code>HTTP_REFERER</code> (the page that requested login) after authentication. But if the user requests 
<code>/accounts/login/</code> directly (or there is no 
<code>HTTP_REFERER</code>), they will be sent to 
<code>DEFAULT_REDIRECT_URL</code>.</li>
<li>Enable the 
<code>login</code> and 
<code>logout</code> views by adding these to your URLconf (customize the URLs if you want):
<pre>
<code>(r'^accounts/login/$', 'your_site.cas.views.login'),
(r'^accounts/logout/$', 'your_site.cas.views.logout'),
</code>
</pre></li>
<li>Add the backend in 
<code>settings.py</code>:
<pre>
<code>AUTHENTICATION_BACKENDS = (
    'your_site.cas.backends.CASBackend',
)
</code>
</pre></li>
<li>Make sure at least the following apps are installed:
<pre>
<code>INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.sessions',
    'your_site.cas',
)
</code>
</pre></li>
<li>Finally, if you have a way to populate the user's name and e-mail address fields from their username, put it in 
<code>cas/backends.py</code> (see the comments). For example, I have LDAP code there.</li>
</ul>P.S.: This just implements the minimum required for CAS authentication. Features like gateway, renew, and proxies are not supported. An alpha version of the presentation system should be online to play with later this week.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>geopy 0.93 Released: distance, util, GeoNames</title
><link href="http://blog.case.edu/bmb12/2006/10/geopy_093_released_distance_util_geonames"
 /><id
>http://blog.case.edu/bmb12/2006/10/geopy_093_released_distance_util_geonames</id
><published
>2006-10-08T23:48:36Z</published
><updated
>2006-10-09T00:02:31Z</updated
><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>Finally released 
<a href="http://cheeseshop.python.org/pypi/geopy/0.93">geopy 0.93</a>, which contains the 
<a href="http://blog.case.edu/bmb12/2006/09/geopy_gets_distance_and_util_modules">distance and util modules I previously mentioned</a>, a 
<a href="http://www.geonames.org/">GeoNames</a> geocoder, and improvements to the Google geocoder in other formats. Updating 
<a href="http://exogen.case.edu/projects/geopy/">the documentation</a> was all that was holding it back, really. You can now pass 
<code>domain</code> and 
<code>resource</code> arguments to the Google geocoder. To query the actual Google Maps interface (instead of their official HTTP geocoder), initialize like so:
<pre>
<code>g = geocoders.Google(resource='maps')</code>
</pre>The JavaScript results tend to be the best for this resource, so change that as well:
<pre>
<code>g = geocoders.Google(resource='maps', output_format='js')</code>
</pre>Finally, for geocoding addresses outside of the US, change the domain being queried:
<pre>
<code>g = geocoders.Google(domain='maps.google.co.uk', resource='maps', output_format='js')</code>
</pre>As 
<a href="http://groups-beta.google.com/group/geopy/browse_frm/thread/3ad6a1f131342a32">James Robinson brought up on the geopy mailing list</a>, work is under way for accuracy support. This will let you determine how precise the geocoded result is for the given location. For example, is it only guaranteed to be the correct city? Street? Is it the exact address? I decided to release this version of geopy without completing this, because not much work is done so far (and we also want to normalize values across geocoders), and the distance module was a pretty big addition. To upgrade:
<pre>
<code>sudo easy_install geopy</code>
</pre></div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>A Web-Based Presentation System for Case</title
><link href="http://blog.case.edu/bmb12/2006/10/a_webbased_presentation_system_for_case"
 /><id
>http://blog.case.edu/bmb12/2006/10/a_webbased_presentation_system_for_case</id
><published
>2006-10-02T05:37:15Z</published
><updated
>2006-10-02T10:08:03Z</updated
><category term="Case" label="Case"
 /><category term="JavaScript" label="JavaScript"
 /><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>If you saw the new 
<a href="http://www.eecs.case.edu/tech/bounties">EECS department bounty system</a>, you may have noticed the bounty for a web-based presentation system. This is now a work-in-progress by yours truly. The goal is to let Case people generate slideshows through the web with Case-themed templates, mostly for use on the plasma displays in the EECS department (well, the displays that 
<em>will be</em> in Glennan). Another use could be by faculty in place of PowerPoint (so it should have familiar features such as transitions). You may have seen those displays in the fishbowl last year showing pictures and such. Apparently these are a pain to administer because they are connected by a wireless connection and thus are not easy to connect to via Remote Desktop. So when they are rebooted or PowerPoint closes somehow, getting the slideshow back up and running can be a real hassle. Having them in kiosk mode in a web browser will allow them to easily refresh if anything goes wrong, and will also let them grab updated content. This is how the plasma displays around campus show their (I think Flash-based) slideshows. There are two such tools on which to base such a system: 
<a href="http://meyerweb.com/eric/tools/s5/">S5</a> and 
<a href="http://www.w3.org/Talks/Tools/Slidy/">Slidy</a>. If you've ever viewed a text-based presentation online, it was probably with S5. This is the first time I have heard of Slidy, but it actually looks a lot better to me. Every time I've encountered S5 in the wild, the JavaScript has eventually completely messed up the text sizing and positioning, making everything unreadable. There are existing services that combine web presentation systems with editing and sharing, such as 
<a href="http://rubyforge.org/projects/s5presents/">S5 Presents</a> and 
<a href="http://soapbx.com/">SoapBX</a>. SoapBX is not open source while S5 Presents is GPL licensed and in Ruby. I would have no problem with using S5 Presents, but apparently it has gone unmaintained for almost two years, and Simon was disappointed when he looked into configuring it to run under a real web server. Tonight I decided to see what actually using S5 is like, so I customized the default theme to resemble the Case PowerPoint template found on the 
<a href="http://www.case.edu/univrel/marcomm/branding/logos/index.html">Case branding page</a>. So here is my 
<a href="http://exogen.case.edu/s5/s5-blank/s5-blank/s5-case.html">Case-themed S5 presentation</a>. It's pretty incomplete and doesn't match exactly, but it's close (and anyway, the goal is to make it look good, not match exactly&#8212;their PowerPoint template could use some work). When I first considered this bounty, I thought "hmm, web-based, which web framework will I use?" One of the major goals is to be easily maintainable once I leave, so something popular or at least very easy would be the best choice. Which best fits this description? 
<a href="http://www.djangoproject.com">Django</a>? 
<a href="http://www.turbogears.org/">TurboGears</a>? 
<a href="http://webpy.org/">web.py</a>? I've never really considered the maintainability of any of these, since usually I can choose whichever I like at the moment. After a bit of talking with 
<a href="http://blog.case.edu/csh11">Chris</a>, we determined that any server-side code at all isn't even really needed. The only thing it would be used for is to display view and edit pages for the presentations and to store them. The first two can be done with static HTML and JavaScript, while the third can be used by POSTing with AJAX to some location. For example, we could POST presentations to the 
<a href="http://www.eecs.case.edu/wiki/dokuwiki">EECS department's new DokuWiki-powered site</a> with the appropriate permissions. Since S5 and Slidy presentations are just HTML, the wiki page itself would be the presentation outline, and the slideshow viewer could just grab the markup from there. Or we could use AJAX to upload the presentation via WebDAV to the author's 
<a href="http://filer.case.edu">Filer</a> account. Since there would be heavy JavaScript usage regardless of the selected framework, the author would need to be fluent in it anyway. So why not a pure JavaScript solution, given the possibilities above? This is what I'll be looking into for the next few weeks. The code will soon live at 
<a href="http://opensource.case.edu/projects/present/">opensource.case.edu/projects/present</a>. If anyone has experience with S5 or Slidy and would like to share their opinions, please do (even if it's just which system you like more).</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>dmath: Math routines for Python's arbitrary-precision Decimal type</title
><link href="http://blog.case.edu/bmb12/2006/09/dmath_math_routines_for_pythons_arbitraryprecision_decimal_type"
 /><id
>http://blog.case.edu/bmb12/2006/09/dmath_math_routines_for_pythons_arbitraryprecision_decimal_type</id
><published
>2006-09-25T20:27:33Z</published
><updated
>2006-09-25T20:31:14Z</updated
><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>Yesterday 
<a href="http://blog.case.edu/csh11/">Chris</a> and I spent all day writing math functions for 
<a href="http://docs.python.org/lib/module-decimal.html">Python's Decimal type</a>. The result is our new 
<a href="http://cheeseshop.python.org/pypi/dmath">dmath</a> library, 
<a href="http://code.google.com/p/dmath/">available on Google Code</a> and the 
<a href="http://cheeseshop.python.org/pypi/dmath">Cheese Shop</a> under the MIT/X11 license. Sparked by the routine for 
<code>atan</code> in 
<a href="http://blog.case.edu/bmb12/2006/09/geocoding_and_pythons_decimal_module">my last post</a>, I decided it wouldn't be too hard to go ahead and do the rest of the functions already offered by 
<code>math</code> and 
<code>cmath</code>. We now have 
<code>acos</code>, 
<code>asin</code>, 
<code>atan</code>, 
<code>atan2</code>, 
<code>ceil</code>, 
<code>cos</code>, 
<code>cosh</code>, 
<code>degrees</code>, 
<code>e</code>, 
<code>exp</code>, 
<code>floor</code>, 
<code>golden_ratio</code>, 
<code>hypot</code>, 
<code>log</code>, 
<code>log10</code>, 
<code>pi</code>, 
<code>pow</code>, 
<code>radians</code>, 
<code>sign</code>, 
<code>sin</code>, 
<code>sinh</code>, 
<code>sqrt</code>, 
<code>tan</code>, and 
<code>tanh</code>. Check it out:
<pre>
<code>&gt;&gt;&gt; from dmath import *
&gt;&gt;&gt; from decimal import Decimal as D, getcontext
&gt;&gt;&gt; getcontext().prec = 50
&gt;&gt;&gt; asin(D(1))
Decimal("1.5707963267948966192313216916397514420985846996876")
&gt;&gt;&gt; golden_ratio()
Decimal("1.6180339887498948482045868343656381177203091798058")
</code>
</pre>We're calling this release 0.9 because it just needs some testing and maybe some speed improvements, otherwise it's ready to use. There is currently some 
<a href="http://svn.python.org/projects/sandbox/trunk/decimal-c/">work being done</a> in Python sandbox/trunk to convert the decimal module to C, and maybe they'll include fast versions of all these routines. But hey, you can use these right now! Arbitrary precision is one of the coolest things in programming. We spent a lot of time in 
<a href="http://www.wolfram.com/">Mathematica</a>, where if you ask it to tell you the precision, it says 'Infinity'. During our testing, we actually stumbled across a bug in 
<a href="http://functions.wolfram.com/ElementaryFunctions/ArcTan2/">Mathematica's ArcTan function</a>! 
<a href="http://functions.wolfram.com/ElementaryFunctions/ArcTan2/03/01/01/">This page</a> correctly states that ArcTan[-Infinity, y] should always be Pi (with the sign of y). However, Mathematica always returns 0. I sent a message with my findings to the 
<a href="http://groups.google.com/group/comp.soft-sys.math.mathematica">Mathematica mailing list</a> and Daniel Lichtblau of Wolfram Research confirmed that it is indeed a simple bug. ArcTan users, beware! Anyway, enjoy 
<a href="http://cheeseshop.python.org/pypi/dmath">dmath</a>. Contributions are welcome, especially if you have any speed tips!</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Geocoding and Python's decimal module</title
><link href="http://blog.case.edu/bmb12/2006/09/geocoding_and_pythons_decimal_module"
 /><id
>http://blog.case.edu/bmb12/2006/09/geocoding_and_pythons_decimal_module</id
><published
>2006-09-24T09:56:00Z</published
><updated
>2006-09-24T20:56:23Z</updated
><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><category term="geopy" label="geopy"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>Python has an awesome 
<a href="http://docs.python.org/lib/module-decimal.html">decimal module</a> for decimal floating point arithmetic. It has configurable precision and keeps track of significant digits and does some other neat stuff. While I was adding the 
<a href="http://blog.case.edu/bmb12/2006/09/geopy_gets_distance_and_util_modules">geopy distance module</a>, I began to wonder if it would be worth the effort to switch everything in geopy over to use Decimals instead of floats. After checking out the decimal module (I had never used it before), I decided that I had nothing to lose, so I went for it... I quickly ran into some snags when I realized that I'd have to code my own trigonometric functions for use with Decimals, since those that come with Python are for complex or floating point numbers. The 
<a href="http://docs.python.org/lib/decimal-recipes.html">decimal recipes</a> page in the documentation has functions for 
<code>sin</code> and 
<code>cos</code>, but 
<code>distance</code> uses 
<code>asin</code>, 
<code>acos</code>, 
<code>atan</code>, and 
<code>atan2</code>. 
<a href="http://www.gdssw.com">Don Peterson</a> has a nice 
<a href="http://cheeseshop.python.org/pypi/decimalfuncs">decimalfuncs module</a> with most of these, but it's GPL (and would be an uncommon dependency) &#8212; geopy is 
<a href="http://www.opensource.org/licenses/mit-license.php">MIT/X11</a>. So I went ahead and started on these... I decided it would be easiest to define 
<code>asin</code> and 
<code>acos</code> in terms of 
<code>atan</code>, and it turns out there is a (relatively) quickly converging algorithm for that. Here's what I came up with for a Decimal-compatible 
<code>atan</code>:
<pre>
<code>def atan(x):
    if x == D('-Inf'):
        return pi() / -2
    elif x == 0:
        return D(0)
    elif x == D('Inf'):
        return pi() / 2
    
    if x &lt; -1:
        c = pi() / -2
        x = 1 / x
    elif x &gt; 1:
        c = pi() / 2
        x = 1 / x
    else:
        c = 0
    
    getcontext().prec += 2
    x_squared = x ** 2
    y = x_squared / (1 + x_squared)
    y_over_x = y / x
    i, lasts, s, coeff, num = D(0), 0, y_over_x, 1, y_over_x
    while s != lasts:
        lasts = s    
        i += 2
        coeff *= i / (i + 1)
        num *= y
        s += num * coeff
    if c:
        s = c - s
    getcontext().prec -= 2
    return +s
</code>
</pre>It depends on the 
<code>pi</code> function from the 
<a href="http://docs.python.org/lib/decimal-recipes.html">decimal recipes page</a>, which calculates pi to the currently configured precision. Upon finishing this, 
<a href="http://blog.case.edu/csh11/">Chris</a> came home and I told him what I was doing. Immediately, he tried to talk me out of it, asserting that floating point was good enough for geocoding. I tried to counter by explaining all the floating point calculations being performed in 
<code>distance</code>, but in the end he won. I no longer think it would be a very important change to convert everything in geopy to use the Decimal type. What finally convinced me was this quote from the 
<a href="http://www.movable-type.co.uk/scripts/LatLongVincenty.html">Vincenty distance page</a> I used for reference:
<blockquote>Vincenty&#226;&#8364;&#8482;s formula is accurate to within 0.5mm, or 0.000015&#226;&#8364;&#179; (!), on the ellipsoid being used.</blockquote>0.000015 arcseconds is about 4.16667e-9 degrees. Well, if floating point is good to about 10 decimal places, I guess Chris wins this time... Still, if anyone wants Decimal support in the future, maybe I'll just ask Don Peterson for permission to include decimalfuncs with geopy... 
<strong>Update:</strong> On second thought, maybe I will just continue implementing my own trig functions for Decimals. Chris and I just spent a while investigating the precision of my atan vs. decimalfunc's, and mine seems to be faster and more precise.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>geopy gets distance and util modules</title
><link href="http://blog.case.edu/bmb12/2006/09/geopy_gets_distance_and_util_modules"
 /><id
>http://blog.case.edu/bmb12/2006/09/geopy_gets_distance_and_util_modules</id
><published
>2006-09-24T09:20:50Z</published
><updated
>2006-10-08T23:02:07Z</updated
><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><category term="geopy" label="geopy"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>If you check out 
<a href="http://geopy.googlecode.com/svn/trunk/">geopy trunk</a> right now you'll notice a few changes. I introduced two modules: 
<a href="http://geopy.googlecode.com/svn/trunk/geopy/util.py">
<code>util</code>
</a> and 
<a href="http://geopy.googlecode.com/svn/trunk/geopy/distance.py">
<code>distance</code>
</a>. 
<code>util</code> now contains the 
<code>parse_geo</code> and 
<code>arc_angle</code> functions, and will grow more in the future. 
<code>distance</code> is a bigger addition and contains helpful functions for calculating geodesic distances. I planned to add this eventually, but development was sparked by a request from 
<a href="http://polimetrix.com">Chris Mulligan</a>. There are two distance formulas: 
<a href="http://en.wikipedia.org/wiki/Great-circle_distance">Great-circle (aka haversine, aka spherical law of cosines) distance</a> and 
<a href="http://www.movable-type.co.uk/scripts/LatLongVincenty.html">Vincenty distance</a>. Great-circle distance uses a spherical model of the earth, using the average great-circle radius of 6372.795 kilometers (this is configurable). This results in an error of up to about 0.5%. Vincenty distance uses a more accurate ellipsoidal model of the earth. This is the default distance formula, and is thus aliased as 
<code>distance.distance</code> &#8212; so you can easily swap out distance formulas just by changing 
<code>distance.distance</code> at the top of your code. There are multiple popular ellipsoidal models, and which one will be the most accurate depends on where your points are located on the earth. geopy includes a few good models in the 
<code>distance.ELLIPSOIDS</code> dictionary:
<pre>
<code>#             model             major (km)   minor (km)     flattening
ELLIPSOIDS = {'WGS-84':        (6378.137,    6356.7523142,  1 / 298.257223563),
              'GRS-80':        (6378.137,    6356.7523141,  1 / 298.257222101),
              'Airy (1830)':   (6377.563396, 6356.256909,   1 / 299.3249646),
              'Intl 1924':     (6378.388,    6356.911946,   1 / 297.0),
              'Clarke (1880)': (6378.249145, 6356.51486955, 1 / 293.465),
              'GRS-67':        (6378.1600,   6356.774719,   1 / 298.25),
              }
</code>
</pre>Here's an example usage of 
<code>distance.distance</code>:
<pre>
<code>&gt;&gt;&gt; from geopy import distance
&gt;&gt;&gt; import Case
&gt;&gt;&gt; wiki = Case.Geocode.CaseWikiGeocoder()
&gt;&gt;&gt; _, a = wiki.geocode('Wade')
&gt;&gt;&gt; _, b = wiki.geocode('Fribley')
&gt;&gt;&gt; distance.distance(a, b).kilometers
1.342250272726943
&gt;&gt;&gt; distance.distance(a, b).miles
0.83403565192666562
</code>
</pre>Using Great-circle distance:
<pre>
<code>&gt;&gt;&gt; distance.distance = distance.GreatCircleDistance
&gt;&gt;&gt; distance.distance(a, b).miles
0.835175984734287
</code>
</pre>You can change the ellipsoid model used by the Vincenty formula like so:
<pre>
<code>&gt;&gt;&gt; distance.VincentyDistance.ELLIPSOID = 'Intl 1924'
</code>
</pre>The above model name will automatically be retrieved from the ELLIPSOIDS dictionary. Alternatively, you can specify the model values directly:
<pre>
<code>&gt;&gt;&gt; distance.VincentyDistance.ELLIPSOID = (6377., 6356., 1 / 297.)
</code>
</pre>Oh yeah, you can add distances too (for paths and such). Here's the distance from 
<a href="http://wiki.case.edu/Fribley">Fribley</a> to 
<a href="http://wiki.case.edu/Wade">Wade</a> to 
<a href="http://wiki.case.edu/Phi_Kappa_Theta">Phi Kappa Theta</a>:
<pre>
<code>&gt;&gt;&gt; _, c = wiki.geocode('Phi Kappa Theta')
&gt;&gt;&gt; (distance.distance(b, a) + distance.distance(a, c)).miles
1.0596624112817861
</code>
</pre>Also included in the 
<code>distance</code> module are functions for converting between length units (kilometers, miles, feet, nautical miles), and calculating a destination given a starting point, initial bearing, and distance. This stuff is still just in trunk, no egg or updated documentation yet...</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>geopy: Now on Google Code, More Geocoders</title
><link href="http://blog.case.edu/bmb12/2006/09/geopy_now_on_google_code_more_geocoders"
 /><id
>http://blog.case.edu/bmb12/2006/09/geopy_now_on_google_code_more_geocoders</id
><published
>2006-09-11T03:11:23Z</published
><updated
>2006-09-11T03:15:10Z</updated
><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><category term="geopy" label="geopy"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>I decided to try out the Google's 
<a href="http://code.google.com/hosting/">Project Hosting</a> feature for 
<a href="http://exogen.case.edu/projects/geopy">geopy</a>. You can find the hosted page at 
<a href="http://code.google.com/p/geopy/">code.google.com/p/geopy</a>. So far it seems pretty sweet and very easy to administer. I added a geocoder for Microsoft's 
<a href="http://local.live.com">Windows Live Local</a> (powered by 
<a href="http://virtualearth.net">Virtual Earth</a>) to the 
<a href="http://exogen.case.edu/projects/geopy/source/geopy.geocoders.html">geocoders module</a>. Sadly, they don't actually have a non-JavaScript geocoding API, so I had to reverse-engineer it. Norman Khine and I have been 
<a href="http://groups.google.com/group/geopy/browse_thread/thread/d316e0a782bdb73f/#">investigating issues geocoding UK addresses</a> with the Google Maps API. Due to contractual reasons, they can't offer geocoded addresses with their HTTP geocoder. So instead I again had to reverse-engineer their JavaScript to get it to work. The geocoded results aren't always accurate, but this is Google's problem and not geopy's. I also tried to add a geocoder for 
<a href="http://www.mapquest.com/features/main.adp?page=developer_tools_oapi">MapQuest's OpenAPI</a>. It is possible to get geocoded results over HTTP (although they don't tell you how, you have to look at their JavaScript or guess), but unfortunately they require you to parse the input location first. This is totally lame. You're telling me they can't parse the address into street, city, country for me? I didn't want to have to do this, but I now plan to add address parsing methods to geopy.</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>geopy: A Geocoding Toolbox for Python</title
><link href="http://blog.case.edu/bmb12/2006/09/geopy_a_geocoding_toolbox_for_python"
 /><id
>http://blog.case.edu/bmb12/2006/09/geopy_a_geocoding_toolbox_for_python</id
><published
>2006-09-08T04:40:09Z</published
><updated
>2006-09-08T06:47:13Z</updated
><category term="Programming" label="Programming"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>I just uploaded the 
<a href="http://cheeseshop.python.org/pypi/geopy/">first release of geopy</a> to the 
<a href="http://cheeseshop.python.org/pypi/">Python Cheese Shop</a>. The web site (with a little documentation) is located at 
<a href="http://exogen.case.edu/projects/geopy/">exogen.case.edu/projects/geopy/</a>. There are five geocoders: Google, Yahoo!, geocoder.us, MediaWiki, and Semantic MediaWiki. Usage examples are given on 
<a href="http://exogen.case.edu/projects/geopy/">the web site</a>. I need to read up more on 
<a href="http://peak.telecommunity.com/DevCenter/setuptools">setuptools</a> to make my package maintenance life easier. For instance, telling the Cheese Shop to install my package from Subversion would be nice right now, but I'm not sure how. 
<strong>Update:</strong> You can now view the 
<a href="http://exogen.case.edu/projects/geopy/source/geopy.geocoders.html">pretty syntax-highlighted source code</a> on the web site. Thanks to the wonderful KDevelop for that ability!</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>Geocoding Tools for Python (and CaseClasses)</title
><link href="http://blog.case.edu/bmb12/2006/09/geocoding_tools_for_python_and_caseclasses"
 /><id
>http://blog.case.edu/bmb12/2006/09/geocoding_tools_for_python_and_caseclasses</id
><published
>2006-09-03T23:30:23Z</published
><updated
>2006-09-05T09:20:40Z</updated
><category term="Case" label="Case"
 /><category term="Case" label="Case"
 /><category term="Projects" label="Projects"
 /><category term="Python" label="Python"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>I've been working on some geocoding classes in Python. Right now I've got tools made for 
<a href="http://www.mediawiki.org/">MediaWiki</a>, 
<a href="http://wiki.ontoworld.org/wiki/Semantic_MediaWiki">Semantic MediaWiki</a>, and the 
<a href="http://www.google.com/apis/maps/documentation/#Geocoding_HTTP_Request">Google geocoder</a>. I plan to include the 
<a href="http://developer.yahoo.com/maps/rest/V1/geocode.html">Yahoo! geocoder</a> in this toolbox soon. I think this could be a useful package, so I plan to upload it to the 
<a href="http://cheeseshop.python.org/pypi">Cheese Shop</a> soon. I 
<a href="http://opensource.case.edu/projects/CaseClasses/browser/python/trunk/Case/case_geo.py">added the relevant geocoder classes</a> to the Python 
<a href="http://opensource.case.edu/projects/CaseClasses">CaseClasses</a> so that developers can easily geocode strings using 
<a href="http://wiki.case.edu">Case's Semantic MediaWiki</a>. Check it out. First grab the geocoder:
<pre>
<code>&gt;&gt;&gt; from Case import Geocode
&gt;&gt;&gt; wiki = Geocode.CaseWikiGeocoder()
</code>
</pre>Then start geocoding:
<pre>
<code>&gt;&gt;&gt; place, (lat, lng) = wiki.geocode('KSL')
Fetching http://wiki.case.edu/KSL...
&gt;&gt;&gt; print "%s: %.5f, %.5f" % (place, lat, lng)
Kelvin_Smith_Library: 41.50727, -81.60950
</code>
</pre>
<code>geocode</code> returns a tuple consisting of the location name found and the coordinates (another tuple). Here's where the Semantic part comes in. The 
<a href="http://wiki.case.edu/Project_Club">Project Club</a> article isn't geocoded, but it is 
<a href="http://wiki.case.edu/Relation:Located_in">located in</a> 
<a href="http://wiki.case.edu/Olin_Building">Olin</a>, which is geocoded:
<pre>
<code>&gt;&gt;&gt; place, (lat, lng) = wiki.geocode('Project Club')
Fetching http://wiki.case.edu/Project_Club...
Fetching http://wiki.case.edu/index.php/Special:ExportRDF/Project_Club?xmlmime=rdf...
Fetching http://wiki.case.edu/index.php/Olin_Building...
&gt;&gt;&gt; print "%s: %.5f, %.5f" % (place, lat, lng)
Olin_Building: 41.50224, -81.60778
</code>
</pre>CaseWikiGeocoder is a subclass of SemanticMediaWikiGeocoder and is defined by only the following:
<pre>
<code>class CaseWikiGeocoder(SemanticMediaWikiGeocoder):
    def __init__(self):
        super(CaseWikiGeocoder, self).__init__("http://wiki.case.edu/%s",
                                               relations=['Located in'])
</code>
</pre>This creates a SemanticMediaWikiGeocoder with a base URL of 'http://wiki.case.edu/' that follows the 'Located in' relation if a page fails to geocode. So SemanticMediaWikiGeocoder could easily be used for any Semantic MediaWiki with any set of relationships defined. This class is brand new and has only been tested on the Case Wiki, so it might be buggy. MediaWikiGeocoder relies on 
<a href="http://www.crummy.com/software/BeautifulSoup/">BeautifulSoup</a> since it assumes wiki pages can be malformed. Remember, if you have 
<a href="http://peak.telecommunity.com/DevCenter/EasyInstall">easy_install</a>, you can simply type this to install CaseClasses:
<pre>
<code>sudo easy_install http://opensource.case.edu/svn/CaseClasses/python/trunk</code>
</pre>After this geocoding toolbox is complete, I'll see if I can make that 
<a href="http://blog.case.edu/bmb12/2006/08/javascript_contour_mapping_still_seems_feasible">Case geocoder web service</a> we talked about on the forum. 
<strong>Update:</strong> Per Greg's comment, support for reading coordinates from semantic attributes is 
<a href="http://opensource.case.edu/projects/CaseClasses/changeset/195">now in Case.Geocode</a> (along with some other small improvements). CaseWikiGeocoder is now defined as:
<pre>
<code>class CaseWikiGeocoder(SemanticMediaWikiGeocoder):
    def __init__(self):
        base = super(CaseWikiGeocoder, self)
        base.__init__("http://wiki.case.edu/%s",
                      attributes=['Geographical coordinate'],
                      relations=['Located in'], prefer_semantic=True)
</code>
</pre></div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
><entry
><title
>JavaScript Contour Mapping: Still Seems Feasible</title
><link href="http://blog.case.edu/bmb12/2006/08/javascript_contour_mapping_still_seems_feasible"
 /><id
>http://blog.case.edu/bmb12/2006/08/javascript_contour_mapping_still_seems_feasible</id
><published
>2006-08-31T01:12:57Z</published
><updated
>2006-08-31T01:16:11Z</updated
><category term="Google Maps" label="Google Maps"
 /><category term="JavaScript" label="JavaScript"
 /><category term="Projects" label="Projects"
 /><content type="xhtml"
><div xmlns="http://www.w3.org/1999/xhtml"
>A few posts ago I talked about 
<a href="http://blog.case.edu/bmb12/2006/08/generic_contour_plotting_for_google_maps">generic contour plotting for Google Maps</a> using only JavaScript. After some more work and research into contouring algorithms, it still seems like a reasonable idea. Instead of gridding the irregularly spaced points (crime locations in my case) I learned that many contouring algorithms instead use 
<a href="http://en.wikipedia.org/wiki/Delaunay_triangulation">Delaunay triangulation</a>. So I implemented incremental triangulation in JavaScript and 
<a href="http://exogen.case.edu/contour/test_triangulate.html">the result</a> seems fast enough (note: still needs Firefox or 
<a href="http://www.adobe.com/svg/">Adobe SVG</a>). On my (older) computer it gets unreasonably slow around a few hundred points, but this is more than I expect to render at a time anyway (consider that the 
<a href="http://exogen.case.edu/crime/2006/">Crimes in 2006</a> page only has about 60 unique locations). The next step is to modify the algorithm to use 
<a href="http://mech.fsv.cvut.cz/~dr/papers/Rome05/node2.html">Constrained Delaunay</a> or 
<a href="http://www.cs.cmu.edu/~quake/tripaper/triangle3.html">Ruppert's Refined Delaunay</a>. After that, it's just a matter of interpolating the crime density (so that levels in between high and low densities are created if they don't exist), 
<a href="http://exogen.case.edu/contour/test_bezier.html">smoothing</a> the polygons, and drawing &amp; coloring them with SVG. I checked out a nice collection of research papers from 
<a href="http://library.case.edu">KSL</a> called Geometric Modelling which includes some contouring discussion. I plan to make another visit to the library this week to see if I can find anything helpful about the steps above. Googling for this stuff has been extremely helpful, but no single page has very comprehensive information, I just have to piece it together. On the 
<a href="http://forum.case.edu">Case Forum</a> 
<a href="http://blog.case.edu/gps10/">Greg</a> 
<a href="http://forum.case.edu/read/7/9319/9351#msg-9351">mentioned his idea</a> for a Case geocoding service that would aggregate information from geocoding services such as Google Maps and the 
<a href="http://wiki.case.edu">Case Wiki</a> so that any Case web service could easily geocode local places (
<a href="http://blog.case.edu/ajw33/">Andrew</a> mentioned geocoding 
<a href="http://photos.case.edu">photos</a>, for example). I think the 
<a href="http://exogen.case.edu/crime/recent/">crime log</a> location parser fits this bill pretty nicely! The 
<a href="http://opensource.case.edu/projects/crime/browser/trunk/CampusCrimeMap/crimes/parser.py">version in trunk</a> has tons of Case-specific location parsing techniques that could save other web services a lot of work. It just needs to be put on a server that will accept strings and send back geocoded results. More crime &amp; mapping news to come...</div
></content
><author
><name
>Brian Beck</name
><email
>brian.beck@case.edu</email
><uri
>http://blog.case.edu/bmb12</uri
></author
></entry
></feed
>