<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Cullen King - Home</title>
  <id>tag:cullenking.com,2010:mephisto/</id>
  <generator uri="http://mephistoblog.com" version="0.8.0">Mephisto Drax</generator>
  <link href="http://cullenking.com/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://cullenking.com/" rel="alternate" type="text/html"/>
  <updated>2010-07-19T18:06:14Z</updated>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-07-19:9151</id>
    <published>2010-07-19T17:54:00Z</published>
    <updated>2010-07-19T18:06:14Z</updated>
    <link href="http://cullenking.com/2010/7/19/nginx-error-log-filling-up-with-rewrite-rules" rel="alternate" type="text/html"/>
    <title>nginx error log filling up with rewrite rules</title>
<content type="html">
            &lt;p&gt;I will be the first to admit that I am not a stellar sysadmin, however I met a new low when I realized my nginx error log for my &lt;a href=&quot;http://ridewithgps.com&quot;&gt;bike route mapping&lt;/a&gt; site was filled with 5 gigs of rewrite notifications.  After thinking the solution would be easy, simply setting rewrite_log to off, I found out that didn&#8217;t actually do anything.&lt;/p&gt;


	&lt;p&gt;So, if you are trying to turn off rewrite rule logging in your nginx error log, you must do two things:&lt;/p&gt;


	&lt;p&gt;First, add&lt;/p&gt;


	&lt;pre&gt;&lt;code&gt;&lt;strong&gt;rewrite_log off;&lt;/strong&gt;&lt;/code&gt;&lt;/pre&gt;


	&lt;p&gt;to your main configuration.  Then, make sure each error_log declaration has a level other than critical.  I picked &#8216;warn&#8217;, and we&#8217;ll see if this fills up quick.  My guess is for heavy hit sites, &#8216;error&#8217; is the correct value.  Your error log line should look like:&lt;/p&gt;


	&lt;pre&gt;&lt;code&gt;&lt;strong&gt;error_log /var/log/nginx.ridewithgps.com.error.log warn;&lt;/strong&gt;&lt;/code&gt;&lt;/pre&gt;


	&lt;p&gt;With those changes you should have a much smaller error log footprint, and grepping through your logs won&#8217;t grind on your disks too much.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-06-16:7134</id>
    <published>2010-06-16T17:13:00Z</published>
    <updated>2010-06-16T17:51:31Z</updated>
    <link href="http://cullenking.com/2010/6/16/how-not-to-do-business-wakemate-is-either-scamming-or-completely-clueless" rel="alternate" type="text/html"/>
    <title>How Not To Do Business: WakeMate's Missed Opportunity</title>
<content type="html">
            &lt;p&gt;&lt;/p&gt;


	&lt;p&gt;After all the discussion surrounding the dissatisfaction with WakeMate&#8217;s silence, most sane business people would have gotten the hint: be more transparent when you borrow people&#8217;s money.  Their promises to be more vocal on Twitter and their blog have been mediocre at best.  I know that talking to your users can be a time consuming task, but it&#8217;s a large part of success especially when you ask them to go out on a limb and loan you money for an untold number of months.  The worst part is they could have turned this sequence of events into an educational opportunity for fellow entrepreneurs.  By talking about the difficulties of bringing a product to market, getting things tested/bugfixed and otherwise being vocal about the process, much of the ill will could have been avoided.  Possibly, they could have even made a good name for themselves; just as readers of Hacker News know Patrick McKenzie due to his frequent posts and writeups about the trials of running his business, WakeMate could have fostered a sense of community support.&lt;/p&gt;


	&lt;p&gt;The one thing that keeps bugging me about this situation, is this company is a Y Combinator funded startup.  YC is there to mentor and support startups and their founders in becoming successful.  Are they failing to give the WakeMate team decent advice, or is the WakeMate team failing to take it?  Is my concept of decent advice, to communicate and be open with the people placing pre-orders, wrong?  Either way, this is not how I want to do business or want people to do business with me.  I know it&#8217;s a measly $5, however I feel like I am being scammed, which is a dirty feeling even if it&#8217;s only five bucks.  I am pulling my preorder.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-06-02:6486</id>
    <published>2010-06-02T00:53:00Z</published>
    <updated>2010-06-02T01:54:40Z</updated>
    <category term="elevations"/>
    <category term="ridewithgps"/>
    <link href="http://cullenking.com/2010/6/2/converting-dems-from-geotiff-to-gridfloat" rel="alternate" type="text/html"/>
    <title>Converting DEMs from GeoTIFF to GridFloat</title>
<content type="html">
            &lt;p&gt;I just recently acquired a patched and improved &lt;span class=&quot;caps&quot;&gt;SRTM&lt;/span&gt; elevation dataset for the entire world.  This will allow our route planner to use only our own elevation service rather than relying on the &lt;span class=&quot;caps&quot;&gt;USGS&lt;/span&gt; and geonames for elevations outside the United States.  However, the format choices for the DEMs was either GeoTIFF or an &lt;span class=&quot;caps&quot;&gt;ASCII&lt;/span&gt; grid.  Since the &lt;span class=&quot;caps&quot;&gt;ASCII&lt;/span&gt; grid of elevations just means the elevations are literally space delimited in a plain &lt;span class=&quot;caps&quot;&gt;ASCII&lt;/span&gt; file, I decided that was a huge waste of space.&lt;/p&gt;


	&lt;p&gt;However, after spending a bit of time learning about &lt;span class=&quot;caps&quot;&gt;GDAL&lt;/span&gt; then writing a program in C (made a ruby module), I found out that accessing individual pixels in a GeoTIFF using &lt;span class=&quot;caps&quot;&gt;GDAL&lt;/span&gt; is slowwww.  So slow, that it was unusable for our application.  After poking around and thinking, I decided to drop the GeoTIFF format and to convert the files to a good old GridFloat.  The GridFloat format is very simple: it&#8217;s a binary file where each 32 bits is an elevation.  You can easily calculate where in the binary file to access your elevation by knowing the latitude and longitude of the corner, and that the binary file is a flattened 2D array of elevation data in row-major order.  Meaning, the first X bits of the file are row1 of the grid, followed by row2, etc etc.&lt;/p&gt;


	&lt;p&gt;My first conversion program was naive, and it read the &lt;span class=&quot;caps&quot;&gt;TIFF&lt;/span&gt; pixel by pixel, writing each pixel to the output file individually.  As expected, this was incredibly slow considering each &lt;span class=&quot;caps&quot;&gt;TIFF&lt;/span&gt; was a 6001&#215;6001 grid of pixels.  Converting a single 69mb &lt;span class=&quot;caps&quot;&gt;TIFF&lt;/span&gt; took 3-5 minutes.&lt;/p&gt;


	&lt;p&gt;My second iteration of the program read row by row, writing each row to the output file, and was considerably faster, clocking in at only 3 seconds per file.  However, I realized that each read then write was sending the disk head all over the platter, and was definitely inefficient.  So, I sacrificed some &lt;span class=&quot;caps&quot;&gt;RAM&lt;/span&gt; and just read the entire &lt;span class=&quot;caps&quot;&gt;TIFF&lt;/span&gt; into an array, then wrote that array to disk using a single fwrite() call.  This ended up being incredibly fast, taking only 1 second to read a 69 meg &lt;span class=&quot;caps&quot;&gt;TIFF&lt;/span&gt; and writeout a 138 meg GridFloat file.&lt;/p&gt;


	&lt;p&gt;Here is the final conversion code:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
