<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title><![CDATA[Fuzzy Tolerance]]></title>
  
  <link href="/atom.xml" rel="self"/>
  <link href="http://fuzzytolerance.info/"/>
  <updated>2014-06-03T00:53:41.054Z</updated>
  <id>http://fuzzytolerance.info/</id>
  
  <author>
    <name><![CDATA[Tobin Bradley]]></name>
    
  </author>
  
  <generator uri="http://zespia.tw/hexo/">Hexo</generator>
  
  <entry>
    <title><![CDATA[Customizing the Quality of Life Dashboard]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/06/02/customizing-the-quality-of-life-dashboard/"/>
    <id>http://fuzzytolerance.info/blog/2014/06/02/customizing-the-quality-of-life-dashboard/</id>
    <published>2014-06-02T23:43:46.000Z</published>
    <updated>2014-06-02T23:50:31.000Z</updated>
    <content type="html"><![CDATA[<p>The first release of the Quality of Life Dashboard was a beast to customize for your own locale or area of interest (though some plucky folks have done <a href="http://compass.durhamnc.gov/" target="_blank">a great job of it</a>. The next release is much, much easier. So easy I bungled my way through it in less than half an hour using a different area, neighborhoods, metrics, and base tiles.</p>
<!-- read more -->

<iframe width="1280" height="720" src="//www.youtube.com/embed/qmx2mZXeHZQ?rel=0" frameborder="0" allowfullscreen></iframe>

<p>You can get the current beta release on <a href="https://github.com/tobinbradley/Mecklenburg-County-Quality-of-Life-Dashboard" target="_blank">Github</a>.</p>
]]></content>
    
    
  </entry>
  
  <entry>
    <title><![CDATA[Praise for Learning QGIS 2.0]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/04/02/2014-04-02-praise-for-learning-qgis-2-dot-0/"/>
    <id>http://fuzzytolerance.info/blog/2014/04/02/2014-04-02-praise-for-learning-qgis-2-dot-0/</id>
    <published>2014-04-02T21:49:35.000Z</published>
    <updated>2014-04-02T23:59:59.000Z</updated>
    <content type="html"><![CDATA[<p>I’m garbage at desktop GIS.</p>
<p>I don’t know what happened. I was once an <del>idiot</del> savant at desktop GIS. I wrote data maintenance and analysis apps in AML (painful), ODE (excruciating), Avenue (we don’t talk about Avenue), and ArcObjects (once, on a dare), and a number of them worked some of the time. I could tell you how long I could spend at the gym while a geoprocessing task ran with &#177;5 minute accuracy. I could make a map that wouldn’t make <a href="http://gretchenpeterson.com/" target="_blank">Gretchen</a> stick pencils through her retinas. Probably.</p>
<p>Not anymore. Most of my time is making web toys, and my analysis needs are usually covered by some SQL/PostGIS machinations. When I do have pull up a desktop GIS, I suck pretty hard. </p>
<p>Being garbage at desktop GIS isn’t a normal thing for a GIS person. For 99% of GIS people, desktop GIS <em>is GIS</em>. Combining that secret shame with a long-standing desire to dig into QGIS, I picked up <a href="http://anitagraser.com/" target="_blank">Anita Graser’s</a> <a href="http://www.amazon.com/Learning-QGIS-2-0-Anita-Graser-ebook/dp/B00F969TGQ/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1396479634&amp;sr=1-1&amp;keywords=learning+qgis+2.0" target="_blank">Learning QGIS 2.0</a>.</p>
<figure class="left"><br>  <img src="http://i.imgur.com/MwhMcO8.jpg" alt="'QGIS: The Good Parts'"><br></figure>

<p>If you have any interest in QGIS, you should get this book. It sits in that sweet spot between a voluminous tomb that gives you too much and a tiny pamphlet that gives you too little. You will get everything you need to get proficient and ready to do real work with QGIS in a few hours. Just 15 minutes into the Data Creation and Editing chapter I totally groked the interface for the first time and my editing experience improved 100% from my usual sporadic fumblings. Learning the inns and outs of spatial analysis and map creation after that was gravy.</p>
<p>All of which is a long way of saying (1) QGIS is a <strong>legitimate</strong> ArcGIS Desktop replacement and (2) <a href="http://www.amazon.com/Learning-QGIS-2-0-Anita-Graser-ebook/dp/B00F969TGQ/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1396479634&amp;sr=1-1&amp;keywords=learning+qgis+2.0" target="_blank">Learning QGIS 2.0</a> is a great way to get started. Big thanks to Anita Graser for helping me not suck at desktop GIS.</p>
]]></content>
    
    
  </entry>
  
  <entry>
    <title><![CDATA[Screencast 34: State of the Office 2014]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/03/21/2014-03-21-screencast-34-state-of-the-office-2014/"/>
    <id>http://fuzzytolerance.info/blog/2014/03/21/2014-03-21-screencast-34-state-of-the-office-2014/</id>
    <published>2014-03-21T17:47:23.000Z</published>
    <updated>2014-03-21T17:47:59.000Z</updated>
    <content type="html"><![CDATA[<iframe width="853" height="480" src="//www.youtube.com/embed/SMmFpQ2qjZM" frameborder="0" allowfullscreen></iframe>
]]></content>
    
    
  </entry>
  
  <entry>
    <title><![CDATA[Screencast 33: Setting up a Cloud MBTiles Server with Benchmarks]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/03/03/2014-03-03-screencast-33-setting-up-a-cloud-mbtiles-server-with-benchmarks/"/>
    <id>http://fuzzytolerance.info/blog/2014/03/03/2014-03-03-screencast-33-setting-up-a-cloud-mbtiles-server-with-benchmarks/</id>
    <published>2014-03-03T22:11:22.000Z</published>
    <updated>2014-03-03T22:35:31.000Z</updated>
    <content type="html"><![CDATA[<iframe width="1280" height="720" src="//www.youtube.com/embed/zLMKDGIMrPs?rel=0" frameborder="0" allowfullscreen></iframe>

<a id="more"></a>

<p>I ran some benchmarks via Apache ab on the local network of both our local physical tile server (8 core 4GB Windows) and the Digital Ocean cloud server (1 core 512MB). The $5/month cloud host was faster, though it isn’t a night-and-day difference. Microsoft has worked pretty hard to make Nodejs performant on Windows. Still, it says a lot that a $5/month host can tell a beefy physical box to bang off.</p>
<figure class="highlight text"><figcaption><span>Physical Server</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre>Requests per <span class="keyword">second</span>:    <span class="number">270.71</span> [<span class="comment">#/sec] (mean)</span>
Time per request:       <span class="number">14.776</span> [ms] (mean)
Time per request:       <span class="number">3.694</span> [ms] (mean, across all concurrent requests)
Transfer rate:          <span class="number">8181.02</span> [Kbytes/<span class="built_in">sec</span>] received

Connection Times (ms)
              <span class="built_in">min</span>  mean[+/-sd] <span class="built_in">median</span>   <span class="built_in">max</span>
Connect:        <span class="number">0</span>    <span class="number">0</span>   <span class="number">0.4</span>      <span class="number">0</span>       <span class="number">2</span>
Processing:    <span class="number">10</span>   <span class="number">14</span>   <span class="number">8.2</span>     <span class="number">13</span>     <span class="number">110</span>
Waiting:        <span class="number">6</span>   <span class="number">11</span>   <span class="number">7.9</span>      <span class="number">9</span>     <span class="number">103</span>
</pre></td></tr></table></figure>

<figure class="highlight text"><figcaption><span>Digital Ocean $5/month</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre>Requests per <span class="keyword">second</span>:    <span class="number">283.02</span> [<span class="comment">#/sec] (mean)</span>
Time per request:       <span class="number">14.133</span> [ms] (mean)
Time per request:       <span class="number">3.533</span> [ms] (mean, across all concurrent requests)
Transfer rate:          <span class="number">8540.27</span> [Kbytes/<span class="built_in">sec</span>] received

Connection Times (ms)
              <span class="built_in">min</span>  mean[+/-sd] <span class="built_in">median</span>   <span class="built_in">max</span>
Connect:        <span class="number">0</span>    <span class="number">2</span>   <span class="number">2.1</span>      <span class="number">1</span>      <span class="number">21</span>
Processing:     <span class="number">4</span>   <span class="number">12</span>   <span class="number">7.7</span>     <span class="number">10</span>      <span class="number">73</span>
Waiting:        <span class="number">0</span>    <span class="number">9</span>   <span class="number">7.3</span>      <span class="number">8</span>      <span class="number">69</span>
</pre></td></tr></table></figure>

<figure class="highlight bash"><figcaption><span>Commands Used</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="comment"># install software</span>
apt-get install nodejs nodejs-legacy npm git
npm install -g forever

<span class="comment"># pull in git repo</span>
git clone https://github.com/tobinbradley/mbtiles-server.git tiles

<span class="comment"># from tiles folder to start server</span>
chmod +x starter.sh
./starter.sh
</pre></td></tr></table></figure>

<h3 id="Resources">Resources</h3>
<ul>
<li><a href="https://www.digitalocean.com/" target="_blank">Digital Ocean</a></li>
<li><a href="http://www.jupiterbroadcasting.com/52657/your-database-is-slow-cr-91/" target="_blank">Coder Radio</a></li>
<li><a href="https://github.com/tobinbradley/mbtiles-server" target="_blank">MBTiles-Server</a></li>
<li><a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/" target="_blank">Putty</a> (Windows SSH)</li>
<li><a href="http://winscp.net/eng/index.php" target="_blank">WinSCP</a> (Windows SSH file copy)</li>
</ul>
]]></content>
    <summary type="html"><![CDATA[<iframe width="1280" height="720" src="//www.youtube.com/embed/zLMKDGIMrPs?rel=0" frameborder="0" allowfullscreen></iframe>

]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[PHP cURL to Google Civic API]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/02/26/2014-02-26-php-curl-to-google-civic-api/"/>
    <id>http://fuzzytolerance.info/blog/2014/02/26/2014-02-26-php-curl-to-google-civic-api/</id>
    <published>2014-02-26T22:18:38.000Z</published>
    <updated>2014-02-26T22:47:29.000Z</updated>
    <content type="html"><![CDATA[<p>Well, this stumped me for like an hour.</p>
<p>The <a href="https://developers.google.com/civic-information/" target="_blank">Google Civic Information API</a> is a handy way to grab voting and elected official information. The current way I do that on <a href="http://maps.co.mecklenburg.nc.us" target="_blank">GeoPortal</a> is to enter some representative information locally and update it when somebody emails me in ALL CAPS. That is not a good way.</p>
<p>But the Google Civics API’s representative service (oddly) only does POST, and POST doesn’t do JSONP, necessitating a proxy. And to further complicate things, it isn’t a straight-forward cURL operation - you have to send the POST argument as JSON and not the default x-form or you’ll get an error message. So, do this:</p>
<figure class="highlight php"><figcaption><span>civicproxy.php</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="code"><pre><span class="comment">// The only arguments are "address" and "callback" (the latter optional for JSONP)</span>

<span class="variable">$url</span> = <span class="string">"https://www.googleapis.com/civicinfo/us_v1/representatives/lookup?key=_your_api_key_here_"</span>;

<span class="variable">$ch</span> = curl_init();

curl_setopt(<span class="variable">$ch</span>, CURLOPT_URL, <span class="variable">$url</span>);
curl_setopt(<span class="variable">$ch</span>, CURLOPT_POST, <span class="number">1</span>);

<span class="comment">// I swore a lot over these next two lines</span>
curl_setopt(<span class="variable">$ch</span>, CURLOPT_HTTPHEADER, <span class="keyword">array</span>(<span class="string">"Content-type: application/json"</span>));
curl_setopt(<span class="variable">$ch</span>, CURLOPT_POSTFIELDS, <span class="string">'{"address":"'</span> . <span class="variable">$_GET</span>[<span class="string">"address"</span>] . <span class="string">'"}'</span>);

curl_setopt(<span class="variable">$ch</span>, CURLOPT_RETURNTRANSFER, <span class="keyword">true</span>);

<span class="variable">$json</span> = curl_exec(<span class="variable">$ch</span>);

curl_close(<span class="variable">$ch</span>);

<span class="comment">// if a callback is set send back jsonp</span>
<span class="keyword">echo</span> <span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">'callback'</span>]) ? <span class="string">"{$_GET['callback']}($json)"</span> : <span class="variable">$json</span>;
</pre></td></tr></table></figure>

<p>And then a little jQuery to eat it.</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre>$.ajax({
    url: <span class="string">'civicproxy.php'</span>,
    type: <span class="string">'GET'</span>,
    dataType: <span class="string">'json'</span>,
    data: {
        <span class="string">'address'</span>: activeRecord.address
    },
    success: <span class="function"><span class="keyword">function</span> <span class="params">(data)</span> {</span>
        <span class="comment">// do magical things</span>
    }
});
</pre></td></tr></table></figure>



]]></content>
    
    
  </entry>
  
  <entry>
    <title><![CDATA[Fuzzy Tolerance 32: Task Runners and Modern Workflows]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/02/21/2014-02-21-fuzzy-tolerance-32-task-runners-and-modern-workflows/"/>
    <id>http://fuzzytolerance.info/blog/2014/02/21/2014-02-21-fuzzy-tolerance-32-task-runners-and-modern-workflows/</id>
    <published>2014-02-21T21:23:35.000Z</published>
    <updated>2014-02-21T21:31:14.000Z</updated>
    <content type="html"><![CDATA[<iframe width="853" height="480" src="//www.youtube.com/embed/DVRMm1LPXMw?rel=0" frameborder="0" allowfullscreen></iframe>

<a id="more"></a>

<figure class="highlight bash"><figcaption><span>Install Gulp and Packages</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>npm install gulp -g
npm install gulp gulp-less gulp-minify-css gulp-autoprefixer gulp-uglify gulp-imagemin gulp-concat gulp-replace gulp-livereload tiny-lr
</pre></td></tr></table></figure>

<figure class="highlight javascript"><figcaption><span>Gulp File</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
</pre></td><td class="code"><pre><span class="comment">// Load plugins</span>
<span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>),
    less = <span class="built_in">require</span>(<span class="string">'gulp-less'</span>),
    autoprefixer = <span class="built_in">require</span>(<span class="string">'gulp-autoprefixer'</span>),
    minifycss = <span class="built_in">require</span>(<span class="string">'gulp-minify-css'</span>),
    uglify = <span class="built_in">require</span>(<span class="string">'gulp-uglify'</span>),
    imagemin = <span class="built_in">require</span>(<span class="string">'gulp-imagemin'</span>),
    concat = <span class="built_in">require</span>(<span class="string">'gulp-concat'</span>),
    replace = <span class="built_in">require</span>(<span class="string">'gulp-replace'</span>),
    refresh = <span class="built_in">require</span>(<span class="string">'gulp-livereload'</span>),
    lr = <span class="built_in">require</span>(<span class="string">'tiny-lr'</span>),
    server = lr();

<span class="keyword">var</span> jsFiles = [
    <span class="string">'assets/scripts/vendor/jquery-2.0.3.min.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/modal.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/transition.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/button.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/collapse.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/dropdown.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/tooltip.js'</span>,
    <span class="string">'assets/scripts/vendor/leaflet/leaflet.js'</span>,
    <span class="string">'assets/scripts/vendor/leaflet/leaflet.d3.js'</span>,
    <span class="string">'assets/scripts/vendor/jquery-ui-1.10.3.custom.min.js'</span>,
    <span class="string">'assets/scripts/vendor/chosen.jquery.js'</span>,
    <span class="string">'assets/scripts/vendor/d3.v3.js'</span>,
    <span class="string">'assets/scripts/vendor/pagedown.js'</span>,
    <span class="string">'assets/scripts/vendor/pubsub.js'</span>,
    <span class="string">'assets/scripts/vendor/queue.v1.min.js'</span>,
    <span class="string">'assets/scripts/vendor/topojson.v0.js'</span>,
    <span class="string">'assets/scripts/vendor/d3.tip.v0.6.3.js'</span>,
    <span class="string">'assets/scripts/vendor/typeahead.js'</span>,
    <span class="string">'assets/scripts/vendor/underscore-min.js'</span>,
    <span class="string">'assets/scripts/vis/*.js'</span>,
    <span class="string">'assets/scripts/datameta.js'</span>,
    <span class="string">'assets/scripts/page.js'</span>
];

<span class="comment">// Less preprocessing with autoprefixer and minify</span>
gulp.task(<span class="string">'styles'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'assets/less/main.less'</span>)
        .pipe(less())
        .pipe(autoprefixer(<span class="string">'last 2 version'</span>, <span class="string">'safari 5'</span>, <span class="string">'ie 9'</span>, <span class="string">'opera 12.1'</span>, <span class="string">'ios 6'</span>, <span class="string">'android 4'</span>))
        .pipe(minifycss())
        .pipe(gulp.dest(<span class="string">'public/css'</span>))
        .pipe(refresh(server));
});

<span class="comment">// Script concatenation</span>
gulp.task(<span class="string">'scripts'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(jsFiles)
        .pipe(concat(<span class="string">'main.js'</span>))
        .pipe(gulp.dest(<span class="string">'public/js'</span>))
        .pipe(refresh(server));
});

<span class="comment">// Script uglify</span>
gulp.task(<span class="string">'uglify'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'public/js/main.js'</span>)
        .pipe(uglify())
        .pipe(gulp.dest(<span class="string">'public/js'</span>));
});

<span class="comment">// Image Minification</span>
gulp.task(<span class="string">'imagemin'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'assets/images/build/*'</span>)
        .pipe(imagemin({
            optimizationLevel: <span class="number">3</span>,
            progressive: <span class="literal">true</span>,
            interlaced: <span class="literal">true</span>
        }))
        .pipe(gulp.dest(<span class="string">'public/images'</span>));
});

<span class="comment">// HTML refresh</span>
gulp.task(<span class="string">'html'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src([<span class="string">'public/**/*.html'</span>])
        .pipe(refresh(server));
});

<span class="comment">// Image refresh</span>
gulp.task(<span class="string">'images'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src([<span class="string">'public/**/*.png'</span>, <span class="string">'public/**/*.gif'</span>, <span class="string">'public/**/*.jpg'</span>, <span class="string">'public/**/*.svg'</span>])
        .pipe(refresh(server));
});

<span class="comment">// Cache busting</span>
gulp.task(<span class="string">'replace'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'public/index.html'</span>)
        .pipe(replace(<span class="regexp">/foo=[0-9]*/g</span>, <span class="string">'foo='</span> + <span class="built_in">Math</span>.floor((<span class="built_in">Math</span>.random() * <span class="number">100000</span>) + <span class="number">1</span>)))
        .pipe(gulp.dest(<span class="string">'public/'</span>));
});

