<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Azavea Labs &#187; tips</title>
	<atom:link href="http://www.azavea.com/blogs/labs/tag/tips/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.azavea.com/blogs/labs</link>
	<description>Insight on what our engineers are doing</description>
	<lastBuildDate>Mon, 06 Feb 2012 22:32:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>Using django_sorting without text anchors</title>
		<link>http://www.azavea.com/blogs/labs/2011/05/using-jdango_sorting-without-text-anchors/</link>
		<comments>http://www.azavea.com/blogs/labs/2011/05/using-jdango_sorting-without-text-anchors/#comments</comments>
		<pubDate>Mon, 09 May 2011 18:45:04 +0000</pubDate>
		<dc:creator>Carissa Brittain</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=1404</guid>
		<description><![CDATA[In creating the OpenDataPhilly website, we knew we needed to pay extra attention to the usability and features available on the search page. After all, what use is a data catalog if you can&#8217;t rely on the search results? While designing the page, we decided we wanted to include several ways to sort and filter [...]]]></description>
			<content:encoded><![CDATA[<p>In creating the <a title="Philadelphia's Open Data Catalog" href="http://www.opendataphilly.org" target="_blank">OpenDataPhilly website</a>, we knew we needed to pay extra attention to the usability and features available on the search page. After all, what use is a data <a title="So many..." href="http://en.wikipedia.org/wiki/Catalog" target="_blank">catalog</a> if you can&#8217;t rely on the search results? While designing the page, we decided we wanted to include several ways to sort and filter results along with the standard text search. I&#8217;d used <a title="Official site" href="https://github.com/directeur/django-sorting" target="_blank">directeur&#8217;s django_sorting</a> module before, and was highly impressed at how well it integrated with other info already in search parameters, so I decided to <a title="Like we did in the olden days" href="http://www.umcs.maine.edu/~ftp/wisr/SEN-pap/SEN-pap.html" target="_blank">use it again</a>. The only thing keeping me from seamlessly dropping in this module was our desire to use images instead of words for the &#8220;click on this to sort&#8221; link. Django_sorting was built with table headers in mind; so much so that the examples are all about table header tags with a link inside them. We had a slightly different implementation in mind.</p>
<p>The first hurdle that you might think of would be to not require a table structure. Thankfully, django_sorting doesn&#8217;t care how you display the data, it only cares about the fields you want to sort on. You can put the sorting links anywhere and it just works. So far, so good.</p>
<p>The second possible hurdle here is that the anchor template tag specification calls for two parameters: the field to sort on, and a string for the link. Since we didn&#8217;t want a text link, we really didn&#8217;t care about the second parameter. To my surprise, neither did django_sorting! So our anchor template tags look something like this:</p>
<pre>&lt;li id="sort_rating_score"&gt;{% anchor rating_score %}&lt;/li&gt;</pre>
<p>This winds up creating a link that still has something for the title attribute and for the inner text:</p>
<pre>&lt;li ...&gt;&lt;a title="Rating_score" href="/blah/blah/?sort=rating_score"&gt;Rating_score&lt;/a&gt;&lt;/li&gt;</pre>
<p>So our template tag is nice and clean, but we still have to deal with some text in the link. Using either straight-up javascript, or some smaller and nicer jQuery, removing the innerHTML is easy so long as the dom can uniquely identify our sort links. I just gave the link&#8217;s parent container an id and cleared the innerHTML of each link. At the same time, I added a class and some css to define the image and size.</p>
<p>So now we have a django_sorting anchor with an image instead of the default text link. All done, right? Not quite. We didn&#8217;t just want to use an image here, we wanted some mouseover and focus <a title="Why they're cool" href="http://css-tricks.com/css-sprites/" target="_blank">sprite action</a>, too. Another chuck of jQuery, and a <a title="Aptly named 'query'" href="http://plugins.jquery.com/project/query-object" target="_blank">querystring plugin</a>, helps us out again:</p>
<pre>$("#sort .sort_image").each(function () {
    $(this).hover(function() {
        this.style.backgroundPosition="0 -89px"; //the hover image location
    }, function () {
        var filter_split = this.parentNode.id.split('sort_');
        if ($.query.get('sort') &amp;&amp; $.query.get('sort') == filter_split[1]) {
            this.style.backgroundPosition="0 -45px";  //the active image location
        } else {
            this.style.backgroundPosition="0 0"; //the non-active image location
        }
    });
 });</pre>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2011/05/using-jdango_sorting-without-text-anchors/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery Star Rating plugin as a frontend for Django-ratings</title>
		<link>http://www.azavea.com/blogs/labs/2011/04/jquery-star-rating-with-django-ratings/</link>
		<comments>http://www.azavea.com/blogs/labs/2011/04/jquery-star-rating-with-django-ratings/#comments</comments>
		<pubDate>Fri, 08 Apr 2011 14:15:06 +0000</pubDate>
		<dc:creator>Carissa Brittain</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[widgets]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=1391</guid>
		<description><![CDATA[My current Django project, OpenDataPhilly, needed a ratings system and, guess what? Django has a ratings module! All done, right? Weeelllll.. the thing is, jQuery also has a beautiful and well-behaved ratings widget that is very easy to re-skin. How do you choose between two stable plugins? In this case there was no need. I [...]]]></description>
			<content:encoded><![CDATA[<p>My current Django project, <a title="Launching Soon! Will you be there?" href="http://opendataphilly.org/" target="_blank">OpenDataPhilly</a>, needed a ratings system and, guess what? Django has a <a title="Simple and easy to implement" href="https://github.com/dcramer/django-ratings" target="_blank">ratings module</a>! All done, right? Weeelllll.. the thing is, jQuery also has a beautiful and well-behaved <a title="Gorgeous and slick!" href="http://www.fyneworks.com/jquery/star-rating/#tab-Overview" target="_blank">ratings widget</a> that is very easy to re-skin. How do you choose between two stable plugins? In this case there was no need. I decided to use the Django-ratings module for everything but the visual element and the jQuery Star Rating Plugin for some flashy effect.</p>
<p>So why blog about it then? The implementation was <a title="It's harder than it looks..." href="http://tvtropes.org/pmwiki/pmwiki.php/Main/SurpriseDifficulty" target="_blank">not quite as easy as it sounds</a>.</p>
<p>First thing&#8217;s first: download and install the Django-ratings module the same way you would any other module. Follow their <a title="Django-ratings how-to" href="https://github.com/dcramer/django-ratings/blob/master/README.rst" target="_blank">instructions </a>to add a ratings field to your model and fix any import or database issues. You should wind up with something like this in your models file:</p>
<pre>from djangoratings.fields import RatingField

class MyClass(models.Model):
    ....
    rating = RatingField(range=5)
    ...</pre>
<p>Since we&#8217;re using jQuery for the front end, that&#8217;s all we need to do in the models file.</p>
<p>Next, download the jQuery plugin and put the files somewhere accessible by your app. I put them in my project&#8217;s <a title="Shocking!" href="http://en.wikipedia.org/wiki/Triboelectric_effect" target="_blank">static </a>folder, but I think anywhere is fine. The plugin worked right out of the box for me, no tweaking necessary.</p>
<p>On to the slightly hard part! If you look in the jQuery plugin&#8217;s <a title="Page source really.." href="http://www.fyneworks.com/jquery/star-rating/#tab-Testing" target="_blank">documentation</a>, it expects a pile of radio buttons to skin. No divs or lists or other tricks of nice formatting, because the plugin does all that. Unfortunately, Django&#8217;s radio button widgets think they need to do all that, too. Someone has to be told &#8216;<a title="Doesn't always work though" href="http://bartsblackboard.com/i-will-not-get-very-far-with-this-attitude/season-2/79/" target="_blank">No</a>&#8216; very firmly. Since the whole point of using the jQuery plugin is to have a pretty and easily skinable front end, we&#8217;re going to tell Django to stop rendering radio buttons quite as nicely as it does. And that&#8217;s going to take some doing.</p>
<p>Somewhere in your project, create a widgets.py file and hunt down the django.forms.widget.py file so you can copy a few things. The two classes we&#8217;re interested in are RadioInput and RadioFieldRenderer. Copy those two classes from Django&#8217;s file into your new one and close the Django file. We don&#8217;t want to accidentally mess something up in there.  First, we&#8217;ll need a few imports at the top of our file:</p>
<pre>from django.forms.util import flatatt

from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe</pre>
<p>Next, rename the classes to something not in Django. I chose StarsRadioInput and StarsRadioFieldRenderer. Also the StarsRadioFieldRenderer makes a few references to the RadioInput class. Change these to StarsRadioInput, too. Now that the classes are safely renamed, there are two methods in charge of the html coming out of our custom widget: StarsRadioInput.tag and StarsRadioFieldRenderer.render. We basically need to strip out all of the labels and list tags so that the jQuery plugin can do its css magic.</p>
<p>Change the StarsRadioInput.tag method to:</p>
<pre>def tag(self):
  if 'id' in self.attrs:
    self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
    final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
  if self.is_checked():
    final_attrs['checked'] = 'checked'
  return mark_safe(u'&lt;input%s /&gt;' % flatatt(final_attrs))</pre>
<p>And change the StarsRadioFieldRenderer.render method to</p>
<pre>def render(self):
  return mark_safe(u'\n%s\n' % u'\n'.join([u'%s'
                   % force_unicode(w) for w in self]))</pre>
<p>Now, all you have to do is make your form add our special renderer to the field and push the rating value into the right data model when the form is submitted.</p>
<p>In the forms.py file, add a field to your form. Don&#8217;t forget to create the rating choices tuple and add the &#8216;star&#8217; class to the field.</p>
<pre>RATING_CHOICES = ((1,1), (2,2), (3,3), (4,4), (5,5),)
forms.CharField(widget=forms.RadioSelect(renderer=StarsRadioFieldRenderer, attrs={'class':'star'}, choices=RATING_CHOICES))</pre>
<p>In your form&#8217;s model, add this to the save method to connect the form data to the actual model&#8217;s data (if you need to):</p>
<pre>def save(self, *args, **kwargs):
  myobject.rating.add(score=self.rating, user=self.user, ip_address=self.ip_address)
  super(CommentWithRating, self).save(*args, **kwargs)</pre>
<p>Here&#8217;s what it looks like attached to a comments form:</p>
<div id="attachment_1392" class="wp-caption aligncenter" style="width: 485px"><a rel="attachment wp-att-1392" href="http://www.azavea.com/blogs/labs/2011/04/jquery-star-rating-with-django-ratings/comment_rating/"><img class="size-medium wp-image-1392 " title="django-rating and jquery star rating" src="http://www.azavea.com/blogs/labs/wp-content/uploads/2011/04/comment_rating-475x297.gif" alt="django-rating and jquery star rating" width="475" height="297" /></a><p class="wp-caption-text">django-rating and jquery star rating together</p></div>
<p style="text-align: center;">&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2011/04/jquery-star-rating-with-django-ratings/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PhillyHistory Augmented Reality Journal 2: Building Data Services</title>
		<link>http://www.azavea.com/blogs/labs/2011/04/phillyhistory-augmented-reality-journal-2-building-data-services/</link>
		<comments>http://www.azavea.com/blogs/labs/2011/04/phillyhistory-augmented-reality-journal-2-building-data-services/#comments</comments>
		<pubDate>Tue, 05 Apr 2011 17:19:32 +0000</pubDate>
		<dc:creator>Josh Marcus</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[Augmented Reality]]></category>
		<category><![CDATA[Humanities]]></category>
		<category><![CDATA[PhillyHistory]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[webservices]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=1195</guid>
		<description><![CDATA[As I talked about in a previous journal, we&#8217;re exploring two different approaches to putting together an augmented reality application:  rolling our own client and using an existing framework and client. But regardless of what kind of client you&#8217;re using, the data (and images) have to come from somewhere, and that&#8217;s where data services come in [...]]]></description>
			<content:encoded><![CDATA[<p>As I talked about in <a href="http://www.azavea.com/blogs/labs/2011/01/phillyhistory-augmented-reality-developer-journal-1/">a previous journal</a>, we&#8217;re exploring two different approaches to putting together an augmented reality application:  rolling our own client and using an existing framework and client.  But regardless of what kind of client you&#8217;re using, the data (and images) have to come from somewhere, and that&#8217;s where data services come in to play.  To support different client-side augmented reality viewers, we wanted to build an architecture that separated the data services from the client technology (the actual application that runs on a phone to provide the AR experience).  This means that there could be a single source of digital asset information for any number of augmented reality clients.</p>
<div id="attachment_1238" class="wp-caption aligncenter" style="width: 485px"><a rel="attachment wp-att-1238" href="http://www.azavea.com/blogs/labs/2011/04/phillyhistory-augmented-reality-journal-2-building-data-services/desales_horiz/"><img class="size-medium wp-image-1238" title="desales_horiz" src="http://www.azavea.com/blogs/labs/wp-content/uploads/2011/04/desales_horiz-475x285.png" alt="" width="475" height="285" /></a><p class="wp-caption-text">Augmented Reality out my window (from our AR app!)</p></div>
<p>After reviewing the available technology and standards, we decided to build web services that conformed to the Layar standards, a mobile augmented reality platform developed in The Netherlands. Launched in 2009, Layar has quickly become ubiquitous as a platform for augmented reality applications. To implement an augmented reality layer in Layar, one publishes the &#8220;augmentations,&#8221; the points of interest that are visible in the augmented reality application, by creating a web service that client applications can query for information about what&#8217;s around them. A web service is simply a term for a standard method of allowing two computer programs to request and communicate data in a structured way. For example, a request to this &#8220;augmentation&#8221; database might request all of the points of interest within 200 meters of a given latitude and longitude. While the Layar webservice format has some limitations, it is relatively simple to implement both server-and client-side support for it.  While not strictly what is often called a &#8220;RESTful&#8221; web service, which is a lightweight style of web service that in many ways is similar to loading a web page, the service can be implemented with a simple web application that can read in POST variables. As there is no independent standard for requesting and publishing augmented reality points of interest, the Layar service is as close as we could find. It had the additional advantage that we could directly test the result in the Layar clients available for both the Android and iPhone platforms.</p>
<div id="attachment_1212" class="wp-caption aligncenter" style="width: 485px"><a rel="attachment wp-att-1212" href="http://www.azavea.com/blogs/labs/2011/04/phillyhistory-augmented-reality-journal-2-building-data-services/layar-api/"><img class="size-medium wp-image-1212" title="layar API" src="http://www.azavea.com/blogs/labs/wp-content/uploads/2011/04/layar-API-475x259.jpg" alt="" width="475" height="259" /></a><p class="wp-caption-text">Layar API Architecture (courtesy of Layar)</p></div>
<p>&nbsp;</p>
<p>The API documentation for Layar&#8217;s REST services are on their <a title="documentation wiki" href="http://layar.pbworks.com/w/page/7783228/FrontPage">documentation wiki</a>.  Overall they have done a great job, but there are two big gotchas here.  First of all, they are in heavy development and their platform doesn&#8217;t yet seem fully stable and mature &#8212; we often felt like we were developing against a moving target, as what was supported overall and from device to device kept changing.  Secondly, their documentation wiki is somewhere between a really fantastic wiki and relatively chaotic and poorly organized documentation.  The details are spread out across various pages with various comments (some of which are totally critical) thrown in the mix.  We would have loved stable, versioned API documentation that was separate from the wiki and had all of the specifications in one place.   But my overall feeling is a bit like the Churchill quote: &#8220;Democracy is the worst form of Gov­ern­ment except for all those other forms that have been tried from time to time.&#8221;  While there are problems, it&#8217;s a relatively complete API and it&#8217;s a defacto standard for augmented reality data services.  That said, I&#8217;d love to see an open source standard for augmented reality data services  &#8211; and some open source clients that support it!</p>
<div id="attachment_1220" class="wp-caption alignright" style="width: 235px"><a href="http://en.wikipedia.org/wiki/Winston_Churchill"><img class="size-full wp-image-1220" title="Churchill_1881_ZZZ_7555D" src="http://www.azavea.com/blogs/labs/wp-content/uploads/2011/04/Churchill_1881_ZZZ_7555D.jpg" alt="" width="225" height="350" /></a><p class="wp-caption-text">Winston Churchill at age 7 (thanks wikipedia.org)</p></div>
<p>While there are many advantages to this architecture, it is important to keep in mind that it means that all imagery is being transmitted to the mobile device while the user is using it.  This creates a number of issues.  If the user is has no or poor connectivity, the application will not be able to load photos – and even under good circumstances there will be a noticable delay, and there are restrictions to how many photos can be sent in a short amount of time over a network.  An alternate approach would be to package the asset images with the application, and install those images along with the application.  But while this approach might work well for a custom built application with 100 photos, the storage requirements would make this impractical for a large collection of 100k photos.</p>
<p>So &#8230; <a title="data munging" href="http://www.eduunix.ccut.edu.cn/index/html/oracle/O'Reilly%20-%20Perl.For.Oracle.DBAs.eBook-LiB/oracleperl-APP-D-SECT-1.html">data munging</a> (transforming from one format to another) projects  are almost always kind of sticky, but we hit some particular challenges in working with data for augmented reality. As discussed earlier, we used Google Street View as a tool to identify and select the desired angle in 3D space that we wished to place each photograph. However, Google Street View and Layar specify this angle differently. Both Layar and Google Street View represent a viewer facing north with a value of 0 degrees (in Layar this is the &#8220;angle&#8221; parameter and in Google Street View it is called &#8220;yaw&#8221;).  However, in Google Street View rotations go clockwise (so 90 degrees is East and -90 degrees is West) whereas in Layar rotations go counter-clockwise (so 90 degrees is West and -90 degrees is East). However, the effort required to make these transformations was worthwhile. By January 2011, the PhillyHistory.org database management team had “pinned” more than 10,850 images to their Google Street View coordinates, providing a large subset of materials with which to test the 3D space options.</p>
<p>We chose to use to use a spatial database (PostgreSQL database with the PostGIS spatial extensions) to store our assets. A spatial database is designed to store and reason about objects in space – for example, it is possible to ask a spatial database to find the assets that are within a specific distance from the viewer.   Check out this <a title="newsletter article from Robert about PostGIS" href="http://www.azavea.com/blogs/newsletter/v3i3/what-the-heck-is-postgis/" target="_blank">newsletter article from Robert about PostGIS</a> to learn more.   It is possible to add a stored procedure to a non-spatial database to make the same query (for example, the Layar wiki documentation provides such a function) but we found that with large numbers of points, the optimizations found in a spatial database were necessary for reasonable performance.  The creation of a “<a title="spatial index" href="http://postgis.refractions.net/documentation/manual-1.3/ch03.html#id2570609" target="_blank">spatial index</a>” allows the database to limit its searches very quickly to likely candidates found within the database, instead of needing to search through all of the assets in the database.    That said, however, the overall performance of an AR application is limited by the network transmission time far more significantly than the backend server performance &#8212; but with 90k points it sure doesn&#8217;t hurt.</p>
<div id="attachment_1208" class="wp-caption alignright" style="width: 251px"><a rel="attachment wp-att-1208" href="http://www.azavea.com/blogs/labs/2011/04/phillyhistory-augmented-reality-journal-2-building-data-services/wizard/"><img class="size-full wp-image-1208" title="The ImageMagick wizard" src="http://www.azavea.com/blogs/labs/wp-content/uploads/2011/04/wizard.jpg" alt="" width="241" height="320" /></a><p class="wp-caption-text">The ImageMagick wizard</p></div>
<p>Some image processing also needs to occur before images can be displayed on the small screen of a mobile device. This is extremely important because we found that clients (such as Layar’s client) will silently drop images that did not fit their specification.  Because the client will not include the photo for a number of distinct reasons but there is no feedback for the developer explaining why the photo was not included, the process of diagnosing missing photos can be tricky.  For Layar, the file size of all images must be smaller than 75 kB, and there are specific resolution limitations (e.g. full images in Layar must be less than 640&#215;480). Given that mobile device screens have significantly smaller resolution than 640&#215;480, that resolution is probably much higher than necessary. Additionally, some clients (like Layar) do not support making images transparent. It is therefore necessary to set the alpha channel of the photos in a pre-processing step. For example, using the open source <a title="ImageMagick" href="http://www.imagemagick.org/script/index.php" target="_blank">ImageMagick </a>package, the following command line invocation could perform the necessary scaling and transparency conversion on an incoming image stream (on a linux box): &#8220;cat input_image.jpg | convert &#8211; PNG32:- | convert -scale 240&#215;180 -channel Alpha -evaluate Multiply 0.9 output.png&#8221;.</p>
<p>&nbsp;</p>
<p>There are already a number of open source platforms for publishing data services compatable with the Layar API, most notably <a href="http://code.google.com/p/porpoise/">PorPOISe (PHP)</a>, <a href="https://github.com/sunlightlabs/django-layar/">django-layar (Python)</a> from our open data crush <a href="http://sunlightlabs.com/">Sunlight Labs</a> and <a href="http://layardotnet.codeplex.com/">LayarDotNet (C#)</a>, with PorPOISe being the most fully featured of the platforms we reviewed.  However, PorPOISE lacked some crucial 3D features at the time of our review.  The beta release of an online service called <a href="http://augmentation.hoppala.eu/">Hoppala Augmentation</a> does support 3D layers, but we were unable to get the 3D service to work and found the documentation and usability to be underdeveloped. It is certainly necessary to have a full understanding of the Layar protocol to use the Hoppala service (at this point) as the API allows developers to set a range of settings without explanation or checks on invalid or conflicting settings. Given these limitations and our desire to implement our own interactive capabilities and user settings, we decided to develop our own data web services in Python, which turned out to be a great choice for us because it let us prioritize, shape, and alter the results in a variety of ways.   Our next journal (from Erik) will feature some of the ways that we needed to change the 3D placement of photos and how we went about it.</p>
<p><script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script> <script src="/blogs/labs/wp-content/themes/labs/js/wp-blog-form.js" type="text/javascript"></script> <script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js" type="text/javascript"></script></p>
<div id="blogFormContainer">
<form id="blogForm" action="https://www.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">
<h3>Want to know more about our AR work?</h3>
<input name="oid" type="hidden" value="00D30000000efK8" />
<input name="retURL" type="hidden" value="http://www.azavea.com/Confirmation.aspx" />
<input id="lead_source" name="lead_source" type="hidden" value="Web" />
<input id="Campaign_ID" name="Campaign_ID" type="hidden" value="70130000000P4OH" />
<input id="00N30000004RyN1" name="00N30000004RyN1" type="hidden" value="1" />
<table border="0">
<tbody>
<tr>
<td><label class="mc_var_label" for="first_name">First Name</label></p>
<input id="first_name" class="mc_input required" maxlength="40" name="first_name" size="18" type="text" /></td>
<td><label class="mc_var_label" for="last_name">Last Name</label></p>
<input id="last_name" class="mc_input" maxlength="40" name="last_name" size="18" type="text" /></td>
</tr>
<tr>
<td><label class="mc_var_label" for="company">Company</label></p>
<input id="company" class="mc_input" maxlength="40" name="company" size="18" type="text" /></td>
<td><label class="mc_var_label" for="email">Email</label></p>
<input id="email" class="mc_input required email" maxlength="40" name="email" size="18" type="text" /></td>
</tr>
<tr>
<td colspan="2"><label class="mc_var_label" for="description">Message</label></p>
<div><textarea name="description"></textarea></div>
</td>
</tr>
<tr>
<td colspan="2" align="center"><span class="sendButton"> </span></td>
</tr>
</tbody>
</table>
</form>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2011/04/phillyhistory-augmented-reality-journal-2-building-data-services/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>When is seven days not a week?</title>
		<link>http://www.azavea.com/blogs/labs/2011/03/when-is-seven-days-not-a-week/</link>
		<comments>http://www.azavea.com/blogs/labs/2011/03/when-is-seven-days-not-a-week/#comments</comments>
		<pubDate>Wed, 23 Mar 2011 14:03:56 +0000</pubDate>
		<dc:creator>David Middlecamp</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[date.js]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[time]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=1152</guid>
		<description><![CDATA[I&#8217;ve been using Date.js on a project, and it&#8217;s an awesome library. I especially love when someone else handles dates for me (am-i-right fellow developers? ), and everything was going great until we discovered a rare time bug. I had forgotten something very important regarding time: it&#8217;s relative. We&#8217;re not writing guidance software for rockets, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been using <a href="http://www.datejs.com/">Date.js</a> on a project, and it&#8217;s an awesome library.  I especially love when someone else handles dates for me (am-i-right fellow developers? <img src='http://www.azavea.com/blogs/labs/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  ), and everything was going great until we discovered a rare time bug.  I had forgotten something very important regarding time: it&#8217;s <em>relative</em>.</p>
<p>We&#8217;re not writing guidance software for rockets, so we don&#8217;t have to deal with relativity in <a href="http://en.wikipedia.org/wiki/Special_relativity">the Einstein sense,</a> but relative in a more day-to-day and somewhat <a href="http://en.wikipedia.org/wiki/Daylight_saving_time">artificial sense</a>.  Although I&#8217;m not exactly sure how <a href="http://en.wikipedia.org/wiki/Benjamin_Franklin">Benjamin Franklin</a> really felt about daylight savings time, it certainly makes programming a little trickier for us Philadelphians.</p>
<p>Here is an example for those following along in firebug:</p>
<pre>
var start = new Date('2010/01/01');
var end = new Date('2011/01/01');
var diff = (new TimeSpan(end - start)).days;

for(var i=0;i&lt;diff;i++) {
    var today = new Date(start).add(i).days();
    var plusSeven = today.clone().add(7).days();
    var oneWeek = (new TimeSpan(plusSeven - today));</code>

    if ((oneWeek.days != 7) || (oneWeek.hours != 0)) {
        console.debug("Time Warp: ", today);
    }
}
</pre>
<p>Say you made some bit of code that generated a date range by adding or subtracting some number of days off a date, you&#8217;d have a bug from March 8th through March 14th, and then it&#8217;d be gone until November 1st through  the 7th!  Not only is this sneaky, <a href="http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005#Change_to_daylight_saving_time">it&#8217;s hard to predict</a>!  Although this isn&#8217;t really a bug in Date.js, it requires us to change how we think about time.</p>
<p>The <a href="http://lmgtfy.com/?q=date.js+daylight+savings+time">recommended Googling</a> didn&#8217;t provide a straightforward, and rock-solid technique for identifying and adjusting for this effect in any given time zone.</p>
<p>So I made one:</p>
<p>First, a quick example:<br />
(my apologies if this is obvious or full of mistakes)</p>
<pre>
//Lets say you wanted to setup some DateTime controls
//with a rolling date range, lets say using an offset in days
//with Date.js you'd probably do something like this:</code>

var offsetDays = 7;
var start = new Date('2010/03/09');
var end = start.clone().add(offsetDays).days();</code>

//so far so good, we got the intended date:
//Tue, March 16th, 2010
//but we want to update and store the rolling date range,
//so lets get that range back out:

// ... snip! ...

var diff = (new TimeSpan(end - start));
if (diff.days != offsetDays) {
    //daylight savings time caused time to 'spring ahead',
    //and our 7 days is instead 6 days 23 hours
    console.debug('user changed date');
}
</pre>
<p>We&#8217;re going to need a simple function that gets a TimeSpan that is adjusted for the daylight savings time adjustment:</p>
<pre>
// gets the difference between two dates,
// adjusting for daylight savings time.
var getDSTAdjustedDiff = function(startDate, endDate) {
    var startOffset = startDate.getUTCOffset();
    var endingOffset = endDate.getUTCOffset();

    if (startOffset &gt; endingOffset) {
        startDate = startDate.clone().add({hours: -1 });
    }
    if (endingOffset &gt; startOffset) {
        endDate = endDate.clone().add({hours: -1 });
    }
    return (new TimeSpan(endDate - startDate));
};
</pre>
<p>If we revisit our example from before:</p>
<pre>
var start = new Date('2010/01/01');
var end = new Date('2011/01/01');
var diff = (new TimeSpan(end - start)).days;

for(var i=0;i&lt;diff;i++) {
    var today = new Date(start).add(i).days();
    var plusSeven = today.clone().add(7).days();
    var oneWeek = (new TimeSpan(plusSeven - today));
    var adjustedDiff = getDSTAdjustedDiff(today, plusSeven);

    if ((oneWeek.days != 7) || (oneWeek.hours != 0)) {
        console.debug("Time Warp: ", today);
    }
    if ((adjustedDiff.days != 7) || (adjustedDiff.hours != 0)) {
        console.debug("Adjustment fail!: ", today, adjustedDiff );
    }
}
</pre>
<p>If everything went well, this code block should run and run without the second block ever firing.  I hope this helps someone else out there avoid this pitfall, now if I can just figure out time zones&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2011/03/when-is-seven-days-not-a-week/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery, OpenLayers.Layer.Vector and IE8</title>
		<link>http://www.azavea.com/blogs/labs/2011/02/jquery-vectors-and-ie8/</link>
		<comments>http://www.azavea.com/blogs/labs/2011/02/jquery-vectors-and-ie8/#comments</comments>
		<pubDate>Mon, 28 Feb 2011 21:56:12 +0000</pubDate>
		<dc:creator>Carissa Brittain</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[IE8]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[openlayers]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=1116</guid>
		<description><![CDATA[One of the very first things you learn about OpenLayers is the importance if initializing maps in either the body tag&#8217;s onload event or in script tags at the bottom of the page. The basic reason for this is that many of the OpenLayers components need a page&#8217;s dom to be complete before it starts [...]]]></description>
			<content:encoded><![CDATA[<p>One of the very first things you learn about <a title="For beginners" href="http://trac.osgeo.org/openlayers/wiki/NewToOpenLayers" target="_blank">OpenLayers</a> is the importance if initializing maps in either the body tag&#8217;s onload event or in script tags at the bottom of the page. The basic reason for this is that many of the OpenLayers components need a page&#8217;s dom to be complete before it starts adding it&#8217;s own dom structure. The onload event is thrown after the browser is finished loading everything else, and the bottom of the page naturally gets rendered last, so these two places tend to work the best. Makes <a title=".. not so common maybe." href="http://en.wikipedia.org/wiki/Common_sense" target="_blank">sense</a> right?</p>
<p>If you&#8217;ve been using additional frameworks, like <a title="jQuery" href="http://jquery.com/" target="_blank"> jQuery</a> ( or <a title="Ext, bu Sencha" href="http://www.sencha.com/products/extjs/" target="_blank">EXT</a> or <a title="Django" href="http://www.djangoproject.com/" target="_blank">Django</a> or <a title="Drupal" href="http://drupal.org/" target="_blank">Drupal</a> or <a title="Missing a javascript section..." href="http://en.wikipedia.org/wiki/Comparison_of_web_application_frameworks" target="_blank">whatever</a>) you&#8217;ve learned to rely on that framework&#8217;s <a title="It's only the second subject in their docs.." href="http://docs.jquery.com/How_jQuery_Works#Launching_Code_on_Document_Ready" target="_blank">onready event</a>, or its rough equivalent.  This event tends to be thrown before the onload event so that the frameworks don&#8217;t need to wait for every last image and tag to be loaded. In theory, this could be a <a title="Though maybe not this problematic" href="http://www.motifake.com/image/demotivational-poster/1005/captain-i-think-we-have-a-problem-scotty-star-trek-scotch-demotivational-poster-1275263980.jpg" target="_blank">very problematic</a> place to put OpenLayers map initialization code. The dom elements that OpenLayers is looking for may or may not be there, and there&#8217;s no way to tell. In practice, most browsers, and most OpenLayers components, work fine when initialized in a framework&#8217;s onready event. One layer in particular (OpenLayers.Layer.Vector) will not play nice with one particular browser (IE8) if initialized in a framework&#8217;s onready event. The vector layer depends on the document object&#8217;s namespace attribute, and this attribute simply <a title="Nice writeup" href="http://drupal.org/node/613002" target="_blank">isn&#8217;t always available</a> before IE&#8217;s onload event (which is called after the onready event). As you can imagine, this was one headache I wish I could have avoided.</p>
<p>Thankfully, there is a fairly simple fix. Instead of the usual convention of :</p>
<pre>$.ready(function() { init_my_map(); });
</pre>
<p>Add a stand-alone script tag to the extreme end of the body content on the pages where you need a vector layer:</p>
<pre>&lt;script&gt;
    init_my_map();
&lt;/script&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2011/02/jquery-vectors-and-ie8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How We Launched a New CMS (301 redirects and counting)</title>
		<link>http://www.azavea.com/blogs/labs/2010/08/how-we-launched-a-new-cms-301-redirects-and-counting/</link>
		<comments>http://www.azavea.com/blogs/labs/2010/08/how-we-launched-a-new-cms-301-redirects-and-counting/#comments</comments>
		<pubDate>Wed, 18 Aug 2010 20:15:10 +0000</pubDate>
		<dc:creator>Jeremy Heffner</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[cms]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[Varnish]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=820</guid>
		<description><![CDATA[When we decided to give our website a brand new look, we also evaluated whether to change Content Management Systems and decided to migrate our content management system to Concrete 5 from DotNetNuke.  DotNetNuke had served us well, but we wanted to move to a more modern and lightweight framework with a better administrative interface. [...]]]></description>
			<content:encoded><![CDATA[<p>When we decided to give our website <a href="http://www.azavea.com/blogs/atlas/2010/08/a-brand-new-look/">a brand new look</a>, we also evaluated whether to change Content Management Systems and decided to migrate our content management system to <a href="http://www.concrete5.org/">Concrete 5</a> from <a href="http://www.dotnetnuke.com/">DotNetNuke</a>.  DotNetNuke had served us well, but we wanted to move to a more modern and lightweight framework with a better administrative interface.   This migration presented us with a few challenges to overcome.</p>
<p>While we redesigned our company website, we didn&#8217;t want to wait to launch it until we had migrated all of our product websites to Concrete 5.   To combine the two systems under one domain name, we leveraged <a href="http://varnish-cache.org/">Varnish</a> as a proxy to split requests between our LAMP stack hosting Concrete 5 and our Windows server hosting DotNetNuke.    We&#8217;ve found Varnish to be a robust and yet lightweight tool for proxying, caching, and load balancing.  You can read more about how we used <a href="http://www.azavea.com/blogs/labs/2009/12/scaling-walkshed-org-with-varnish-and-amazon-web-services/">Varnish to host Walkshed on Amazon Web Services in this related blog post</a>.</p>
<p>Another concern when migrating CMS&#8217;s is making sure that the old URLs continue to function and issue the right redirects to users and search engines.   <a href="http://www.azavea.com/about-us/staff-profiles/andrew-jennings/">Andrew Jennings</a> took this project on and whipped up a URL redirection system for us using Apache&#8217;s <a href="http://httpd.apache.org/docs/1.3/mod/mod_rewrite.html">mod_rewrite</a> module.</p>
<ul>
<li>As requests come into Varnish, we first test to see if Concrete5 can respond to the URL.</li>
<li>If Concrete5 isn&#8217;t managing the content for the URL, the request bounces into our redirection system that maps old URLs to new URLs using a <a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritemap">binary hashtable</a> and issues <a href="http://en.wikipedia.org/wiki/URL_redirection#HTTP_status_codes_3xx">301 permanent redirects</a> to the client.   We found that this approach is faster than simply relying on <a href="http://httpd.apache.org/docs/1.3/howto/htaccess.html">Apache .htaccess</a> files since we have several thousand legacy URLs to support.</li>
<li>If the URL request is not in the mapping file, we simply forward the request to the Windows server to be served by DotNetNuke.</li>
<li>If DotNetNuke returns a 404 error, we redirect the request back to Concrete 5 to respond with a 404 error so that our redirection is invisible to the user.</li>
</ul>
<p>A final goal of the CMS migration was to improve page load times for our visitors.   Having pages that load quickly not only <a href="http://www.hitwise.com/news/au201001.html#body_third">improves the experience for our web visitors</a>, but also has an <a href="http://searchengineland.com/google-now-counts-site-speed-as-ranking-factor-39708">impact on search engine rankings</a>.    Concrete5 suggested we setup <a href="http://pecl.php.net/package/APC">Alternative PHP Cache (APC)</a> to speed up the pages it is serving, which has definitely improved performance on our new pages.</p>
<p>We found a great WordPress plugin (which runs our blogs) that can also leverage APC called <a href="http://wordpress.org/extend/plugins/w3-total-cache/">W3 Total Cache</a>.   It provides a host of other functionality that we&#8217;re exploring including HTML, JS, and CSS minification as well as pushing design assets to Amazon&#8217;s CDN.</p>
<p>I always find it amusing how something as straight forward as a new website launch has so much happening behind the scenes.   To find out more <a href="http://www.azavea.com/blogs/atlas/2010/09/whats-in-a-new-website-beyond-a-new-look-content-that-defines-a-whole-brand/">about the content that went into the new Azavea website read Rachel&#8217;s related blog post</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2010/08/how-we-launched-a-new-cms-301-redirects-and-counting/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Getting an ArcGIS Server Map Cache in S3</title>
		<link>http://www.azavea.com/blogs/labs/2010/02/arcgis-server-cache-in-s3/</link>
		<comments>http://www.azavea.com/blogs/labs/2010/02/arcgis-server-cache-in-s3/#comments</comments>
		<pubDate>Thu, 04 Feb 2010 15:08:44 +0000</pubDate>
		<dc:creator>Sean McGinnis</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[Amazon Web Services]]></category>
		<category><![CDATA[ArcGIS Server]]></category>
		<category><![CDATA[openlayers]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=430</guid>
		<description><![CDATA[When deciding how to best handle the air photos in the new Philadelphia Water Department Stormwater Map Viewer, we kicked around a few ideas. We decided to put the cache in Amazon&#8217;s Simple Storage Service to offload some of the local disk requirements and leverage their fast data storage and delivery infrastructure. In moving the [...]]]></description>
			<content:encoded><![CDATA[<p>When deciding how to best handle the air photos in the new <a href="http://phillystormwater.org">Philadelphia Water Department Stormwater Map Viewer</a>, we kicked around a few ideas. We decided to put the cache in <a href="http://aws.amazon.com/s3/">Amazon&#8217;s Simple Storage Service</a> to offload some of the local disk requirements and leverage their fast data storage and delivery infrastructure. In moving the process, we learned a few things:</p>
<h1>Tune Your Cache</h1>
<p>Make sure you spend time planning the cache. Not only will the cache look better in the final application, but it will also load to S3 faster and cost less in the long run.</p>
<ul>
<li>Set the extents in the MXD or MSD before publishing to a map service. The overhead of transferring the 254 byte empty tiles caused a lot of unnecessary burden on the upload process as well as the fact that you are paying for them to be stored in the cloud. If it doesn&#8217;t need to be there, don&#8217;t build it.</li>
<li>Choose the correct image format for the cache. If you are caching a base map and do not need to support transparency, make it a JPEG. If it needs to support background transparency, use PNG. <a href="http://www.esri.com">ESRI&#8217;s</a> suggestions for planning a map cache can be found <a href="http://webhelp.esri.com/arcgisserver/9.3.1/dotNet/index.htm#planning_a_map_cache.htm">here</a>.</li>
</ul>
<h1>Get a Good Tool to Transfer the Files</h1>
<p>I started using the free version of <a href="http://cloudberrylab.com/?page=cloudberry-explorer-amazon-s3">Cloudberry Labs S3 explorer</a>. But I had to move over 90 Gbs worth of data to my S3 bucket. The <a href="http://cloudberrylab.com/?page=s3-explorer-pro">CloudBerry S3 Explorer &#8211; Pro</a> supported multithreading which allowed for up to 5 threads to either enumerate through the folders, copy the files or apply the ACL. It is a low cost application that more than pays for itself when moving a lot of files up to a bucket.</p>
<p>When transferring the files up, I was working in blocks of directories, not the whole scale level. It was quicker for me to work in 20 to 30 subdirectories than grabbing a whole scale level. It did require a little bit more management on my end, but more steady progress was made.</p>
<h1>Accessing the Tiles</h1>
<p>ArcGIS Server does not support cloud hosted caches at the 9.3.1 release.  The ESRI <a href="http://resources.esri.com/arcgisserver/index.cfm?fa=JSAPIs">Javascript API</a> and <a href="http://resources.esri.com/arcgisserver/apis/flex/">Flex API</a> can be extended to use caches hosted in the cloud (<a href="http://thunderheadxpler.blogspot.com/2008/09/arcgis-tiles-on-amazon-s3.html">Flex example</a> from <a href="http://thunderheadxpler.blogspot.com/">Mansour Raad</a>), so you&#8217;ll have to roll your own. For the <a href="http://www.phillystormwater.org">Philly Storm Water</a> project, we were using the <a href="http://openlayers.org/">Open Layers</a> and someone has rolled one for us. There is a <a href="http://trac.openlayers.org/attachment/ticket/1967/ArcGISCache.patch">patch</a> that can be used to access the cache without communicating through ArcGIS Server straight from the client-side library. The one thing to note is that the Tile Origin is pretty touchy, we had to make some adjustments to the origin values to make sure everything lined up correctly.</p>
<h1>Summary</h1>
<p>Now that the site is up there and we are starting to get some traffic hitting it, putting the tiles in S3 was the right decision. There is no reason for ArcGIS Server to waste any cycles moving tiles around, let it do the heavy lifting with the vector layers and queries. Hopefully the <a href="http://events.esri.com/uc/QandA/index.cfm?fuseaction=Answer&amp;ConferenceID=2A8E2713-1422-2418-7F20BB7C186B5B83&amp;QuestionID=2645">rumors</a> are true, and the ArcGIS Server 10 release will be more aligned with cloud computing. Until then, there are still plenty of ways to take advantage of the benefits.  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2010/02/arcgis-server-cache-in-s3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Bracing for (Potential) Catastrophic Success &#8212; Amazon&#8217;s Cloudfront CDN</title>
		<link>http://www.azavea.com/blogs/labs/2009/10/bracing-for-potential-catastrophic-success-amazons-cloudfront-cdn/</link>
		<comments>http://www.azavea.com/blogs/labs/2009/10/bracing-for-potential-catastrophic-success-amazons-cloudfront-cdn/#comments</comments>
		<pubDate>Wed, 21 Oct 2009 19:13:23 +0000</pubDate>
		<dc:creator>Jeremy Heffner</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[Amazon Web Services]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=304</guid>
		<description><![CDATA[Most of the web applications we build are either used internally by our clients or have a steady stream of public user activity.   With our recent Redistricting the Nation launch we wanted to experiment with some optimizations to make our site more resilient to traffic spikes as well as to improve the user experience. [...]]]></description>
			<content:encoded><![CDATA[<p>Most of the web applications we build are either used internally by our clients or have a steady stream of public user activity.   With our recent <a href="http://www.redistrictingthenation.com">Redistricting the Nation</a> launch we wanted to experiment with some optimizations to make our site more resilient to traffic spikes as well as to improve the user experience.</p>
<p>Our strategy is broken down into a few components:</p>
<ul>
<li>Improving download speed using <a href="http://aws.amazon.com/cloudfront/">Amazon&#8217;s Content Delivery Network, Cloudfront</a></li>
<li>Reducing the size of the page components to improve loads times</li>
<li>Measuring results with Yahoo&#8217;s <a href="http://developer.yahoo.com/yslow/">YSlow</a> toolkit</li>
</ul>
<p>This post covers the Cloudfront CDN.</p>
<p>Previously, we had experimented with Amazon&#8217;s Web Services stack to host applications, but we hadn&#8217;t experimented with their <a href="http://aws.amazon.com/cloudfront/">Cloudfront CDN</a> product.   Pricing for the CDN is quite similar to <a href="http://aws.amazon.com/s3/">Amazon S3</a> and allows organizations to build scalable applications without the upfront cost of most CDNs.  We decided to use the CDN to host some large Javascript assets as well as our image components.</p>
<p>Cloudfront is quite easy to setup.   We simply created an Amazon S3 bucket called<em><strong> s3.azavea.com</strong></em> and pointed a CNAME record for <strong><em>s3.azavea.com</em></strong> to the full bucket domain &#8212; <strong><em>s3.azavea.com.s3.amazonaws.com.    <span style="font-weight: normal;"><span style="font-style: normal;">Then, we enabled a Cloudfront distribution for the</span><strong> s3.azavea.com</strong><span style="font-style: normal;"> bucket using the free tool <a href="http://cloudberrylab.com/">Cloudberry</a>.   Finally, we setup a CNAME record for <strong><em>cdn.azavea.com</em></strong> to the Cloudfront distribution domain </span><strong>d17ib0dlm1q8qa.cloudfront.net</strong><span style="font-style: normal;"> and we were rolling.</span></span></em></strong></p>
<p><strong><em><span style="font-style: normal; font-weight: normal;">Since the CDN is heavily cached, it was easiest to use </span>s3.azavea.com<span style="font-style: normal; font-weight: normal;"> links during development to reduce the amount of file versioning that was necessary.   Once we were settled on our assets, we switched to </span>cdn.azavea.com<span style="font-style: normal; font-weight: normal;"> links and started using the CDN. </span></em></strong></p>
<p><strong><em><span style="font-style: normal; font-weight: normal;">The speed of the CDN is quite astounding.  Splitting assets across another domain name also improves the browser&#8217;s ability to request more files at once improving the user experience.  We were quite pleased with how easily we could offload assets to Cloudfront and realize gains with limited time investment.</span></em></strong></p>
<p><strong><em><span style="font-style: normal; font-weight: normal;">A few notes to keep in mind when you are working with a CDN for the first time:</span></em></strong></p>
<ul>
<li>Since there is no way to flush assets out of Cloudfront&#8217;s edge nodes, be sure to use file name versioning.   This was a bit alien to us, but is easy to incorporate once you think it through.   For instance, we decided not to set a far-future expiration header on our PDF assets as they are often directly linked to and we wanted to be able to update them regularly.</li>
<li>Speaking of PDFs, it seems that while Cloudfront supports byte-range requests for assets, it doesn&#8217;t assert the &#8220;Accept-Ranges: bytes&#8221; HTTP header. This makes our large PDFs fully download before Adobe displays them within the browser.  Unfortunately there is no way to add this header at the moment.</li>
<li><a href="http://cloudberrylab.com/">Cloudberry</a> is great to add HTTP headers to S3 assets.   We decided that most of our assets would have a six month cache lifespan by asserting the &#8220;Cache-Control: max-age=15552000&#8243; header.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2009/10/bracing-for-potential-catastrophic-success-amazons-cloudfront-cdn/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Nesting Comments Using Ext.XTemplate</title>
		<link>http://www.azavea.com/blogs/labs/2009/10/nesting-comments-using-ext-xtemplate/</link>
		<comments>http://www.azavea.com/blogs/labs/2009/10/nesting-comments-using-ext-xtemplate/#comments</comments>
		<pubDate>Thu, 01 Oct 2009 18:38:42 +0000</pubDate>
		<dc:creator>Carissa Brittain</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[visualization]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=300</guid>
		<description><![CDATA[When considering a nested comment implementation, we really only have to deal with two types of comment: root comments and child or nest comments. Root comments are easy to describe. They are not a child of any other comment. They're what you get when someone has a brilliant new insight, hits the "New Comment" button and dazzles us all. Nest comments, on the other hand, seem like they should be more complicated. If you're allowing replies to comments, then a nest comment could have its own nests. Wouldn't that make a comment both a nest and a root? Not in this post. For the purposes of this example: roots don't have parents; everything else is a nest. Simple.]]></description>
			<content:encoded><![CDATA[<p>When considering a <a title="No, not for them..." href="http://en.wikipedia.org/wiki/Nesting,_Shetland" target="_blank">nested comment implementation</a>, we really only have to deal with two types of comment: root comments and child or nest comments. <a title="Hold me up!" href="http://en.wikipedia.org/wiki/File:Koreni-00.JPG" target="_blank">Root </a>comments are easy to describe. They are not a child of any other comment. They&#8217;re what you get when someone has a brilliant new insight, hits the &#8220;New Comment&#8221; button and dazzles us all. <a title="Cheep, cheep." href="http://en.wikipedia.org/wiki/File:Bald_eagle_nesting.JPG" target="_blank">Nest</a> comments, on the other hand, seem like they should be more complicated. If you&#8217;re allowing replies to comments, then a nest comment could have its own nests. Wouldn&#8217;t that make a comment both a nest and a root? Not in this post. For the purposes of this example: roots don&#8217;t have parents; everything else is a nest. Simple.</p>
<p>Let&#8217;s address a few data prerequisites before diving into the actual how-to of template nesting. No matter how your comment data gets to your page, it should be <a title="But which way?" href="http://www.molly.com/2009/09/29/why-bottom-posting-sucks/" target="_blank" class="broken_link" rel="nofollow">in order</a>. For example: say we have 3 comments :</p>
<ul>
<li>Root A &#8211; Oct 1
<ul>
<li>Nest 1 &#8211; Oct 15</li>
</ul>
</li>
<li>Root B &#8211; Oct 4</li>
</ul>
<p>We seem to be following a <a title="Smarter than your average 4th grader?" href="http://fcit.usf.edu/FCAT/strategies/co/activity2.htm" target="_blank">chronological paradigm</a> here and everything is in a logical order. In order to do the least processing in your javascript, your data should come to the page in this same order: Root A, Nest 1 then Root B.</p>
<p>Each comment should have some basic information available to the template. Root comments only need some kind of id along with the actual comment information (text, date, <a title="What's in it, Egg?" href="http://en.wikipedia.org/wiki/Six_Demon_Bag#Big_Trouble_in_Little_China" target="_blank">all that kind of thing</a>). Nest comments need a bit more information. They should know the id of their parent (whether a root or another nest) and their nesting level or how deeply they&#8217;re nested from the top of the conversation. For our example above, a sample data set should look something like this:</p>
<pre>{ [id: 100, text: "Root A", date: "Oct 1", nestLevel: 0, parent: 0],
  [id: 350, text: "Nest 1", date: "Oct 15", nestLevel: 1, parent: 100],
  [id: 288, text: "Root B", date: "Oct 4", nestLevel: 0, parent: 0] }</pre>
<p>Notice that our root comments have nestLevel and parent values of 0 (<a title="Wasn't always there" href="http://en.wikipedia.org/wiki/Zero#Early_history" target="_blank">zero</a>). This makes sense: Roots can&#8217;t have parents by our definition above, and they will always have a nest level of 0. Take a look at the second entry, our nest comment. The parent value is 100 which is the same as the id value for our first root comment, Root A. In effect, we have an internal relationship <a title="An internal foreign key maybe?" href="http://www.geocities.com/SiliconValley/Vista/2207/sql3.html#Keys" target="_blank" class="broken_link" rel="nofollow">key for our data</a>.</p>
<p>Now that we have data in the right order and our nests know their parents and <a title="Hopefully not this deep!" href="http://ant.edb.miyakyo-u.ac.jp/INTRODUCTION/Gakken79E/Page_20.html" target="_blank">how deep they are</a>, we can set up our Ext.XTemplate object to handle the visual component of our nesting.</p>
<pre>var tplComments = new Ext.XTemplate(
 '&lt;tpl for="."&gt;',
    '&lt;div id="comment-{id}" style="margin-left:{[values.nestLevel === 0 ? "5" : "15"]}px;
              background-color:{[values.nestLevel % 2 === 0 ? "#fff" : "#f9f9dd"]};
              border:1px #ccc solid;
              padding:5px;"&gt;',
        //comment text, date and other info here
    '&lt;/div&gt;',
 '&lt;/tpl&gt;',
);</pre>
<p>Lets pick apart our template and see how it visually nests our comments for us. On the first line, we&#8217;re declaring our object, nothing new here. The next line uses a template looping mechanism (for=&#8221;.&#8221;) to create the internal tag structure for each data element in our comment set.  Now things get interesting.</p>
<p>The id of our eventual div tags will include the id of the comment being templated. This is done using some <a title="An intro to the subject" href="http://www.simple-talk.com/dotnet/asp.net/token-replacement-in-asp.net/" target="_blank">token replacement</a> where you see {id}. So the html for our first comment will be in the div with the id &#8220;comment-100&#8243;.</p>
<p>In the style attribute of our comment div are two blocks of javascript surrounded by {[    ]} that the template will treat as javascript instead of just text. Inside those blocks, the &#8220;values&#8221; object refers to the particular comment object we&#8217;re dealing with inside our loop. The attributes on this object are the same as the property names in our data set: id, text, date, nestLevel and parent. When <a title="Ternary - think if-then-else" href="http://en.wikipedia.org/wiki/Ternary_logic" target="_blank">English-a-fied</a>, the first block reads: If the nestLevel is 0 (meaning a root comment) then write 5 here, for any other value write 15. This will give our roots a left margin of 5px and our nests a left margin of 15. While this doesn&#8217;t quite get us to an infinitely-nested set of comments, it gets us close and provides a visual cue to the audience. We&#8217;ll get closer to a truly nested solution in the javascript utilizing this template later.</p>
<p>The second block of javascript in the style attribute switches the background from white to cream on alternating comments. Its English sentence might read: If the nestLevel <a title="Look for modulo" href="http://www.javascriptkit.com/jsref/arithmetic_operators.shtml" target="_blank">is an even number</a>, use a white background, otherwise use cream.</p>
<p>Now for some javascript to bring it all together and make our nesting work properly. After your comment data is available in the page, you can use the <a title="The spec" href="http://www.extjs.com/deploy/dev/docs/?class=Ext.XTemplate" target="_blank">XTemplate</a>&#8216;s append function and some basic Ext DOM querying to stack your comments indefinitely.</p>
<pre>if(comment.get("nestLevel") === 0)
{
   tplComments.append(commentLocation, comment.data);
}
else
{
    tplComments.append(Ext.get("comment-" + comment.get("parent")), comment.data);
}</pre>
<p>This block of javascript splits our comments set into the two definitions from the beginning: roots and nests. Roots just get appended to the end of the commentLocation element on your page. Nothing fancy there. Nests, however, take a bit more thought. A nest knows the id of it&#8217;s parent comment. In our template object, each comment is contained in a div with the same id as the comment. In order to nest a comment under its proper parent, use Ext&#8217;s built-in DOM searching capabilities to locate the parent, then append the nest.</p>
<p>Each nest will wind up nested 15px in from the left edge of its parent; alternating comments have subtle color changes and each wrapping div is associated to a comment by id for future tinkering. And we&#8217;re done!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2009/10/nesting-comments-using-ext-xtemplate/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ubiquity Firefox Plugin</title>
		<link>http://www.azavea.com/blogs/labs/2009/08/ubiquity-firefox-plugin/</link>
		<comments>http://www.azavea.com/blogs/labs/2009/08/ubiquity-firefox-plugin/#comments</comments>
		<pubDate>Mon, 10 Aug 2009 20:07:47 +0000</pubDate>
		<dc:creator>David Zwarg</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[wiki]]></category>

		<guid isPermaLink="false">http://www.azavea.com/blogs/labs/?p=152</guid>
		<description><![CDATA[I have been following the Ubiquity project from Mozilla Labs, and I gotta say, it's pretty rad. If you are a Javascript Ninja or an aspiring one, Ubiquity can make your web surfing super slick.

I slapped together a browser command that I use to search our internal wiki in record time.  Sample code included!]]></description>
			<content:encoded><![CDATA[<p>I have been following the <a href="https://wiki.mozilla.org/Labs/Ubiquity">Ubiquity</a> project from <a href="http://www.mozilla.org/">Mozilla</a> <a href="https://wiki.mozilla.org/Labs">Labs</a>, and I gotta say, it&#8217;s pretty rad. If you are a Javascript Ninja or an aspiring one, Ubiquity can make your web surfing super slick.</p>
<p>We use an <a href="http://www.azavea.com/newsletter/v1i1/index.html#wiki">internal wiki</a> here at the office, using the <a href="http://www.splitbrain.org/projects/dokuwiki">Dokuwiki</a> framework.  I personally look in the wiki for something at least 5 times a day.  Usually, this involves:</p>
<ol>
<li>Opening a new browser tab</li>
<li>Go to our Intranet page</li>
<li>Click on the wiki link</li>
<li>Search the wiki (let&#8217;s say for &#8220;configuration&#8221;)</li>
<li>Look through the list of search hits</li>
<li>Maybe repeat step 4</li>
</ol>
<p>As I was looking at the <a href="https://wiki.mozilla.org/Labs/Ubiquity/Ubiquity_0.5_Author_Tutorial">Ubiquity tutorials</a>, it became apparent that with the right Ubiquity command, the whole workflow could be simplified to:</p>
<ol>
<li>Control-Space</li>
<li>Type &#8220;dw configuration&#8221;</li>
<li>Review search hits</li>
<li>Maybe repeat step 2</li>
</ol>
<p>Ok, so not a profound difference in the number of steps, but I can do all of these steps without letting my hands leave the keyboard.  Some Firefox purists out there may argue that I could do the same with the first set of steps, but there are fewer Alts and Controls in the second set &#8212; anyway, this is just an experiment, so lighten up, ok?</p>
<p>I won&#8217;t paste the whole code here, but I wanted to point out a few neat tricks that are part of the command:</p>
<ol>
<li><strong>XML RPC</strong><br />
A transport protocol for web services, <a href="http://www.xmlrpc.com/">XML Remote Procedure Calls</a> let you invoke a web service with some arguments, and get a result back.  Dokuwiki has an <a href="http://www.dokuwiki.org/devel:xmlrpc">XML-RPC interface</a>, where one can perform basic searching and editing functionality. <strong>Note</strong>: this interface is disabled on the main Dokuwiki site, and is something a system administrator needs to manually enable on an installation of Dokuwiki.</p>
<p>In this chunk of code, I set up the xmlrpc url, and create an xml blob that will contain the remote command that I want to execute (wiki.getAllPages).  This will return a list of all the pages in the Dokuwiki instance.</p>
<pre>...
// the command preview
preview: function(pblock, args) {
  if ( args.object.text != '' ) {
    // set the url for the xmlrpc command
    var url = 'http://www.dokuwiki.org/lib/exe/xmlrpc.php';
    // set up the data to POST
    var data = '&lt;?xml version="1.0"?&gt;&lt;methodCall&gt;&lt;methodName&gt;' +
      'wiki.getAllPages&lt;/methodName&gt;&lt;/methodCall&gt;';
    // provide some feedback to the user
    pblock.innerHTML = 'Searching for page matches for ' +
      args.object.text + '...';
    // hold on to a reference to the currently-scoped object
    var me = this;
    jQuery.ajax( {
      async: true,
      contentType: 'text/xml',
      data: data,
      dataType: 'xml',
      url: url,
      type: 'POST',
      success: function(x) {
        ...
      }
    });
  }
},
...</pre>
</li>
<li><strong>jQuery</strong><br />
Ubiquity has a version of <a href="http://jquery.com/">jQuery</a> built into it, so you have the full power of jQuery at your disposal.  I used this to do some ajax calls to the XML RPC interface of Dokuwiki.</p>
<p>You can see this in action in the above code excerpt.</li>
<li><strong>XPath</strong><br />
No mention of XML would be complete without some way to navigate your XML document using <a href="http://www.w3.org/TR/xpath">XPath</a>.  To get the search results and other information, I used some of Firefox&#8217;s built in objects to extract the relevant search hits.</p>
<p>The object returned from the success method above is an xml document (since I specified the dataType in the jQuery.ajax call). With an xml document, one can perform XPath tricks with the evaluate() method.  This code selects the &#8220;name&#8221; elements where the page id is the same as the command argument (in this case &#8220;configuration&#8221;).</p>
<pre>// get the pages with IDs that match the input string
var pages = x.evaluate( '//struct/member/name[text()="id"]' +
  '/../value/string[contains(text(),"' + args.object.text + '")]',
  x, null, XPathResult.ANY_TYPE, null );
var nuggets = [];
// return the top 10 results, if multiple matches are found
var item = pages.iterateNext();
while ( item &amp;&amp; nuggets.length &lt; 10 ) {
  nuggets.push( item.textContent );
  item = pages.iterateNext();
}
// if there are many hits, show a list of matches
if ( nuggets.length &gt; 1 ) {
  CmdUtils.previewList( pblock, nuggets, function(){
    me.gotoUrl(nuggets[arguments[0]]);
  });
}
// if there is one hit, generate an html preview in the
// ubiquity preview frame
else {
  pblock.innerHTML = 'Found match, generating preview...';
  ...</pre>
</li>
</ol>
<p>That was my first stab at it, and it has a couple quirks, but I find myself using it to find information really quickly.  I think there is a great amount of potential here, especially with the collection of commands that are already built in to Ubiquity.</p>
<p>For the fearless, the full command is available for download here: <a href="http://www.azavea.com/blogs/labs/wp-content/uploads/2009/08/dokuwiki_search.js">dokuwiki search</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.azavea.com/blogs/labs/2009/08/ubiquity-firefox-plugin/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

