<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Vladimir Agafonkin on Medium]]></title>
        <description><![CDATA[Stories by Vladimir Agafonkin on Medium]]></description>
        <link>https://medium.com/@agafonkin?source=rss-bcc7cdd477fe------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*ja8REqzVDgkudRHw.jpg</url>
            <title>Stories by Vladimir Agafonkin on Medium</title>
            <link>https://medium.com/@agafonkin?source=rss-bcc7cdd477fe------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 07:29:11 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@agafonkin/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Visualizing street orientations on an interactive map]]></title>
            <link>https://medium.com/mapbox/visualizing-street-orientations-on-an-interactive-map-1eefa6002afc?source=rss-bcc7cdd477fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/1eefa6002afc</guid>
            <category><![CDATA[maps]]></category>
            <category><![CDATA[news]]></category>
            <category><![CDATA[city-planning]]></category>
            <category><![CDATA[data-visualization]]></category>
            <category><![CDATA[built-with-mapbox]]></category>
            <dc:creator><![CDATA[Vladimir Agafonkin]]></dc:creator>
            <pubDate>Tue, 17 Jul 2018 12:53:47 GMT</pubDate>
            <atom:updated>2018-07-17T21:48:59.481Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y8TJ_lErK8vg9YGZze-_ZQ.png" /><figcaption>A screenshot from <a href="https://mourner.github.io/road-orientation-map/">an interactive map of road orientations</a></figcaption></figure><p>Cities can look and feel very different depending on how they were planned and built, and this is especially apparent when you explore them from above, on a map. Some cities strictly align to a grid, others seem like they grew without any structure, and in some cases, patterns only appear when you examine a city more closely, with each neighborhood having their own distinct style. Could we visualize these patterns?</p><p><a href="http://vizual-statistix.tumblr.com/post/80468941142/unlike-like-emperor-kuzco-i-was-actually-born">Seth Kadish</a> and recently <a href="http://geoffboeing.com/2018/07/comparing-city-street-orientations/">Geoff Boeing</a> showed one great way to do this. They divided 360° into a set of orientation ranges, counted how many road segments belong to each range, and produced a <em>polar histogram </em>that gives us a profound insight into the street pattern of each particular city:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ShNc_dXc3Incns8J.png" /><figcaption><a href="http://vizual-statistix.tumblr.com/post/80468941142/unlike-like-emperor-kuzco-i-was-actually-born">Road orientations visualization</a> by <a href="https://twitter.com/VizualStatistix">Seth Kadish</a></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*22tBjIwG66shbHmx.png" /><figcaption>Detroit from <a href="http://geoffboeing.com/2018/07/comparing-city-street-orientations/">City Street Orientations</a> by <a href="https://twitter.com/gboeing">Geoff Boeing</a></figcaption></figure><p>When I saw this, I was hooked instantly— what an ingenious way to look at how a city is built! But I wanted to explore more. How would such a chart look for my city, Kyiv? Or for some of my favorite but lesser known places? How would different neighbors of the same city compare? Or, on a larger scale, how would a European road network compare to the one in the US?</p><p>Extracting and processing the road data for every place of interest to generate a polar chart seemed like too much work. Could I do it on an interactive map? It turns out that this is a perfect use case for <a href="https://www.mapbox.com/maps/">Mapbox vector maps</a> — since the map data is there on the client, we can analyze and visualize it instantly for any place in the world. Play with the map below to see it in action!</p><iframe src="https://cdn.embedly.com/widgets/media.html?url=https%3A%2F%2Fjsfiddle.net%2Fzbdu3fkg%2Fshow%2F&amp;src=https%3A%2F%2Fjsfiddle.net%2Fzbdu3fkg%2Fshow%2Fembedded%2F&amp;type=text%2Fhtml&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/7aa8ab3c0f61d5c5ce807bc3ca47e7d3/href">https://medium.com/media/7aa8ab3c0f61d5c5ce807bc3ca47e7d3/href</a></iframe><p><strong>Update:</strong> just discovered that the same idea of an interactive map was <a href="https://blog.veloviewer.com/interactive-road-orientation-distributions-how-ordered-is-your-town/">explored in 2014 by Ben Lowe</a>. His demo no longer works unfortunately, but I’m glad I found it!</p><h3>How I built it</h3><p>The full app is about 80 lines of code. After initializing the map, we set it up to update the visualization every time we move it:</p><pre>map.on(&#39;load&#39;, function () {<br>    updateOrientations();<br>    map.on(&#39;moveend&#39;, updateOrientations);<br>});</pre><p>In the update routine, we can fetch all the roads on the screen with a single line of code, getting the results in <a href="http://geojson.org/">GeoJSON format</a> for easy processing:</p><pre>var features = map.queryRenderedFeatures({layers: [&#39;road&#39;]});</pre><p>To make sure we only visualize road segments that we actually see, not including parts of roads that go off the view, we use a tiny library called <a href="https://github.com/mapbox/lineclip">lineclip</a> to clip every road feature to the current bounding box:</p><pre>var clippedLines = [];<br>for (var j = 0; j &lt; lines.length; j++) {<br>  clippedLines.push.apply(clippedLines, lineclip(lines[j], bbox));<br>}</pre><p>Calculating orientations and lengths for every road segment can be expensive if there are hundreds of thousands of them in our view. So we use <a href="https://github.com/mapbox/cheap-ruler">cheap-ruler</a>, a library for <a href="https://blog.mapbox.com/fast-geodesic-approximations-with-cheap-ruler-106f229ad016">very fast approximations</a> of geodesic calculations like this:</p><pre>var ruler = cheapRuler(map.getCenter().lat);<br>...<br>for (var i = 0; i &lt; line.length - 1; i++) {<br>  var bearing = ruler.bearing(line[i], line[i + 1]);<br>  var distance = ruler.distance(line[i], line[i + 1]);<br>  ...</pre><p>For every road segment, we calculate the “bin” it belongs to (we have 64 bins that cover 360°) and accumulate segment lengths for every bin, while also counting every two-way road twice (in both directions):</p><pre>var k0 = Math.round((bearing + 360) * numBins / 360) % numBins;<br>var k1 = Math.round((bearing + 180) * numBins / 360) % numBins;</pre><pre>bins[k0] += distance;<br>if (isTwoWay) bins[k1] += distance;</pre><p>Finally, to get those pretty, uniform rainbow colors for all orientations on our chart, we use the <a href="https://beta.observablehq.com/@mbostock/sinebow"><em>sinebow</em></a> function popularized by my coworker Charlie Loyd.</p><p>That’s it!</p><p>Now play with the <a href="https://mourner.github.io/road-orientation-map/">interactive map</a> — I spent hours exploring it, and hope you enjoy it as much as I do! Check out the full <a href="https://github.com/mourner/road-orientation-map">source code</a>, and hit me up <a href="https://twitter.com/mourner">on Twitter</a> if you have any questions!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1eefa6002afc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mapbox/visualizing-street-orientations-on-an-interactive-map-1eefa6002afc">Visualizing street orientations on an interactive map</a> was originally published in <a href="https://medium.com/mapbox">maps for developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing heatmaps in Mapbox GL JS]]></title>
            <link>https://medium.com/mapbox/introducing-heatmaps-in-mapbox-gl-js-71355ada9e6c?source=rss-bcc7cdd477fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/71355ada9e6c</guid>
            <category><![CDATA[business-intelligence]]></category>
            <category><![CDATA[product]]></category>
            <category><![CDATA[maps]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[data-visualization]]></category>
            <dc:creator><![CDATA[Vladimir Agafonkin]]></dc:creator>
            <pubDate>Mon, 30 Oct 2017 19:57:54 GMT</pubDate>
            <atom:updated>2017-10-30T19:59:29.369Z</atom:updated>
            <content:encoded><![CDATA[<p>By: <a href="https://medium.com/@agafonkin?source=user_popover"><em>Vladimir Agafonkin</em></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Cptxfx40Ue-0p4xGL5KjHQ.png" /><figcaption>Circle (left) and heatmap (right) layers representing earthquakes in California</figcaption></figure><p>With the <a href="https://www.mapbox.com/mapbox-gl-js/api/">0.41 release of Mapbox GL JS</a> (and <a href="https://www.mapbox.com/mobile/">mobile SDKs</a> to follow), we’re introducing heatmaps — a beautiful way to visualize and explore massive point datasets.</p><p>A heatmap is a data visualization where a range of colors represent the <em>density</em> of points in a particular area. Heatmaps help us see the “shape” of the data and highlight areas of concentration where many points are clustered closely together. The dynamic heatmap layer in Mapbox GL JS renders smoothly at 60 fps while panning and zooming. It’s fast and can handle large amounts of data (especially when combined with clustering). Let’s see what an interactive heatmap of 400,000 points looks like:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F222186846&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F222186846&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F640648602_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1356" height="720" frameborder="0" scrolling="no"><a href="https://medium.com/media/eb3c9b44bfb8ebe0b36baaacf139e320/href">https://medium.com/media/eb3c9b44bfb8ebe0b36baaacf139e320/href</a></iframe><h3>Getting started with heatmaps</h3><p>If you want to get started with heatmaps right away, dive into <a href="https://www.mapbox.com/mapbox-gl-js/example/heatmap-layer/">our official example</a> or take a look at a <a href="https://www.mapbox.com/help/make-a-heatmap-with-mapbox-gl-js/">detailed tutorial</a>. Want to know more? Read on for an overview.</p><p>You can start by adding a layer with type “heatmap” to your style:</p><pre>map.addSource(&#39;earthquakes&#39;, {<br>    type: &#39;geojson&#39;,<br>    data: &#39;earthquakes.geojson&#39;<br>});</pre><pre>map.addLayer({<br>    id: &#39;earthquakes&#39;,<br>    source: &#39;earthquakes&#39;,<br>    type: &#39;heatmap&#39;<br>});</pre><p>Heatmaps can require a lot of customization based on your use-case. Thankfully, Mapbox GL JS provides a set of powerful paint style properties for controlling every aspect of the layer’s appearance in real time.</p><h4>heatmap-radius</h4><p>This property affects how detailed the heatmap will be by setting the “radius of influence” of each point in pixels (an equivalent of the bandwidth parameter in <a href="https://en.wikipedia.org/wiki/Kernel_density_estimation">kernel density estimation</a>). Higher values make for a smoother, more generalized look. The property can also be a zoom function, allowing you to adjust the look as you zoom in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7Kxaqt9FBmN_xm3UW2EaVA.png" /><figcaption>Adjusting heatmap-radius: 5 (left), 40 (right)</figcaption></figure><pre>map.addLayer({<br>    ...<br>    paint: {<br>        &#39;heatmap-radius&#39;: 40<br>    }<br>});</pre><h4>heatmap-color</h4><p>This style property controls the most important aspect of heatmap design — how the “density” value of each pixel on a map translates to color:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sTlp9cnCdcjbX6RWFF6BmQ.png" /><figcaption>Examples of different heatmap-color values</figcaption></figure><p>The value of the property is a function which maps density values ranging from 0.0 to 1.0 to colors. This example will make the most sparse areas blue while gradually transitioning through yellow to red at the most crowded spots.</p><pre>&quot;heatmap-color&quot;: {<br>    &quot;stops&quot;: [<br>        [0.0, &quot;blue&quot;], <br>        [0.5, &quot;yellow&quot;], <br>        [1.0, &quot;red&quot;]<br>    ]<br>}</pre><h4>heatmap-intensity</h4><p>This property allows you to adjust the intensity of the heatmap appearance globally. The higher the value, the more “weight” each point will contribute to the look. The relation is linear — e.g., setting heatmap-intensity to 10.0 is equivalent to duplicating the dataset ten times and rendering it with the default 1.0 value.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PcKoO5jIWK0VrBvTJo1CRQ.png" /><figcaption>Adjusting heatmap-intensity: 0.1 (left), 1 (right)</figcaption></figure><p>heatmap-intensity is very useful for adjusting the look of the heatmap to different zoom levels — if you leave it constant, the heatmap on lower zooms will look much heavier because points will be visually closer together.</p><h4>heatmap-weight</h4><p>This style property works the same as heatmap-intensity, except that it can be data-driven, adjusting intensity of individual points depending on their feature properties:</p><pre>&quot;heatmap-weight&quot;: {<br>    &quot;type&quot;: &quot;identity&quot;, <br>    &quot;property&quot;: &quot;point_count&quot;<br>}</pre><p>In this example, a point with point_count value of 100 will look the same as 100 points with point_count of 1. Combined with <a href="https://www.mapbox.com/mapbox-gl-js/example/cluster/">clustering</a>, this improves heatmap performance by drastically reducing the number of points to draw.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yUxoUsny233Ihw6SjkAWcg.png" /></figure><h4>heatmap-opacity</h4><p>This property controls the global opacity of the heatmap layer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VPKYNw2rxyatOEuUEiIkNg.png" /><figcaption>Heatmap with heatmap-opacity of 1.0 (left) and 0.5 (right)</figcaption></figure><p>One useful application of heatmap-opacity is fading out the heatmap for a more detailed look when zooming in:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/440/1*N0oh-3SqPRpr5MVWNnoshg.gif" /></figure><h3>How heatmaps work under the hood</h3><p>In mathematical terms, Mapbox GL heatmaps are a bivariate (2D) <a href="https://en.wikipedia.org/wiki/Kernel_density_estimation">kernel density estimation</a> with a <a href="https://en.wikipedia.org/wiki/Gaussian_function">Gaussian kernel</a>. It means that each data point has an area of “influence” around it (called a kernel) where the numerical value of influence (which we call density) decreases as you go further from the point. If we sum density values of all points in every pixel of the screen, we get a combined density value which we then map to a heatmap color.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oz0Fick1nUapz0pity8jSw.png" /><figcaption>Individual kernels (dashed red lines) and the combined kernel density estimation (blue line). Source: <a href="https://en.wikipedia.org/wiki/Kernel_density_estimation">Wikipedia</a></figcaption></figure><p>To implement this with OpenGL, for each data point, we draw the kernel (which looks like a blurred circle) into an offscreen half-float texture with additive blending (so that values are summed together when kernels overlap), and then we colorize this greyscale texture in a separate step.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T5loHulP_BFhdsBGJrQhwA.png" /><figcaption>Additive blending of two Gaussian kernels.</figcaption></figure><p>Ideally, we need to calculate density for every screen pixel separately for each data point because this value never becomes fully zero, even when far away from the corresponding point. But that would be too expensive — even a modest amount of points to draw would require billions of operations. To speed things up, we crop each kernel to a square that’s just big enough for all values outside to be very small (under a certain threshold — we arbitrarily picked 1 / 255 / 16). This size will <a href="https://github.com/mapbox/mapbox-gl-js/blob/3eced30265619a2f4bfa91b615d58651e0326cd0/src/shaders/heatmap.vertex.glsl#L38">depend on weight and intensity</a> of each point; heavier points will need a bigger drawing area.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lgSf_dtIRD0qh5m7jwpDCg.png" /></figure><p>Additionally, for drawing the densities, we use a downscaled texture with 4 times smaller sides than the map resolution (resulting in 16 times fewer pixels to draw for each point), and then upscale the texture back with linear interpolation during the colorization step. For heatmaps, this is visually almost indistinguishable from full resolution rendering, but it makes it considerably faster.</p><h3>A different approach to heatmaps</h3><p>Other platforms render heatmaps as static image tiles on the server. Our approach to heatmaps takes advantage of Mapbox GL at its core, rendering smooth transitions at 60fps and providing the ability to control every aspect of heatmap layers at scale — from picking the right color ramp to adjusting the point radius at each zoom level. Combine heatmaps with other GL JS capabilities like <a href="https://blog.mapbox.com/introducing-data-driven-styling-in-mapbox-gl-js-f273121143c3">data-driven styling</a>, <a href="https://blog.mapbox.com/announcing-expressions-in-gl-js-a72b55d0a6af">expressions</a>, and <a href="https://www.mapbox.com/mapbox-gl-js/example/cluster/">clustering</a> for more unique configurations of your data.</p><p>Thank you for reading! Check out <a href="https://www.mapbox.com/mapbox-gl-js/example/heatmap-layer/">this example</a> and <a href="https://www.mapbox.com/help/make-a-heatmap-with-mapbox-gl-js/">tutorial</a> for heatmaps in GL JS, and stay tuned for the launch of this feature in our mobile SDKs later this year. Reach out on Twitter (<a href="https://twitter.com/mourner?lang=en">@mourner</a>) or respond here in Medium if you have questions or comments. Let us know what you make with the tag <a href="https://twitter.com/search?f=tweets&amp;vertical=default&amp;q=%23BuiltWithMapbox">#BuiltWithMapbox</a>.</p><p><a href="https://www.mapbox.com/about/team/vladimir-agafonkin/">Vladimir Agafonkin</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=71355ada9e6c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mapbox/introducing-heatmaps-in-mapbox-gl-js-71355ada9e6c">Introducing heatmaps in Mapbox GL JS</a> was originally published in <a href="https://medium.com/mapbox">maps for developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I built a wind map with WebGL]]></title>
            <link>https://medium.com/mapbox/how-i-built-a-wind-map-with-webgl-b63022b5537f?source=rss-bcc7cdd477fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/b63022b5537f</guid>
            <category><![CDATA[built-with-mapbox]]></category>
            <category><![CDATA[devlog]]></category>
            <category><![CDATA[data-visualization]]></category>
            <category><![CDATA[weather]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Vladimir Agafonkin]]></dc:creator>
            <pubDate>Thu, 27 Jul 2017 16:53:13 GMT</pubDate>
            <atom:updated>2017-07-28T17:24:28.214Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7VX65YYNGw6-CAwVnxP_5A.png" /></figure><p><em>Check out my </em><a href="https://mapbox.github.io/webgl-wind/demo/"><em>WebGL-based wind power simulation demo</em></a><em>! Let’s dive into how it works under the hood.</em></p><p>I have an unflattering confession to make: for the last few years working at <a href="https://www.mapbox.com/">Mapbox</a>, I avoided direct OpenGL/WebGL programming like a plague. For one reason: the OpenGL API and terminology deeply terrified me. It always looked so complex, confusing, ugly, and verbose that I could never take the plunge and dive into it. I would get an uneasy feeling simply hearing terms like stencil masks, mipmap, depth culling, blend functions, and normal maps.</p><p>This year, I decided to finally confront my fears and build something non-trivial with WebGL on my own. 2D wind simulation looked like a perfect opportunity — it’s useful, visually stunning, and challenging, yet it still felt attainable in scope. I was surprised to discover that it was much less scary than it looked!</p><h3>Wind visualization on the CPU</h3><p>There are many examples of wind power visualization online, but the most popular and influential one is <a href="https://earth.nullschool.net/">earth.nullschool.net</a>, a famous project by <a href="https://twitter.com/cambecc">Cameron Beccario</a>. It’s not open source itself, but it has an old <a href="https://github.com/cambecc/earth">open source version</a> which most other implementations based their code on. One notable open source derivation is <a href="https://github.com/Esri/wind-js/">Esri Wind-JS</a>. Popular weather services that use the technique include <a href="https://www.windytv.com/">Windy</a> and <a href="https://www.ventusky.com/">VentuSky</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/730/1*W8AnG2cqh-fmDt_-UqTPsA.png" /><figcaption>Wind power simulation on <a href="https://earth.nullschool.net/">earth.nullschool.net</a></figcaption></figure><p>Typically, such a visualization in the browser relies on the Canvas 2D API and roughly works like this:</p><ol><li>Generate an array of random particle positions on screen and draw the particles.</li><li>For each particle, query the wind data to get the particle speed in its current location, and move it accordingly.</li><li>Reset a small portion of particles to a random position. This makes sure that areas the wind blows away from never become fully empty.</li><li>Fade the current screen a bit and draw newly positioned particles on top.</li></ol><p>This has subsequent performance limitations:</p><ul><li>The number of wind particles should be kept low (e.g. Earth uses ~5k).</li><li>There’s a big delay every time you update the data or the view (e.g. ~2 seconds for Earth) because data processing is expensive and happens on the CPU side.</li></ul><p>Moreover, to integrate it as a part of an interactive WebGL-based map like <a href="https://www.mapbox.com/maps/">Mapbox</a> is that you would have to upload the pixel contents of the Canvas element to the GPU on every frame, which would significantly reduce the performance further.</p><p>I was looking for a way to reimplement the full logic on the GPU side with WebGL, so that it would be fast, capable of drawing millions of particles, and possible to integrate into a Mapbox GL map without a big performance loss. Luckily, I stumbled upon a wonderful tutorial about <a href="http://nullprogram.com/blog/2014/06/29/">particle physics in WebGL</a> by Chris Wellons, and realized that the wind visualization can use the same approach.</p><h3>OpenGL Basics</h3><p>The confusing API and terminology makes OpenGL graphics programming very intimidating to learn, but under the surface, the concept is extremely simple. Here’s a practical definition:</p><blockquote><em>OpenGL provides a 2D API for </em><strong><em>drawing triangles </em></strong><em>efficiently</em><strong><em>.</em></strong></blockquote><p>So basically all you do with GL is draw triangles. The difficulty, besides the scary API, comes from the various math and algorithms required to do this. It can also draw dots and basic lines (without smoothing or round joins/caps), but those are rarely used.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/337/1*bS9eFR6gOHwWE0HOlEacqg.png" /><figcaption>Illustration from <a href="https://users.soe.ucsc.edu/~pang/162/s12/labnotes/shaders.html">Brief Introduction to Shaders Using GLSL</a></figcaption></figure><p>OpenGL provides a special C-like language — GLSL — to write programs that are directly executed by the GPU. Each program is divided into two parts, called shaders — the vertex shader and the fragment shader.</p><p>The <strong>vertex shader</strong> provides the code for converting coordinates. For example, multiplying triangle coordinates by 2 so that our triangle appears twice as big. It will run once for every coordinate we pass to OpenGL when drawing. A basic example:</p><pre>attribute vec2 coord;<br>void main() {<br>    gl_Position = vec4(2.0 * coord, 0, 1);<br>}</pre><p>The <strong>fragment shader</strong> provides the code for determining the color of each drawn pixel. You can do a lot of cool math in it, but in the end it comes down to something like “draw the current pixel of the triangle as green”. Example:</p><pre>void main() {<br>    gl_FragColor = vec4(0, 1, 0, 1);<br>}</pre><p>One of the cool things you can do in both vertex and fragment shaders is adding an image (called texture) as a parameter, and then looking up pixel colors in any spot of this image. We’ll rely on this heavily in the wind visualization.</p><p>The fragment shader code execution is massively parallel and heavily hardware-accelerated, so it’s usually many orders of magnitude faster than an equivalent computation on the CPU.</p><h3>Getting the wind data</h3><p>Every 6 hours, the US National Weather Service publishes <a href="http://nomads.ncep.noaa.gov/">weather data for the whole globe</a>, known as GFS, in the form of a latitude/longitude grid with associated values (including wind velocities). It’s encoded in a special binary format called GRIB, which can be parsed into human-readable JSON with a special <a href="https://software.ecmwf.int/wiki/display/GRIB/Releases">set of tools</a>.</p><p>I wrote a few small scripts that <a href="https://github.com/mapbox/webgl-wind/blob/master/data/download.sh">download</a> and <a href="https://github.com/mapbox/webgl-wind/blob/master/data/prepare.js">convert</a> the wind data into a simple PNG image with wind velocities encoded as RGB colors — horizontal velocity as red and vertical as green in each pixel. It looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*OmWhLD7blCtBCcM0aY60Yg.png" /><figcaption>GFS wind power data encoded as an image</figcaption></figure><p>There are higher resolution versions you can download (2x and 4x), but 360×180 grid is enough for a low-zoom visualization. PNG compression is quite a good fit for this kind of data, and the image above typically weights just about 80 KB.</p><h3>Moving particles on the GPU</h3><p>Existing wind visualizations stored particle state in JavaScript arrays. How do we store and manipulate this state on the GPU side? A new GL feature called <a href="https://www.khronos.org/opengl/wiki/Compute_Shader">compute shaders</a> (in OpenGL ES 3.1 and equivalent WebGL 2.0 specifications) allows you to run shader code on arbitrary data (without any rendering). Unfortunately, support for the new specifications across browsers and mobile devices is marginal, and so we’re left with only one practical option: textures.</p><p>OpenGL allows you to draw not only to the screen, but also to a texture (through a concept called framebuffer). So we can encode particle positions as RGBA colors of an image, load it to the GPU, calculate new positions based on wind velocities in the fragment shader, encode them back into RGBA colors and draw it into a new image.</p><p>To store enough precision for both X and Y, we store each component in two bytes — RG and BA respectively, giving us a range of 65536 distinct values for each.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/273/1*Lw0fX52XI9R2POBMeSLENQ.png" /><figcaption>Illustration from <a href="http://nullprogram.com/blog/2014/06/29/">A GPU Approach to Particle Physics</a></figcaption></figure><p>An example 500×500 image would then hold 250,000 particles, and we would move every particle with the fragment shader. The resulting image would look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*tExbozguOadoXGvt3srzyQ.png" /><figcaption>Illustration from <a href="http://nullprogram.com/blog/2014/06/29/">A GPU Approach to Particle Physics</a></figcaption></figure><p>Here’s how the position is decoded and encoded from RGBA and back in the fragment shader:</p><pre>// lookup particle pixel color<br>vec4 color = texture2D(u_particles, v_tex_pos);</pre><pre>// decode particle position (x, y) from pixel RGBA color<br>vec2 pos = vec2(<br>    color.r / 255.0 + color.b,<br>    color.g / 255.0 + color.a);</pre><pre>... // move the position</pre><pre>// encode the position back into RGBA<br>gl_FragColor = vec4(<br>    fract(pos * 255.0),<br>    floor(pos * 255.0) / 255.0);</pre><p>On the next frame, we can take this new image as the current state and draw the new state into the other image, etc., swapping the two every frame. So, with the help of two particle state textures, we can move all the wind simulation logic to the GPU.</p><p>This approach is extremely fast — instead of updating only 5,000 particles 60 times per second on the browser, we can suddenly handle <strong>one million</strong>.</p><p>One thing to keep in mind is that near the poles, the particles should move much faster along the X axis compared to the ones on the equator, because the same number of longitude degrees represents a much smaller distance. This can be taken into account with the following shader code:</p><pre>float distortion = cos(radians(pos.y * 180.0 - 90.0));<br>// move the particle by (velocity.x / distortion, velocity.y)</pre><h3>Drawing the particles</h3><p>As I mentioned earlier, in addition to triangles, we can also draw basic points — rarely used, but perfect for 1-pixel particles like this.</p><p>To draw each particle, we’ll simply look up its pixel color on the particle state texture in the vertex shader to determine its position; then determine the particle color in the fragment shader by looking up its current velocity from the wind texture; and finally map it to a nice color gradient (I picked the colors from the trusty <a href="http://colorbrewer2.org/">ColorBrewer2</a>). At this point, it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dAWSPjFeSHOpmJORIa69JA.png" /></figure><p>That’s something, if a little empty. But it’s hard to get a sense of wind direction from particle movement alone. We need to add particle trails.</p><h3>Drawing particle trails</h3><p>The first approach to drawing the trails I tried is using preserveDrawingBuffer WebGL option, which makes the screen state persist between frames, so that we can draw particles over and over on top of each other every frame as they move. However, this WebGL feature is a big performance hit, and many WebGL articles recommend against using it.</p><p>Instead, similar to how we do with particle state textures, we can draw the particles into a texture (which is in turn drawn onto a screen), then use this texture on the next frame as the background (slightly dimmed), and swap the input/target textures every frame. In addition to better performance, an advantage of this approach is that we can port it directly to native code (which has no preserveDrawingBuffer equivalent).</p><h3>Interpolating wind lookups</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1002/1*75N-54BVxpiM4fzdcHp9Rg.png" /><figcaption>Illustration from a <a href="https://en.wikipedia.org/wiki/Bilinear_interpolation">Wikipedia article on Bilinear interpolation</a></figcaption></figure><p>The wind data has values for specific points on a latitude/longitude grid, e.g. (50,30),(51,30),(50,31),(51,31)geographical points. How do we get an arbitrary intermediate value, e.g. (50.123,30.744)?</p><p>OpenGL comes with free interpolation when you look up texture colors. However, it’s still leads to blocky, pixelated patterns. Here’s an example of these artifacts in the wind texture when scaled:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/335/1*fb1K08f-2KBPMge-zzFPGg.png" /><figcaption>Native GL linear interpolation</figcaption></figure><p>Luckily, we can smooth out the artifacts by looking up 4 neighbor pixels in each wind probe, and doing manual <a href="https://en.wikipedia.org/wiki/Bilinear_interpolation">bilinear interpolation</a> math on them on top of the native one in the fragment shader. It’s more expensive, but fixes the artifacts and leads to a more fluid wind visualization. Here’s the same area with this technique:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/329/1*No9KDyR1RP3VhfDISSmhDQ.png" /><figcaption>Manual bilinear interpolation on the fragment shader</figcaption></figure><h3>Pseudo-random generator on the GPU</h3><p>There’s one tricky logic left to implement on the GPU — randomly resetting particle locations. Without this, even a huge number of wind particles degenerates into just a few lines on the screen, because areas the wind blows away from become empty with time:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1003/1*9yJaR7nKH95lXA-OYJDCrw.png" /></figure><p>The problem is that shaders don’t have a random number generator. How do we randomly decide if a particle needs to be reset?</p><p>I found a solution <a href="https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl">on StackOverflow</a> — a GLSL function for pseudo-random number generation that accepts a pair of numbers as input:</p><pre>float rand(const vec2 co) {<br>    float t = dot(vec2(12.9898, 78.233), co);<br>    return fract(sin(t) * (4375.85453 + t));<br>}</pre><p>This curious function depends on sin result varying a lot with big values. Then we can do something like this:</p><pre>if (rand(some_numbers) &gt; 0.99) <br>    reset_particle_position();</pre><p>The challenge is picking an input for each particle that would be “random” enough so that the generated values are uniform across the screen and do not exhibit weird patterns.</p><p>Using current particle position as the seed is not perfect because the same particle positions will always generate the same random numbers, so some particles will disappear in the same area.</p><p>Using particle position in the state texture also doesn’t work because the same particles will always disappear.</p><p>What I ended up with eventually depends on both particle position and state position coupled with a random value that’s calculated on every frame and passed to the shader:</p><pre>vec2 seed = (pos + v_tex_pos) * u_rand_seed;</pre><p>But then we have another minor problem — areas where particles are very fast will look much more dense than areas where there’s not much wind. We can balance this out a bit by increasing the particle reset rate for faster particles:</p><pre>float dropRate = u_drop_rate + speed_t * u_drop_rate_bump;</pre><p>speed_t here is a relative speed value (from 0 to 1), and u_drop_rate and u_drop_rate_bump are parameters you can tweak in the final visualization. Here&#39;s an example of how it affects the result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*twfafan_h4YIP7ZnrJYZlw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*A8V8mwxez42NdFH5cCoWeQ.png" /><figcaption>High drop rate (reminds me of a Van Gogh painting)</figcaption></figure><h3>What’s next</h3><p>The result is a <a href="https://mapbox.github.io/webgl-wind/demo/">fully GPU-powered wind visualization</a> that can render a million particles at 60fps. Try playing with the sliders in the demo, and check out <a href="https://github.com/mapbox/webgl-wind">the final code</a> — it’s about 250 lines total, and I tried to make it as readable as possible.</p><p>The next step is integrating this into a <a href="https://www.mapbox.com/maps/">live map</a> you can explore. I’ve made some progress on this but not enough to share a live demo. Here’s a little sneak peek though (sorry for the poor quality capture):</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F227279380&amp;url=https%3A%2F%2Fvimeo.com%2F227279380%2Ff5bced0373&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F647064618_1280x671.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1366" height="716" frameborder="0" scrolling="no"><a href="https://medium.com/media/bb3cf849b05983918dc866f1c2da7000/href">https://medium.com/media/bb3cf849b05983918dc866f1c2da7000/href</a></iframe><p>Thank you for reading and stay tuned for more updates! And check out my <a href="https://medium.com/@agafonkin/a-dive-into-spatial-search-algorithms-ebd0c5e39d2a">previous article on spatial algorithms</a> if you missed it.</p><p>Very special, warm-hearted thank you to my Mapbox team mates <a href="https://github.com/kkaefer">@kkaefer</a> and <a href="https://github.com/ansis">@ansis</a>, who patiently answered all my silly questions about graphics programming, gave tons of valuable tips and helped me learn so much. ❤️</p><p>P.S. Want to work on cool stuff like this? Check out our <a href="https://www.mapbox.com/jobs/">job openings</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b63022b5537f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mapbox/how-i-built-a-wind-map-with-webgl-b63022b5537f">How I built a wind map with WebGL</a> was originally published in <a href="https://medium.com/mapbox">maps for developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A dive into spatial search algorithms]]></title>
            <link>https://medium.com/mapbox/a-dive-into-spatial-search-algorithms-ebd0c5e39d2a?source=rss-bcc7cdd477fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/ebd0c5e39d2a</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[algorithms]]></category>
            <category><![CDATA[data-structures]]></category>
            <category><![CDATA[devlog]]></category>
            <category><![CDATA[maps]]></category>
            <dc:creator><![CDATA[Vladimir Agafonkin]]></dc:creator>
            <pubDate>Thu, 27 Apr 2017 13:14:40 GMT</pubDate>
            <atom:updated>2017-06-30T17:24:57.344Z</atom:updated>
            <content:encoded><![CDATA[<h4>Searching through millions of points in an instant</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RsZ300nAnsPCxrd2bu5sQw.png" /><figcaption>A visualization of an R-tree for 138k populated places on Earth</figcaption></figure><p>I’m obsessed with software performance. One of my main responsibilities at <a href="https://www.mapbox.com/">Mapbox</a> is discovering ways to make our mapping platform faster. And when it comes to processing and displaying spatial data at scale, there’s no concept more useful and important than a <a href="https://en.wikipedia.org/wiki/Spatial_database#Spatial_index">spatial index</a>.</p><p>Spatial indices are a family of algorithms that arrange geometric data for efficient search. For example, doing queries like “return all buildings in this area”, “find 1000 closest gas stations to this point”, and returning results within milliseconds even when searching millions of objects.</p><p>Spatial indices form the foundation of databases like <a href="http://postgis.net/">PostGIS</a>, which is at the core of our platform. But they’re also immensely useful in many other tasks where performance is critical. In particular, processing telemetry data — e.g. matching millions of GPS speed samples against a road network to generate live traffic data for our navigation service. On the client side, examples include placing labels on a map in real time, and looking up map objects on a mouse hover.</p><p>In the last 4 years, I’ve built a bunch of ultra-fast JavaScript libraries for spatial search: <a href="https://github.com/mourner/rbush">rbush</a>, <a href="https://github.com/mourner/rbush-knn/">rbush-knn</a>, <a href="https://github.com/mourner/kdbush">kdbush</a>, <a href="https://github.com/mourner/geokdbush">geokdbush</a> (with a few more to come). In this post, I’ll attempt to describe how they work under the hood.</p><h3>Spatial search problems</h3><p>Spatial data has two fundamental query types: nearest neighbors and range queries. Both serve as a building block for many geometric and GIS problems.</p><h4>K nearest neighbors</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/930/1*IxO9yA_ZeiZxfGFVKng80g.png" /><figcaption>Three nearest neighbors of a point</figcaption></figure><p>Given thousands of points, such as city locations, how do we retrieve the <a href="https://en.wikipedia.org/wiki/Nearest_neighbor_search">closest points</a> to a given query point?</p><p>An intuitive way to do this is:</p><ol><li>Calculate the distances from the query point to every other point.</li><li>Sort those points by distance.</li><li>Return the first K items.</li></ol><p>This is fine if we have a few hundred points. But if we have millions, these queries will be too slow to use in practice.</p><h4>Range and radius queries</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/938/1*e_JqSc_WqpoHFM7Qf07e6w.png" /></figure><p>How do we retrieve all points inside…</p><ul><li>a rectangle? (range query)</li><li>a circle? (radius query)</li></ul><p>The naive approach is to loop through all the points. But this will fail if the database is big and gets thousands of queries per second.</p><h3>How spatial trees work</h3><p>Solving both problems at scale requires putting the points into a spatial index. Data changes are usually much less frequent than queries, so incurring an initial cost of processing data into an index is a fair price to pay for instant searches afterwards.</p><p>Almost all spatial data structures share the same principle to enable efficient search: <a href="https://en.wikipedia.org/wiki/Branch_and_bound">branch and bound</a>. It means arranging data in a tree-like structure that allows discarding branches at once if they do not fit our search criteria.</p><h4>R-tree</h4><p>To see how this works, let’s start with a bunch of input points and sort them into 9 rectangular boxes with about the same number of points in each:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DkXjkWxMkT41Tupa8SUMCQ.png" /></figure><p>Now let’s take each box and sort it into 9 smaller boxes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kJBBppwt8vrLjQ0zqoMg9g.png" /></figure><p>We’ll repeat the same process a few more times until the final boxes contain 9 points at most:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HGrNIlYjee5z6uhOX53zzQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hJj3HZmiKXiXkK12ha26Rw.png" /></figure><p>And now we’ve got an <a href="https://en.wikipedia.org/wiki/R-tree">R-tree</a>! This is arguably the most common spatial data structure. It’s used by all modern spatial databases and many game engines. R-tree is also implemented in my <a href="https://github.com/mourner/rbush">rbush</a> JS library.</p><p>Besides points, R-tree can contain rectangles, which can in turn represent any kinds of geometric objects. It can also extend to 3 or more dimensions. But for simplicity, we’ll talk about 2D points in the rest of the article.</p><h4>K-d tree</h4><p><a href="https://en.wikipedia.org/wiki/K-d_tree">K-d tree</a> is another popular spatial data structure. <a href="https://github.com/mourner/kdbush">kdbush</a>, my JS library for static 2D point indices, is based on it. K-d tree is similar to R-tree, but instead of sorting the points into several boxes at each tree level, we sort them into two halves (around a median point) — either left and right, or top and bottom, alternating between <em>x</em> and <em>y</em> split on each level. Like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ppX0pPNycdeBYiBeffPtyQ.png" /><figcaption>First three levels of a K-d tree</figcaption></figure><p>Compared to R-tree, K-d tree can usually only contain points (not rectangles), and doesn’t handle adding and removing points. But it’s much easier to implement, and it’s very fast.</p><p>Both R-tree and K-d tree share the principle of partitioning data into axis-aligned tree nodes. So the search algorithms discussed below are the same for both trees.</p><h3>Range queries in trees</h3><p>A typical spatial tree looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9BDPv_CI_7vJMGquYTQoOQ.png" /></figure><p>Each node has a fixed number of children (in our R-tree example, 9). How deep is the resulting tree? For one million points, the tree height will equal ceil(log(1000000) / log(9)) = 7.</p><p>When performing a range search on such a tree, we can start from the top tree level and drill down, ignoring all the boxes that don’t intersect our query box. For a small query box, this means discarding all but a few boxes at each level of the tree. So getting the results won’t need much more than sixty box comparisons (7 * 9 = 63) instead of a <em>million</em>. Making it ~16000 times faster than a naive loop search in this case.</p><p>In academic terms, a range search in an R-tree takes O(K log(N)) time in average (where K is the number of results), compared to O(N) of a linear search. In other words, it’s extremely fast.</p><p>We chose 9 as the node size because it’s a good default, but as a rule of thumb, higher value means faster indexing and slower queries, and vice versa.</p><h3>K nearest neighbors (kNN) queries</h3><p>Neighbors search is slightly harder. For a particular query point, how do we know which tree nodes to search for the closest points? We could make a radius query, but we don’t know which radius to pick — the closest point could be pretty far away. And doing many radius queries with an increasing radius in hopes of getting some results is inefficient.</p><p>To search a spatial tree for nearest neighbors, we’ll take advantage of another neat data structure — a <a href="https://en.wikipedia.org/wiki/Priority_queue">priority queue</a>. It allows keeping an ordered list of items with a very fast way to pull out the “smallest” one. I like to write things from scratch to understand how they work, so here’s the best ever priority queue JS library: <a href="https://github.com/mourner/tinyqueue">tinyqueue</a>.</p><p>Let’s take a look at our example R-tree again:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HGrNIlYjee5z6uhOX53zzQ.png" /></figure><p>An intuitive observation: when we search a particular set of boxes for K closest points, the boxes that are closer to the query point are more likely to have the points we look for. To use that to our advantage, we start our search at the top level by arranging the biggest boxes into a queue in the order from nearest to farthest:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/395/1*UzBafKxaJzsgvqg8MZd0Tg.png" /><figcaption>level 1 tree nodes</figcaption></figure><p>Next, we “open” the nearest box, removing it from the queue and putting all its children (smaller boxes) back into the queue alongside the bigger ones:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/481/1*-gWKwkPTQqmItVSihxDriQ.png" /><figcaption>level 1 and 2 tree nodes mixed together</figcaption></figure><p>We go on like that, opening the nearest box each time and putting its children back into the queue. When the nearest item removed from the queue is an actual point, it’s <em>guaranteed</em> to be the nearest point. The second point from the top of the queue will be second nearest, and so on.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/671/1*PPecx9vIJY-XgIqbON7UkA.png" /><figcaption>nodes of all levels (including points) mixed together</figcaption></figure><p>This comes from the fact that all boxes we didn’t yet open only contain points that are farther than the distance to this box, so any points we pull from the queue will be nearer than points in any remaining boxes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*dh5XDBxHZrZ83jAt_1ib7w.png" /><figcaption>distance from a point to a box is a <strong>lower bound</strong> of distances to points within the box</figcaption></figure><p>If our spatial tree is well balanced (meaning the branches are approximately the same size), we’ll only have to deal with a handful of boxes — leaving all the rest unopened during the search. This makes this algorithm extremely fast.</p><p>For rbush, it’s implemented in <a href="https://github.com/mourner/rbush-knn/">rbush-knn</a> module. For geographic points, I recently released another kNN library — <a href="https://github.com/mourner/geokdbush">geokdbush</a>, which gracefully handles curvature of the Earth and date line wrapping. It deserves a separate article — it was the first time I ever applied calculus at work.</p><h3>Custom kNN distance metrics</h3><p>This box-unpacking approach is quite flexible, and works for other distance types besides point-to-point distances. The algorithm relies on a defined <em>lower bound</em> of distances between the query and all objects inside a box. If we can define this lower bound for a custom metric, we can use the same algorithm for it.</p><p>This means we can, for example, change the algorithm to search K points closest to a <em>line segment</em> (instead of a point):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/514/1*3St20pbAhN_NK7vgFw8_ew.png" /><figcaption>distance from a line segment to a bounding box as a <strong>lower bound </strong>of distances to points within the box</figcaption></figure><p>The only modification to the algorithm we need is replacing <em>point-to-point</em> and <em>point-to-box</em> distance calculations with <em>segment-to-point</em> and <em>segment-to-box</em> distances.</p><p>In particular, this came in handy when I built <a href="https://github.com/mapbox/concaveman">Concaveman</a>, a fast 2D concave hull library in JS. It takes a bunch of points and generates an outline like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Qr7YzmZ_P15IJ8BFcDRN2Q.png" /><figcaption>Concave hull (red line) generated by Concaveman</figcaption></figure><p>The algorithm starts with a <a href="https://github.com/mikolalysenko/monotone-convex-hull-2d">convex hull</a> (which is fast to calculate), and then flexes its segments inward by connecting them through one of the closest points:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/712/1*eWhMCp7HFyBVbIGpKMjikQ.png" /><figcaption>From the paper: <a href="http://www.iis.sinica.edu.tw/page/jise/2012/201205_10.pdf">A New Concave Hull Algorithm and Concaveness Measure for n-dimensional Datasets</a>, 2012</figcaption></figure><p>Here’s a quote from the paper on that operation:</p><blockquote><em>In our proposed concave hull algorithm, finding nearest inside points — these are candidates of target spots for digging — from boundary edges is a time-consuming process. Developing a more efficient method for this process </em>is a<em> </em>future research topic<em>.</em></blockquote><p>Which I advanced, by a lucky coincidence! Indexing the points and performing “closest points to a segment” queries made the algorithm very fast.</p><h3>To be continued</h3><p>In future articles in the series, I’ll cover extending the kNN algorithm to geographic objects, and go into detail on tree packing algorithms (how to sort points into “boxes” optimally).</p><p>Thanks for reading! Feel free to comment and ask questions, and stay tuned for more. Play with our <a href="https://www.mapbox.com/products/">awesome SDKs</a>, and if you’re fired up about hard engineering challenges and maps, check out our <a href="https://www.mapbox.com/jobs/">job openings</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ebd0c5e39d2a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/mapbox/a-dive-into-spatial-search-algorithms-ebd0c5e39d2a">A dive into spatial search algorithms</a> was originally published in <a href="https://medium.com/mapbox">maps for developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Give Awesome Public Talks]]></title>
            <link>https://medium.com/@agafonkin/how-to-give-awesome-public-talks-a2b727f4bc24?source=rss-bcc7cdd477fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/a2b727f4bc24</guid>
            <category><![CDATA[talks]]></category>
            <category><![CDATA[speaking]]></category>
            <category><![CDATA[presentations]]></category>
            <dc:creator><![CDATA[Vladimir Agafonkin]]></dc:creator>
            <pubDate>Sun, 15 Mar 2015 10:27:38 GMT</pubDate>
            <atom:updated>2015-03-17T10:31:27.157Z</atom:updated>
            <content:encoded><![CDATA[<h4>a guide by an introvert</h4><p>I’m by no means an expert on talks. But, as an introvert, I overcame the fear of public speaking, gave dozens of talks at tech conferences, made lots of mistakes, and listened to hundreds of other speakers over the last years.</p><p>As a result, I learned a lot about what makes a talk engaging, inspiring and enjoyable.</p><h3>A good talk is a story</h3><p>Human beings go to conferences to <em>connect</em> to other human beings. A good talk is <strong>not a report</strong>, and <strong>not an advertisement</strong> — it’s a <strong>talk</strong>.</p><p>If all you do is commenting a bunch of slides, you will loose your listeners’ attention — they’ll probably make a better use of their time by reading a website or an article on the matter instead. People want to hear your story, to feel your enthusiasm and get inspired.</p><p>So a good talk is a <em>story</em>. Talk to the audience as if you’re telling it to a friend. Even complex, technical topics can be approached this way. Then it will flow naturally and engage your listeners.</p><p>And if you’re telling a story, be a human! Don’t be afraid to show through your personality. Laugh, cry, make stupid jokes, throw weird gestures, make funny faces, show funny GIFs, tell about the things you love, tell people how you screwed up. Being nervous, weird, awkward or silly is perfectly fine!</p><p>Be yourself. This will give your talk humanity and warmth. People will connect to you on a personal level, and they’ll love it.</p><h3>Getting comfortable on stage</h3><p>Giving your first ever public talk and feeling like you’re going to pass out? That’s normal!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/661/1*omz1YFZHGJMQ0r3F9N2VTQ.jpeg" /><figcaption>that historical first talk, Kyiv, 2008</figcaption></figure><p>Being a pretty introverted person, I gave my first talk at a crowded web development conference in 2008, talking about web accessibility. In a suit, pale like snow, with a gothic grimace on my face.</p><p>The hand holding the microphone shook intensely, with me looking as if I’m going to throw it any minute and run away. Occasionally I’d make a deep loud breath right into the mic, with the audience giving out laughs every time that happened.</p><p>Anyway, I cared about my topic, the content was useful, and people liked it.</p><p>When I’m on stage <em>now</em>, I often feel like I’m on a couch sitting with a bunch of friends beside a fire, even if it’s a hall with half a thousand people.</p><p>English isn’t my native language, so I often make glaring mistakes, or awkward pauses while recalling the right word. But it doesn’t bother me at all. This feeling of relaxed joy, <strong>the joy of sharing something you love with like-minded people</strong>, just comes naturally with practice.</p><p>If you’re not <em>passionate</em> about things you’re going to talk about, don’t bother talking at all. Otherwise, you’ll be perfectly fine! The comfort will gradually come with more talks given.</p><h3>How to make great slides</h3><p>The biggest problem with slides is that they have two purposes:</p><ul><li>to guide and complement your <strong>talk</strong></li><li>to be <strong>shared</strong> with others after the talk</li></ul><p>These purposes are <strong>not compatible</strong>. Presenters tend to focus on making slides shareable, packing each slide with information. If you do this, it can <strong>ruin your talk</strong>:</p><ol><li>More information distracts from your message. The audience reads your slide <em>while you talk</em>. And we are ridiculously bad at doing multiple things at once — especially reading and listening simultaneously.</li><li>Things on the slides have to be smaller (including font sizes) — many people just won’t be able to read it, as not everyone has perfect vision.</li><li>You can’t convey a strong message with a noisy slide. Strong messages are simple. Just a few words or a picture hit immediately; a big paragraph, a list, a table, or anything you have to stare at for a minute to understand just can’t have the same emotional impact.</li><li>When you pack your whole speech into slides, it <em>sounds</em> awkward — as if you simply read aloud instead of actually talking to people in front. It is a sure way to loose connection to your audience.</li></ol><p>If you want to have a great live presentation, <strong>make the slides complementary to your talk</strong>, and not your talk complementary to the slides.</p><p>The slides will still do well when shared, even if they don’t have the full picture. People will crave for more — they’ll look up mentioned things, go through links, go to your website, follow you on Twitter, read the follow-up blog posts and wait patiently for the video of the talk. It’s fine.</p><h4>Rules of thumb for slides</h4><p><strong>Make things simple. Remove clutter.</strong> That’s all you have to do to make your slides click. Specific tips:</p><ol><li>One thing per slide: a simple phrase in a huge font size, a strong image or a video.</li><li>Remove anything that doesn’t serve a useful purpose: titles, frames, ornaments, logos, background patterns, distracting animations, etc.</li><li>Prefer many simple slides over one complex slide.</li><li>If you need several things on one slide, make them appear one by one.</li><li>It’s better to talk without slides at all than comment a bunch of complex slides.</li><li>Limit the variety of slide looks. In particular, limit text to a few different styles at most — e.g. a white background, a single font, with black as a primary color and one or two additional colors for emphasis.</li><li>Don’t assume the projector will work perfectly and under favorable lighting conditions — make sure slides have enough contrast to be readable in any case. Also, accept as a fact that colors will probably be messed up.</li><li>Don’t make the slides too flashy and intense — most of the time, the attention should be on you, not the slides.</li></ol><p><a href="https://speakerdeck.com/mourner/how-simplicity-will-save-gis">Here’s a slide deck</a> from one of my recent talks for an example.</p><h3>Technical tips</h3><p><strong>Never rely on internet connection</strong> in presentations. I see fails because of this <em>all the time.</em> Even if you’re sure that it’ll work. Even if it always worked perfectly before. Even if slow connection should be enough. Try to prepare your presentations assuming you’ll be <strong>offline</strong>.</p><p>E.g. if your presentation is HTML/JS, turn off the WiFi and make sure each slide still works.</p><p><strong>Use recorded video</strong> instead of doing live demos:</p><ol><li>Video is infinitely more reliable — no surprises. Awkward mumbling about bad WiFi — never again!</li><li>Remember that people are bad at multi-tasking? You’ll have a <em>much easier time</em> talking strongly and connecting with your audience if you don’t stare at the screen and click something with your mouse or type on a keyboard while you talk.</li><li>It’ll save you some awkward time switching between slides and the right browser tab or whatever.</li></ol><p>Also, <strong>use recorded video instead of GIFs for product demos</strong>. The most important reason is the framerate — your listeners don’t know that it’s a GIF, so they will decide that the product is slow and janky instead.</p><p>GIFs are great because they are easily shareable. But remember that <strong>performance is a feature</strong>. Engineers put <em>so much effort</em> into making apps fast and smooth — don’t make the audience miss this. And don’t forget that GIFs mess up colors even further.</p><p>To record a screen video on a Mac, launch QuickTime, then click “File” — “New Screen Recording”. A moment later, you have a smooth, sharp, full-colored video for your presentation.</p><h3>Final notes</h3><p>I was deeply scared of public performances, but decided that in order to grow, I have to do what I fear. So I started sending out talk submissions, and it went from there. Not only is the fear gone now, — I actually enjoy talking publicly a lot.</p><p>Try it yourself — at the least, you’ll learn a very useful skill. And at the most, you’ll become a great speaker and inspire thousands of people. Even if you don’t feel like it at the moment.</p><p>If you need some inspiration, I’ll state the obvious — go to <a href="http://ted.com/">TED.com</a>. This incredible learning resource has thousands of videos with some of the best talks ever given.</p><p>Finally, feedback is very welcome! Thanks for reading and sharing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CKIOzVQGH-J-_xD59mu1zA.jpeg" /><figcaption>JSGeo 2014, Portland</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a2b727f4bc24" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>