#&lt;span class=&quot;Keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;lt;&lt;/span&gt;stdlib.h&lt;span class=&quot;String&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
#&lt;span class=&quot;Keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;lt;&lt;/span&gt;math.h&lt;span class=&quot;String&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
#&lt;span class=&quot;Keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;gdal.h&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;Storage&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;ma&lt;span class=&quot;Entity&quot;&gt;in&lt;/span&gt;&lt;/span&gt;(&lt;span class=&quot;Storage&quot;&gt;int&lt;/span&gt; argc, &lt;span class=&quot;Storage&quot;&gt;char&lt;/span&gt; *argv[]) {
        &lt;span class=&quot;Storage&quot;&gt;char&lt;/span&gt; *filename = argv[&lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;];
        &lt;span class=&quot;Storage&quot;&gt;char&lt;/span&gt; *outfilename = argv[&lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;];
        &lt;span class=&quot;Storage&quot;&gt;char&lt;/span&gt; *headerfilename = argv[&lt;span class=&quot;Constant&quot;&gt;3&lt;/span&gt;];
        &lt;span class=&quot;Storage&quot;&gt;char&lt;/span&gt; str[&lt;span class=&quot;Constant&quot;&gt;128&lt;/span&gt;];
        GDALDatasetH dataset;
        GDALRasterBandH *band;
        &lt;span class=&quot;Storage&quot;&gt;int&lt;/span&gt; xSize;
        &lt;span class=&quot;Storage&quot;&gt;int&lt;/span&gt; ySize;
        &lt;span class=&quot;Storage&quot;&gt;int&lt;/span&gt; i, j;
        &lt;span class=&quot;Storage&quot;&gt;double&lt;/span&gt; geoTransform[&lt;span class=&quot;Constant&quot;&gt;9&lt;/span&gt;];
        &lt;span class=&quot;Storage&quot;&gt;int&lt;/span&gt; sizee = &lt;span class=&quot;Keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;Storage&quot;&gt;float&lt;/span&gt;);
        &lt;span class=&quot;Storage&quot;&gt;int&lt;/span&gt; count = &lt;span class=&quot;Constant&quot;&gt;6001&lt;/span&gt;*&lt;span class=&quot;Constant&quot;&gt;6001&lt;/span&gt;;
        &lt;span class=&quot;Storage&quot;&gt;float&lt;/span&gt; *outt = &lt;span class=&quot;Support&quot;&gt;malloc&lt;/span&gt;(&lt;span class=&quot;Keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;Storage&quot;&gt;float&lt;/span&gt;)*count);
        &lt;span class=&quot;Storage&quot;&gt;float&lt;/span&gt; *window;
        FILE *outfp;

        GDALAllRegister(); &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt;initialize GDAL&lt;/span&gt;

        dataset = GDALOpen(filename, GA_ReadOnly);
        &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt;(dataset == &lt;span class=&quot;Constant&quot;&gt;NULL&lt;/span&gt;) {
                &lt;span class=&quot;Support&quot;&gt;fprintf&lt;/span&gt;(stderr, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Couldn't open geotiff file &lt;span class=&quot;StringInterpolation&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, filename);
                &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;;
        }   

        band = GDALGetRasterBand(dataset, &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;); 

        xSize = GDALGetRasterXSize(band);
        ySize = GDALGetRasterYSize(band);
        window = (&lt;span class=&quot;Storage&quot;&gt;float&lt;/span&gt; *) CPLMalloc(&lt;span class=&quot;Keyword&quot;&gt;sizeof&lt;/span&gt;(&lt;span class=&quot;Storage&quot;&gt;float&lt;/span&gt;)*xSize);

        GDALGetGeoTransform(dataset, geoTransform);
        &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt;((outfp = &lt;span class=&quot;Support&quot;&gt;fopen&lt;/span&gt;(outfilename, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;w&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)) == &lt;span class=&quot;Constant&quot;&gt;NULL&lt;/span&gt;) {
                &lt;span class=&quot;Support&quot;&gt;printf&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;shit, something went wrong opening file&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
                &lt;span class=&quot;Support&quot;&gt;fflush&lt;/span&gt;(stdout);
                &lt;span class=&quot;Support&quot;&gt;exit&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;);
        }   

        &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;/*&lt;/span&gt;  &lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;         * If I naively just loop through each pixel and write that pixel, it takes so long to actually&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;         * perform the conversion.  So we loop through row then order them in-memory&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;         * in an array, then write that array in a single fwrite call.  This is orders of&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;         * magnitude faster!  1 second vs 3-5 minutes per 69 meg in -&amp;gt; 138 meg out file.&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;         &lt;span class=&quot;Comment&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;Keyword&quot;&gt;for&lt;/span&gt;(i = &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;; i &amp;lt; xSize; i++) {
                GDALRasterIO(band, GF_Read, &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;, i, xSize, &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;, window, ySize, &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;, GDT_Float32, &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;); 
                &lt;span class=&quot;Keyword&quot;&gt;for&lt;/span&gt;(j = &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;; j &amp;lt; ySize; j++) {
                        outt[j + i*xSize] = window[j];
                }
        }   
        &lt;span class=&quot;Support&quot;&gt;fwrite&lt;/span&gt;(outt, sizee, count, outfp);
        &lt;span class=&quot;Support&quot;&gt;fclose&lt;/span&gt;(outfp);

        outfp = &lt;span class=&quot;Support&quot;&gt;fopen&lt;/span&gt;(headerfilename, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;w&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
        &lt;span class=&quot;Support&quot;&gt;sprintf&lt;/span&gt;(str, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;ncols         10812&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;nrows         10812&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
        &lt;span class=&quot;Support&quot;&gt;fputs&lt;/span&gt;(str, outfp);
        &lt;span class=&quot;Support&quot;&gt;sprintf&lt;/span&gt;(str, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;xllcorner     &lt;span class=&quot;StringInterpolation&quot;&gt;%f&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;yllcorner     &lt;span class=&quot;StringInterpolation&quot;&gt;%f&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, geoTransform[&lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;], geoTransform[&lt;span class=&quot;Constant&quot;&gt;3&lt;/span&gt;] - &lt;span class=&quot;Constant&quot;&gt;5.0008333333333&lt;/span&gt;);
        &lt;span class=&quot;Support&quot;&gt;fputs&lt;/span&gt;(str, outfp);
        &lt;span class=&quot;Support&quot;&gt;sprintf&lt;/span&gt;(str, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;cellsize      0.0008333333333&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;NODATA_value  -32768&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;byteorder     LSBFIRST&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
        &lt;span class=&quot;Support&quot;&gt;fputs&lt;/span&gt;(str, outfp);
        &lt;span class=&quot;Support&quot;&gt;fclose&lt;/span&gt;(outfp);

        &lt;span class=&quot;Support&quot;&gt;exit&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;);
}
&lt;/pre&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-04-21:6454</id>
    <published>2010-04-21T23:26:00Z</published>
    <updated>2010-04-21T23:29:42Z</updated>
    <link href="http://cullenking.com/2010/4/21/error-1010-a-term-is-undefined-and-has-no-properties" rel="alternate" type="text/html"/>
    <title>Error #1010: A term is undefined and has no properties</title>
<content type="html">
            &lt;p&gt;If you see this error popup while working with ActionScript 3, it&#8217;s most likely because you are trying to access a property of a null object.  It happens most often to me when looping through an array and having an element be null unexpectedly.  It&#8217;s a pretty undescriptive message, but easily fixed.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-04-20:6453</id>
    <published>2010-04-20T20:51:00Z</published>
    <updated>2010-04-20T20:58:01Z</updated>
    <link href="http://cullenking.com/2010/4/20/installing-ggplot2-with-r-on-ubuntu" rel="alternate" type="text/html"/>
    <title>Installing ggplot2 with R on Ubuntu</title>
<content type="html">
            &lt;p&gt;Had an issue installing the ggplot2 library for making a few graphs using R.  I ran into a snag when I opened up the R console and tried to install the library, getting an error:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
  &amp;gt; install.packages(&amp;quot;ggplot2&amp;quot;)
  install.packages(&amp;quot;ggplot2&amp;quot;) : package ‘plyr’ is not available
&lt;/pre&gt;

	&lt;p&gt;Turns out, Ubuntu has an older version of R than the latest ggplot requires, so, I had to pull down and install R from source.  Not too hard, you can get the source at &lt;a href=&quot;http://cran.r-project.org/&quot;&gt;The Comprehensive R Archive Network&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Installing from source is as simple as the standard:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
  ./configure
  make -j8
  sudo make install
&lt;/pre&gt;

	&lt;p&gt;where -j8 is taking advantage of the quad-core processor in my machine.
Enjoy!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-03-05:6423</id>
    <published>2010-03-05T01:53:00Z</published>
    <updated>2010-03-05T01:56:29Z</updated>
    <category term="fit"/>
    <category term="garmin"/>
    <category term="ridewithgps"/>
    <link href="http://cullenking.com/2010/3/5/garmin-communicator-getting-fit-support" rel="alternate" type="text/html"/>
    <title>Garmin Communicator getting FIT support</title>
<content type="html">
            &lt;p&gt;After the Garmin Edge 500 came out with its new fileformat, &lt;span class=&quot;caps&quot;&gt;FIT&lt;/span&gt;, I tried to get support for the device on the ridewithgps Garmin Sync page.  However, using the exact same method Garmin Connect uses in the &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;, I was unable to get anything to work.  Turns out, they blocked third party websites from using that portion of the &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;.  Best guess says it is for bug testing purposes&#8230;It&#8217;s easier to debug something in a controlled environment you control, rather than hearing grief from all the developers using the &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;.  In any event, they announced a beta developers plugin with support for &lt;span class=&quot;caps&quot;&gt;FIT&lt;/span&gt; devices.&lt;/p&gt;


	&lt;p&gt;Announced in their forums here:&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;https://forums.garmin.com/showthread.php?t=6213&quot;&gt;https://forums.garmin.com&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;ridewithgps might be one of the first sites to support direct sync with &lt;span class=&quot;caps&quot;&gt;FIT&lt;/span&gt; devices!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-03-01:6422</id>
    <published>2010-03-01T22:31:00Z</published>
    <updated>2010-03-01T22:45:35Z</updated>
    <link href="http://cullenking.com/2010/3/1/recovering-a-lost-commit-in-git" rel="alternate" type="text/html"/>
    <title>Recovering a lost commit in GIT</title>
<content type="html">
            &lt;p&gt;I just recovered from a scary situation in which I lost a whole commit to git by accidentally working out of the &#8220;no branch&#8221; branch in git.  I would have only lost around 2 hours of work, however, two hours is a decent amount when you are already pressed for time!  After about three minutes of googling I came across a link which saved me.  The gist of it is below:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
  kingcu@kingcu-desktop:~/ridewithgps$ git reflog show

  b709fac... HEAD@{3}: pull origin new_garmin_sync: Merge made by recursive.
  b6e1616... HEAD@{4}: commit: intermediate checkin for Cam. UI is wired up to send to server, now just needs some massaging for generating extra info for a trip like associated route, etc

&lt;/pre&gt;

	&lt;p&gt;The commit that was not in any branch is the &#8216;intermediate checkin&#8217; commit.  Now that we have a part of the &lt;span class=&quot;caps&quot;&gt;SHA&lt;/span&gt; hash, we can use git log to show the commit, get the full hash and then merge it into the branch we want:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;

  kingcu@kingcu-desktop:~/ridewithgps$ git log b6e1616
  .....
  .....
  kingcu@kingcu-desktop:~/ridewithgps$ git merge b6e16166608b5be148b5dc0a721af3e8d15de282

&lt;/pre&gt;

	&lt;p&gt;Here is the blog which helped me arrive at this solution:&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;http://techblog.ironfroggy.com/2007/12/how-to-recover-lost-git-branches.html&quot;&gt;http://techblog.ironfroggy.com/2007/12/how-to-recover-lost-git-branches.html&lt;/a&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-02-26:6419</id>
    <published>2010-02-26T20:43:00Z</published>
    <updated>2010-02-26T20:47:32Z</updated>
    <category term="actionscript"/>
    <category term="ridewithgps"/>
    <link href="http://cullenking.com/2010/2/26/don-t-change-the-containing-div-of-a-flash-movie" rel="alternate" type="text/html"/>
    <title>Don't change the containing div of a flash movie!</title>
<content type="html">
            &lt;p&gt;I just tracked down a bug that was affecting some versions of Firefox on Windows, which is usually not a combination to worry about or test for!&lt;/p&gt;


	&lt;p&gt;To combat weird z-index issues between flash and &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; under Linux, we have been moving the flash movie off screen and changing some styles, then moving it back when the overlay is closed.  One of the styles being changes was the overflow property of the &lt;span class=&quot;caps&quot;&gt;DIV&lt;/span&gt; containing the flash movie.  This style change was causing the flash movie to be completely reloaded, which broke all ExternalInterface callbacks setup on the flash movie.&lt;/p&gt;


	&lt;p&gt;Removing the offending style changes solved the problem.  What a pain to track down!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2010-02-04:6412</id>
    <published>2010-02-04T23:50:00Z</published>
    <updated>2010-02-05T00:05:47Z</updated>
    <category term="garmin"/>
    <category term="ridewithgps"/>
    <link href="http://cullenking.com/2010/2/4/garmin-communicator-and-fit-devices" rel="alternate" type="text/html"/>
    <title>Garmin Communicator and FIT devices</title>
<content type="html">
            &lt;p&gt;This week I attempted to get our Garmin Communicator implementation for &lt;a href=&quot;http://ridewithgps.com&quot;&gt;http://ridewithgps.com&lt;/a&gt; to support Garmin&#8217;s new line of cycling computers, the Edge 500.  The 500 uses a new file format, and a new protocol to get files off the device, that is definitely a step backwards for third party developers.  After some amount of wrestling attempting to get the 500 to play nicely with Communicator, I realized that Garmin must have disabled third party sites from using Communicator to sync with new &lt;span class=&quot;caps&quot;&gt;FIT&lt;/span&gt; devices.&lt;/p&gt;


	&lt;p&gt;We know the plugin and associated javascript are capable of it, since both are the same on Garmin&#8217;s own site, so the only thing that must be different is only their site is permitted to use the methods needed to get data off the device.  I am not quite sure the motivation, perhaps they are doing this to make sure everything goes smooth and all errors get fixed before releasing the functionality to the rest of the developers.&lt;/p&gt;


	&lt;p&gt;So, for anyone working with an Edge 500 and Garmin Communicator, the exception being thrown by the plugin when executing the method getBinaryFile() which states &#8216;Web site is not privileged&#8217; is just letting you know you are locked out until they release an update.  There is no current expected release date for this functionality.&lt;/p&gt;


	&lt;p&gt;From Garmin Connect&#8217;s Twitter account:&lt;/p&gt;


	&lt;p&gt;&#8221;@cullenking Looking into opening it up. No time line as of yet.&#8221;&lt;/p&gt;


	&lt;p&gt;And, from their forum:&lt;/p&gt;


	&lt;p&gt;&lt;a href=&quot;https://forums.garmin.com/showthread.php?t=5692&quot;&gt;https://forums.garmin.com/showthread.php?t=5692&lt;/a&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2009-12-17:6403</id>
    <published>2009-12-17T19:00:00Z</published>
    <updated>2009-12-17T19:01:17Z</updated>
    <category term="rails"/>
    <category term="ridewithgps"/>
    <link href="http://cullenking.com/2009/12/17/database-optimizations" rel="alternate" type="text/html"/>
    <title>Database Optimizations</title>
<content type="html">
            &lt;p&gt;When we first started working on ridewithgps, we made the decision to store all route information as &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; text inside the database.  The downside should be pretty obvious, in that a route or trip can be a fairly large string; I have some trips saved from my &lt;span class=&quot;caps&quot;&gt;GPS&lt;/span&gt; unit that are 1.5 megs of xml and around 600kb of &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt;.  To cope with the performance hit when using activerecord to pull routes/trips from the database, we used a plugin called attribute_lazy that delayed loading of the fields until they are explicitly queried for.  However, this adds an additional layer of metaprogramming complexity, and turned out to be a pain to work with in certain situations.&lt;/p&gt;


	&lt;p&gt;The final straw was an ever-growing database size, which resulted in database dumps being 3GB with our current ~22k routes/trips.  Considering we need offsite backups as well as an easy way to bring the dataset in locally for development purposes, I desperately needed to use rsync for incremental and efficient transfer of only the modified and/or new routes!&lt;/p&gt;


	&lt;p&gt;There was one thing to think of when deciding on this solution: filesystems use a single inode per stored file, and you can actually run out of inodes before you run out of disk space, depending on your inode size and the size of the files you are storing.  Since each route and trip will take about 4 separate files (I store reduced versions of the route for low resolution maps which need fast load times), I did some calculations based on having around 120 million free inodes.  Without considering other files being uploaded, such as images, I have around 30 million routes/trips I can store without running into the inode limit on my current 1 terrabyte &lt;span class=&quot;caps&quot;&gt;RAID10&lt;/span&gt; array.  If we don&#8217;t have the money to expand storage options with another couple million routes/photos, then we are doing something wrong.&lt;/p&gt;


	&lt;p&gt;This task was fairly straight forward with one exception: storing a file corresponding to a particular trip or route requires the ID of that trip or route in the database.  This ID is not available until after the database entry is saved, so we have to persist the &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; until we have that ID available.  The canonical rails way of doing this is of course using the after_save method.  I keep the &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; around by shoving it into an instance variable on the particular route/trip instance we are saving, then clearing that variable when the file has been written.&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;data_full=&lt;/span&gt; json
    &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;self&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;data_full&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; json
      &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; json
      &lt;span class=&quot;Support&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;open&lt;/span&gt;(df_path, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;w+&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;) { |&lt;span class=&quot;Variable&quot;&gt;file&lt;/span&gt;| file &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; data.&lt;span class=&quot;Entity&quot;&gt;to_json&lt;/span&gt; }
    &lt;span class=&quot;Keyword&quot;&gt;elsif&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;exists?&lt;/span&gt;(df_path)
      &lt;span class=&quot;Support&quot;&gt;File&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;delete&lt;/span&gt;(df_path)
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;data_full&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;A simplified version of what we are using is above, which is loaded into routes and trips using a module.  Basically, we first check if the ID exisits and if not, populate the instance variable with our text.  If it does, we open the flatfile in truncate mode (overwrite if it already exists).  If a nil value is passed in, we delete the file if the file exists.&lt;/p&gt;


	&lt;p&gt;The after save method for routes and trips is trivial as well, since we just call the assignment again.  On this second call, the assignment falls past the conditional check for a nil id, and the file write occurs.&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;after_save&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;data_full&lt;/span&gt;
      &lt;span class=&quot;Variable&quot;&gt;self&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;data_full&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;data_full&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;    &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;some boring stuff also happens...&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;The final bit of the puzzle is removing files if a route/trip is destroyed, which is pretty trivial.  This will greatly improve throughput on heavy loads, which will be welcomed when we get a huge influx of users next riding season.  The best part? our 3GB database dump was now whittled down to a mere 26MB!  We can easily hit 100k users and 500k routes/trips and not break the 1GB mark, which means database scaling will only be for performance rather than space considerations.  The directory structure containing the flatfiles is currently at 2.6 gigs.  With rsync, backups are now speedy and efficient.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2009-11-12:4425</id>
    <published>2009-11-12T03:20:00Z</published>
    <updated>2009-11-12T03:22:17Z</updated>
    <category term="amqp"/>
    <category term="nanite"/>
    <category term="passenger"/>
    <category term="ridewithgps"/>
    <category term="ruby"/>
    <link href="http://cullenking.com/2009/11/12/nanite-rails-redis-deployment-gotchas" rel="alternate" type="text/html"/>
    <title>Nanite, Rails, Redis deployment gotchas</title>
<content type="html">
            &lt;p&gt;One of the problems (joys?) with new and exciting open source software is the relative scarcity of information available.  Compound this with powerful/complex software and you get  a real recipe for learning.  This has been the case with getting a reliable Nanite deployment for &lt;a href=&quot;http://ridewithgps.com&quot;&gt;http://ridewithgps.com.&lt;/a&gt;  Nanite is an incredibly powerful distributed background worker written in Ruby, which leverages the &lt;span class=&quot;caps&quot;&gt;AMQP&lt;/span&gt; messaging protocol and the RabbitMQ server for fast, efficient and safe job creation and passing.  I am not going to bother detailing what Nanite is, since that information is readily available on the GitHub page, but rather am going to talk about a few gotchas.&lt;/p&gt;


	&lt;p&gt;Nanite is fairly simple to get running, however there are a few caveats.  If you plan on using it with a Rails application, you have some choices: you can either do a few things by hand (not too bad) or you can use the nanite-rails plugin.  I started with the plugin, but decided less is better and stripped it out, and am now just running Nanite plain.  The one thing I did keep was the nanite.rb initializer file for getting started with various servers.  Since I test locally on thin and deploy on Passenger, using the nanite-rails provided script worked well.&lt;/p&gt;


	&lt;p&gt;The first issue I noticed, is that multiple agents were receiving the same message (job) from my rails mapper(s) when the job was pushed from within an agent itself, rather from code in the mappers eventmachine loop.  Simply put: all agents were receiving the same job when the job was sent from within an agent.  It turns out that this is due to each mapper having no idea what other mappers are doing.  This is possibly considered a &#8220;feature&#8221; and may or may not be addressed by the Nanite developers.  The reason they are non-committal is due to the fact the problem is solved when you couple Nanite with Redis.  If you don&#8217;t know what Redis is, go ahead and readup, but basically it is a key/value storage similar to memcached, except it can persist data to disk and you can store simple data structures within, rather than plain strings.  In any event, Nanite can use Redis to store state, which is central to all mappers.  This way, every mapper knows about every other mapper, and duplicate requests are not assigned to agents.&lt;/p&gt;


	&lt;p&gt;Here is a &lt;a href=&quot;http://github.com/ezmobius/nanite/issues#issue/7&quot;&gt;direct link&lt;/a&gt; to the issue.&lt;/p&gt;


	&lt;p&gt;To use Redis with Nanite, simply include a configuration option when starting a mapper:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
&lt;span class=&quot;Support&quot;&gt;Nanite&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;start_mapper&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;host&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;localhost&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, ...., &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;redis&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;REDIS_HOST:REDIS_PORT&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;/pre&gt;

	&lt;p&gt;If you don&#8217;t want to use Redis, the problem can be worked around with some massaging of the Nanite code.  You can checkout how this is done by visiting &lt;a href=&quot;http://github.com/nofxx/nanite/commit/7804058cf297088f063cf5d1d2695c8b15ab71a0&quot;&gt;nofxx&#8217;s fork&lt;/a&gt; of Nanite on GitHub and seeing what he did.&lt;/p&gt;


	&lt;p&gt;The next problem I encountered was my agents getting swamped with messages, timing out and freezing.  The reason this happens, is that RabbitMQ stuffs data down a socket and keeps it full.  The moment that socket is emptied, RabbitMQ shoves more down the socket.  However on the receiving end the &lt;span class=&quot;caps&quot;&gt;AMQP&lt;/span&gt; client utilizes EventMachine, which has a reactor loop that visits each socket within the loop in turn.  It keeps a connection open to the socket corresponding to the queue it is subscribed to (queue for that agent), and blocks while that socket is full, meaning it is unable to move on to the next socket/queue.  So, RabbitMQ keeps the socket full and EventMachine keeps emptying the socket as long as it is full, and we get a recipe for a headache.&lt;/p&gt;


	&lt;p&gt;RabbitMQ versions 1.6.0 and greated now have a configuration option called &#8220;prefetch&#8221;.  This option limits the number of messages to stuff into that socket at any one time.  So, where you would have 100+ messages in a socket by default, always causing that socket to be full, you can now tell RabbitMQ to give you one message at a time in the socket.  This allows EventMachine to keep up with all the sockets.  If you have longer running tasks (multiple seconds), setting prefetch to 1 will keep everything happy.  If you can slam through tons of messages a second, larger prefetch values will avoid extra looping and can help speed things up.&lt;/p&gt;


	&lt;p&gt;Now this is where we have a problem!!!  Nanite offers a prefetch configuration option, but it is only on the mapper!  The problem we need to solve is the queues corresponding to the agents, so the prefetch configuration option needs to be set for agent queues, not the mapper queue (which can handle large numbe of messages, since they are just acks, pings and responses from agents).  This is where we need to dive into the Nanite source and change some stuff.  The less adventerous can just pull my &lt;a href=&quot;http://github.com/kingcu/nanite&quot;&gt;Nanite fork&lt;/a&gt; on GitHub until these changes are accepted upstream, the more adventerous can look at how the prefetch option is passed to the mapper, and repeat it on the agent.  You can see below my modified setup_queue() method, with the first three lines added.&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
    &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;setup_queue&lt;/span&gt;
      &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; amq.&lt;span class=&quot;Entity&quot;&gt;respond_to?&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;prefetch&lt;/span&gt;) &lt;span class=&quot;Keyword&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;options&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;has_key?&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;prefetch&lt;/span&gt;)
        amq.&lt;span class=&quot;Entity&quot;&gt;prefetch&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;options&lt;/span&gt;[&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;prefetch&lt;/span&gt;])
      &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
      amq.&lt;span class=&quot;Entity&quot;&gt;queue&lt;/span&gt;(identity, &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;durable&lt;/span&gt; =&amp;gt; &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;).&lt;span class=&quot;Entity&quot;&gt;subscribe&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;ack&lt;/span&gt; =&amp;gt; &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;) &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;info&lt;/span&gt;, &lt;span class=&quot;Variable&quot;&gt;msg&lt;/span&gt;|
        &lt;span class=&quot;Keyword&quot;&gt;begin&lt;/span&gt;
          info.&lt;span class=&quot;Entity&quot;&gt;ack&lt;/span&gt;
          &lt;span class=&quot;Entity&quot;&gt;receive&lt;/span&gt;(serializer.&lt;span class=&quot;Entity&quot;&gt;load&lt;/span&gt;(msg))
        &lt;span class=&quot;Keyword&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;Exception&lt;/span&gt; =&amp;gt; e
          &lt;span class=&quot;Support&quot;&gt;Nanite&lt;/span&gt;::&lt;span class=&quot;Entity&quot;&gt;Log&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;error&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;RECV &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;#{&lt;/span&gt;e&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;Entity&quot;&gt;message&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
        &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;Finally, I was having problems with intermittently losing jobs spawned from my web interface, which is deployed on passenger.  After a huge amount of struggling, I finally conceded that eventmachine and passenger are not compatible in passengers smart spawning mode.  So, I set passengers spawning mode to conservative and enjoyed a much more robust deployment!&lt;/p&gt;


	&lt;p&gt;I am sure some more info will follow, but this should be enough to get you going.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2009-11-10:4348</id>
    <published>2009-11-10T22:53:00Z</published>
    <updated>2009-11-10T23:12:56Z</updated>
    <category term="nanite"/>
    <category term="passenger"/>
    <category term="ruby"/>
    <link href="http://cullenking.com/2009/11/10/nanite-under-passenger-issues" rel="alternate" type="text/html"/>
    <title>Nanite under Passenger issues</title>
<content type="html">
            &lt;p&gt;We had been having some serious issues running Nanite under Phusion Passenger, namely we kept losing jobs, and all subsequent jobs did not make it to any agent.  It turns out this is an issue with the amqp gem!&lt;/p&gt;


	&lt;p&gt;The problem:  messages saying eventmachine is not initialized:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
&lt;span class=&quot;Variable&quot;&gt;RuntimeError&lt;/span&gt; (eventmachine &lt;span class=&quot;Keyword&quot;&gt;not&lt;/span&gt; initialized:
evma_send_data_to_connection):
  eventmachine (&lt;span class=&quot;Constant&quot;&gt;0.12&lt;/span&gt;.&lt;span class=&quot;Constant&quot;&gt;8&lt;/span&gt;) lib&lt;span class=&quot;Keyword&quot;&gt;/&lt;/span&gt;em&lt;span class=&quot;Keyword&quot;&gt;/&lt;/span&gt;connection.&lt;span class=&quot;Entity&quot;&gt;rb&lt;/span&gt;:&lt;span class=&quot;Constant&quot;&gt;216&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;in&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;`&lt;/span&gt;send_data'&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;  eventmachine (0.12.8) lib/em/connection.rb:216:in &lt;span class=&quot;String&quot;&gt;`&lt;/span&gt;&lt;/span&gt;send_data&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt; &lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;After some digging around, I found the issue on the nanite github page:  &lt;a href=&quot;http://github.com/ezmobius/nanite/issues#issue/8&quot;&gt;ezmobius nanite page&lt;/a&gt; The solution is easy, upgrade amqp to 0.6.5 (or greater, 0.6.5 is latest at time of writing).&lt;/p&gt;


	&lt;p&gt;Voila!  No more lost jobs, things appear to be stable.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2009-07-02:79</id>
    <published>2009-07-02T17:49:00Z</published>
    <updated>2009-07-02T18:24:12Z</updated>
    <category term="facebook"/>
    <category term="ridewithgps"/>
    <link href="http://cullenking.com/2009/7/2/make-your-site-friendly-to-link-to-on-facebook" rel="alternate" type="text/html"/>
    <title>Make your site friendly to link to on Facebook</title>
<content type="html">
            &lt;p&gt;Here is a quick but helpful tidbit for any site.  You can direct Facebook to a particular preview thumbnail to show when a user links to your page on Facebook.  If you don&#8217;t specify a thumbnail, Facebook will give a user a few options to choose from.  Where this comes in handy is on pages that don&#8217;t need that thumbnail visible, but having that thumbnail available as a preview is valuable.&lt;/p&gt;


	&lt;p&gt;For example, when viewing a route or trip on &lt;a href=&quot;http://ridewithgps.com&quot;&gt;http://ridewithgps.com&lt;/a&gt; there is already a large interactive map of the route for a user to play with.  Placing a thumbnail of that route somewhere on the page becomes redundant on an already cluttered layout.  However, when linking to the route from Facebook we want users to see a small thumbnail preview of the route.  Simply add a link in your head tag like so:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;

  &lt;span class=&quot;MetaTag&quot;&gt;&lt;span class=&quot;MetaTag&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;MetaTag&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;MetaTag&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;http://ridewithgps.com/routes/2602/thumb.gif&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;MetaTag&quot;&gt;rel&lt;/span&gt;=&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;image_src&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; /&lt;span class=&quot;MetaTag&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;

&lt;/pre&gt;

	&lt;p&gt;This will produce a neat looking preview that looks something like this:&lt;/p&gt;


	&lt;p&gt;&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2009-06-12:56</id>
    <published>2009-06-12T20:19:00Z</published>
    <updated>2009-06-12T20:19:53Z</updated>
    <category term="elevation smoothing"/>
    <category term="gaussian function"/>
    <category term="signal processing"/>
    <category term="sinc function"/>
    <link href="http://cullenking.com/2009/6/12/using-gaussian-and-sinc-filters-on-elevation-data" rel="alternate" type="text/html"/>
    <title>Using Gaussian and sinc filters on elevation data</title>
<content type="html">
            &lt;p&gt;To begin, let me state that I am not an expert, or even that knowledgeable about signal processing.  My math skills are just enough to understand the concepts involved in using and selecting a particular filter.  This is merely a collection of a couple days worth of reading and playing around.  Much of the knowledge I absorbed came from the Wikipedia articles on the subjects, conversations with my friend Joe (a good friend and &lt;span class=&quot;caps&quot;&gt;PHD&lt;/span&gt; student in AI at Oregon State) and one helpful EE professor at Oregon State.  Living in a college town has benefits: I knocked on the doors of several professors that specialize in digital signal processing, and found a friendly one with 10 minutes of free time :)&lt;/p&gt;


	&lt;p&gt;This exploration of filters is an attempt to solve an accuracy problem when modeling elevation data for bicycle rides on &lt;a href=&quot;http://ridewithgps.com&quot;&gt;http://ridewithgps.com&lt;/a&gt; &#8211; there are two scenarios for elevation data: before a user ever goes on a bike ride, they can plot out the route on a Google map and an elevation profile is generated from the &lt;span class=&quot;caps&quot;&gt;USGS&lt;/span&gt; provided national elevation dataset.  The second scenario occurs when a user comes back from the ride they just took, and uploads their logged data from a &lt;span class=&quot;caps&quot;&gt;GPS&lt;/span&gt; unit.  This log file includes elevation data, which in the case of the Garmin 305, is provided by a fairly accurate barometric sensor based altimeter.  The two datasets are inaccurate in separate ways.  The &lt;span class=&quot;caps&quot;&gt;USGS&lt;/span&gt; data is highly accurate for the points in the dataset, however, those points are spaced 10 meters apart.  As a result, asking for an elevation point on some road that rides along a steep hillside may give me a point that is 20 meters above/below the actual requested point.  Over the course of plotting a longer route, we can get off by 2000 feet in some cases!  There are ways to improve this, and I am exploring interpolation when querying the elevation dataset DEMs, but that is another topic for another day.&lt;/p&gt;


	&lt;p&gt;The logged elevation data has a different problem: the Garmin saves the raw data, which has a some variation up and down due to sensor accuracy.  As a result, if you ride along a perfectly flat road for 50 miles (0 elevation gain and loss), the logged data will have jitter up and down of 0.5 to 1 meter or so.  When calculating the elevation gain and loss of this flat ride, we can come up with upwards of 1000 feet of gain and loss!  So, in order to accurately calculate a ride or planned rides elevation gain and loss, we must smooth out these jitters.&lt;/p&gt;


	&lt;p&gt;&lt;/p&gt;


	&lt;p&gt;Above is an example of two datasets with a filter applied to each.  Keep in mind this is a zoomed in section of the dataset, and as a result the jaggedness is exacerbated.  The green line is the raw &lt;span class=&quot;caps&quot;&gt;USGS&lt;/span&gt; data, and you can see how jittery it is.  The red line running through it is a sinc filtered dataset, with a max window size of 21 (can be smaller dynamically), and a frequency of 0.04.  The jagged purple line is raw elevation data taken from my Garmin 305 log.  The altimeter has a step size of .4805 meters, and an accuracy of something like +/- 1 meter.  As you can see, it is a much cleaner dataset when compared to the &lt;span class=&quot;caps&quot;&gt;USGS&lt;/span&gt; dataset.  The blue line running through it is the sinc filtered data.  When calculating the gain/loss of the filtered data, we get a much more accurate representation of the real gain and loss.  Even though the &lt;span class=&quot;caps&quot;&gt;USGS&lt;/span&gt; data looks like it is a long ways off from the actual data, the gain and loss numbers average out to be fairly close to reality, especially when graphing a longer route.&lt;/p&gt;


	&lt;p&gt;Now onto the details!  The three filters I am about to describe all utilize a near identical programming implementation.  They are window filters, which means you take a neighborhood of points surrounding the central point that you would like to filter, and use them to generate the new central point value.  The simplest of these is a &lt;a href=&quot;http://en.wikipedia.org/wiki/Moving_average_(technical_analysis&quot;&gt;central moving average.&lt;/a&gt;)  With a &lt;span class=&quot;caps&quot;&gt;CMA&lt;/span&gt;, you average all the points in your window, and assign the result to the central point.  This is a quick and easy filter, which produces decent results.  More complex filters use the same concept, but instead of taking a simple average of all points in the neighborhood, we take a weighted average, where the weights of the surrounding points are generated by different mathematical functions.  As a result, with the same code I can merely choose a different function to generate a &#8220;kernel&#8221; with different weights.  This kernel is just a set of n weights, where n is the length of my window size.  So, for a central moving average, the weights would be equal: with a 5 point window, each weight in the kernel would be 0.20.  In something like a Gaussian filter, the 5 point kernel would look something like  [0.01, 0.15, 0.3, 0.15, 0,01], where those made up values are points on the Gaussian function.&lt;/p&gt;


	&lt;p&gt;I explored both a &lt;a href=&quot;http://en.wikipedia.org/wiki/Sinc_filter&quot;&gt;sinc filter&lt;/a&gt; and a &lt;a href=&quot;http://en.wikipedia.org/wiki/Gaussian_filter&quot;&gt;Gaussian filter&lt;/a&gt;  when working with my elevation data.  There are more complex and accurate functions available, but approach serious overkill for the problem I am solving.  Additionally, the more powerful signal processing techniques involved regularly sampled datasets, which we do not have (explained below).  In order to use those, we would need to fill-in our datasets with interpolated data, which starts to get messy.&lt;/p&gt;


	&lt;p&gt;You may recognize the common bell shaped curve, which is apparent when you graph the kernel created by the Gaussian function:&lt;/p&gt;


	&lt;p&gt;&lt;/p&gt;


	&lt;p&gt;The Gaussian filter outputs a `weighted average&#8217; of each point&#8217;s (or pixel, since filtering is typically used in image manipulation and recognition) neighborhood, with a larger weight towards the value of the central points.  These weights are generated by the function shown above, where x is the current index of the kernel we are creating and theta is the standard deviation.  The central point in the kernel has the strongest weight.  What does all this mean?  Simply put, a Gaussian filter provides gentler smoothing and preserves edges better than a mean filter.  Intuitively this makes sense; we are saying the immediately neighboring points are much more relevant when identifying the real value of the current point.  As the points get further away from the current point their values become less relevant.  It is tuned by changing the window size and the standard deviation.  The standard deviation is chosen to be the standard deviation of the noise from the underlying form.&lt;/p&gt;


	&lt;p&gt;The sinc filter is implemented identically, though we use a different function to generate our kernel of weights.  This filter is considered an ideal low-pass filter (in its raw mathematical form), with perfect cutoff.  Without going into much detail about a topic I don&#8217;t know much about, the sinc filter performs better when dealing with different elevation sets that have disparate accuracies.  This is important, since I need to smooth both altimeter recorded (accurate and clean) and the &lt;span class=&quot;caps&quot;&gt;USGS&lt;/span&gt; provided data (inaccurate and unclean).  For the sinc filter, we tune it by changing the window size (like the Gaussian), and a frequency value, f.  The electrical engineers choose f based on some logic, but I do not know how to apply that logic to my dataset (since it&#8217;s not an actual signal) and instead I found a good value of f by trial and error.&lt;/p&gt;


	&lt;p&gt;The graph of a sinc function:
&lt;/p&gt;


	&lt;p&gt;The sinc function used in the filter is a specific variation of the sinc function with a Blackman window, which was developed by some smart EEs in the 60&#8217;s.  For more details check out this excellent intro from &lt;a href=&quot;http://www.dspguide.com/ch16.htm&quot;&gt;dspguide.com&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;Selecting different standard deviations and window sizes will change the profile of this curve, and thus the resulting smoothing of your data.  If you have very tight sampling (your data points are close together), a larger window size can be beneficial.  If your data points are spaced far apart, then a smaller window size is needed, to avoid giving weight to less important (far out and unrelated) surrounding points.  This presents an interesting problem for the data I am attempting to smooth: I do not have a regular sample rate for these elevation points.  Since many &lt;span class=&quot;caps&quot;&gt;GPS&lt;/span&gt; units have a &#8220;smart recording&#8221; feature, they only record data points when it is necessary.  So, if I am riding along a straight line with no change in heading or elevation, the Garmin 305 puts down something close to 1 point every 16 seconds.  As I make a turn, my logging rate approaches 1 point per second.  Similarly, when drawing a route on the google map, we have irregularly spaced lat/lon points returned.  With this in mind, we need to consider a dynamic window size.  If we link the window size to a distance rather than number of points, we can achieve a more consistant weighting of relevant points in regards to our horizontal scale.  This can include even dropping the concept of filtering when the distance between points is great, by having a window size of 1.&lt;/p&gt;


	&lt;p&gt;Below is the code, in ruby, for the sinc filter.  The guassian is left up to the reader, but is a fairly simple drop in replacement.&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;SincFilter&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;initialize&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;start_n&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;Variable&quot;&gt;,&lt;/span&gt; win_width&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;Variable&quot;&gt;,&lt;/span&gt; freq&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;0.04&lt;/span&gt;&lt;/span&gt;)
    &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;start_n&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; start_n &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;try this window size, and increase or decrease based on point density&lt;/span&gt;
    &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;win_width&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; win_width &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;width of window in meters&lt;/span&gt;
    &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;freq&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; freq &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;frequency cutoff&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;filter&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;dists&lt;span class=&quot;Variable&quot;&gt;,&lt;/span&gt; eles&lt;/span&gt;)
    &lt;span class=&quot;Entity&quot;&gt;sincify&lt;/span&gt;(eles, dists)
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;Keyword&quot;&gt;private&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;  &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;if the sum of the kernel vals is not 1, we will shrink everything a bit...&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;  &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;this is a cheap way to normalize so they sum to 1&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;normalize_kernel&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;kern&lt;/span&gt;)
    norm &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; kern.&lt;span class=&quot;Entity&quot;&gt;inject&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;) { |&lt;span class=&quot;Variable&quot;&gt;res&lt;/span&gt;, &lt;span class=&quot;Variable&quot;&gt;val&lt;/span&gt;| res &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; val }
    kern.&lt;span class=&quot;Entity&quot;&gt;map&lt;/span&gt; { |&lt;span class=&quot;Variable&quot;&gt;val&lt;/span&gt;| val &lt;span class=&quot;Keyword&quot;&gt;/=&lt;/span&gt; norm }
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;  &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;The sinc function, using a blackman window.&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;create_kernel&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;size&lt;/span&gt;)
    &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; [&lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; size &lt;span class=&quot;Keyword&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;
    kern &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; []

    (&lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;..size).&lt;span class=&quot;Entity&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;i&lt;/span&gt;|
      kern[i] &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;Constant&quot;&gt;0.54&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0.46&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;Math&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;cos&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;Support&quot;&gt;Math&lt;/span&gt;::&lt;span class=&quot;Entity&quot;&gt;PI&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt;i&lt;span class=&quot;Keyword&quot;&gt;/&lt;/span&gt;size))
      x &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; size&lt;span class=&quot;Keyword&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;
      &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; x &lt;span class=&quot;Keyword&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;
        kern[i] &lt;span class=&quot;Keyword&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;Math&lt;/span&gt;::&lt;span class=&quot;Entity&quot;&gt;PI&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;freq&lt;/span&gt;
      &lt;span class=&quot;Keyword&quot;&gt;else&lt;/span&gt;
        kern[i] &lt;span class=&quot;Keyword&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;Math&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;sin&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;Math&lt;/span&gt;::&lt;span class=&quot;Entity&quot;&gt;PI&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;freq&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; x) &lt;span class=&quot;Keyword&quot;&gt;/&lt;/span&gt; x
      &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; kern
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;  &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;now we calculate a weighted average for each point and it's neighbors,&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;  &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;using our kernel as the weight values&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;sincify&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;eles&lt;span class=&quot;Variable&quot;&gt;,&lt;/span&gt; dists&lt;/span&gt;)
    kern &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;create_kernel&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;start_n&lt;/span&gt;)
    result &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; []

    eles.&lt;span class=&quot;Entity&quot;&gt;each_with_index&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;point&lt;/span&gt;, &lt;span class=&quot;Variable&quot;&gt;i&lt;/span&gt;|
      n &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;start_n&lt;/span&gt;
      z &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; n &lt;span class=&quot;Keyword&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;
      s &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; z
      e &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; z
      ks &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;
      ke &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; n

&lt;span class=&quot;Comment&quot;&gt;      &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;pick our start and end index such that it is within our array bounds&lt;/span&gt;
      &lt;span class=&quot;Keyword&quot;&gt;while&lt;/span&gt; s &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;s &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;; ks &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;Keyword&quot;&gt;while&lt;/span&gt; e &lt;span class=&quot;Keyword&quot;&gt;&amp;gt;=&lt;/span&gt; eles.&lt;span class=&quot;Entity&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;e &lt;span class=&quot;Keyword&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;; ke &lt;span class=&quot;Keyword&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;      &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;dynamically select the window size&lt;/span&gt;
      dist &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; dists[e] &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; dists[s]
      &lt;span class=&quot;Keyword&quot;&gt;while&lt;/span&gt; dist &lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;win_width&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;Keyword&quot;&gt;unless&lt;/span&gt; s &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;or&lt;/span&gt; e &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; s
          s &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;
          ks &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;Keyword&quot;&gt;unless&lt;/span&gt; e &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;or&lt;/span&gt; e &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt; i &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; s
          e &lt;span class=&quot;Keyword&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt; 
          ke &lt;span class=&quot;Keyword&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
        dist &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; dists[e] &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; dists[s]
      &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;

      neighbors &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; []
&lt;span class=&quot;Comment&quot;&gt;      &lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;renormalize the kernel.  If all values don't add up to 1, point will shrink&lt;/span&gt;
      used_kern &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;normalize_kernel&lt;/span&gt;(kern[ks..ke])
      (s..e).&lt;span class=&quot;Entity&quot;&gt;each&lt;/span&gt; { |&lt;span class=&quot;Variable&quot;&gt;j&lt;/span&gt;| neighbors &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; eles[j] &lt;span class=&quot;Keyword&quot;&gt;unless&lt;/span&gt; j &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;or&lt;/span&gt; j &lt;span class=&quot;Keyword&quot;&gt;&amp;gt;=&lt;/span&gt; eles.&lt;span class=&quot;Entity&quot;&gt;length&lt;/span&gt; }

      val &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;
      neighbors.&lt;span class=&quot;Entity&quot;&gt;each_with_index&lt;/span&gt; { |&lt;span class=&quot;Variable&quot;&gt;pt&lt;/span&gt;, &lt;span class=&quot;Variable&quot;&gt;i&lt;/span&gt;| val &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; pt &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; used_kern[i] }
      result &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; (val &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;10000.0&lt;/span&gt;).&lt;span class=&quot;Entity&quot;&gt;round&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;10000.0&lt;/span&gt; &lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt;geto round..no use with more than 4 decimal places&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; result
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;
          </content>  </entry>
  <entry xml:base="http://cullenking.com/">
    <author>
      <name>Cullen King</name>
    </author>
    <id>tag:cullenking.com,2009-06-12:58</id>
    <published>2009-06-12T19:10:00Z</published>
    <updated>2010-03-01T22:45:15Z</updated>
    <category term="gnuplot"/>
    <category term="rails"/>
    <category term="rake"/>
    <category term="ridewithgps"/>
    <link href="http://cullenking.com/2009/6/12/gnuplot-bindings-for-ruby-means-nice-graphs-for-rails" rel="alternate" type="text/html"/>
    <title>gnuplot bindings for ruby means nice graphs for rails</title>
<content type="html">
            &lt;p&gt;In experimenting with different filtering functions for elevation data, I needed a way to generate graphs easily from some ruby code.  It was too cumbersome to output a &lt;span class=&quot;caps&quot;&gt;CSV&lt;/span&gt; and open that in another graphing application.  Unfortunately, Ruby lacks some of the awesome libraries like SciPy which is available for Python.  However, any computer running Linux or &lt;span class=&quot;caps&quot;&gt;OSX&lt;/span&gt; comes with the tried and true gnuplot, a simple but powerful graphing program.  Just generate a text file filled with directions for gnuplot, load it up in gnuplot and away you go!&lt;/p&gt;


	&lt;p&gt;Luckily, there is a rubygem available to make working with gnuplot a bit easier than string concatenation.  Easy to remember, it&#8217;s just called &#8216;gnuplot&#8217;.  Outdated website &lt;a href=&quot;http://rgplot.rubyforge.org/&quot;&gt;here&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;First check that gnuplot is installed by running gnuplot at the command line, and install it if needed.  Then go ahead and install the rubygem.&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
 kingcu@kingcu-desktop:~/ridewithgps$ sudo gem install 'gnuplot'
&lt;/pre&gt;

	&lt;p&gt;After that is out of the way, we are ready to go!  I cooked up this simple ruby class, ModelPlotter, to graph whatever from a rake task:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
require 'rubygems'
require 'gnuplot'

class ModelPlotter
  def initialize(sets, outfile='plot.png', title='graphing things', xlabel='x', ylabel='y')
    @sets = sets
    @outfile = outfile
    @title = title
    @xlabel = xlabel
    @ylabel = ylabel
  end 

  def plot
    sets = []
    @sets.each do |s| 
      sets &amp;lt;&amp;lt; Gnuplot::DataSet.new(generate_vals(s)) { |ds|
        ds.with = 'lines'
        ds.title = s[:title]
        ds.linewidth = 1 
      }   
    end 

    write_plot(sets)
  end 

  private

  def generate_vals(set)
    xvals = []
    yvals = []
    lines = set[:data].split(&amp;quot;\n&amp;quot;)
    lines.each do |line|
      vals = line.split(',')
      xvals &amp;lt;&amp;lt; vals[0].to_f
      yvals &amp;lt;&amp;lt; vals[1].to_f
    end 
    return xvals, yvals
  end 

  def write_plot(sets)
    File.open(@outfile, 'w') do |gp|
      Gnuplot::Plot.new(gp) do |plot|
        plot.term 'png size 800, 600'
        plot.output @outfile.gsub(/\.[\w]*$/, '.png')
        plot.title @title
        plot.ylabel @ylabel
        plot.xlabel @xlabel
        plot.data = sets
      end 
    end 
  end 
end
&lt;/pre&gt;

	&lt;p&gt;Looking at the plot and write_plot methods shows us the basics of how to use gnuplot for ruby.  First generate some sets, then pass those sets to your plot object.  The block you just created gets yielded, and a file gets created filled with gnuplot directives.  I chose to just save the gnuplot text files, since they are smaller in size than an actual png plot, and I can keep them in version control easily enough.&lt;/p&gt;


	&lt;p&gt;The rake task I wrote, to give me graphs of model numbers since my site launched is below.  Warning!  This code is not performant.  There is an &lt;span class=&quot;caps&quot;&gt;SQL&lt;/span&gt; query for every row in the table.  If you have many things in your table, this will be ver slow!  I just slapped that out as a quick proof of concept this morning, using a small dataset.  It&#8217;s up to the reader to make that more efficient.&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
equire &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;model_plotter&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;

namespace &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;graph&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
  desc &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;create a gnuplot file for &amp;lt;model&amp;gt; creation numbers since day 1&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
  task &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;graph_model_totals&lt;/span&gt;, [&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;model&lt;/span&gt;] =&amp;gt; &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;environment&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;t&lt;/span&gt;, &lt;span class=&quot;Variable&quot;&gt;args&lt;/span&gt;|
    args.&lt;span class=&quot;Entity&quot;&gt;with_defaults&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;model&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;User&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
    model &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;eval&lt;/span&gt;(args[&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;model&lt;/span&gt;])
    models_by_day &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {}
    start_date &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; model.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;first&lt;/span&gt;).&lt;span class=&quot;Entity&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;day&lt;/span&gt;
    end_date &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;Time&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;day&lt;/span&gt;
    index &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt; 

    &lt;span class=&quot;Keyword&quot;&gt;while&lt;/span&gt; start_date &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt; end_date
      models_by_day[index] &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; model.&lt;span class=&quot;Entity&quot;&gt;count&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;all&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;conditions&lt;/span&gt; =&amp;gt; [&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;created_at &amp;lt; ?&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, start_date])
      start_date &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;day&lt;/span&gt;
      index &lt;span class=&quot;Keyword&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt; 

    keys &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; []
    models_by_day.&lt;span class=&quot;Entity&quot;&gt;each_key&lt;/span&gt; { |&lt;span class=&quot;Variable&quot;&gt;k&lt;/span&gt;| keys &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; k } 
    keys.&lt;span class=&quot;Entity&quot;&gt;sort!&lt;/span&gt;

    res &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; []
    keys.&lt;span class=&quot;Entity&quot;&gt;each&lt;/span&gt; { |&lt;span class=&quot;Variable&quot;&gt;k&lt;/span&gt;| res &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;#{&lt;/span&gt;k&lt;span class=&quot;String&quot;&gt;}&lt;/span&gt;&lt;/span&gt;,&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;#{&lt;/span&gt;models_by_day&lt;span class=&quot;String&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;String&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; }
    res &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; res.&lt;span class=&quot;Entity&quot;&gt;join&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)

    sets &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; [ {&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;data&lt;/span&gt; =&amp;gt; res, &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;title&lt;/span&gt; =&amp;gt; args[&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;model&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;s&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; } ] 
    ylabel &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Number of &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;#{&lt;/span&gt;args&lt;span class=&quot;String&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;model&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;}&lt;/span&gt;&lt;/span&gt;s&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    xlabel &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Days from launch&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    title &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Total &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;#{&lt;/span&gt;args&lt;span class=&quot;String&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;model&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;}&lt;/span&gt;&lt;/span&gt;s by day since first user, Oct 29, 2007&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
    plotter &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;ModelPlotter&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;new&lt;/span&gt;(sets, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;plotfile.plot&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, title, xlabel, ylabel)
    plotter.&lt;span class=&quot;Entity&quot;&gt;plot&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt; 
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;

	&lt;p&gt;You pass parameters to the rake task using bracket notation:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
  kingcu@kingcu-desktop:~/ridewithgps$ rake graph:graph_model_totals[User]
&lt;/pre&gt;

	&lt;p&gt;To generate a plot from the file &#8216;plotfile.plot&#8217;:&lt;/p&gt;


&lt;pre class=&quot;blackboard&quot;&gt;
  kingcu@kingcu-desktop:~/ridewithgps$ gnuplot plotfile.plot
&lt;/pre&gt;

	&lt;p&gt;This will kickout a png, &#8216;plotfile.png&#8217;.  Do with it what you want!  If you want to do something more advanced, read through the gnuplot &lt;a href=&quot;http://www.gnuplot.info/docs/gnuplot.html&quot;&gt;documentation&lt;/a&gt;&lt;/p&gt;


	&lt;p&gt;&lt;/p&gt;
          </content>  </entry>
</feed>