<span class="comment">// live reload</span>
gulp.task(<span class="string">'livereload'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    server.listen(<span class="number">35729</span>, <span class="function"><span class="keyword">function</span><span class="params">(err)</span> {</span>
        <span class="keyword">if</span> (err) {
            <span class="keyword">return</span> console.log(err);
        }
    });
});

<span class="comment">// dev task</span>
gulp.task(<span class="string">'dev'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    gulp.run(<span class="string">'livereload'</span>);
    gulp.watch(<span class="string">'assets/less/**/*.less'</span>, [<span class="string">'styles'</span>]);
    gulp.watch(<span class="string">'assets/scripts/**/*.js'</span>, [<span class="string">'scripts'</span>]);
    gulp.watch([<span class="string">'public/**/*.html'</span>], [<span class="string">'html'</span>]);
    gulp.watch([<span class="string">'public/**/*.png'</span>, <span class="string">'public/**/*.gif'</span>, <span class="string">'public/**/*.jpg'</span>, <span class="string">'public/**/*.svg'</span>], [<span class="string">'images'</span>]);
});

<span class="comment">// build task</span>
gulp.task(<span class="string">'build'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    gulp.run(<span class="string">'styles'</span>, <span class="string">'replace'</span>, <span class="string">'imagemin'</span>, <span class="string">'scripts'</span>, <span class="string">'uglify'</span>);
});
</pre></td></tr></table></figure>

<h3 id="Resources">Resources</h3>
<ul>
<li><a href="http://nodejs.org/" target="_blank">Node</a></li>
<li><a href="http://gulpjs.com/" target="_blank">Gulp</a></li>
</ul>
]]></content>
    <summary type="html"><![CDATA[<iframe width="853" height="480" src="//www.youtube.com/embed/DVRMm1LPXMw?rel=0" frameborder="0" allowfullscreen></iframe>

]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Fun with Gulp]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/02/11/2014-02-11-fun-with-gulp/"/>
    <id>http://fuzzytolerance.info/blog/2014/02/11/2014-02-11-fun-with-gulp/</id>
    <published>2014-02-11T20:38:59.000Z</published>
    <updated>2014-02-11T20:39:19.000Z</updated>
    <content type="html"><![CDATA[<p>I’ve been a happy <a href="http://gruntjs.com/" target="_blank">Grunt</a> user for a while now. I use it for dev watch/preprocessing/livereload type stuff, and it works great. It is, however, a bit cantankerous to set up. Sometimes you can’t seem to get around making intermediary files when stringing tasks together either, which means crud in your folders. I hate folder crud.</p>
<p><a href="http://gulpjs.com/" target="_blank">Gulp</a> is the new hot stuff for task running. I hadn’t heard of it until a few weeks ago, but after having a play with it it might replace Grunt in my stack. The configuration is easier to set up and read, and because it uses node streams it can net you a decent performance boost.</p>
<a id="more"></a>

<p>Here’s a config file I just made for one of my projects.</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
</pre></td><td class="code"><pre><span class="comment">// Load plugins</span>
<span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>),
    less = <span class="built_in">require</span>(<span class="string">'gulp-less'</span>),
    autoprefixer = <span class="built_in">require</span>(<span class="string">'gulp-autoprefixer'</span>),
    minifycss = <span class="built_in">require</span>(<span class="string">'gulp-minify-css'</span>),
    uglify = <span class="built_in">require</span>(<span class="string">'gulp-uglify'</span>),
    imagemin = <span class="built_in">require</span>(<span class="string">'gulp-imagemin'</span>),
    concat = <span class="built_in">require</span>(<span class="string">'gulp-concat'</span>),
    replace = <span class="built_in">require</span>(<span class="string">'gulp-replace'</span>),
    refresh = <span class="built_in">require</span>(<span class="string">'gulp-livereload'</span>),
    lr = <span class="built_in">require</span>(<span class="string">'tiny-lr'</span>),
    server = lr();

<span class="keyword">var</span> jsFiles = [
    <span class="string">'assets/scripts/vendor/jquery-2.0.3.min.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/transition.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/button.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/collapse.js'</span>,
    <span class="string">'assets/scripts/vendor/bootstrap/dropdown.js'</span>,
    <span class="string">'assets/scripts/vendor/leaflet/leaflet.js'</span>,
    <span class="string">'assets/scripts/vendor/leaflet/leaflet.d3.js'</span>,
    <span class="string">'assets/scripts/vendor/jquery-ui-1.10.3.custom.min.js'</span>,
    <span class="string">'assets/scripts/vendor/chosen.jquery.js'</span>,
    <span class="string">'assets/scripts/vendor/d3.v3.js'</span>,
    <span class="string">'assets/scripts/vendor/pagedown.js'</span>,
    <span class="string">'assets/scripts/vendor/pubsub.js'</span>,
    <span class="string">'assets/scripts/vendor/queue.v1.min.js'</span>,
    <span class="string">'assets/scripts/vendor/topojson.v0.js'</span>,
    <span class="string">'assets/scripts/vendor/d3.tip.v0.6.3.js'</span>,
    <span class="string">'assets/scripts/vendor/typeahead.js'</span>,
    <span class="string">'assets/scripts/vendor/underscore-min.js'</span>,
    <span class="string">'assets/scripts/vis/*.js'</span>,
    <span class="string">'assets/scripts/datameta.js'</span>,
    <span class="string">'assets/scripts/page.js'</span>
];

<span class="comment">// Less preprocessing with autoprefixer and minify</span>
gulp.task(<span class="string">'styles'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'assets/less/main.less'</span>)
        .pipe(less())
        .pipe(autoprefixer(<span class="string">'last 2 version'</span>, <span class="string">'safari 5'</span>, <span class="string">'ie 9'</span>, <span class="string">'opera 12.1'</span>, <span class="string">'ios 6'</span>, <span class="string">'android 4'</span>))
        .pipe(minifycss())
        .pipe(gulp.dest(<span class="string">'public/css'</span>))
        .pipe(refresh(server));
});

<span class="comment">// Script concatenation</span>
gulp.task(<span class="string">'scripts'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(jsFiles)
        .pipe(concat(<span class="string">'main.js'</span>))
        .pipe(gulp.dest(<span class="string">'public/js'</span>))
        .pipe(refresh(server));
});

<span class="comment">// Script uglify</span>
gulp.task(<span class="string">'uglify'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'public/js/main.js'</span>)
        .pipe(uglify())
        .pipe(gulp.dest(<span class="string">'public/js'</span>));
});

<span class="comment">// Image Minification</span>
gulp.task(<span class="string">'imagemin'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'assets/images/build/*'</span>)
        .pipe(imagemin({
            optimizationLevel: <span class="number">3</span>,
            progressive: <span class="literal">true</span>,
            interlaced: <span class="literal">true</span>
        }))
        .pipe(gulp.dest(<span class="string">'public/images'</span>));
});

<span class="comment">// HTML refresh</span>
gulp.task(<span class="string">'html'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src([<span class="string">'public/**/*.html'</span>])
        .pipe(refresh(server));
});

<span class="comment">// Image refresh</span>
gulp.task(<span class="string">'images'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src([<span class="string">'public/**/*.png'</span>, <span class="string">'public/**/*.gif'</span>, <span class="string">'public/**/*.jpg'</span>, <span class="string">'public/**/*.svg'</span>])
        .pipe(refresh(server));
});

<span class="comment">// Cache busting</span>
gulp.task(<span class="string">'replace'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    <span class="keyword">return</span> gulp.src(<span class="string">'public/index.html'</span>)
        .pipe(replace(<span class="regexp">/foo=[0-9]*/g</span>, <span class="string">'foo='</span> + <span class="built_in">Math</span>.floor((<span class="built_in">Math</span>.random() * <span class="number">100000</span>) + <span class="number">1</span>)))
        .pipe(gulp.dest(<span class="string">'public/'</span>));
});

<span class="comment">// live reload</span>
gulp.task(<span class="string">'livereload'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    server.listen(<span class="number">35729</span>, <span class="function"><span class="keyword">function</span><span class="params">(err)</span> {</span>
        <span class="keyword">if</span> (err) {
            <span class="keyword">return</span> console.log(err);
        }
    });
});

<span class="comment">// dev task</span>
gulp.task(<span class="string">'dev'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    gulp.run(<span class="string">'livereload'</span>);
    gulp.watch(<span class="string">'assets/less/**/*.less'</span>, [<span class="string">'styles'</span>]);
    gulp.watch(<span class="string">'assets/scripts/**/*.js'</span>, [<span class="string">'scripts'</span>]);
    gulp.watch([<span class="string">'public/**/*.html'</span>], [<span class="string">'html'</span>]);
    gulp.watch([<span class="string">'public/**/*.png'</span>, <span class="string">'public/**/*.gif'</span>, <span class="string">'public/**/*.jpg'</span>, <span class="string">'public/**/*.svg'</span>], [<span class="string">'images'</span>]);
});

<span class="comment">// build task</span>
gulp.task(<span class="string">'build'</span>, <span class="function"><span class="keyword">function</span><span class="params">()</span> {</span>
    gulp.run(<span class="string">'replace'</span>, <span class="string">'imagemin'</span>, <span class="string">'uglify'</span>);
});
</pre></td></tr></table></figure>

<p>For my web projects, I generally have two workflows. During <em>dev</em>, I watch JavaScript source files and concatenate them, watch less files and preprocess and minify them, and watch HTML and image files for changes, using livereload to refresh the browser as necessary. Then for <em>build</em> I minify images, uglify the JavaScript, and do some cache busting. To keep my HTML, JS, and CSS in sync, I load JS and CSS with a <em>foo=XXXXXX</em> argument. When I push a new build I replace the <em>XXXXXX</em> number with a random number, invalidating the browser’s cache. I was able to replicate all of this with Gulp in under an hour of Googling.</p>
<p>It’s a little faster and easier to configure than Grunt, but not so much so that you should drop whatever you’re doing and replace your Grunt workflow immediately. Still, for new projects or if you’re just getting into task runners, make Gulp your first stop. </p>
]]></content>
    <summary type="html"><![CDATA[<p>I’ve been a happy <a href="http://gruntjs.com/" target="_blank">Grunt</a> user for a while now. I use it for dev watch/preprocessing/livereload type stuff, and it works great. It is, however, a bit cantankerous to set up. Sometimes you can’t seem to get around making intermediary files when stringing tasks together either, which means crud in your folders. I hate folder crud.</p>
<p><a href="http://gulpjs.com/" target="_blank">Gulp</a> is the new hot stuff for task running. I hadn’t heard of it until a few weeks ago, but after having a play with it it might replace Grunt in my stack. The configuration is easier to set up and read, and because it uses node streams it can net you a decent performance boost.</p>
]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Looking Cloudy]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/01/17/2014-01-17-looking-cloudy/"/>
    <id>http://fuzzytolerance.info/blog/2014/01/17/2014-01-17-looking-cloudy/</id>
    <published>2014-01-17T17:54:48.000Z</published>
    <updated>2014-06-01T01:07:28.000Z</updated>
    <content type="html"><![CDATA[<p>I’m in the process of replacing our primary web server for open source stuff<sup>1</sup>, and it seemed like a good opportunity to figure out how to get our content to the “cloud” or, if that word raises your nomenclature hackles, a VPS host. </p>
<a id="more"></a>

<p>There are many reasons to look at cloud hosting, but my top 3 are:</p>
<ul>
<li><strong>Cost</strong>: Like most government organizations, we pay insane amounts for hardware. Replacing our current box would probably run us ~$10k (our tiniest server size), and we’ll have to replace it in 3-4 years. Not to mention storage costs on “your dad’s SAN”.  Not to mention all the human support time. Not to mention the Windows license. Not to mention <em>the freaking power costs</em>.</li>
<li><strong>Convenience</strong>: Try firing up a new server 60 seconds after you thought of it with your IT shop.</li>
<li><strong>Control</strong>: I want to be fast and innovate and try new things. Or at least run Linux on a server like a normal person.</li>
</ul>
<p>I’ve been hearing great things about <a href="https://www.digitalocean.com/" target="_blank">Digital Ocean</a>. For $5/month you get 1 core, 512MB, 20GB SSD storage, 1TB of transfer (upload doesn’t count toward that). The thing that really separates them is the SSD. Our IT shop doesn’t believe in SSD’s, but since the first time I dropped an Intel 40GB SSD into a machine at home I refuse to use anything else. The speed difference is jaw-dropping. </p>
<p>I decided to run a little benchmark. I dropped a simple HTML file on Digital Ocean, our old/dying physical web server, and a new local VM that might replace that physical server.</p>
<figure class="highlight html"><figcaption><span>Test File - ab.html</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="doctype">&lt;!DOCTYPE html&gt;</span>
<span class="tag">&lt;<span class="title">html</span>&gt;</span>
<span class="tag">&lt;<span class="title">head</span>&gt;</span>
<span class="tag">&lt;<span class="title">title</span>&gt;</span>Webserver test<span class="tag">&lt;/<span class="title">title</span>&gt;</span>
<span class="tag">&lt;/<span class="title">head</span>&gt;</span>
<span class="tag">&lt;<span class="title">body</span>&gt;</span>
This is a webserver test page.
<span class="tag">&lt;/<span class="title">body</span>&gt;</span>
<span class="tag">&lt;/<span class="title">html</span>&gt;</span>
</pre></td></tr></table></figure>

<p>The three web servers are like so:</p>
<ul>
<li>Current Physical Server: 4GB RAM, 2 CPU (4 cores each), Windows Server 2003 32bit, Apache (this is an old box)</li>
<li>VMWare Instance: 6GB RAM, 2 CPU, Windows Server 2012 64bit, Apache</li>
<li>Digital Ocean: 512MB RAM, 1 CPU, Ubuntu 13.10, nginx</li>
</ul>
<p>It isn’t exactly a fair comparison - Apache vs nginx, physical vs virtual, single core vs multicore, SSD vs your dad’s SAN. But that’s kind of the point. I don’t care what the backend is as much as comparing the performance of the backends available to me. Plus, I can’t exactly run nginx on Windows<sup>2</sup>.</p>
<p>I used Apache ab to do a simple benchmark on the test file. It was run on the local network (but not the same machine) of the test boxes so we’re not seeing web latency issues in the test. 10,000 requests were made, 20 concurrently.</p>
<figure class="highlight bash"><figcaption><span>Apache ab</span></figcaption><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre>ab -n <span class="number">10000</span> -c <span class="number">20</span> http://thehost/ab.html
</pre></td></tr></table></figure>

<p>Here are the result for our old physical server.</p>
<figure class="highlight bash"><figcaption><span>Physical Server</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre>Concurrency Level:      <span class="number">20</span>
Time taken <span class="keyword">for</span> tests:   <span class="number">16.393</span> seconds
Complete requests:      <span class="number">10000</span>
Failed requests:        <span class="number">0</span>
Write errors:           <span class="number">0</span>
Total transferred:      <span class="number">4710000</span> bytes
HTML transferred:       <span class="number">1290000</span> bytes
Requests per second:    <span class="number">610.01</span> [<span class="comment">#/sec] (mean)</span>
Time per request:       <span class="number">32.786</span> [ms] (mean)
Time per request:       <span class="number">1.639</span> [ms] (mean, across all concurrent requests)
Transfer rate:          <span class="number">280.58</span> [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        <span class="number">1</span>   <span class="number">14</span>   <span class="number">7.0</span>     <span class="number">14</span>      <span class="number">60</span>
Processing:     <span class="number">4</span>   <span class="number">19</span>   <span class="number">9.9</span>     <span class="number">18</span>     <span class="number">193</span>
Waiting:        <span class="number">2</span>   <span class="number">17</span>   <span class="number">9.9</span>     <span class="number">16</span>     <span class="number">193</span>
Total:          <span class="number">6</span>   <span class="number">33</span>  <span class="number">12.8</span>     <span class="number">34</span>     <span class="number">208</span>

Percentage of the requests served within a certain time (ms)
  <span class="number">50</span>%     <span class="number">34</span>
  <span class="number">66</span>%     <span class="number">37</span>
  <span class="number">75</span>%     <span class="number">38</span>
  <span class="number">80</span>%     <span class="number">39</span>
  <span class="number">90</span>%     <span class="number">43</span>
  <span class="number">95</span>%     <span class="number">49</span>
  <span class="number">98</span>%     <span class="number">57</span>
  <span class="number">99</span>%     <span class="number">68</span>
 <span class="number">100</span>%    <span class="number">208</span> (longest request)
</pre></td></tr></table></figure>

<p>Not bad. The requests were handled in <strong>16.393</strong> seconds, averaging <strong>610</strong> requests per second and <strong>33ms</strong> per request.</p>
<p>Now for the VMWare server.</p>
<figure class="highlight bash"><figcaption><span>VMWare Server</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre>Concurrency Level:      <span class="number">20</span>
Time taken <span class="keyword">for</span> tests:   <span class="number">16.896</span> seconds
Complete requests:      <span class="number">10000</span>
Failed requests:        <span class="number">0</span>
Write errors:           <span class="number">0</span>
Total transferred:      <span class="number">3740000</span> bytes
HTML transferred:       <span class="number">1290000</span> bytes
Requests per second:    <span class="number">591.85</span> [<span class="comment">#/sec] (mean)</span>
Time per request:       <span class="number">33.793</span> [ms] (mean)
Time per request:       <span class="number">1.690</span> [ms] (mean, across all concurrent requests)
Transfer rate:          <span class="number">216.16</span> [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        <span class="number">1</span>   <span class="number">14</span>   <span class="number">5.2</span>     <span class="number">14</span>      <span class="number">38</span>
Processing:     <span class="number">4</span>   <span class="number">20</span>   <span class="number">7.7</span>     <span class="number">19</span>     <span class="number">158</span>
Waiting:        <span class="number">3</span>   <span class="number">18</span>   <span class="number">7.6</span>     <span class="number">18</span>     <span class="number">155</span>
Total:          <span class="number">8</span>   <span class="number">34</span>  <span class="number">10.3</span>     <span class="number">35</span>     <span class="number">164</span>

Percentage of the requests served within a certain time (ms)
  <span class="number">50</span>%     <span class="number">35</span>
  <span class="number">66</span>%     <span class="number">37</span>
  <span class="number">75</span>%     <span class="number">38</span>
  <span class="number">80</span>%     <span class="number">39</span>
  <span class="number">90</span>%     <span class="number">42</span>
  <span class="number">95</span>%     <span class="number">49</span>
  <span class="number">98</span>%     <span class="number">56</span>
  <span class="number">99</span>%     <span class="number">62</span>
 <span class="number">100</span>%    <span class="number">164</span> (longest request)
</pre></td></tr></table></figure>

<p>Again, not bad. It’s a tiny bit slower - the requests were handled in <strong>16.896</strong> seconds, averaging <strong>591</strong> requests per second and <strong>34ms*</strong> per request. I had hoped to be the same or a little better than the physical box (the physical box is old), but it’s pretty close. There was some weirdness  however. The test would only complete 1 of every 3 tries, timing out on the others. This might be an <a href="http://stackoverflow.com/questions/10184975/ab-apache-bench-error-apr-poll-the-timeout-specified-has-expired-70007-on" target="_blank">ab bug</a> and not a server issue.</p>
<p>Now for the eye opener.</p>
<figure class="highlight bash"><figcaption><span>Digital Ocean $5/month - seriously</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre>Concurrency Level:      <span class="number">20</span>
Time taken <span class="keyword">for</span> tests:   <span class="number">1.974</span> seconds
Complete requests:      <span class="number">10000</span>
Failed requests:        <span class="number">0</span>
Write errors:           <span class="number">0</span>
Total transferred:      <span class="number">3620000</span> bytes
HTML transferred:       <span class="number">1220000</span> bytes
Requests per second:    <span class="number">5065.36</span> [<span class="comment">#/sec] (mean)</span>
Time per request:       <span class="number">3.948</span> [ms] (mean)
Time per request:       <span class="number">0.197</span> [ms] (mean, across all concurrent requests)
Transfer rate:          <span class="number">1790.68</span> [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        <span class="number">0</span>    <span class="number">0</span>   <span class="number">0.2</span>      <span class="number">0</span>       <span class="number">5</span>
Processing:     <span class="number">0</span>    <span class="number">4</span>   <span class="number">1.3</span>      <span class="number">3</span>      <span class="number">16</span>
Waiting:        <span class="number">0</span>    <span class="number">4</span>   <span class="number">1.3</span>      <span class="number">3</span>      <span class="number">15</span>
Total:          <span class="number">1</span>    <span class="number">4</span>   <span class="number">1.3</span>      <span class="number">4</span>      <span class="number">16</span>

Percentage of the requests served within a certain time (ms)
  <span class="number">50</span>%      <span class="number">4</span>
  <span class="number">66</span>%      <span class="number">4</span>
  <span class="number">75</span>%      <span class="number">4</span>
  <span class="number">80</span>%      <span class="number">4</span>
  <span class="number">90</span>%      <span class="number">5</span>
  <span class="number">95</span>%      <span class="number">7</span>
  <span class="number">98</span>%      <span class="number">8</span>
  <span class="number">99</span>%      <span class="number">9</span>
 <span class="number">100</span>%     <span class="number">16</span> (longest request)
</pre></td></tr></table></figure>

<p>I was hoping for similar speeds or maybe a slight improvement with nginx and a SSD. I didn’t think my eyes would pop out. The requests were handled in <strong>1.974</strong> seconds, averaging <strong>5065.36*</strong> requests per second and <strong>4ms*</strong> per request. </p>
<table>
<thead>
<tr>
<th style="text-align:left"></th>
<th style="text-align:right">Total Time (s)</th>
<th style="text-align:right">Requests/Second</th>
<th style="text-align:right">Mean Request Time (ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>Physical Box</strong></td>
<td style="text-align:right">16.393</td>
<td style="text-align:right">610.01</td>
<td style="text-align:right">32.786</td>
</tr>
<tr>
<td style="text-align:left"><strong>VMware</strong></td>
<td style="text-align:right">16.896</td>
<td style="text-align:right">591.85</td>
<td style="text-align:right">33.793</td>
</tr>
<tr>
<td style="text-align:left"><strong>Digital Ocean</strong></td>
<td style="text-align:right">1.974</td>
<td style="text-align:right">5065.36</td>
<td style="text-align:right">3.948</td>
</tr>
</tbody>
</table>
<p>For $5/month I can replace a $10k+ server with all of the associated costs and headaches, be able to do whatever I want with it, and increase performance by ~900%. And how did that test bump the CPU at Digital Ocean?</p>
<figure class="center"><br>  <img src="http://i.imgur.com/zoiyxhJ.png"><br>  <figcaption>Bored server is bored.</figcaption><br></figure>

<p>Hells yeah I want some of that. So much so I’m willing to jump through potentially soul-crushing procurement hoops to get it<sup>3</sup>.</p>
<div class="footnotes"><br><p><br><sup>1</sup> We separate our open source and proprietary stuff because we don’t want the instability of our proprietary stuff screwing with the uptime of our open source stuff. True story.<br></p><br><p><br><sup>2</sup> Cygwin doesn’t count.<br></p><br><p><br><sup>3</sup> Figuring out how to bill the government $5 a month may well cost $5,000 in staff time.<br></p><br></div>
]]></content>
    <summary type="html"><![CDATA[<p>I’m in the process of replacing our primary web server for open source stuff<sup>1</sup>, and it seemed like a good opportunity to figure out how to get our content to the “cloud” or, if that word raises your nomenclature hackles, a VPS host. </p>
]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[What's in my Toolbox 2014 Edition]]></title>
    <link href="http://fuzzytolerance.info/blog/2014/01/06/2014-01-06-whats-in-my-toolbox-2014-edition/"/>
    <id>http://fuzzytolerance.info/blog/2014/01/06/2014-01-06-whats-in-my-toolbox-2014-edition/</id>
    <published>2014-01-06T22:07:22.000Z</published>
    <updated>2014-01-07T01:10:20.000Z</updated>
    <content type="html"><![CDATA[<img src="http://i.imgur.com/s3hbk41.jpg" class="center" title="gotta have the right tool for the job">

<p>Nothing sets my propeller-hat spinning faster than nerdgear. When people talk about how they work I decouple with my headphones and listen. Because, somewhere deep down, I’m pretty sure I’m doing it wrong.</p>
<p>Without further lolcats, here’s my toolbox at the start of 2014. Note I’m detailing my home kit, as I work there most of the time and talking about my County working conditions would needlessly depress you.</p>
<h2 id="Hardware">Hardware</h2>
<ul>
<li>Lenovo z510 dual-core i5 Haswell laptop, 8GB RAM, SSD for OS</li>
<li>Dual 22” 1080P monitors</li>
<li>CM Storm QuickFire mechanical keyboard</li>
<li>Logitech M570 marble mouse</li>
<li>Blue Microphones NESSIE microphone </li>
<li>Logitech C920 HD webcam</li>
<li>M-Audio AV30 powered monitors </li>
<li>AKG M80 MkII earcans (big meh - won’t last the year)</li>
<li>Samsung Chromebook</li>
<li>Nexus 7 2013 model</li>
<li>Samsung Galaxy Nexus (which will be crushed under heel when my Verizon contract is up)</li>
</ul>
<p>We live in an age of insane personal computing power. So insane I <em>downgraded</em> my home rig. I went from a home-built quad-core i5 with discreet graphics and 16GB of RAM to a Lenovo z510 i5 laptop with a dual-core i5, integrated graphics and 8GB of RAM. It isn’t an even swap - Lenny has Haswell i5 with hyperthreading - but it’s a downgrade. </p>
<blockquote class="pullquote ">

<p>Other than the fact that it sips power, is nearly silent and doesn’t double as a space heater, I never notice the difference. OK, I <em>rarely</em> notice the difference (hi <a href="http://handbrake.fr/" target="_blank">HandBrake</a>!). But overall I’m very happy with this laptop. It stays chained to my desk, lid closed, powering my external monitors. It has a nice 1080p screen, so if I ever unshackled it from my desk I could do serious work at Starbucks. And here’s a free {“ PSA: if you’re on Linux and you’re not a gamer, always get Intel graphics. “}</p>
</blockquote>


<p>I’m rocking my usual dual-monitor setup with a mechanical keyboard. One new toy is my Blue Microphones Nessie, which includes some auto-processing onboard (compression etc.). Beyond that not much has changed.</p>
<p>I use my Chromebook about as much as I use anything else. I love that plastic piece of shit. I almost bought a new Acer model with a Haswell processor, but then I caught <a href="http://www.omgchrome.com/samsung-arm-chromebook-refresh-slated-2014/" target="_blank">a rumor</a> of a $250 Samsung coming out with retina-esque resolution later this year. </p>
<p>For tablets I like my Nexus 7. My iPad rarely gets touched. It’s a tad big and heavy for reading, and the screen on my Nexus 7 is crazy good. That’s mostly a reading/entertainment device - people that say they do real work on tablets are probably crazy.</p>
<h2 id="OS">OS</h2>
<ul>
<li>Kubuntu 13.10 64bit</li>
</ul>
<p>I’m still rocking along with KDE and Kubuntu. I keep threatening to go to Arch. I installed it on a VM and a netbook just to prove to myself that I could. I like <em>new</em> stuff - I don’t want to have to wait 6 months for a new Kernel or software package and dealing with PPA’s can get old. The idea of a rolling release is very appealing. Maybe I’ll jump when I hit the 4-months-of-13.10 blues.</p>
<h2 id="Dev_Stack">Dev Stack</h2>
<ul>
<li>Sublime Text 3 (beta)</li>
<li>Sublime extensions: Package Control, AdvancedNewFilename, AutoFileName, ColorPicker, DocBlockr, EditorConfig, Emmet, HTML-CSS-JS Prettify, LESS, Markdown Preview, Markdown Editing, Nettuts+ Fetch, Sidebar Enhancements, Sublime Linter</li>
<li>Kate, Notepad++ (Windows) and nano for quick edits</li>
<li>Grunt</li>
<li>Grunt extensions: grunt-contrib-watch, grunt-contrib-uglify, grunt-contrib-concat, grunt-contrib-less, grunt-autoprefixer, grunt-text-replace, grunt-contrib-imagemin</li>
<li>Git</li>
<li>Inkscape and GIMP for graphics</li>
<li>Google Chrome</li>
<li>Chrome extensions: LiveReload, PageSpeed Insights, REST Console, Screen Capture, Secure Shell, Web Developer</li>
</ul>
<p>Yep, I’m a web dev. I do some shell scripting in Python and JavaScript (Nodejs), maybe even some bash to fling stuff around sometimes. When I do server side web stuff it’s often PHP, though I’m getting into Nodejs. I also do some DB and server admin, and I do “strategic planning”, which can appear indistinguishable from constipation. But I’m mostly a web dev.</p>
<p>I don’t think there’s a lot here that needs further explanation - if you’re a dev you know most/all of this stuff, if you’re not a dev you don’t care. Sublime Text is still the best editor I’ve run across, and I’m too old to learn Vim + a tiled window manager, though damn if that doesn’t look awesome.</p>
<p>I would like to point a finger and jump up and down about <a href="https://github.com/nDmitry/grunt-autoprefixer" target="_blank">Autoprefixer</a> if you haven’t heard of it. How would you like to never worry about browser prefixes again? Autoprefixer gives you that in a way you can control very precisely. By setting <code>browsers: [&#39;last 2 version&#39;, &quot;&gt; 5%&quot;, &#39;ie 8&#39;]</code> I’m prefixing for the last 2 versions of every browser, any browser with above 5% market share, and &gt;= IE 8 specifically. </p>
<h2 id="Everyday_stuff_I_can’t_live_without">Everyday stuff I can’t live without</h2>
<p>In no particular order:</p>
<ul>
<li>Dropbox</li>
<li>TileMill</li>
<li>QGIS</li>
<li>GeoServer</li>
<li>Postgres</li>
<li>PostGIS</li>
<li>PGAdmin III (because psql is bullshit)</li>
<li>Octopress</li>
<li>Dolphin, the greatest file manager <em>ever</em></li>
<li>SQLiteman</li>
<li>Bootstrap</li>
<li>Leaflet</li>
<li>jQuery</li>
<li>D3, even though it hurts my brain sometimes</li>
<li>Lots of Google services (Gmail, Docs, Keep, to name a few)</li>
<li>Feedly, so I can keep up with you people</li>
<li>Twitter, so I can mimic a social person</li>
</ul>
<h2 id="Stuff_I_want_to_like_in_2014">Stuff I want to like in 2014</h2>
<ul>
<li>A client-side MVC framework. I’ve played with Backbone, Backbone + helpers (because Backbone by itself is a bitch), Angular, Ember, and did the ubiquitous ToDo list app with some others. I’ve tried to like it. I just don’t. I end up with 3x the code and I’ll never be able to share it with co-workers because there’s too much <em>magic</em> overhead. Lots of people like this stuff, so it could just be me. Maybe in 2014 I’ll get it.</li>
<li>I want to do something with QT. I don’t do desktop apps or C++, so the odds of this happening are not great. </li>
<li>A serious Nodejs app, complete with a magical framework like Meteor. I’ve been waiting patiently for Python to eat my PHP addiction, but Nodejs gave it a beating for server-side web stuff. Actually ~10,000 beatings, all running concurrently. Maybe this year I’ll finally get off of PHP. I love PHP, but every time I tell somebody I did something in PHP I feel a little dirty afterwards.</li>
</ul>
]]></content>
    
    
  </entry>
  
  <entry>
    <title><![CDATA[Screencast 31 - Rapid Fire]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/12/20/2013-12-20-screencast-31-rapid-fire/"/>
    <id>http://fuzzytolerance.info/blog/2013/12/20/2013-12-20-screencast-31-rapid-fire/</id>
    <published>2013-12-20T20:32:00.000Z</published>
    <updated>2014-06-01T00:53:34.000Z</updated>
    <content type="html"><![CDATA[<iframe width="853" height="480" src="//www.youtube.com/embed/fsTefq56-QM?rel=0" frameborder="0" allowfullscreen></iframe>

<a id="more"></a>

<img src="https://lh5.googleusercontent.com/-YMqtRnaJcjc/UrSqm321AHI/AAAAAAAAEEs/ld0-6QtGE2o/s800/lolcat1.jpg" class="left">

<h3 id="Resources">Resources</h3>
<ul>
<li>QOL Dashboard (<a href="http://tobinbradley.github.io/Mecklenburg-County-Quality-of-Life-Dashboard/" target="_blank">demo</a>, <a href="https://github.com/tobinbradley/Mecklenburg-County-Quality-of-Life-Dashboard" target="_blank">source</a>)</li>
</ul>
]]></content>
    <summary type="html"><![CDATA[<iframe width="853" height="480" src="//www.youtube.com/embed/fsTefq56-QM?rel=0" frameborder="0" allowfullscreen></iframe>

]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[GIS Day 2013]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/11/18/2013-11-18-gis-day-2013/"/>
    <id>http://fuzzytolerance.info/blog/2013/11/18/2013-11-18-gis-day-2013/</id>
    <published>2013-11-18T20:41:00.000Z</published>
    <updated>2013-11-18T20:51:36.000Z</updated>
    <content type="html"><![CDATA[<img src="https://lh5.googleusercontent.com/-e5BWH3T3weY/Uop76CtqhoI/AAAAAAAAEDE/BaUxZ_2lW5k/s800/newyearlolcat.jpg" class="center" width="560">

<p><a href="http://charmeck.org/mecklenburg/county/GeospatialInformationServices/gisday/Pages/default.aspx" target="_blank">GIS Day 2013</a> has arrived!</p>
<p>We’ll be flaunting our geowares on Wednesday, November 20th from 8:30-3:30 at Spirit Square, <a href="https://www.google.com/maps/preview#!q=345+N.+College+St.%2C+Charlotte+NC&amp;data=!4m15!2m14!1m13!1s0x8856a024cf97aa69%3A0x67f3dbe393791176!3m8!1m3!1d3504964!2d-79.860994!3d35.1705174!3m2!1i1477!2i806!4f13.1!4m2!3d35.2288041!4d-80.8391837" target="_blank">345 N. College St., Charlotte NC</a>. There will be fun for all ages and talks and demos going on most of the day. It’s open to the public, so if you’re in the area feel free to swing by any time. I’ll be there doing a few demos and hanging out most of the day. Look for the toe shoes and come say hi.</p>
]]></content>
    
    
  </entry>
  
  <entry>
    <title><![CDATA[Fun with map borders]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/11/18/2013-11-18-fun-with-map-borders/"/>
    <id>http://fuzzytolerance.info/blog/2013/11/18/2013-11-18-fun-with-map-borders/</id>
    <published>2013-11-18T19:27:00.000Z</published>
    <updated>2013-11-18T20:39:17.000Z</updated>
    <content type="html"><![CDATA[<p>Bear in mind, I make no suggestion one should do these things. This is probably a terrible idea. What can I tell you - trying this out was more fun than the other stuff on my todo list.</p>
<p>Sometimes the border around an interactive map on a web page feels a little harsh. If you’re making an infographic or something similar, you might want the map to appear to be “in” the page rather than “on” it, and that’s hard to do with a chopped-off tile ending in a x-pixel border. Maybe you want to fade the map into the background, or maybe do some sort of torn-page effect. Here’s how you could do that.</p>
<a id="more"></a>

<p>First you’ll need a <figure class="highlight div```"><figcaption><span>to sit on top of your map (our map in this case is going in ```#map```). It's also helpful to put that map into a map container so you can use it to set the size of the map and the overlay. So something like this:</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> html</p>
<p><div class="map-container"><br>    <div id="map"></div><br>    <div class="map-blend"></div><br></div></p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>
<span class="keyword">Then</span> we<span class="attribute">'ll</span> set up the <span class="keyword">map</span> sizing <span class="keyword">and</span> position the overlay <span class="keyword">in</span> CSS.
</pre></td></tr></table></figure> css<br>.map-container {<br>    height: 600px;<br>    width: 600px;<br>    position: relative;<br>}</p>
<h1 id="map_{">map {</h1>
<pre><code><span class="attribute">height</span>: <span class="string">100%;</span>
<span class="attribute">width</span>: <span class="string">100%;</span>
</code></pre><p>}<br>.map-blend {<br>    position: absolute;<br>    top: 0;<br>    bottom: 0;<br>    left: 0;<br>    right: 0;<br>}</p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>
So <span class="keyword">far</span> no surprises. Depending <span class="keyword">on</span> your mapping <span class="keyword">library</span> <span class="keyword">of</span> choice, you might need <span class="keyword">to</span> tack <span class="keyword">on</span> a
</pre></td></tr></table></figure>z-index<figure class="highlight value"><figcaption><span>to ```.map-blend``` to get it where you want. With Leaflet and no messing about with the ```z-index``` this seems to drop ```.map-blend``` over the layers but under the controls, which is kind of magical. Now you can drop an inset shadow to blend your map boundary into the background.</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> css substitute your page background color<br>.map-blend {<br>    box-shadow: inset 0 0 35px 35px #666;<br>    …</p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre>
Now your <span class="keyword">map</span> borders blend <span class="keyword">with</span> your page background.

&lt;iframe width=<span class="string">"100%"</span> height=<span class="string">"550"</span> src=<span class="string">"http://jsfiddle.net/94aB7/embedded/result,html,css,js/"</span> allowfullscreen=<span class="string">"allowfullscreen"</span> frameborder=<span class="string">"0"</span>&gt;&lt;/iframe&gt;

Now <span class="keyword">for</span> some fun. Let<span class="attribute">'s</span> make a torn-page kind <span class="keyword">of</span> effect. First we<span class="attribute">'ll</span> need <span class="keyword">to</span> make an image like that. I did it <span class="keyword">with</span> [Inkscape](http://inkscape.org/). <span class="keyword">To</span> paraphrase the <span class="keyword">process</span>:

+ Make a rectangle the size <span class="keyword">of</span> your <span class="keyword">map</span>.
+ Make a smaller rectangle.
+ Convert the smaller rectangle <span class="keyword">to</span> a path (Path-&gt;Object <span class="keyword">to</span> Path).
+ Go into node editing, <span class="keyword">select</span> <span class="keyword">all</span> the nodes, <span class="keyword">and</span> hit the Node + button a few times <span class="keyword">until</span> you have a fair amount.
+ Go <span class="keyword">to</span> Extensions-&gt;Modify Path-&gt;Jitter Nodes <span class="keyword">and</span> play <span class="keyword">with</span> the settings <span class="keyword">until</span> you get something you like. We<span class="attribute">'re</span> aiming <span class="keyword">for</span> random-ish.
+ Convert the first rectangle into a path, line up the second over the first, <span class="keyword">and</span> do a Path-&gt;Difference <span class="keyword">to</span> chop <span class="keyword">out</span> the middle.

Now set the image <span class="keyword">to</span> the background <span class="keyword">of</span> the overlay, using background-size <span class="keyword">to</span> make it fill the whole
</pre></td></tr></table></figure>div<figure class="highlight ."><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> css<br>.map-blend {<br>    background: url(../images/map-blend.png);<br>    background-size: 100% 100%;<br>    …<br>}</p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre>
Now we have <span class="operator">a</span> fun map border.

&lt;iframe width=<span class="string">"100%"</span> height=<span class="string">"550"</span> src=<span class="string">"http://jsfiddle.net/Y4tR6/1/embedded/result,html,css,js/"</span> allowfullscreen=<span class="string">"allowfullscreen"</span> frameborder=<span class="string">"0"</span>&gt;&lt;/iframe&gt;

Now <span class="keyword">for</span> <span class="operator">the</span> problems.

First, <span class="keyword">if</span> you followed <span class="operator">the</span> directions exactly you<span class="string">'ll quickly notice that the map won'</span>t react <span class="built_in">to</span> <span class="operator">the</span> mouse. That<span class="string">'s because we stuck a</span>
</pre></td></tr></table></figure>div<figure class="highlight in"><figcaption><span>the way. No worries - by setting ```pointer-events``` to ```none``` on the overlay mouse events will pass through it.</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> css<br>.map-blend {<br>    pointer-events: none;<br>    …<br>}</p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>
But our good friend Internet Explorer <span class="keyword">is</span> <span class="keyword">about</span> <span class="keyword">to</span> screw us. Only IE11 supports pointer-events <span class="function_start"><span class="keyword">on</span></span> HTML elements. Furthermore, <span class="keyword">it</span> will kind <span class="keyword">of</span> lie <span class="keyword">about</span> <span class="keyword">it</span>. If you <span class="keyword">try</span> <span class="keyword">to</span> test <span class="keyword">for</span> <span class="keyword">it</span> via
</pre></td></tr></table></figure>document.documentElement.style<figure class="highlight in"><figcaption><span>IE<11 it will reply "hells yeah I support that!", but in reality it only supports it for SVG elements. Best thing to do is head to [Modernizr](http://modernizr.com/) and build it with css-pointerevents (it's under non-core detects). That'll do a little unit test and you can toss out the div if it isn't supported.</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> javascript<br>if (!Modernizr.pointerevents) {<br>    $(‘.map-blend’).remove();<br>    // If you’re a JS purist/snob:<br>    //var eList = document.getElementsByClassName(‘map-blend’);<br>    //while(eList[0]) {<br>    //    eList[0].parentNode.removeChild(eList[0]);<br>    //}<br>}<br>```</p>
]]></content>
    <summary type="html"><![CDATA[<p>Bear in mind, I make no suggestion one should do these things. This is probably a terrible idea. What can I tell you - trying this out was more fun than the other stuff on my todo list.</p>
<p>Sometimes the border around an interactive map on a web page feels a little harsh. If you’re making an infographic or something similar, you might want the map to appear to be “in” the page rather than “on” it, and that’s hard to do with a chopped-off tile ending in a x-pixel border. Maybe you want to fade the map into the background, or maybe do some sort of torn-page effect. Here’s how you could do that.</p>
]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Screencast 30: Fun with D3]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/11/04/2013-11-04-screencast-30-fun-with-d3/"/>
    <id>http://fuzzytolerance.info/blog/2013/11/04/2013-11-04-screencast-30-fun-with-d3/</id>
    <published>2013-11-04T22:45:00.000Z</published>
    <updated>2013-11-04T22:48:48.000Z</updated>
    <content type="html"><![CDATA[<iframe width="853" height="480" src="//www.youtube.com/embed/uz88h04adiQ?rel=0" frameborder="0" allowfullscreen></iframe>

<a id="more"></a>

<h3 id="Resources">Resources</h3>
<ul>
<li><a href="https://github.com/tobinbradley/Mecklenburg-County-Quality-of-Life-Dashboard/tree/dev" target="_blank">Github (dev branch)</a></li>
<li><a href="http://d3js.org/" target="_blank">D3</a></li>
<li><a href="http://sztanko.github.io/crosslet/" target="_blank">Crosslet</a></li>
</ul>
]]></content>
    <summary type="html"><![CDATA[<iframe width="853" height="480" src="//www.youtube.com/embed/uz88h04adiQ?rel=0" frameborder="0" allowfullscreen></iframe>

]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Open Mapping Site and Upcoming Hackathon]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/10/18/2013-10-18-open-mapping-site-and-upcoming-hackathon/"/>
    <id>http://fuzzytolerance.info/blog/2013/10/18/2013-10-18-open-mapping-site-and-upcoming-hackathon/</id>
    <published>2013-10-18T17:23:00.000Z</published>
    <updated>2014-06-01T00:52:48.000Z</updated>
    <content type="html"><![CDATA[<p>Mecklenburg County has <a href="http://maps.co.mecklenburg.nc.us/openmapping/" target="_blank">opened the people’s GIS data</a>. As in <em>really open</em> (<a href="http://en.wikipedia.org/wiki/MIT_License" target="_blank">MIT</a>). As in <em>all of it</em>.</p>
<figure><br>  <img src="https://lh3.googleusercontent.com/-WvAKaqUTYHM/UmFze3gC9_I/AAAAAAAADyk/5bVIoJhm7pk/w1124-h603-no/openmapping.png"><br>  <figcaption>A day will come when I do something that doesn’t scream Bootstrap. But not this day!</figcaption><br></figure>

<a id="more"></a>

<p>One of several unfortunate parts of North Carolina’s public records law is that it allows government to charge for GIS data, and many local governments do, including us up until a few weeks ago. I’ve heard a number of arguments for this practice, with the common thread between them being they’re all terrible. I’ve been hammering away at this for years, opening up some data here and there as I could. I managed to get it to a <em>that’s something we should look at down the road</em> state, which in government is marginally better than <em>no</em>. But the County decided to change payment processors and the vendor of our online sales app wanted ~$10k to migrate it. At our profit levels that would have taken approximately infinity years to recover that cost. Hence we went from <em>down the road</em> to <em>do it before the old site breaks in October</em> in short order. Victory? I don’t know, but I’ll take it.</p>
<p>And here’s the next cool thing - we’re having a <a href="http://charmeck.org/mecklenburg/county/GeospatialInformationServices/gisday/Pages/Hackathon.aspx" target="_blank">Hackathon</a>! Sorta!</p>
<p>The sorta part is that it isn’t really a thon, just a hacka. You don’t have to camp in a room for 24-48 hours alternately pounding a keyboard and Red Bull. Just <a href="http://charmeck.org/mecklenburg/county/GeospatialInformationServices/gisday/Pages/Hackathon-2013-Registration-Form.aspx" target="_blank">sign up</a> by the end of October and be done by November 15th. Teams from 1 to 5 members can participate. You’ll have a chance to display your wares to an audience and a panel of judges at <a href="http://charmeck.org/mecklenburg/county/GeospatialInformationServices/gisday/Pages/default.aspx" target="_blank">GIS Day</a> in Charlotte on November 20th. There are prizes and the winning apps will be prominently displayed on as many web sites as I can prominently display them on (the Open Mapping page for one). If you are a student, that would look really good on a resume. Hint.</p>
<p>If you are in the area, enter our <a href="http://charmeck.org/mecklenburg/county/GeospatialInformationServices/gisday/Pages/Hackathon.aspx" target="_blank">Hackathon</a>, be awesome, win prizes and get recognized!</p>
]]></content>
    <summary type="html"><![CDATA[<p>Mecklenburg County has <a href="http://maps.co.mecklenburg.nc.us/openmapping/" target="_blank">opened the people’s GIS data</a>. As in <em>really open</em> (<a href="http://en.wikipedia.org/wiki/MIT_License" target="_blank">MIT</a>). As in <em>all of it</em>.</p>
<figure><br>  <img src="https://lh3.googleusercontent.com/-WvAKaqUTYHM/UmFze3gC9_I/AAAAAAAADyk/5bVIoJhm7pk/w1124-h603-no/openmapping.png"><br>  <figcaption>A day will come when I do something that doesn’t scream Bootstrap. But not this day!</figcaption><br></figure>

]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Node.js for general scripting]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/09/30/2013-09-30-nodejs-for-general-scripting/"/>
    <id>http://fuzzytolerance.info/blog/2013/09/30/2013-09-30-nodejs-for-general-scripting/</id>
    <published>2013-09-30T13:22:00.000Z</published>
    <updated>2013-09-30T14:04:33.000Z</updated>
    <content type="html"><![CDATA[<p>I have been fiddling around with <a href="http://nodejs.org/" target="_blank">Node</a> lately, and I really, really like it. A lot. It is intended for <em>building fast, scalable network applications</em> and <em>data-intensive real-time applications that run across distributed devices</em>, but it is also very handy for one-off scripting.</p>
<a id="more"></a>

<p>I have a ~2,000 line JSON file for some variable meta that looks like this x 82:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="code"><pre><span class="string">"p1"</span>: {
    <span class="string">"db"</span>: <span class="string">"population"</span>,
    <span class="string">"field"</span>: <span class="string">"p1"</span>,
    <span class="string">"category"</span>: <span class="string">"character"</span>,
    <span class="string">"title"</span>: <span class="string">"Population"</span>,
    <span class="string">"description"</span>: <span class="string">"Total Neighborhood Profile Area (NPA) population."</span>,
    <span class="string">"importance"</span>: <span class="string">"Total population is a baseline measure that indicates the number of people living in an NPA.   It is used to calculate density, and other per capita data.Trends in population show where the community is growing and where it is decreasing, which can help the City, County and Towns as well as other services providers know where infrastructure and other services may be needed.The 2010 Census reported 308.7 million people in the United States, a 9.7 percent increase from the Census 2000 population of 281.4 million."</span>,
    <span class="string">"tech_notes"</span>: <span class="string">"Provides the population based on the 2010 Census for each NPA using Block Group geography. Census block groups are the second smallest unit of measure used in the decennial Census. Only the census block is smaller. A block group is a cluster of census blocks within the same census tract. The average block group contains 39 blocks comprising between 600 and 3,000 people, with an optimum size of 1,500 people."</span>,
    <span class="string">"source"</span>: <span class="string">"U.S. Census, 2010"</span>,
    <span class="string">"links"</span>: <span class="string">"&lt;a href='http://www.census.gov/2010census/data/'&gt;2010 U.S. Census&lt;/a&gt;&lt;br&gt;&lt;a href='http://charmeck.org/city/charlotte/growthstrategy/Pages/default.aspx'&gt;Charlotte-Mecklenburg's growth strategy&lt;/a&gt;"</span>,
    <span class="string">"style"</span>: {
        <span class="string">"breaks"</span>: [
            <span class="number">0</span>,
            <span class="number">1500</span>,
            <span class="number">2500</span>
        ],
        <span class="string">"colors"</span>: [
            <span class="string">"#D8F2ED"</span>,
            <span class="string">"#2ca79e"</span>,
            <span class="string">"#154F4A"</span>
        ]
    }
}
</pre></td></tr></table></figure>

<p>This seemed like a powerful-smart idea at the time. How often do you need to edit that kind of stuff?</p>
<p>If you’re on the Quality of Life project, it turns out the answer is every day for a year. And the people doing the editing aren’t developers. Suddenly putting the meta in JSON is a <em>terrible</em> idea. I didn’t have time to fix it back then, but for the next go-round most of this stuff is getting crammed into 82 markdown files. Now a little one-off script to do that is in order.</p>
<p>Normally I reach for Python for this kind of stuff, and that’s fine - <figure class="highlight json.loads(meta.json)```"><figcaption><span>and you have a Python data structure you can iterate through. But for funsies I thought I'd give it a go in Node instead.</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> javascript<br>var     start = new Date().getTime(),<br>        _ = require(‘./underscore-min.js’),<br>        meta = require(‘./metrics.json’),<br>        toMarkdown = require(‘./to-markdown.js’).toMarkdown,<br>        out = “”,<br>        fs = require(‘fs’);</p>
<p><em>.each(meta, function(item, i) {<br>        out = “## “ + item.title + “\n”;<br>        out += item.description + “\n\n”;<br>        out += “### Why is this important?\n”;<br>        out += item.importance + “\n\n”;<br>        out += “### About the Data\n”;<br>        out += item.tech_notes + “\n\n”;<br>        out += “_Source: “ + item.source + “</em>\n\n”;<br>        out += “### Additional Resources\n”;<br>        _.each(item.links.split(“<br>“), function(item){<br>            out += “+ “ + toMarkdown(item) + “\n”;<br>        });<br>        fs.writeFile(“./meta/“ + item.field + “.md”, out, function(err) {<br>            if(err) {<br>                console.log(err);<br>            }<br>        });<br>});</p>
<p>console.log(“done in “ + Math.abs(start - new Date().getTime()) / 1000 + “ seconds”);</p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
3
4
</pre></td><td class="code"><pre>
I know, I didn't need to use [<span class="link_label">Underscore</span>](<span class="link_url">http://underscorejs.org/</span>) just for this, plain old JavaScript would have been fine. Shut up.

One of the great things about Node is you can usually use the same libraries you use in client-side browser scripting. Aside from Underscore (shut up) I'm grabbing a [<span class="link_label">nifty HTML to Markdown converter</span>](<span class="link_url">https://github.com/domchristie/to-markdown</span>) for the links. I iterate through the JSON, build a markdown string, and write it to a file named after the variable. It outputs files like this:
</pre></td></tr></table></figure> plain p1.markdown</p>
<h2 id="Population">Population</h2>
<p>Total Neighborhood Profile Area (NPA) population.</p>
<h3 id="Why_is_this_important?">Why is this important?</h3>
<p>Total population is a baseline measure that indicates the number of people living in an NPA.   It is used to calculate density, and other per capita data.Trends in population show where the community is growing and where it is decreasing, which can help the City, County and Towns as well as other services providers know where infrastructure and other services may be needed.The 2010 Census reported 308.7 million people in the United States, a 9.7 percent increase from the Census 2000 population of 281.4 million.</p>
<h3 id="About_the_Data">About the Data</h3>
<p>Provides the population based on the 2010 Census for each NPA using Block Group geography. Census block groups are the second smallest unit of measure used in the decennial Census. Only the census block is smaller. A block group is a cluster of census blocks within the same census tract. The average block group contains 39 blocks comprising between 600 and 3,000 people, with an optimum size of 1,500 people.</p>
<p><em>Source: U.S. Census, 2010</em></p>
<h3 id="Additional_Resources">Additional Resources</h3>
<ul>
<li><a href="http://www.census.gov/2010census/data/" target="_blank">2010 U.S. Census</a></li>
<li><a href="http://charmeck.org/city/charlotte/growthstrategy/Pages/default.aspx" target="_blank">Charlotte-Mecklenburg’s growth strategy</a><br>```</li>
</ul>
<p>That I can give to a non-programmer to edit. And they will. Every day. For a year.</p>
<p>Node reads ~2,000 lines of JSON, loops 164 times (82 times + an internal loop in each), and outputs 82 markdown files, in ~0.05 seconds. Yes, that’s <em>5 hundredths of a second</em>. I wasn’t exactly in a hurry for this, but <em>daaaaaaaaaammmn</em>.</p>
<p>For general automation/ETL type stuff and one-off scripts like this, I’m adding Node to my arsenal.</p>
]]></content>
    <summary type="html"><![CDATA[<p>I have been fiddling around with <a href="http://nodejs.org/" target="_blank">Node</a> lately, and I really, really like it. A lot. It is intended for <em>building fast, scalable network applications</em> and <em>data-intensive real-time applications that run across distributed devices</em>, but it is also very handy for one-off scripting.</p>
]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Simple Header and Footer Parallax Effect]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/09/20/2013-09-20-simple-header-and-footer-parallax-effect/"/>
    <id>http://fuzzytolerance.info/blog/2013/09/20/2013-09-20-simple-header-and-footer-parallax-effect/</id>
    <published>2013-09-20T15:42:00.000Z</published>
    <updated>2013-09-20T16:23:24.000Z</updated>
    <content type="html"><![CDATA[<p>I have been working on our <a href="http://maps.co.mecklenburg.nc.us/openmapping/" target="_blank">Open Mapping</a> site for our open data and open source apps, and I wanted to do a fancy-pants parallax effect on the front page so the content eats the header and reveals the footer as you scroll. This turned out to be some straight-forward CSS and didn’t involve any JavaScript scrolling action.</p>
<a id="more"></a>

<p>Let’s take your basic header-&gt;content-&gt;footer structure inside of a wrapper.</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre><span class="tag">&lt;<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"wrapper-parallax"</span>&gt;</span>
    <span class="tag">&lt;<span class="title">header</span>&gt;</span>
        <span class="tag">&lt;<span class="title">h1</span>&gt;</span>Header<span class="tag">&lt;/<span class="title">h1</span>&gt;</span>
    <span class="tag">&lt;/<span class="title">header</span>&gt;</span>

    <span class="tag">&lt;<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"content"</span>&gt;</span>
        <span class="tag">&lt;<span class="title">h1</span>&gt;</span>Content<span class="tag">&lt;/<span class="title">h1</span>&gt;</span>
    <span class="tag">&lt;/<span class="title">div</span>&gt;</span>

    <span class="tag">&lt;<span class="title">footer</span>&gt;</span>
        <span class="tag">&lt;<span class="title">h1</span>&gt;</span>Footer<span class="tag">&lt;/<span class="title">h1</span>&gt;</span>
    <span class="tag">&lt;/<span class="title">footer</span>&gt;</span>
<span class="tag">&lt;/<span class="title">div</span>&gt;</span>
</pre></td></tr></table></figure>

<p>The first thing you’ll need to do is set the position of the header and footer fixed to the top and bottom respectively with a <figure class="highlight z-index```"><figcaption><span>of -1 to push it below the regular page content. And although I'm sure you are already CSS-normalizing, for the example we will specifically toss the body margin.</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> css<br>body {<br>    margin: 0;<br>}<br>header {<br>    position: fixed;<br>    top: 0;<br>    width: 100%;<br>    z-index: -1;<br>    height: 100px;<br>    background: cyan;<br>}<br>footer {<br>    width: 100%;<br>    position: fixed;<br>    z-index: -1;<br>    bottom: 0;<br>    background: green;<br>    height: 60px;<br>}</p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>
Next we'll give <span class="keyword">the</span> content a minimum height <span class="keyword">to</span> avoid having <span class="keyword">the</span> footer lost <span class="keyword">in</span> <span class="constant">space</span> <span class="keyword">in</span> our example, a solid background color so <span class="keyword">it</span> can eat stuff, <span class="keyword">and</span> a
</pre></td></tr></table></figure>z-index<figure class="highlight of"><figcaption><span>1 so it'll sit on top.</span></figcaption><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>

</pre></td></tr></table></figure> css<br>.content {<br>    position: relative;<br>    z-index: 1;<br>    border-top: 1px solid black;<br>    border-bottom: 1px solid black;<br>    background: white;<br>    min-height: 500px;<br>}</p>
<p><figure class="highlight"><table><tr><td class="gutter"><pre>1
2
</pre></td><td class="code"><pre>
Now we <span class="built_in">set</span> <span class="operator">the</span> wrapper margin off <span class="operator">the</span> top <span class="operator">and</span> bottom <span class="operator">the</span> same size <span class="keyword">as</span> <span class="operator">the</span> header <span class="operator">and</span> footer.
</pre></td></tr></table></figure> css<br>.wrapper-parallax {<br>    margin-top: 100px;<br>    margin-bottom: 60px;<br>}<br>```</p>
<p>And that’s it - simple header and footer parallax effect without JavaScript.</p>
<iframe width="100%" height="400" src="http://jsfiddle.net/KsdeX/1/embedded/result,html,css" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
]]></content>
    <summary type="html"><![CDATA[<p>I have been working on our <a href="http://maps.co.mecklenburg.nc.us/openmapping/" target="_blank">Open Mapping</a> site for our open data and open source apps, and I wanted to do a fancy-pants parallax effect on the front page so the content eats the header and reveals the footer as you scroll. This turned out to be some straight-forward CSS and didn’t involve any JavaScript scrolling action.</p>
]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[GIS people need to get out more]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/09/09/2013-09-09-gis-people-need-to-get-out-more/"/>
    <id>http://fuzzytolerance.info/blog/2013/09/09/2013-09-09-gis-people-need-to-get-out-more/</id>
    <published>2013-09-09T18:14:00.000Z</published>
    <updated>2014-05-30T20:42:29.000Z</updated>
    <content type="html"><![CDATA[<blockquote>

<p>Always remember, you’re special. Just like everybody else.</p>
</blockquote>


<p>We GIS professionals are an insular group. That isn’t entirely our fault. When a lot of us started we were the only people in the organization working on Unix workstations. We programmed in bizarre languages like AML and Avenue. We had an enormous rack of books we referenced to make a map, and we thought that was OK. Spatial wasn’t special, but it was <em>other</em>, because it sucked really bad.</p>
<p>But it is <em>mostly</em> our fault. We don’t use weird hardware or odd programming languages any more, and if I’m presented with a solution that involves a bookshelf I immediately start looking for a different solution. But that sense of <em>other</em> still exists in the GIS community. Not only that, we tend to revel in it, like a dog rolling around on a dead thing. We have even invented certifications to separate ourselves from others via placard. When we were pounding away on Sun workstations and were generally a wart on IT’s ass, when we really were <em>other</em>, nobody saw a need for GIS certifications. Now that we are no longer so different, certs are a big deal (or at least a vocal minority wants them to be).</p>
<a id="more"></a>

<p>This kind of insular thinking in GIS is starting to harm our profession. When GIS people say “spatial is special”, they are picturing something like this:</p>
 <img src="https://lh3.googleusercontent.com/-a-pxln6ueSs/Ui4hapAqA_I/AAAAAAAADyE/AfysjXQt4mQ/w449-h338-no/superheroes.jpg">

<p>The reality is more like this:</p>
 <img src="https://lh3.googleusercontent.com/-emMeWjKhphc/Ui4haqpGE9I/AAAAAAAADx8/6tyBMPo0MiU/w449-h330-no/banjo.jpg">

<p>Which brings me to my point: <strong>My GIS brothers and sisters, we need to get out more</strong>.</p>
 <figure class="right"><br>  <img src="https://lh6.googleusercontent.com/-HVkq-og83a8/Ui4hajGrPMI/AAAAAAAADyI/tQQEVNAdB3w/w348-h350-no/dogfooding.jpg"  width="400" height="300"><br>  <figcaption>Dogfooding</figcaption><br></figure>

<p>Last week I attended <a href="http://www.blendconf.com/" target="_blank">Blendconf</a>, “A generalist conference featuring 50 diverse speakers sharing ideas and conversations about user experience, design, and web development”. Three days filled with designers, UI/UX professionals, and developers. I didn’t meet a GIS professional or see a single map. And never, ever, have I learned so much in such a short period of time.</p>
<p>The Blendconf organizers get a lot of credit for that - it was a superbly organized and run conference - but the bigger benefit for me was listening and talking to so many people that <em>are not GIS professionals</em> and discovering (a) they are not so different and (b) they have a lot of ideas that can directly impact the challenges GIS professions tackle every day. It’s pretty rare that I attend a GIS conference and see things that blow my mind (FOSS4G being a notable exception), because I live in that world. It has been a couple of days since Blendconf and my mind is so blown I’m still trying to gather the pieces and process it all.</p>
<p>Training budgets are tight, and it might be hard to sell a non-GIS conference to your boss, but make the effort. If you are a cartographer, guess what: you’re really a designer. Learn about design. If you make web sites with maps on them, guess what: you just make websites. Go learn about UI/UX. If you are a GIS programmer, guess what: no such thing. You’re a programmer. Go to programmer meetups in your area and hone your craft. Spatial isn’t special, and that is a great thing.</p>
<p>Get out more, my GIS brothers and sisters. You’ll be better GIS professionals for it.</p>
]]></content>
    <summary type="html"><![CDATA[<blockquote>

<p>Always remember, you’re special. Just like everybody else.</p>
</blockquote>


<p>We GIS professionals are an insular group. That isn’t entirely our fault. When a lot of us started we were the only people in the organization working on Unix workstations. We programmed in bizarre languages like AML and Avenue. We had an enormous rack of books we referenced to make a map, and we thought that was OK. Spatial wasn’t special, but it was <em>other</em>, because it sucked really bad.</p>
<p>But it is <em>mostly</em> our fault. We don’t use weird hardware or odd programming languages any more, and if I’m presented with a solution that involves a bookshelf I immediately start looking for a different solution. But that sense of <em>other</em> still exists in the GIS community. Not only that, we tend to revel in it, like a dog rolling around on a dead thing. We have even invented certifications to separate ourselves from others via placard. When we were pounding away on Sun workstations and were generally a wart on IT’s ass, when we really were <em>other</em>, nobody saw a need for GIS certifications. Now that we are no longer so different, certs are a big deal (or at least a vocal minority wants them to be).</p>
]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Screencast 29: Bones]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/08/30/2013-08-30-screencast-29-bones/"/>
    <id>http://fuzzytolerance.info/blog/2013/08/30/2013-08-30-screencast-29-bones/</id>
    <published>2013-08-30T15:57:00.000Z</published>
    <updated>2014-04-24T00:37:04.000Z</updated>
    <content type="html"><![CDATA[<iframe width="640" height="480" src="//www.youtube.com/embed/fj37zhpPyBo?rel=0" frameborder="0" allowfullscreen></iframe>

<a id="more"></a>

<figure class="highlight"><figcaption><span>Powershell Script - bones.ps1</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
</pre></td><td class="code"><pre>
&lt;<span class="comment">#</span>
.SYNOPSIS
    Simple HTML report generator <span class="keyword">for</span> server status, free RAM, and Disk capacity.
    The report info is injected into a web page. If anything is <span class="keyword">in</span> the red it<span class="string">'ll yell
    at you via email.
.NOTES
    Original script from Martin Pugh (@TheSurlyAdm1n), greatly hacked/rewritten/made worse
    by Tobin Bradley (@fuzzytolerance).
#&gt;

[CmdletBinding()]
Param (
    [Parameter(ValueFromPipeline=$true,
        ValueFromPipelinebyPropertyName=$true)]
    [Alias("Servers")]
    [string[]]$Name = (Get-Content ".\servers.txt"),
    [int]$DriveAlertThreshold = 10,
    [int]$RAMAlertThreshold = 512,
    [string]$Credential = "domain\username",
    [string]$PathToCred = ".\",
    [string]$smtpServer = "servername",
    [string]$publishPath = "\\where\to\put\it\index.html",
    [string]$toMail = "hottub@fun.com"
)

Begin {
    $path = split-path -parent $MyInvocation.MyCommand.Definition
    Set-Location -Path $Path

    Function Get-Credentials {
        Param (
            [String]$AuthUser = $env:USERNAME
        )
        $Key = [byte]29,36,18,74,72,75,85,52,73,44,0,21,98,76,99,27

        #Build the path to the credential file
        $CredFile = $AuthUser.Replace("\","~")
        $File = $PathToCred + "\Credentials-$CredFile.crd"
        #And find out if it'</span>s there, <span class="keyword">if</span> not create it
        If (-not (Test-Path <span class="variable">$File</span>)) {
            (Get-Credential <span class="variable">$AuthUser</span>).Password | ConvertFrom-SecureString -Key <span class="variable">$Key</span> | Set-Content <span class="variable">$File</span>
        }
        <span class="comment">#Load the credential file</span>
        <span class="variable">$Password</span> = Get-Content <span class="variable">$File</span> | ConvertTo-SecureString -Key <span class="variable">$Key</span>
        <span class="variable">$AuthUser</span> = (Split-Path <span class="variable">$File</span> -Leaf).Substring(<span class="number">12</span>).Replace(<span class="string">"~"</span>,<span class="string">"\")
        <span class="variable">$AuthUser</span> = <span class="variable">$AuthUser</span>.Substring(0,<span class="variable">$AuthUser</span>.Length - 4)
        <span class="variable">$Credential</span> = New-Object System.Management.Automation.PsCredential(<span class="variable">$AuthUser</span>,<span class="variable">$Password</span>)
        Return <span class="variable">$Credential</span>
    }

    Function Send-Mail {
        param(<span class="variable">$To</span>, <span class="variable">$From</span>, <span class="variable">$Subject</span>, <span class="variable">$Body</span>, <span class="variable">$mailServer</span>)
        <span class="variable">$msg</span> = new-object Net.Mail.MailMessage
        <span class="variable">$smtp</span> = new-object Net.Mail.SmtpClient(<span class="variable">$mailServer</span>)
        <span class="variable">$msg</span>.From = <span class="variable">$From</span>
        <span class="variable">$msg</span>.To.Add(<span class="variable">$To</span>)
        <span class="variable">$msg</span>.Subject = <span class="variable">$Subject</span>
        <span class="variable">$msg</span>.IsBodyHtml = 1
        <span class="variable">$msg</span>.Body = <span class="variable">$Body</span>
        <span class="variable">$smtp</span>.Send(<span class="variable">$msg</span>)
    }

    Write-Verbose "</span>$(Get-Date): Script begins!<span class="string">"
    <span class="variable">$AllComputers</span> = @()
}

Process {
    #Gather all computer names before processing
    <span class="variable">$Name</span> = <span class="variable">$Name</span> | Sort
    ForEach (<span class="variable">$Computer</span> in <span class="variable">$Name</span>) {
        <span class="variable">$AllComputers</span> += <span class="variable">$Computer</span>
    }
}

End {
    #Sort the servers by name, then start getting information
    Write-Verbose "</span>Sort server names and gather Credential information<span class="string">"

    <span class="variable">$DiskData</span> = @()
    <span class="variable">$date</span> = Get-Date
    <span class="variable">$thedate</span> = "</span>&lt;p&gt;Last Ran <span class="string">" + <span class="variable">$date</span> + "</span>&lt;/p&gt;<span class="string">"
    <span class="variable">$errors</span> = <span class="variable">$null</span>

    If (<span class="variable">$Credential</span>) {
        <span class="variable">$Cred</span> = Get-Credentials <span class="variable">$Credential</span>
    }

    ForEach (<span class="variable">$Computer</span> in <span class="variable">$AllComputers</span>) {
        Write-Verbose "</span>Testing <span class="variable">$Computer</span>...<span class="string">"
        <span class="variable">$ErrorReport</span> = <span class="variable">$null</span>
        <span class="variable">$DriveReport</span> = <span class="variable">$null</span>
        If (Test-Connection <span class="variable">$Computer</span> -Quiet) {
            #Set parameters for splat, determine if checking local
            <span class="variable">$CredParameter</span> = @{
                ComputerName = <span class="variable">$Computer</span>
                ErrorAction = "</span>Stop<span class="string">"
            }
            If (<span class="variable">$Computer</span>.ToUpper() -notlike "</span>*$(<span class="variable">$env</span>:COMPUTERNAME.ToUpper())*<span class="string">" -and <span class="variable">$Cred</span>) {
                <span class="variable">$CredParameter</span>.Add("</span>Credential<span class="string">",<span class="variable">$Cred</span>)
            }

            #Get uptime and memory information
            Try {
                <span class="variable">$WMI</span> = Get-WmiObject Win32_OperatingSystem @CredParameter
                If (<span class="variable">$WMI</span>) {
                    <span class="variable">$Uptime</span> = New-TimeSpan -Start <span class="variable">$($WMI.ConvertToDateTime($WMI.LastBootUpTime)</span>) -End (Get-Date)
                    <span class="variable">$UpText</span> = "</span>&lt;td&gt;Up <span class="keyword">for</span> $(<span class="variable">$Uptime</span>.Days)d, $(<span class="variable">$Uptime</span>.Hours)h, $(<span class="variable">$Uptime</span>.Minutes)m&lt;/td&gt;<span class="string">"
                }
                Else {
                    <span class="variable">$UpText</span> = "</span>&lt;td class=<span class="string">""</span>up<span class="string">""</span>&gt;Up&lt;/td&gt;<span class="string">"
                }
                #Get disk information and pretty up the data
                <span class="variable">$Disks</span> = Get-WmiObject Win32_LogicalDisk -Filter "</span>DriveType=<span class="number">3</span><span class="string">" @CredParameter | Select `
                    @{LABEL="</span>Server<span class="string">";EXPRESSION={<span class="variable">$Computer</span>}},
                    @{LABEL="</span>DriveLetter<span class="string">";EXPRESSION={<span class="variable">$_</span>.DeviceID}},
                    @{LABEL="</span>Size<span class="string">";EXPRESSION={[int]("</span>{<span class="number">0</span>:N0}<span class="string">" -f (<span class="variable">$_</span>.Size/1gb))}},
                    @{LABEL="</span>FreeSize<span class="string">";EXPRESSION={[int]("</span>{<span class="number">0</span>:N0}<span class="string">" -f (<span class="variable">$_</span>.FreeSpace/1gb))}},
                    @{LABEL="</span>perUsed<span class="string">";EXPRESSION={[int]("</span>{<span class="number">0</span>:N0}<span class="string">" -f (((<span class="variable">$_</span>.Size - <span class="variable">$_</span>.FreeSpace)/<span class="variable">$_</span>.Size)*100))}},
                    @{LABEL="</span>perFree<span class="string">";EXPRESSION={[int]("</span>{<span class="number">0</span>:N0}<span class="string">" -f (100-((<span class="variable">$_</span>.Size - <span class="variable">$_</span>.FreeSpace)/<span class="variable">$_</span>.Size)*100))}},
                    VolumeName
                <span class="variable">$DiskData</span> += <span class="variable">$Disks</span>

                # check the ram
                <span class="variable">$theram</span> = ([math]::round(<span class="variable">$WMI</span>.FreePhysicalMemory / 1024))
                if (<span class="variable">$theram</span> -ge <span class="variable">$RAMAlertThreshold</span>) {
                    <span class="variable">$FreeRAM</span> = "</span>&lt;td class=<span class="string">""</span>text-right<span class="string">""</span>&gt;<span class="variable">$theram</span> MB&lt;/td&gt;<span class="string">"
                }
                else {
                    <span class="variable">$FreeRAM</span> = "</span>&lt;td class=<span class="string">""</span>down text-right<span class="string">""</span>&gt;<span class="variable">$theram</span> MB&lt;/td&gt;<span class="string">"
                    <span class="variable">$errors</span> += "</span>Low free RAM on <span class="variable">$Computer</span>.&lt;br&gt;<span class="string">"
                }
            }
            Catch {
                Write-Verbose "</span>Error encountered gathering information <span class="keyword">for</span> <span class="variable">$Computer</span><span class="string">"
                <span class="variable">$ErrorReport</span> = <span class="variable">$Error</span>[0]
                <span class="variable">$Error</span>.Clear | Out-Null
            }

            #Create the simple Status table
            If (<span class="variable">$ErrorReport</span>) {
                <span class="variable">$UpText</span> = "</span>&lt;td class=<span class="string">""</span>down<span class="string">""</span>&gt;WMI Error&lt;/td&gt;<span class="string">"
                <span class="variable">$DiskHTML</span> = "</span>&lt;div class=<span class="string">""</span>red<span class="string">""</span>&gt;$(<span class="variable">$Error</span>[<span class="number">0</span>])&lt;/div&gt;<span class="string">"
            }
            ElseIf (<span class="variable">$Disks</span>) {
                <span class="variable">$DiskHTML</span> = <span class="variable">$null</span>
                ForEach (<span class="variable">$Disk</span> in <span class="variable">$Disks</span>) {
                    If (<span class="variable">$Disk</span>.perFree -le <span class="variable">$DriveAlertThreshold</span>) {
                        <span class="variable">$FreeClass</span> = "</span>red<span class="string">"
                        <span class="variable">$errors</span> += "</span><span class="variable">$Computer</span> has less than <span class="number">10</span>% free space on drive $(<span class="variable">$Disk</span>.DriveLetter) &lt;br&gt;<span class="string">"
                    }
                    Else {
                        <span class="variable">$FreeClass</span> = "</span>free<span class="string">"
                    }
                    <span class="variable">$DiskHTML</span> += "</span>&lt;div class=<span class="string">""</span>green<span class="string">""</span> style=<span class="string">""</span>width:$(<span class="variable">$Disk</span>.perUsed)%<span class="string">""</span>&gt;$(<span class="variable">$Disk</span>.DriveLetter) $(<span class="variable">$Disk</span>.Size)gb&lt;/div&gt;&lt;div class=<span class="string">""</span><span class="variable">$FreeClass</span><span class="string">""</span> style=<span class="string">""</span>width:$(<span class="variable">$Disk</span>.perFree)%<span class="string">""</span>&gt;$(<span class="variable">$Disk</span>.FreeSize)gb ($(<span class="variable">$Disk</span>.perFree)%)&lt;/div&gt;`n&lt;div style=<span class="string">""</span>clear:both<span class="string">""</span>&gt;&lt;/div&gt;<span class="string">"
                }
            }
            Else {
                <span class="variable">$DiskHTML</span> = "</span><span class="string">"
            }
            <span class="variable">$DetailHTML</span> += "</span>&lt;tr&gt;&lt;td&gt;<span class="variable">$Computer</span>&lt;/td&gt;<span class="variable">$UpText</span> <span class="variable">$FreeRAM</span>&lt;td&gt;<span class="variable">$DiskHTML</span>&lt;/td&gt;&lt;/tr&gt;`n<span class="string">"
        }
        Else {
            <span class="variable">$DetailHTML</span> += "</span>&lt;tr&gt;&lt;td&gt;<span class="variable">$Computer</span>&lt;/td&gt;&lt;td class=<span class="string">""</span>down<span class="string">""</span>&gt;DOWN&lt;/td&gt;&lt;td class=<span class="string">""</span>down<span class="string">""</span>&gt;&lt;/td&gt;&lt;td class=<span class="string">""</span>down<span class="string">""</span>&gt;&lt;/td&gt;&lt;/tr&gt;`n<span class="string">"
            <span class="variable">$errors</span> += "</span><span class="variable">$Computer</span> is down.&lt;br&gt;<span class="string">"
        }
    }

    # Insert Stuff into index.html
    (Get-Content .\index.html) -join "</span><span class="string">" | foreach{<span class="variable">$_</span> -replace "</span>&lt;!-- Server Report --&gt;(.*?)&lt;!-- End Server Report --&gt;<span class="string">","</span>&lt;!-- Server Report --&gt;<span class="variable">$DetailHTML</span>&lt;!-- End Server Report --&gt;<span class="string">"} | Set-Content ./index.html
    (Get-Content .\index.html) -join "</span><span class="string">" | foreach{<span class="variable">$_</span> -replace "</span>&lt;!-- Updated --&gt;(.*?)&lt;!-- End Updated --&gt;<span class="string">","</span>&lt;!-- Updated --&gt;<span class="variable">$date</span>&lt;!-- End Updated --&gt;<span class="string">"} | Set-Content ./index.html

    # Copy index.html to web server
    Copy-Item -Path .\index.html -Destination <span class="variable">$publishPath</span>

    #Send email if shit went bad
    if (<span class="variable">$errors</span>) {
        Send-Mail -To <span class="variable">$toMail</span> -From "</span>noreply@noreply.com<span class="string">"  -Subject "</span>He<span class="string">'s dead Jim!" -Body "&lt;h1&gt;Oh shit.&lt;/h1&gt; &lt;p&gt; $errors &lt;/p&gt; &lt;p&gt;Head to http://maps.co.mecklenburg.nc.us/bones for more information.&lt;/p&gt;" -mailServer $smtpServer
    }

    Write-Verbose "$(Get-Date): Script completed!"
}</span>
</pre></td></tr></table></figure>

<figure class="highlight"><figcaption><span>Sample Index.html</span></figcaption><table><tr><td class="gutter"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
</pre></td><td class="code"><pre><span class="doctype">&lt;!DOCTYPE html&gt;</span>
<span class="tag">&lt;<span class="title">html</span> <span class="attribute">lang</span>=<span class="value">"en"</span>&gt;</span>

<span class="tag">&lt;<span class="title">head</span>&gt;</span>
    <span class="tag">&lt;<span class="title">meta</span> <span class="attribute">charset</span>=<span class="value">"utf-8"</span>&gt;</span>
    <span class="tag">&lt;<span class="title">META</span> <span class="attribute">NAME</span>=<span class="value">"ROBOTS"</span> <span class="attribute">CONTENT</span>=<span class="value">"NOINDEX, NOFOLLOW"</span>&gt;</span>
    <span class="tag">&lt;<span class="title">title</span>&gt;</span>Page Title<span class="tag">&lt;/<span class="title">title</span>&gt;</span>
    <span class="tag">&lt;<span class="title">meta</span> <span class="attribute">name</span>=<span class="value">"viewport"</span> <span class="attribute">content</span>=<span class="value">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="comment">&lt;!-- Bootstrap --&gt;</span>
    <span class="tag">&lt;<span class="title">link</span> <span class="attribute">rel</span>=<span class="value">"stylesheet"</span> <span class="attribute">href</span>=<span class="value">"//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"</span>&gt;</span>
    <span class="tag">&lt;<span class="title">style</span>&gt;</span><span class="css">
        <span class="tag">img</span> <span class="rules">{
            <span class="rule"><span class="attribute">max-width</span>:<span class="value"> <span class="number">100</span>%</span></span>;
        <span class="rule">}</span></span>
        <span class="tag">header</span> <span class="rules">{
            <span class="rule"><span class="attribute">margin</span>:<span class="value"> <span class="number">75</span>px <span class="number">0</span> <span class="number">40</span>px</span></span>;
        <span class="rule">}</span></span>
        <span class="tag">footer</span> <span class="rules">{
            <span class="rule"><span class="attribute">background</span>:<span class="value"> <span class="hexcolor">#d42127</span></span></span>;
            <span class="rule"><span class="attribute">margin-top</span>:<span class="value"> <span class="number">20</span>px</span></span>;
            <span class="rule"><span class="attribute">box-shadow</span>:<span class="value"> <span class="number">0</span> -<span class="number">2</span>px <span class="number">10</span>px <span class="hexcolor">#666</span></span></span>;
        <span class="rule">}</span></span>
        <span class="tag">nav</span> <span class="rules">{
            <span class="rule"><span class="attribute">box-shadow</span>:<span class="value"> <span class="number">0</span> <span class="number">2</span>px <span class="number">10</span>px <span class="hexcolor">#666</span></span></span>;
        <span class="rule">}</span></span>
        <span class="tag">td</span>, <span class="tag">td</span><span class="class">.div</span> <span class="rules">{ <span class="rule"><span class="attribute">white-space</span>:<span class="value"> nowrap</span></span>; <span class="rule">}</span></span>
        <span class="tag">td</span><span class="class">.div</span> <span class="rules">{ <span class="rule"><span class="attribute">overflow</span>:<span class="value"> hidden</span></span>; <span class="rule">}</span></span>
        <span class="tag">td</span><span class="class">.up</span> <span class="rules">{ <span class="rule"><span class="attribute">background-color</span>:<span class="value"><span class="hexcolor">#32CD32</span></span></span>; <span class="rule">}</span></span>
        <span class="tag">td</span><span class="class">.down</span> <span class="rules">{ <span class="rule"><span class="attribute">background-color</span>:<span class="value"><span class="hexcolor">#B22222</span></span></span>; <span class="rule">}</span></span>
        <span class="tag">div</span><span class="class">.red</span> <span class="rules">{ <span class="rule"><span class="attribute">background-color</span>:<span class="value"><span class="hexcolor">#B22222</span></span></span>;
          <span class="rule"><span class="attribute">float</span>:<span class="value">left</span></span>;
          <span class="rule"><span class="attribute">text-align</span>:<span class="value">right</span></span>;
        <span class="rule">}</span></span>
        <span class="tag">div</span><span class="class">.green</span> <span class="rules">{
            <span class="rule"><span class="attribute">background-color</span>:<span class="value"><span class="hexcolor">#32CD32</span></span></span>;
            <span class="rule"><span class="attribute">float</span>:<span class="value">left</span></span>;
        <span class="rule">}</span></span>
        <span class="tag">div</span><span class="class">.free</span> <span class="rules">{
          <span class="rule"><span class="attribute">background-color</span>:<span class="value"><span class="hexcolor">#7FFF00</span></span></span>;
          <span class="rule"><span class="attribute">float</span>:<span class="value">left</span></span>;
          <span class="rule"><span class="attribute">text-align</span>:<span class="value">right</span></span>;
        <span class="rule">}</span></span>
        <span class="tag">span</span><span class="class">.green</span> <span class="rules">{
            <span class="rule"><span class="attribute">background</span>:<span class="value"> <span class="hexcolor">#32CD32</span></span></span>;
        <span class="rule">}</span></span>
        <span class="tag">span</span><span class="class">.free</span> <span class="rules">{
            <span class="rule"><span class="attribute">background</span>:<span class="value"> <span class="hexcolor">#7FFF00</span></span></span>;
        <span class="rule">}</span></span>
        <span class="class">.up</span>, <span class="class">.down</span>, <span class="class">.red</span>, <span class="class">.green</span>, <span class="class">.free</span> <span class="rules">{
            <span class="rule"><span class="attribute">padding</span>:<span class="value"> <span class="number">5</span>px</span></span>;
        <span class="rule">}</span></span>

    </span><span class="tag">&lt;/<span class="title">style</span>&gt;</span>
<span class="tag">&lt;/<span class="title">head</span>&gt;</span>

<span class="tag">&lt;<span class="title">body</span>&gt;</span>
    <span class="tag">&lt;<span class="title">div</span> <span class="attribute">class</span>=<span class="value">"container"</span>&gt;</span>
        <span class="tag">&lt;<span class="title">header</span> <span class="attribute">class</span>=<span class="value">"text-center"</span>&gt;</span>
            Updated <span class="comment">&lt;!-- Updated --&gt;</span><span class="comment">&lt;!-- End Updated --&gt;</span>
        <span class="tag">&lt;/<span class="title">header</span>&gt;</span>
        <span class="tag">&lt;<span class="title">div</span>&gt;</span>
            <span class="tag">&lt;<span class="title">h2</span>&gt;</span>Server Report<span class="tag">&lt;/<span class="title">h2</span>&gt;</span>
            <span class="tag">&lt;<span class="title">table</span> <span class="attribute">class</span>=<span class="value">"table"</span>&gt;</span>
                <span class="tag">&lt;<span class="title">thead</span>&gt;</span>
                    <span class="tag">&lt;<span class="title">tr</span>&gt;</span>
                        <span class="tag">&lt;<span class="title">th</span>&gt;</span>Server<span class="tag">&lt;/<span class="title">th</span>&gt;</span>
                        <span class="tag">&lt;<span class="title">th</span>&gt;</span>Status<span class="tag">&lt;/<span class="title">th</span>&gt;</span>
                        <span class="tag">&lt;<span class="title">th</span> <span class="attribute">class</span>=<span class="value">"text-right"</span>&gt;</span>Free Ram<span class="tag">&lt;/<span class="title">th</span>&gt;</span>
                        <span class="tag">&lt;<span class="title">th</span> <span class="attribute">style</span>=<span class="value">"width: 60%"</span>&gt;</span>Disk Status (
                            <span class="tag">&lt;<span class="title">span</span> <span class="attribute">class</span>=<span class="value">"green"</span>&gt;</span>Capacity<span class="tag">&lt;/<span class="title">span</span>&gt;</span>/
                            <span class="tag">&lt;<span class="title">span</span> <span class="attribute">class</span>=<span class="value">"free"</span>&gt;</span>Free<span class="tag">&lt;/<span class="title">span</span>&gt;</span>)<span class="tag">&lt;/<span class="title">th</span>&gt;</span>
                    <span class="tag">&lt;/<span class="title">tr</span>&gt;</span>
                <span class="tag">&lt;/<span class="title">thead</span>&gt;</span>
                <span class="tag">&lt;<span class="title">tbody</span>&gt;</span>
                    <span class="comment">&lt;!-- Server Report --&gt;</span><span class="comment">&lt;!-- End Server Report --&gt;</span>
                <span class="tag">&lt;/<span class="title">tbody</span>&gt;</span>
            <span class="tag">&lt;/<span class="title">table</span>&gt;</span>
        <span class="tag">&lt;/<span class="title">div</span>&gt;</span>
    <span class="tag">&lt;/<span class="title">div</span>&gt;</span>
<span class="tag">&lt;/<span class="title">body</span>&gt;</span>

<span class="tag">&lt;/<span class="title">html</span>&gt;</span>
</pre></td></tr></table></figure>

<h3 id="Resources">Resources</h3>
<ul>
<li><a href="http://community.spiceworks.com/scripts/show/1641-simple-server-status-report" target="_blank">Martin Pugh’s Original Script</a></li>
</ul>
]]></content>
    <summary type="html"><![CDATA[<iframe width="640" height="480" src="//www.youtube.com/embed/fj37zhpPyBo?rel=0" frameborder="0" allowfullscreen></iframe>

]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Simple Works]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/08/16/2013-08-16-simple-works/"/>
    <id>http://fuzzytolerance.info/blog/2013/08/16/2013-08-16-simple-works/</id>
    <published>2013-08-16T20:58:00.000Z</published>
    <updated>2014-05-30T20:42:22.000Z</updated>
    <content type="html"><![CDATA[<p>A short time ago (2013-07-09) I rolled out a new release of <a href="http://maps.co.mecklenburg.nc.us/" target="_blank">GeoPortal</a> with vastly improved UI/UX and aesthetics.<sup>1</sup></p>
<figure class="left"><br>  <img src="https://lh4.googleusercontent.com/-k9H8kEClSxA/Ug6X-P9S4ZI/AAAAAAAADxU/x1wLRGvpNEs/w1063-h660-no/oldgp.png"  width="400" height="300"><br>  <figcaption>Before</figcaption><br></figure><br><figure class="right"><br>  <img src="https://lh6.googleusercontent.com/-sGHRqjxY6zE/Ug6X8W7hT4I/AAAAAAAADxM/GVes8DB0ejs/w1062-h641-no/newgp.png"  width="400" height="300"><br>  <figcaption>After</figcaption><br></figure>

<div style="clear: both"></div>

<a id="more"></a>

<p>There was some grumbling from the locals (read: GIS people that like their websites to look like ArcMap), but I got positive feedback from non-GIS junkies. Which is all fine and good, but for public facing websites anecdotes are no substitute for analytics.</p>
<ul>
<li>Last full month with the old site: <strong>12,072 visitors</strong></li>
<li>Last 30 days with the new site: <strong>16,088 visitors</strong></li>
</ul>
<h1 style="text-align: center">+33% Visitors!</h1>

<p>All of the other stats you care about - page views (virtual ones via pushstate - it’s a SPA), return visitors, etc. - are all trending up. There was no advertising push other than a tweet or two on our corporate twitter account and a post on this blog, neither one of which is exactly making it rain, and those events occurred before the last 30 days of stats. I did some checking with different time periods for the old GeoPortal and the stats are honest. And the new version has only been out <em>~45 days!</em></p>
<p>Simple works.</p>
<p>In other news, I just updated it to Bootstrap 3RC2 and switched to typeahead.js for the autocomplete (mad props to @<a href="http://twitter.com/brymcbride" target="_blank">brymcbride</a> for some code I lifted for that). The source code is up on <a href="https://github.com/tobinbradley/Mecklenburg-County-GeoPortal" target="_blank">Github</a>.</p>
<p class="footnotes"><br><sup>1</sup> At least I think so. I don’t wear skinny jeans, carry a MacBook Air, or have a reserved seat at a local Starbucks, so take my UI advice with a grain of mocha. But I did read some shit.<br></p>
]]></content>
    <summary type="html"><![CDATA[<p>A short time ago (2013-07-09) I rolled out a new release of <a href="http://maps.co.mecklenburg.nc.us/" target="_blank">GeoPortal</a> with vastly improved UI/UX and aesthetics.<sup>1</sup></p>
<figure class="left"><br>  <img src="https://lh4.googleusercontent.com/-k9H8kEClSxA/Ug6X-P9S4ZI/AAAAAAAADxU/x1wLRGvpNEs/w1063-h660-no/oldgp.png"  width="400" height="300"><br>  <figcaption>Before</figcaption><br></figure><br><figure class="right"><br>  <img src="https://lh6.googleusercontent.com/-sGHRqjxY6zE/Ug6X8W7hT4I/AAAAAAAADxM/GVes8DB0ejs/w1062-h641-no/newgp.png"  width="400" height="300"><br>  <figcaption>After</figcaption><br></figure>

<div style="clear: both"></div>

]]></summary>
    
  </entry>
  
  <entry>
    <title><![CDATA[Modifying map tiles in the browser with CSS]]></title>
    <link href="http://fuzzytolerance.info/blog/2013/08/12/2013-08-12-modifying-map-tiles-in-the-browser-with-css/"/>
    <id>http://fuzzytolerance.info/blog/2013/08/12/2013-08-12-modifying-map-tiles-in-the-browser-with-css/</id>
    <published>2013-08-12T19:40:00.000Z</published>
    <updated>2014-04-24T00:37:04.000Z</updated>
    <content type="html"><![CDATA[<img src="https://lh6.googleusercontent.com/-D4MJhki4N5U/Ugk58B-lGZI/AAAAAAAADwc/7hgwvOBlN9E/w348-h168-no/snap3.png">


<p>Whoa. Browser support for this is crap right now (current Firefox and IE releases are out), but…whoa. This could be incredibly handy down the road.</p>
<a id="more"></a>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: none"><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">none</span>
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: hue-rotate(180deg) invert(100%) saturate(30%); filter: hue-rotate(180deg) invert(100%) saturate(30%); "><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">hue-rotate</span>(180<span class="tag">deg</span>) <span class="tag">invert</span>(100%) <span class="tag">saturate</span>(30%)
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: hue-rotate(50deg); "><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">hue-rotate</span>(50<span class="tag">deg</span>);
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: grayscale(100%); "><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">grayscale</span>(100%);
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: sepia(100%); "><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">sepia</span>(100%);
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: blur(2px);"><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">blur</span>(2<span class="tag">px</span>);
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: brightness(115%);  "><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">brightness</span>(115%);
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: invert(100%);    "><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">invert</span>(100%);
</pre></td></tr></table></figure>

<div class="textcenter"><br><img src="https://lh5.googleusercontent.com/-oPCe04RlOmM/Ugk7QolnjtI/AAAAAAAADw8/RaqXGy-qCjM/w566-h459-no/tile.png" style="-webkit-filter: saturate(3); "><br></div>

<figure class="highlight css"><table><tr><td class="gutter"><pre>1
</pre></td><td class="code"><pre><span class="tag">-webkit-filter</span>: <span class="tag">saturate</span>(3);
</pre></td></tr></table></figure>
]]></content>
    <summary type="html"><![CDATA[<img src="https://lh6.googleusercontent.com/-D4MJhki4N5U/Ugk58B-lGZI/AAAAAAAADwc/7hgwvOBlN9E/w348-h168-no/snap3.png">


<p>Whoa. Browser support for this is crap right now (current Firefox and IE releases are out), but…whoa. This could be incredibly handy down the road.</p>
]]></summary>
    
  </entry>
  
</feed>
