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

<channel>
	<title>vis4.net &#187; tutorials</title>
	<atom:link href="http://vis4.net/blog/tutorials/feed/" rel="self" type="application/rss+xml" />
	<link>http://vis4.net</link>
	<description>The geeky side of information visualization</description>
	<lastBuildDate>Thu, 12 Jan 2012 22:59:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Matching Regions of GeoIP and Natural Earth</title>
		<link>http://vis4.net/blog/posts/piwik-matching-regions/?piwik_campaign=rss&#038;piwik_kwd=2939</link>
		<comments>http://vis4.net/blog/posts/piwik-matching-regions/?piwik_campaign=rss&#038;piwik_kwd=2939#comments</comments>
		<pubDate>Tue, 01 Nov 2011 14:45:57 +0000</pubDate>
		<dc:creator>Gregor Aisch</dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[geocitylite]]></category>
		<category><![CDATA[gis]]></category>
		<category><![CDATA[naturalearth]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://vis4.net/blog/posts/piwik-matching-regions/</guid>
		<description><![CDATA[In this post I'll describe the process of bringing together the region shapes from the Natural Earth dataset with the regions provided in the GeoCityLite database. In the GeoCityLite db, the regions are referenced by a two-letter ID (FIPS10-4 for some countries, ISO3366-2 for others). Initially I thought that those IDs would be same as [...]]]></description>
			<content:encoded><![CDATA[<p><!--:en-->In this post I'll describe the process of bringing together the region shapes from the Natural Earth dataset with the regions provided in the GeoCityLite database. In the GeoCityLite db, the regions are referenced by a <a href="http://www.maxmind.com/app/fips10_4">two-letter ID</a> (FIPS10-4 for some countries, ISO3366-2 for others). Initially I thought that those IDs would be same as used in the Geonames <a href="http://download.geonames.org/export/dump/admin1CodesASCII.txt">admin-level 1 region db</a>, which brought me to the first idea of mapping the regions via name similarity.</p>
<p><!--:--><span id="more-2939"></span><!--:en--></p>
<h2>Trying to match via region names..</h2>
<p>So, the basic idea was to use the region meta-data in the Geonames db to map the Natural Earth regions with the GeoCityLite db. Both Geonames and Natural Earth store different versions of the region names that could be thrown into a word similarity measurement like Levenshtein to compute similarities. Before I actually started to matches the regions I grabbed some statistics on the distribution of countries and region in both datasets. Here's a summary of the results:</p>
<p><img class="aligncenter" style="margin-top: 10px; margin-bottom: 10px;" title="overview" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-26-um-23.09.52.png" alt="" width="444" height="180" />In all datasets combined, a total of <strong>237</strong> countries is included. Of those countries, only <strong>90</strong> have the same number of region polygons and region meta-data (shown in green). The red slices represent countries that still have polygons and meta-data available, but there is either a surplus in region meta-data (<strong>46</strong>) or in region polygons (<strong>35</strong>). Finally, the blue slices represent countries that have no region polygons at all (<strong>60</strong>) or that have polygons but no meta-data (<strong>6</strong>).</p>
<p>To learn a little more about those countries, I displayed the results on a world map:</p>
<p><a href="http://vis4.net/blog/wp-content/uploads/2011/10/region-merging-map-01.png"><img class="aligncenter size-medium wp-image-3050" title="region-merging-map-01" src="http://vis4.net/blog/wp-content/uploads/2011/10/region-merging-map-01-522x291.png" alt="" width="522" height="291" /></a></p>
<p>However, I started the matching and created this more detailed map of coherence between Geonames and Natural Earth regions. Green means perfect matching (no regions left on both sides) while dark red means that almost no region could be mapped properly. The map reveals the differences between France and UK. In one case, only some island regions are missing while in the other case there's a complete mismatch between regions.</p>
<p><a href="http://vis4.net/blog/wp-content/uploads/2011/10/ne-admin1.png"><img class="aligncenter size-medium wp-image-3055" title="ne-admin1" src="http://vis4.net/blog/wp-content/uploads/2011/10/ne-admin1-522x269.png" alt="" width="522" height="269" /></a></p>
<h2>..and what I learned from this</h2>
<p>The good side of trying out the region name matching was that this made me aware of the UK case, which fundamentally changed my initial assumptions. To begin with, here's the region map of the UK regions in the Natural Earth shapefile:</p>
<p><img class="aligncenter size-full wp-image-3058" title="UK regions" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-31-um-13.53.58.png" alt="" width="520" height="356" /></p>
<p>The first thing I learned is that the Natural Earth regions are very detailed in some cases. In fact, they're too detailed to be useful for our purpose. The regions simply get too small to be clicked. The second thing I learned is that Geonames and Natural Earth have different definitions of administrative level 1 regions. While Geonames sees England, Scotland, Wales and Northern Ireland as first level regions, Natural Earth goes down to a very detailed level shown above. Then I checked if the GeoCityLite database also stores just the four UK "regions", which would be very sad because they're too general. And surprise, GeoCityLite uses different regions for UK.</p>
<p>The implications are that we need a completely different way of matching and that we need to merge regions in some countries to a reasonable degree.</p>
<h2>Merging too detailed regions</h2>
<p>To find out which countries regions need to be merged I looked at the countries that have the highest number of regions. This involves United Kingdom, Slovenia, Philippines, Macedonia and Uganda.</p>
<p>To merge the regions I used meta-data I (fortunately) found in the Natural Earth shapefile. For instance, the UK provinces (e.g. Derby) stored the name of the region they belong to (e.g. East Midlands). The actual merging was done by my good friend the Python Polygon package.</p>
<p>However, for Macedonia, the region names turned out to be incomplete, so I had to match the regions by hand using an overlay image I took from Wikipedia. Here's a screencast that shows me doing this:</p>
<p><object style="height: 300px; width: 520px;" width="520" height="300" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="src" value="http://www.youtube.com/v/QB2iLqT7NBw?version=3&amp;feature=player_detailpage" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><embed style="height: 300px; width: 520px;" width="520" height="300" type="application/x-shockwave-flash" src="http://www.youtube.com/v/QB2iLqT7NBw?version=3&amp;feature=player_detailpage" allowFullScreen="true" allowScriptAccess="always" allowfullscreen="true" allowscriptaccess="always" /></object></p>
<p>At the end, I got a CSV files that stores all regions that need to be joined along with the new region id. The data is used both for rendering the maps and in the next step of matching the GeoIP regions. For instance, here's how the merged regions look like for the United Kingdom:</p>
<p><img class="aligncenter size-full wp-image-3076" title="UK" src="http://vis4.net/blog/wp-content/uploads/2011/11/UK.png" alt="" width="332" height="317" /></p>
<h2>And, finally, matching the regions</h2>
<p>Since the definitions of what's an admin-level 1 region seem to vary across Natural Earth and GeoCityLite, I needed to use a more low-level approach to match the regions. Fortunately, the GeoCityLite db stores plenty of locations for each regions, which I used to match.</p>
<p>For every <em>source region</em> (GeoCityLite) I randomly selected five locations. Then I did point-in-polygon tests for each location with each of the <em>target regions</em> (Natural Earth), and the region that contains most of the locations probably is the one that matches the region.</p>
<p>I checked the accuracy by counting the number of matched regions:</p>
<ul>
<li>regions that matches to <strong>no</strong> target region: 73</li>
<li>regions that match to exactly <strong>one</strong> region: 2434</li>
<li>regions that match to <strong>two</strong> regions: 437</li>
<li>regions that match to <strong>three or more</strong> regions: ~140</li>
</ul>
<p>The cause of the <strong>missing matches</strong> could be that some regions are simply missing in the map (e.g. small island polygons). I think those cases can be ignored. Another reason could be that a country has been split up recently (like Sudan) and thus some regions couldn't be found.</p>
<p>The cause of the <strong>multiple matches</strong> is that some GeoCityLite regions are more general (or larger) than the regions in the Natural Earth map and one solution would be to merge the affected regions.</p>
<h2>Investigating the multiple matches</h2>
<p>So, again, let's look at the details of what we've done. Here's a close-up view on a case in Northern Germany where multiple map regions where matched to the same GeoLiteCity region. Obviously, in this case the cause is imprecision of the Natural Earth shapefile.</p>
<p><img class="aligncenter size-full wp-image-3071" title="region-matching-bremen" src="http://vis4.net/blog/wp-content/uploads/2011/10/region-matching-bremen.png" alt="" width="522" height="246" /></p>
<p>Here's another example, showing the Northern Switzerland where a region matched to four map regions. We can see that there are errors in the GeoLiteCity locations, too.</p>
<p><img class="aligncenter size-full wp-image-3072" title="region-match-che" src="http://vis4.net/blog/wp-content/uploads/2011/10/region-match-che.png" alt="" width="521" height="194" /></p>
<p>Looking at those examples, I think the best idea is to simply take the region that matches the most locations. We'll hopefully get some more feedback from local Piwik users.</p>
<p>As suspected, the locations for South Sudan still are assigned to Sudan in the GeoLiteCity database, which is going to be fixed.</p>
<p><img class="aligncenter size-full wp-image-3073" title="sudan" src="http://vis4.net/blog/wp-content/uploads/2011/10/sudan.png" alt="" width="247" height="293" /></p>
<p>At the end, I'm quite happy with this solution. Next time, I will start to write the JS code that "renders" the SVG maps using RaphaelJS and that also allows to display some data – either region-based or location-based – in the map.</p>
<h2 id="update">Update 1</h2>
<p>I further investigated the "missing" regions that can't be matched to any of the map polygons. For instance, here's a close-up view on Southern England, where some regions couldn't be matched. The reason is that the locations are a little bit outside the polygon.</p>
<p><img class="aligncenter size-full wp-image-3081" title="gbr-missing2" src="http://vis4.net/blog/wp-content/uploads/2011/11/gbr-missing2.png" alt="" width="367" height="129" /></p>
<p>The core of the problem is that the data is not accurate enough: either the region outline or the location coordinates are wrong. An easy fix is to build this inaccuracy into the matching algorithm. Since we don't know the <em>exact</em> coordinates of a given GeoIP location, the same location could as well be moved (slightly) in any direction. The algorithm now also checks the neighborhood for each point, which reduced the number of missing regions to 14.</p>
<p><img class="aligncenter size-full wp-image-3082" title="jittering" src="http://vis4.net/blog/wp-content/uploads/2011/11/Bildschirmfoto-2011-11-03-um-10.41.16.png" alt="" width="328" height="150" /></p>
<p>The remaining missing regions are due to missing polygons for small island parts of countries like the United States or Finland:</p>
<p><a href="http://vis4.net/blog/wp-content/uploads/2011/11/Bildschirmfoto-2011-11-03-um-13.11.37.png"><img class="aligncenter size-full wp-image-3087" title="Missing regions in Southern Finland" src="http://vis4.net/blog/wp-content/uploads/2011/11/Bildschirmfoto-2011-11-03-um-13.11.37.png" alt="" width="422" height="118" /></a><!--:--></p>
]]></content:encoded>
			<wfw:commentRss>http://vis4.net/blog/posts/piwik-matching-regions/?piwik_campaign=rss&#038;piwik_kwd=2939/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<series:name><![CDATA[Recreating the Piwik Maps in SVG]]></series:name>
	</item>
		<item>
		<title>Rendering SVG Country Maps in Python</title>
		<link>http://vis4.net/blog/posts/rendering_country_maps/?piwik_campaign=rss&#038;piwik_kwd=3020</link>
		<comments>http://vis4.net/blog/posts/rendering_country_maps/?piwik_campaign=rss&#038;piwik_kwd=3020#comments</comments>
		<pubDate>Wed, 26 Oct 2011 09:30:09 +0000</pubDate>
		<dc:creator>Gregor Aisch</dc:creator>
				<category><![CDATA[cartography]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://vis4.net/blog/posts/rendering_country_maps/</guid>
		<description><![CDATA[In this post I will explain the rendering process of the Piwik country maps. The results will basically look like this: Getting the viewport The first step of drawing a map is to define the viewport, which is a rectangle containing the geometry that will be visible in each map. To simplify things a lot, [...]]]></description>
			<content:encoded><![CDATA[<p><!--:en-->In this post I will explain the rendering process of the Piwik country maps. The results will basically look like this:</p>
<p><img title="FR" src="http://vis4.net/blog/wp-content/uploads/2011/10/FR1.png" alt="" width="520" height="311" /><br />
<span id="more-3020"></span></p>
<h2>Getting the viewport</h2>
<p>The first step of drawing a map is to define the viewport, which is a rectangle containing the geometry that will be visible in each map. To simplify things a lot, we decided to use a fixed aspect ratio for the maps. What we need next is a way to get the outer bounding box of the countries.</p>
<p>The simple approach is to use the lat/lon bounding box, but this actually doesn't work for the orthographic projection, as the minimum latitude of a country doesn't necessarily correspond with the minimum y of the projected country. For instance, the following image shows the lat/lon bounding box of China:</p>
<p><img title="lat/lon bbox china" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-24-um-13.19.37.png" alt="" width="522" height="342" /></p>
<p>Instead, you need to project the polygons first, and then compute the bounding box in x/y space. As explained in the last post, I will use the orthographic projection. To avoid distortions and ensure that the country is fully visible, the <strong>projection must be centered on the target country</strong>. For the computation of the center latitude and longitude I used the Polygon package (a Python wrapper around the GPC library) which provides a function for computation of center of gravity (also called <em>centroids</em>). <a href="https://gist.github.com/1315736">Code</a>.</p>
<p>Now we can project the country polygons and compute the exact bounding box, which then can be used to scale and position the map inside the viewport. Here's the bounding box of China, again:</p>
<p><img title="correct bounding box of China" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-24-um-13.22.05.png" alt="" width="522" height="331" /></p>
<p>However, while this approach works for most countries, other countries are a bit trickier to handle. As you can see in the next image, Spain has one big polygon commonly known as Spain along with several little polygons (islands) which located far away of the main country. This leads to a much bigger bounding box that we want, since in our scenario, we want the maps to be focused on the major part of each country. We have to find a better way to compute the bounding box.</p>
<p><img title="full bounding box of Spain" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-24-um-13.25.49.png" alt="" width="522" height="274" /></p>
<p>My first thought was to only consider the largest polygon when computing the bounding box. The shape area computation is done by a nice algorithm I found <a href="http://forum.worldwindcentral.com/showthread.php?t=20724">here</a> and was ported to Python <a href="https://gist.github.com/1267470">there</a>. Again, this works in many cases (like Spain, USA), but it won't work in other cases, like New Zealand:</p>
<p><img title="Wrong bounding box of New Zealand" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-24-um-13.38.23.png" alt="" width="522" height="264" /></p>
<p>My final solution was to compare each country polygon area with the maximum polygon area of that country. Every polygon below a fixed threshold (for instance 10% of the maximum polygon area) is discarded, which seem to work out very well. For five countries (including Japan), I had to manually change the threshold to get a nicer bounding box, which was faster and easier than spending more time in algorithm improvements.</p>
<p>&nbsp;</p>
<h2>Projection</h2>
<p>Now we can proceed with the actual projection of all visible countries. For the orthographic projection, I initially wanted to use the <a href="http://code.google.com/p/pyproj/">pyproj package</a>, a wrapper around the de facto 'gold standard' PROJ.4. Unfortunately, the design of the map requires that the client-side also needs to be able to perform projections. Therefore, I would have to port parts of the PROJ.4 code to JavaScript, which is something <a href="http://vis4.net/blog/posts/as3-proj/">I've done before in ActionScript</a> and actually don't want to do again. Instead, I found a nice and simple <a href="http://www.mccarroll.net/snippets/svgworld/#orthographic">Python implementation of the orthographic projection</a> by <a href="http://www.mccarroll.net">Niall McCarroll</a>, which was very easy to port to JavaScript.</p>
<p>Using this class it was very easy to render such a beautiful globe in SVG:</p>
<p><a href="http://vis4.net/tmp/globe.svg"><img title="globe" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-24-um-14.08.48.png" alt="" width="412" height="410" /></a></p>
<p>The bounding boxes computed in the last step are now used to detect all countries that (probably) intersect our viewport. This is important to know because we need to reduce the data to keep the rendering time low. To give the country a little more context, the bounding boxes are inflated by 30% and then centered inside the viewport. Now every countrys bounding box is checked for  intersections with the viewport.</p>
<p><img title="viewport" src="http://vis4.net/blog/wp-content/uploads/2011/10/viewport1.png" alt="" width="401" height="241" /></p>
<p>The basic idea now is to render the inner-country administrative boundaries for the focus country and the national boundaries for the neighbor countries. At the end of this step, we end up with a map like this.</p>
<p><img title="France, unclipped" src="http://vis4.net/blog/wp-content/uploads/2011/10/Bildschirmfoto-2011-10-24-um-14.31.04.png" alt="" width="522" height="445" /></p>
<h2>Polygon simplification and clipping</h2>
<p>What you can't see in the above screenshot is that the resulting SVG file has a size of 578kB, which is way to much given the fact that we need to store more than 200 maps. I actually agreed with the Piwik core developers that the zipped final size of the SVG maps should not 1 megabyte. So, what we need to do is to simplify or <em>generalize</em> the polygons.</p>
<p>There are several algorithms for polygon simplification, like the ones by <a href="http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm">Douglas &amp; Peuker</a> and <a href="http://www2.dcs.hull.ac.uk/CISRG/projects/Royal-Inst/demos/mahes.html">Visvalingam</a>, but I used a very fast and simple one. Actually, it's the same algorithm that <a href="http://vis4.net/blog/posts/simplification-of-country-outlines/">I used a few years ago</a>, when I <a href="http://vis4.net/blog/posts/rendering-world-maps-in-as3/">rendered my first world maps in Flash</a>.</p>
<p><img title="simplification" src="http://vis4.net/blog/wp-content/uploads/2011/10/simplification.png" alt="" width="522" height="400" /></p>
<p>Since we don't need the surrounding countries in the same quality as the focus country, I used different qualities for the simplification, which is great for reducing the map size. After simplification, the file size has reduced to 21kB or 3%.</p>
<p>The next task was to throw away everything that lays outside of the viewport. It's important to do this after simplification since otherwise the simplification could destroy the viewport borders.</p>
<p>To clip the polygons to the viewport I used the <a href="http://polygon.origo.ethz.ch/">Python Polygon package</a> again, which allows for syntactic sugar like this:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="python"><pre class="de1">clippedPoly <span class="sy0">=</span> polygon &amp;amp<span class="sy0">;</span> viewport</pre></div></div></div></div></div></div></div>


<h2>Conclusion</h2>
<p>After clipping, I rendered the polygons as <a href="http://vis4.net/tmp/FRA.svg">SVG file</a> to get the final map. The final <a href="http://vis4.net/tmp/all_maps.zip">zip-file with  all 238 country maps</a> is 840kB.</p>
<p><img title="FR" src="http://vis4.net/blog/wp-content/uploads/2011/10/FR1.png" alt="" width="520" height="311" /></p>
<p>In the next post I will write about how to connect the region ids of the Natural Earth shapefile with the region ids of the GeoIP database.</p>
<h2>Update (Nov. 3)</h2>
<p>Instead of the Orthographic projection, the maps now are projected using the <a href="http://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection_4nAJ9A&amp;sig2=UXwl4kHA1ZFdFtU-eVaT3w&amp;cad=rja">Lambert Azimuthal Equal Area</a> projection, which is widely used in cartography. For most countries, the differences are invisible, but the surrounding of larger countries like Russia look much less distorted.<!--:--></p>
]]></content:encoded>
			<wfw:commentRss>http://vis4.net/blog/posts/rendering_country_maps/?piwik_campaign=rss&#038;piwik_kwd=3020/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<series:name><![CDATA[Recreating the Piwik Maps in SVG]]></series:name>
	</item>
		<item>
		<title>Moving on with the Piwik Maps</title>
		<link>http://vis4.net/blog/posts/moving-on-with-the-piwik-maps/?piwik_campaign=rss&#038;piwik_kwd=2874</link>
		<comments>http://vis4.net/blog/posts/moving-on-with-the-piwik-maps/?piwik_campaign=rss&#038;piwik_kwd=2874#comments</comments>
		<pubDate>Thu, 06 Oct 2011 22:56:50 +0000</pubDate>
		<dc:creator>Gregor Aisch</dc:creator>
				<category><![CDATA[cartography]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[sketch]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://vis4.net/blog/posts/moving-on-with-the-piwik-maps/</guid>
		<description><![CDATA[Here's a short documentation of my current progress in re-creating the Piwik maps in SVG/JS. Since we're going to create at least one pre-generated map source file per country, file size is going to be an important issue. That's why we thought about removing all polygons whose area is below a certain threshold, say 10 [...]]]></description>
			<content:encoded><![CDATA[<p><!--:en-->Here's a short documentation of my current progress in <a href="http://vis4.net/blog/posts/recreating-the-piwik-map/">re-creating the Piwik maps in SVG/JS</a>. Since we're going to create at least one pre-generated map source file per country, file size is going to be an important issue. That's why we <a href="http://dev.piwik.org/trac/ticket/1652#comment:26">thought about</a> removing all polygons whose area is below a certain threshold, say 10 square km.<br />
<!--:--><span id="more-2874"></span><!--:en--></p>
<h2>What to do with those tiny island states?</h2>
<p>To get started I opened a world shapefile and computed the areas of all 3761 polygons (multiple polygons per countries). Filtering every polygon smaller than 10 square km would remove 434 polygons, or 11% of the polygons. I checked the names of the countries which the removed polygons belong to and found many island states among them (as expected).</p>
<p>However, I think a hard cut at 10 sq km has some problems: One the one hand it removes some countries entirely (like the Maldives Islands) while still keeping many very many small islands (starting from 11sqkm). Click image to zoom.</p>
<p><a href="http://vis4.net/tmp/world-hardcut.png"><img class="  alignnone" title="World minus every polygon smaller than 50 square km" src="http://vis4.net/tmp/world-hardcut.png" alt="" width="511" /></a></p>
<p>Then I tried a different rule: remove all polygons smaller than 5% of the maximum polygon area of that country. Since the maximum area of the Maldives is 9sqkm, all islands are kept, while all small islands of the USA are removed. Also this halved the resulting SVG file size keeping the map correct in terms of includedness of countries.</p>
<p><a href="http://vis4.net/tmp/world-softcut.png"><img class="  alignnone" title="World minus every polygon smaller 1% of the maximum country area" src="http://vis4.net/tmp/world-softcut.png" alt="" width="511" /></a></p>
<p>However, removing every polygon smaller than 5% of the maximum per country is not satisfactory as well. Big and also well-known islands like Hawaii or <a href="http://en.wikipedia.org/wiki/Novaya_Zemlya"> Novaya Zemlya</a> shouldn't be removed in my opinion. Finally I came up with a combination of both rules: remove every polygon smaller than the minimum of x square km and 5% of the maximum area per country. This removes the small and unimportant islands but keeps the tiny island states.</p>
<p><a href="http://vis4.net/tmp/maldives.png"><img class="alignleft" src="http://vis4.net/tmp/maldives.png" alt="" width="80" height="264" /></a></p>
<p>Being very happy with this solution I looked at the Maldives islands for the first time (those black little dots on the left). Obviously, the islands seem to be way too small to be rendered in a meaningful way. This leads to this important question:</p>
<p><em>Does the map need to include all countries or is it acceptable to ignore some countries, like the Maldives Islands? </em></p>
<p>In the old map widget this wasn't important because there was no country level view.</p>
<h2 style="clear: both;">One projection for all countries?</h2>
<p>A few thoughts regarding the projection that's going to be used for the country-level views. I would like to simplify the whole thing by using the same projection for every country, but with different parameters (namely the projection center).</p>
<p>One of the simpler projections is the <a href="http://en.wikipedia.org/wiki/Orthographic_projection_%28cartography%29">orthographic projection</a>, which looks the same as if looking onto a 3D globe. This gives quite good and less-distorted views for almost every country. The only country that looks quite distorted is Russia.</p>
<p><a href="http://vis4.net/tmp/russia.png"><img class="alignnone" title="Russia is getting a bit distorted in the orthographic projection" src="http://vis4.net/tmp/russia.png" alt="" height="264" /></a></p>
<p>My opinion is that this distortion is acceptable given the simplicity of rendering maps in a single projection. In an ideal mapping world, however, one would use specialized projection for every country, like the New Zealand Map Grid for New Zealand etc.</p>
<h2>Fixed vs. dynamic aspect ratio</h2>
<p>The next design decision has to do with the aspect ratio of the maps. At some point in the map generation process I need to crop the country-level maps to a bounding box. The idea is to fit the countries as big as possible into the views while also showing a bit of their neighbour countries for navigation.</p>
<p>Now the question arises to which aspect ratio the maps should be cropped. As far as I see do we have to options:</p>
<ol>
<li>A <strong>fixed aspect ratio</strong> (presumably some wide-screenish format) would be the simplest solution. The complete cropping could be done in the preprocessing stage, which would reduce complexity of map rendering. However, the obvious drawback of this solution is that the maps won't look as good as they could when displaying countries in the "opposite" aspect ratio. For some portrait countries (like <a href="http://vis4.net/tmp/germany.png">Germany</a>), this should be acceptable. For other, more extreme aspect ratios (my favourite example is <a href="http://vis4.net/tmp/chile.png">Chile</a>) don't.</li>
<li>In the latter cases, a <strong>dynamic aspect ratio</strong> would make much more sense. It would be perfect if we could present the Chilean Piwik users (I assume there are some) with a portrait view of their country. Dynamic aspect ratios could be done either by choosing a fixed ratio per country or by cropping the maps at rendering time. Choosing a fixed ratio per country has the drawback that those countries could not fit a landscape ratio in fullscreen mode. In contrast, cropping at runtime may take more CPU. Also, I don't know if the current dashboard supports dynamic resizing of widgets, but this may be easy to implement.</li>
</ol>
<p><a href="http://vis4.net/tmp/chilean-dashboard.png"><img src="http://vis4.net/tmp/chilean-dashboard.png" width="522" title="Chilean users would love dynamic aspect ratios" /></a><!--:--></p>
]]></content:encoded>
			<wfw:commentRss>http://vis4.net/blog/posts/moving-on-with-the-piwik-maps/?piwik_campaign=rss&#038;piwik_kwd=2874/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<series:name><![CDATA[Recreating the Piwik Maps in SVG]]></series:name>
	</item>
		<item>
		<title>Finding a Good Data Source For a World Map</title>
		<link>http://vis4.net/blog/posts/recreating-the-piwik-map/?piwik_campaign=rss&#038;piwik_kwd=2785</link>
		<comments>http://vis4.net/blog/posts/recreating-the-piwik-map/?piwik_campaign=rss&#038;piwik_kwd=2785#comments</comments>
		<pubDate>Mon, 26 Sep 2011 08:42:56 +0000</pubDate>
		<dc:creator>Gregor Aisch</dc:creator>
				<category><![CDATA[cartography]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://vis4.net/blog/?p=2785</guid>
		<description><![CDATA[Last year I contributed a Flash-based world map to the open source Piwik Web Analytics project. At that time, all of the Piwik charts where rendered in Flash, but as with release 1.5, all Flash charts were replaced with JS/canvas charts. Now, my map plugin is the only Flash component in the whole project, which [...]]]></description>
			<content:encoded><![CDATA[<p>Last year I contributed a <a href="http://piwik.org/blog/2010/08/open-source-world-map-for-piwik/">Flash-based world map</a> to the open source <a href="http://piwik.org">Piwik</a> Web Analytics project. At that time, all of the Piwik charts where rendered in Flash, but as with release 1.5, all Flash charts were <a href="http://piwik.org/blog/2011/06/piwik-innovative-with-javascript-canvas-chart-and-contributing-by-jqplot-creator/">replaced with JS/canvas charts</a>. Now, my map plugin is the only Flash component in the whole project, which is why they asked me if I could rewrite it in SVG. I will take some time in October to do this and I want to write here about my work. In contrast to many past projects where I wrote making-of like posts <em>after</em> finishing, I now want to write while working – literally. This kind of invites everyone interested to take part in the design process and, besides of that, provides a good amount of documentation and tutorial-like resources.</p>
<p><span id="more-2785"></span></p>
<p>So, let's start the project. A year ago, I already wrote a <a href="http://vis4.net/blog/en/posts/piwik-maps-2/">proposal for some new features for the Piwik map</a>, which I now want to implement. The first part of the work will be to prepare the maps, the second part will be to render the maps using SVG and, presumably, a JS/SVG framework like RaphaelJS. The process underlying the first part looks like this:</p>
<p><img class="aligncenter size-full wp-image-2830" title="process" src="http://vis4.net/blog/wp-content/uploads/2011/09/Bildschirmfoto-2011-09-26-um-10.29.58.png" alt="" width="512" height="78" /></p>
<p>In this post I will focus on the initial step, the map data source.</p>
<h2>Finding a map data source</h2>
<p>The biggest challenge was to find map data of inner-country regions (like the <a href="http://en.wikipedia.org/wiki/States_of_the_United_States">U.S. states</a>, the German <a href="http://en.wikipedia.org/wiki/States_of_Germany">Bundesl&auml;nder</a> or the <a href="http://en.wikipedia.org/wiki/Regions_of_France">Regions of France</a>) for <em>all</em> countries of the world. Initially I thought of extracting the administrative boundaries from the huge OpenStreetMap database, but this is not as easy as it sounds. Finally I decided to look for other data sources – and succeeded. I found gadm.org, a website whose entire purpose is to provide <del>free</del> map files of the <a href="http://gadm.org">worlds administrative boundaries</a> and they had exactly what I was looking for: a shapefile of all countries with their first-level sub-divisions.</p>
<p><em>Update</em>: I will use the Natural Earth shapefile instead of GADM, see below.</p>
<p><a href="http://gadm.org/img/gadm_v1_level1_high.png"><img class="alignnone" src="http://gadm.org/img/gadm_v1_level1_low.png" alt="" /></a></p>
<h2>Handling huge shapefiles</h2>
<p>After unpacking, the shapefiles where 439MB big. I wanted to know a little more about the data before starting to actually work with them, so I wrote a small Python script to get some information. There are plenty of Python libraries for reading shapefiles, I kind of like <a href="http://packages.python.org/Python%20Shapefile%20Library/">this one</a>, mostly because its simplicity. So here's the script:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="python"><pre class="de1"><span class="kw1">import</span> shapefile<span class="sy0">,</span> <span class="kw3">csv</span>
<span class="co1"># create a new csv file for writing and write csv header</span>
csvout <span class="sy0">=</span> <span class="kw3">csv</span>.<span class="me1">writer</span><span class="br0">&#40;</span><span class="kw2">open</span><span class="br0">&#40;</span><span class="st0">'shp_meta.csv'</span><span class="sy0">,</span> <span class="st0">'w'</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
<span class="co1"># open the shapefile and read records</span>
sf <span class="sy0">=</span> shapefile.<span class="me1">Reader</span><span class="br0">&#40;</span><span class="st0">'gadm1_lev1.shp'</span><span class="br0">&#41;</span>
shape_records <span class="sy0">=</span> sf.<span class="me1">shapeRecords</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp;
<span class="kw1">for</span> record <span class="kw1">in</span> shape_records:
	numPoints <span class="sy0">=</span> <span class="kw2">len</span><span class="br0">&#40;</span>record.<span class="me1">shape</span>.<span class="me1">points</span><span class="br0">&#41;</span>
	numParts <span class="sy0">=</span> <span class="kw2">len</span><span class="br0">&#40;</span>record.<span class="me1">shape</span>.<span class="me1">parts</span><span class="br0">&#41;</span>
	shapeMeta <span class="sy0">=</span> record.<span class="me1">record</span>
	csvout.<span class="me1">writerow</span><span class="br0">&#40;</span><span class="br0">&#91;</span> numParts<span class="sy0">,</span> numPoints<span class="br0">&#93;</span> + record.<span class="me1">record</span><span class="br0">&#41;</span></pre></div></div></div></div></div></div></div>


<p>The first interesting fact is that executing the script took my Macbook a little more than three minutes and the RAM usage peaked at something around 3GB(!). This means that the script might not run on some machines. Maybe I should consider trying out other shapefile libraries later, to test their shapefile reading performance.</p>
<h2>Analyzing the map data</h2>
<p>However, here's the <a href="http://vis4.net/blog/wp-content/uploads/2011/09/shp_meta.csv">resulting csv file</a>. The most interesting thing about it is what is stored in the shape record dBase metadata. Fortunately, the shapefile stores the <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-3">countries ISO codes</a>, which I need for mapping them to the country ids used in Piwik. Next good thing is that we also have official two-letter codes and names language for the level-1 boundaries. The names are provided both in English and in the countries local language, which is very good since we can use them later for the map rendering.</p>
<p>Another thing of great interest is the quality of the shapefile. The big file size is a first indicator for a high level of detail, but I wanted to know more about it. This is why I added two more columns: the total number of points per shape and the number of shape parts (a shape may contain multiple parts, which are polygons in our case). To analyze this data in more detail, I loaded the csv file into <a href="http://www.r-project.org/">R</a>, which is a great (if not the best) software for data analyzing:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="r"><pre class="de1">data = read.csv('shp_meta.csv', header=TRUE)
pc = data$point.count
sc = data$shape.count
country = data$iso3
region = data$region.code</pre></div></div></div></div></div></div></div>


<p>The first thing I looked at was the total number of points in the shapefile, which is 28.5 million points(!) or something around 8500 points per shape.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="r"><pre class="de1">&amp;gt; sum(pc)
[1] 28565495
&amp;gt; mean(pc)
[1] 8486.481
&amp;gt; plot(pc)</pre></div></div></div></div></div></div></div>


<p>However, after looking at the scatterplot of point counts I saw that only few of the shapes seem to have that much of points.</p>
<p><img class="aligncenter size-full wp-image-2807" title="plot-1" src="http://vis4.net/blog/wp-content/uploads/2011/09/Bildschirmfoto-2011-09-25-um-23.12.02.png" alt="" width="508" height="344" /></p>
<p>Here's a look at the distributions of parts per shape, points per shape, points per part and points per country. I used a boxplot of the log10 counts for each measurement. One can now see, that the median of parts per shape is at 10^0, which means that at least half of the shapes consist of only one part. However there are some shapes that are made up of almost 10^4 or 10000 shapes, which I assume to be near pole-regions that contain many small island-like parts. The points per shape has it's median at 10^3 or 1000 with a few outliers to the top. Points per country peaks at 10^6, which means we have to deal with up to one million points per country.</p>
<p><img class="aligncenter size-full wp-image-2827" title="boxplot" src="http://vis4.net/blog/wp-content/uploads/2011/09/Bildschirmfoto-2011-09-26-um-10.07.02.png" alt="" width="501" height="316" /></p>
<p>Of course I wanted to know what countries and regions are responsible for those outliers in point counts, so I replaced the dots with labels. Interestingly, it's Chile, Canada and the United States.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="r"><pre class="de1">plot(pc, col=&quot;white&quot;)
text(1:length(pc), pc, iso3)</pre></div></div></div></div></div></div></div>


<p><img class="aligncenter size-full wp-image-2809" title="plot-2" src="http://vis4.net/blog/wp-content/uploads/2011/09/Bildschirmfoto-2011-09-25-um-23.16.04.png" alt="" width="512" height="346" /></p>
<p>This is the region <a href="http://en.wikipedia.org/wiki/Magallanes_and_Ant%C3%A1rtica_Chilena_Region">Magallanes and Antártica Chilena</a>, which is the one with an outstanding number of about 1.2 million points.</p>
<p><img class="aligncenter size-full wp-image-2824" title="chile" src="http://vis4.net/blog/wp-content/uploads/2011/09/Bildschirmfoto-2011-09-26-um-00.32.331.png" alt="" width="324" height="400" /></p>
<p>One of the next smaller regions is Alaska, which has about 600 000 points.</p>
<p><img class="aligncenter size-full wp-image-2821" title="Bildschirmfoto 2011-09-26 um 00.26.32" src="http://vis4.net/blog/wp-content/uploads/2011/09/Bildschirmfoto-2011-09-26-um-00.26.32.png" alt="alaska" width="481" height="274" /></p>
<h2>Closing thoughts</h2>
<p>Since the admin-level 1 shapefile doesn't contain the admin-level 0 boundaries, at least not explicitly, we will either need to compute them using the "outer" regional boundaries of each country, or take them from the admin-level 0 shapefile from gadm.org.</p>
<p>Obviously, 1.2 mio points for a single shape are way too much for display in a browser using SVG. Therefore, in the next session we will proceed with map projection and the polygon simplification process. We will take a good look to Magallanes and Antártica Chilena then.</p>
<h3><em>Update (Oct, 25)</em></h3>
<p>Unfortunately, the GADM maps are licensed under a not-so-free license, which doesn't permits commercial use. Also, it seems that GADM doesn't update their shapefiles very frequently, since it still doesn't contain South Sudan. Fortunately, <cite>Manu</cite> told me about <a href="http://www.naturalearthdata.com/downloads/">Natural Earth</a>, a site which I doesn't only provide hi-res images of the earth (which I initially thought), but also provides detailed shapefiles. I checked the <a href="http://www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-admin-1-states-provinces/">Admin 1 - States and Provinces</a> shapefile and it contains South Sudan. And all data is licensed as public domain. That's not a great reason to switch. So, no GADM anymore.</p>
]]></content:encoded>
			<wfw:commentRss>http://vis4.net/blog/posts/recreating-the-piwik-map/?piwik_campaign=rss&#038;piwik_kwd=2785/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<series:name><![CDATA[Recreating the Piwik Maps in SVG]]></series:name>
	</item>
		<item>
		<title>Tutorial: Using the Bubble Tree for Display of Random Data</title>
		<link>http://vis4.net/blog/posts/tutorial-bubble-tree/?piwik_campaign=rss&#038;piwik_kwd=2695</link>
		<comments>http://vis4.net/blog/posts/tutorial-bubble-tree/?piwik_campaign=rss&#038;piwik_kwd=2695#comments</comments>
		<pubDate>Thu, 21 Jul 2011 10:28:24 +0000</pubDate>
		<dc:creator>Gregor Aisch</dc:creator>
				<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://vis4.net/blog/?p=2695</guid>
		<description><![CDATA[Recently I finished the work on the interactive Bubble Tree for the OpenSpending project. This tutorial demonstrates how to use the visualization for display of your own data. Here's the link to the resulting visualization. Setting up the page You need to checkout the Github repository or download the source files to go through this [...]]]></description>
			<content:encoded><![CDATA[<p><img src='http://vis4.net/blog/wp-content/plugins/simple-post-thumbnails/timthumb.php?src=net/blog/wp-content/thumbnails/2695.png&amp;w=250&amp;h=0&amp;zc=1&amp;ft=png' alt='post thumbnail' /></p>
<p><!--:en--><img src="http://vis4.net/blog/wp-content/uploads/2011/07/bubbletree-preview.png" alt="" title="bubbletree-preview" width="522" height="95" class="aligncenter size-full wp-image-2719" /></p>
<p>Recently I finished the work on the interactive Bubble Tree for the <a href="http://openspending.org">OpenSpending</a> project. This tutorial demonstrates how to use the visualization for display of your own data. Here's the <a href="http://clients.driven-by-data.net/okfn/bubbletree/demos/random/">link to the resulting visualization</a>.</p>
<p><!--:--><!--:de--><!--:--><span id="more-2695"></span><!--:en--></p>
<h2>Setting up the page</h2>
<p>You need to checkout the <a href="https://github.com/okfn/bubbletree/">Github repository</a> or <a href="https://github.com/okfn/bubbletree/zipball/master">download the source files</a> to go through this tutorial yourself. At first we're going to create the container HTML page.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="html"><pre class="de1">&lt;html&gt;
&lt;head&gt;
	&lt;meta charset=&quot;UTF-8&quot;/&gt;
	&lt;title&gt;Minimal BubbleTree Demo&lt;/title&gt;
	&lt;script type=&quot;text/javascript&quot; src=&quot;bubbletree/lib/jquery-1.5.2.min.js&quot;&gt;&lt;/script&gt;
	&lt;script type=&quot;text/javascript&quot; src=&quot;bubbletree/lib/jquery.history.js&quot;&gt;&lt;/script&gt;
	&lt;script type=&quot;text/javascript&quot; src=&quot;bubbletree/lib/raphael.js&quot;&gt;&lt;/script&gt;
	&lt;script type=&quot;text/javascript&quot; src=&quot;bubbletree/lib/vis4.js&quot;&gt;&lt;/script&gt;
	&lt;script type=&quot;text/javascript&quot; src=&quot;bubbletree/lib/Tween.js&quot;&gt;&lt;/script&gt;
	&lt;script type=&quot;text/javascript&quot; src=&quot;bubbletree/build/bubbletree.js&quot;&gt;&lt;/script&gt;
	&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;bubbletree/build/bubbletree.css&quot; /&gt;	
	&lt;script type=&quot;text/javascript&quot;&gt;
&nbsp;
		$(function() {
			new BubbleTree({
				data: data,
				container: '.bubbletree'
			});
		});
&lt;/script&gt;&lt;/head&gt;
&lt;body&gt;
	&lt;div class=&quot;bubbletree-wrapper&quot;&gt;
		&lt;div class=&quot;bubbletree&quot;&gt;&lt;/div&gt;
	&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</pre></div></div></div></div></div></div></div>


<p>&nbsp;</p>
<p>Now the BubbleTree would load and try to display what's inside the data, which we will build in the next step.</p>
<h2>Building the random data</h2>
<p>The next thing we do is to prepare a random data set. The BubbleTree uses a very simple nested tree structure. The nodes must follow the following format:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="javascript"><pre class="de1"><span class="kw2">var</span> data <span class="sy0">=</span> <span class="br0">&#123;</span>
	label<span class="sy0">:</span> <span class="st0">&quot;I am a node&quot;</span><span class="sy0">,</span>
	amount<span class="sy0">:</span> <span class="nu0">123456789</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<p>We could now run the visualization and would get the following result:</p>
<p><img src="http://vis4.net/blog/wp-content/uploads/2011/07/bubbletree-simple.png" alt="" title="bubbletree-simple" width="447" height="226" class="aligncenter size-full wp-image-2703" /></p>
<p>We can insert child nodes by putting them into the <em>children</em> Array:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="javascript"><pre class="de1"><span class="kw2">var</span> data <span class="sy0">=</span> <span class="br0">&#123;</span>
	label<span class="sy0">:</span> <span class="st0">&quot;I am a node&quot;</span><span class="sy0">,</span>
	amount<span class="sy0">:</span> <span class="nu0">1000000</span><span class="sy0">,</span>
	children<span class="sy0">:</span> <span class="br0">&#91;</span><span class="br0">&#123;</span>
		label<span class="sy0">:</span> <span class="st0">&quot;I am the 1st child&quot;</span><span class="sy0">,</span>
		amount<span class="sy0">:</span> <span class="nu0">400000</span>
	<span class="br0">&#125;</span><span class="sy0">,</span> <span class="br0">&#123;</span>
		label<span class="sy0">:</span> <span class="st0">&quot;I am the 2nd child&quot;</span><span class="sy0">,</span>
		amount<span class="sy0">:</span> <span class="nu0">600000</span>
	<span class="br0">&#125;</span><span class="br0">&#93;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<p><img src="http://vis4.net/blog/wp-content/uploads/2011/07/bubbletree-2children.png" alt="" title="bubbletree-2children" width="333" height="243" class="aligncenter size-full wp-image-2707" /></p>
<p>Instead of inserting all the children by hand, we will use a function to generate a random set of children for a given node.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="javascript"><pre class="de1"><span class="kw2">function</span> generateChildren<span class="br0">&#40;</span>node<span class="br0">&#41;</span> <span class="br0">&#123;</span>
	<span class="kw2">var</span> numChildren <span class="sy0">=</span> <span class="nu0">8</span><span class="sy0">;</span>
	node.<span class="me1">children</span> <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">;</span>
	<span class="kw2">var</span> amount <span class="sy0">=</span> node.<span class="me1">amount</span><span class="sy0">;</span>
	<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw2">var</span> i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;</span>numchildren <span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
		<span class="kw2">var</span> child <span class="sy0">=</span> <span class="br0">&#123;</span> 
			label<span class="sy0">:</span> <span class="st0">'Child #'</span><span class="sy0">+</span><span class="br0">&#40;</span>i<span class="sy0">+</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">,</span> 
			amount<span class="sy0">:</span> i<span class="sy0">+</span><span class="nu0">1</span> <span class="sy0">&lt;</span> numChildren <span class="sy0">?</span> 
				amount<span class="sy0">*</span>Math.<span class="me1">random</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">*</span>.6 <span class="sy0">:</span> amount
		<span class="br0">&#125;</span><span class="sy0">;</span>
		amount <span class="sy0">-=</span> child.<span class="me1">amount</span><span class="sy0">;</span>
		node.<span class="me1">children</span>.<span class="me1">push</span><span class="br0">&#40;</span>child<span class="br0">&#41;</span><span class="sy0">;</span>
	<span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">var</span> data <span class="sy0">=</span> <span class="br0">&#123;</span>
	label<span class="sy0">:</span> <span class="st0">&quot;I am a node&quot;</span><span class="sy0">,</span>
	amount<span class="sy0">:</span> <span class="nu0">1000000</span><span class="sy0">,</span>
<span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp;
generateChildren<span class="br0">&#40;</span>data<span class="br0">&#41;</span><span class="sy0">;</span></pre></div></div></div></div></div></div></div>


<p>Et voila, the result looks like this:</p>
<p><img src="http://vis4.net/blog/wp-content/uploads/2011/07/bubbletree-randomchildren.png" alt="" title="bubbletree-randomchildren" width="366" height="355" class="aligncenter size-full wp-image-2710" /></p>
<h2>Adding some color to the nodes</h2>
<p>This is the perfect time to setup some styling for the nodes. I will use my little helper library <em>vis4color</em> to generate nice random HSL colors.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="javascript"><pre class="de1">child.<span class="me1">color</span> <span class="sy0">=</span> vis4color.<span class="me1">fromHSL</span><span class="br0">&#40;</span>i<span class="sy0">/</span>numChildren<span class="sy0">*</span><span class="nu0">360</span><span class="sy0">,</span> .7<span class="sy0">,</span> .5<span class="br0">&#41;</span>.<span class="me1">x</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<h2>Generating children of children of children</h2>
<p>The last thing to do is to call our <em>generateChildren</em> recursively for each child node we generate. To know when to stop the recursion, we will also pass a new parameter <em>level</em> to the function.</p>
<p>For a little extra fun, I added an Array <em>randomNames</em> which contains a set of randomly chosen forenames. I will use this array to add random labels to the nodes.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="javascript"><pre class="de1"><span class="kw2">var</span> randomNames <span class="sy0">=</span> <span class="br0">&#91;</span><span class="st0">'Burgis'</span><span class="sy0">,</span> <span class="st0">'Pascal'</span><span class="sy0">,</span> <span class="st0">'Lysann'</span><span class="sy0">,</span> <span class="st0">'Theo'</span><span class="sy0">,</span> ...<span class="br0">&#93;</span><span class="sy0">;</span>
&nbsp;
<span class="kw2">function</span> generateChildren<span class="br0">&#40;</span>node<span class="sy0">,</span> level<span class="br0">&#41;</span> <span class="br0">&#123;</span>
	<span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span>level<span class="br0">&#41;</span> level <span class="sy0">=</span> <span class="nu0">1</span><span class="sy0">;</span>
	<span class="kw2">var</span> numChildren <span class="sy0">=</span> <span class="nu0">8</span><span class="sy0">;</span>
	node.<span class="me1">children</span> <span class="sy0">=</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">;</span>
	<span class="kw2">var</span> amount <span class="sy0">=</span> node.<span class="me1">amount</span><span class="sy0">;</span>
	<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw2">var</span> i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;</span>numchildren <span class="sy0">;</span> i<span class="sy0">++</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
		<span class="kw2">var</span> child <span class="sy0">=</span> <span class="br0">&#123;</span> 
			label<span class="sy0">:</span> randomNames<span class="br0">&#91;</span>Number<span class="br0">&#40;</span>String<span class="br0">&#40;</span>level<span class="sy0">-</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">+</span>String<span class="br0">&#40;</span>i<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#93;</span><span class="sy0">,</span> 
			amount<span class="sy0">:</span> i<span class="sy0">+</span><span class="nu0">1</span> <span class="sy0">&lt;</span> numChildren <span class="sy0">?</span> 
				amount<span class="sy0">*</span>Math.<span class="me1">random</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">*</span>.6 <span class="sy0">:</span> amount
		<span class="br0">&#125;</span><span class="sy0">;</span>
		<span class="kw1">if</span> <span class="br0">&#40;</span>level <span class="sy0">==</span> <span class="nu0">1</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
			<span class="co1">// random color for level 1 children</span>
			child.<span class="me1">color</span> <span class="sy0">=</span> vis4color.<span class="me1">fromHSL</span><span class="br0">&#40;</span>i<span class="sy0">/</span>numChildren<span class="sy0">*</span><span class="nu0">360</span><span class="sy0">,</span> .7<span class="sy0">,</span> .5<span class="br0">&#41;</span>.<span class="me1">x</span><span class="sy0">;</span>
		<span class="br0">&#125;</span>
		<span class="kw1">if</span> <span class="br0">&#40;</span>level <span class="sy0">==</span> <span class="nu0">2</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
			<span class="co1">// children of level 2 will get a similar color of their parent</span>
			child.<span class="me1">color</span> <span class="sy0">=</span> vis4color.<span class="me1">fromHex</span><span class="br0">&#40;</span>node.<span class="me1">color</span><span class="br0">&#41;</span>
				.<span class="me1">lightness</span><span class="br0">&#40;</span><span class="st0">'*'</span><span class="sy0">+</span><span class="br0">&#40;</span>.5<span class="sy0">+</span>Math.<span class="me1">random</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">*</span>.5<span class="br0">&#41;</span><span class="br0">&#41;</span>.<span class="me1">x</span><span class="sy0">;</span>
		<span class="br0">&#125;</span>
		amount <span class="sy0">-=</span> child.<span class="me1">amount</span><span class="sy0">;</span>
		node.<span class="me1">children</span>.<span class="me1">push</span><span class="br0">&#40;</span>child<span class="br0">&#41;</span><span class="sy0">;</span>
		<span class="kw1">if</span> <span class="br0">&#40;</span>level <span class="sy0">&lt;</span> <span class="nu0">4</span><span class="br0">&#41;</span> generateChildren<span class="br0">&#40;</span>child<span class="sy0">,</span> level<span class="sy0">+</span><span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
	<span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<p>&nbsp;</p>
<p>Here is our final output. Click on the image to get to the interactive demo. You will find all the source files inside the <em>demos/random/</em> folder in the <a href="https://github.com/okfn/bubbletree">Github repository</a>.</p>
<p><a href="http://clients.driven-by-data.net/okfn/bubbletree/demos/random/"><img src="http://vis4.net/blog/wp-content/uploads/2011/07/bubbletree-final.png" alt="" title="bubbletree-final" width="522" height="426" class="aligncenter size-full wp-image-2722" /></a><!--:--></numchildren></pre>
<p></numchildren></pre>
]]></content:encoded>
			<wfw:commentRss>http://vis4.net/blog/posts/tutorial-bubble-tree/?piwik_campaign=rss&#038;piwik_kwd=2695/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Tutorial: Rendering a World Map in ActionScript</title>
		<link>http://vis4.net/blog/posts/rendering-world-maps-in-as3/?piwik_campaign=rss&#038;piwik_kwd=1248</link>
		<comments>http://vis4.net/blog/posts/rendering-world-maps-in-as3/?piwik_campaign=rss&#038;piwik_kwd=1248#comments</comments>
		<pubDate>Mon, 26 Apr 2010 22:52:30 +0000</pubDate>
		<dc:creator>Gregor Aisch</dc:creator>
				<category><![CDATA[cartography]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[actionscript]]></category>
		<category><![CDATA[featured]]></category>
		<category><![CDATA[maps]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[projection]]></category>
		<category><![CDATA[shapefile]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[World]]></category>

		<guid isPermaLink="false">http://vis4.net/blog/?p=1248</guid>
		<description><![CDATA[This tutorial is the third part of a tutorial series about customized map projection in flash/actionscript. It demonstrates how to draw a world map using the as3-proj library and some helper classes. I assume that we read in a shapefile using the SHP library as shown in the previous tutorial. 1 Reading Shapefiles in PHP, [...]]]></description>
			<content:encoded><![CDATA[<div style="border: 3px double #dd7; background: #fafae8; padding: 20px 1em 0; margin: 0 0 23px; color: #661;">
<p>This tutorial is the third part of a tutorial series about customized map projection in flash/actionscript. It demonstrates how to draw a world map using the as3-proj library and some helper classes. <span style="display:none">I assume that we read in a shapefile using the SHP library as shown <a href="http://vis4.net/blog/2010/04/reading-shapefiles-in-as3/">in the previous tutorial</a>.</span><span id="more-1248"></span></p>
<p><a href="http://vis4.net/blog/2010/04/reading-esri-shapefiles-in-php"><strong>1</strong> Reading Shapefiles in PHP</a>, <a href="http://vis4.net/blog/2010/04/reading-shapefiles-in-as3/"><strong>2</strong> Reading Shapefiles in ActionScript</a>, <strong>3</strong> <span style="color:#000">Displaying a Static World Map in ActionScript</span>.
</div>
<p>This tutorial assumes that we read in a shapefile using the SHP library as shown <a href="http://vis4.net/blog/2010/04/reading-shapefiles-in-as3/">in the previous tutorial</a>. At first I present you a very quick and dirty way of rendering a map, which means that I tried to reduce dependencies to other classes or libraries and put almost all code in one function. On the next page you'll see a solution that are more (re-)usable.</p>
<h3>The Quick and Dirty Way</h3>
<p>After we read in the shapefiles, we're now going to project the coordinates. Therefore we must chose and initialize a map projection. I'm using the <a href="http://www.csiss.org/map-projections/Pseudocylindrical/Wagner_5.pdf">Wagner V projection</a> of the <a href="http://vis4.net/blog/2010/04/as3-proj/">as3-proj library</a>.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="actionscript3"><pre class="de1"><span class="kw2">var</span> projection<span class="sy0">:</span>Projection = <span class="kw1">new</span> Wagner5Projection<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
projection<span class="sy0">.</span>initialize<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre></div></div></div></div></div></div></div>


<p>The next thing we need to do is to calculate the range of the projected coordinates in order to map them to our desired map size. Since we don't know the output range of the projection formulas we initialize the variables for the minimum and maximum values with infinite numbers.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="actionscript3"><pre class="de1"><span class="kw2">var</span> min_x<span class="sy0">:</span><span class="kw5">Number</span> = <span class="kw5">Number</span><span class="sy0">.</span><span class="kw8">POSITIVE_INFINITY</span><span class="sy0">,</span> 
    max_x<span class="sy0">:</span><span class="kw5">Number</span> = <span class="kw5">Number</span><span class="sy0">.</span><span class="kw8">NEGATIVE_INFINITY</span><span class="sy0">,</span>
    min_y<span class="sy0">:</span><span class="kw5">Number</span> = <span class="kw5">Number</span><span class="sy0">.</span><span class="kw8">POSITIVE_INFINITY</span><span class="sy0">,</span> 
    max_y<span class="sy0">:</span><span class="kw5">Number</span> = <span class="kw5">Number</span><span class="sy0">.</span><span class="kw8">NEGATIVE_INFINITY</span><span class="sy0">;</span></pre></div></div></div></div></div></div></div>


<p>Now we iterate over all coordinates, project them with the <a href="http://api.vis4.net/as3-proj/index.html?net/vis4/map/proj/Projection.html&#038;net/vis4/map/proj/class-list.html" class="tt">Projection.project()</a> method and update the min/max values.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="actionscript3"><pre class="de1"><span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span><span class="kw2">var</span> record<span class="sy0">:</span>ShpRecord <span class="kw1">in</span> records<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>record<span class="sy0">.</span>shapeType == ShpType<span class="sy0">.</span>SHAPE_POLYGON<span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw2">var</span> poly<span class="sy0">:</span>ShpPolygon = record<span class="sy0">.</span>shape <span class="kw1">as</span> ShpPolygon<span class="sy0">;</span>
        <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span><span class="kw2">var</span> coords<span class="sy0">:</span><span class="kw5">Array</span> <span class="kw1">in</span> poly<span class="sy0">.</span>rings<span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span><span class="kw2">var</span> pt<span class="sy0">:</span>ShpPoint <span class="kw1">in</span> coords<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw2">var</span> o<span class="sy0">:</span><span class="kw5">Point</span><span class="sy0">;</span>
                o = projection<span class="sy0">.</span>project<span class="br0">&#40;</span>deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">x</span><span class="br0">&#41;</span><span class="sy0">,</span> deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw1">new</span> <span class="kw5">Point</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
                min_x = <span class="kw5">Math</span><span class="sy0">.</span><span class="kw7">min</span><span class="br0">&#40;</span>min_x<span class="sy0">,</span> o<span class="sy0">.</span><span class="kw7">x</span><span class="br0">&#41;</span><span class="sy0">;</span>
                max_x = <span class="kw5">Math</span><span class="sy0">.</span><span class="kw7">max</span><span class="br0">&#40;</span>max_x<span class="sy0">,</span> o<span class="sy0">.</span><span class="kw7">x</span><span class="br0">&#41;</span><span class="sy0">;</span>
                min_y = <span class="kw5">Math</span><span class="sy0">.</span><span class="kw7">min</span><span class="br0">&#40;</span>min_y<span class="sy0">,</span> o<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">;</span>
                max_y = <span class="kw5">Math</span><span class="sy0">.</span><span class="kw7">max</span><span class="br0">&#40;</span>max_y<span class="sy0">,</span> o<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="br0">&#125;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<p>Once we have the min/max values for x and y we can proceed with the map drawing. We need to scale and translate the projected coordinates to place them inside our desired map. The scale is calculated beforehand. The drawing is done using the <tt>moveTo()</tt> and <tt>lineTo()</tt> methods of the flash drawing api.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="actionscript3"><pre class="de1"><span class="kw2">var</span> w<span class="sy0">:</span><span class="kw5">Number</span> = <span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">stageWidth</span><span class="sy0">,</span> h<span class="sy0">:</span><span class="kw5">Number</span> = <span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">stageHeight</span><span class="sy0">;</span>
<span class="kw2">var</span> s<span class="sy0">:</span><span class="kw5">Number</span> = <span class="kw5">Math</span><span class="sy0">.</span><span class="kw7">min</span><span class="br0">&#40;</span>w <span class="sy0">/</span> <span class="br0">&#40;</span>max_x <span class="sy0">-</span> min_x<span class="br0">&#41;</span><span class="sy0">,</span> h <span class="sy0">/</span> <span class="br0">&#40;</span>max_y <span class="sy0">-</span> min_y<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// the scale</span>
&nbsp;
<span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>record <span class="kw1">in</span> records<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>record<span class="sy0">.</span>shapeType == ShpType<span class="sy0">.</span>SHAPE_POLYGON<span class="br0">&#41;</span> <span class="br0">&#123;</span>
        poly = record<span class="sy0">.</span>shape <span class="kw1">as</span> ShpPolygon<span class="sy0">;</span>
        <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>coords <span class="kw1">in</span> poly<span class="sy0">.</span>rings<span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineStyle</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>pt <span class="kw1">in</span> coords<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                o = projection<span class="sy0">.</span>project<span class="br0">&#40;</span>deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">x</span><span class="br0">&#41;</span><span class="sy0">,</span> deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw1">new</span> <span class="kw5">Point</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
                o<span class="sy0">.</span><span class="kw7">x</span> = <span class="br0">&#40;</span>o<span class="sy0">.</span><span class="kw7">x</span> <span class="sy0">-</span> min_x<span class="br0">&#41;</span> <span class="sy0">*</span> s <span class="sy0">+</span> <span class="br0">&#40;</span>w<span class="sy0">-</span><span class="br0">&#40;</span>max_x<span class="sy0">-</span>min_x<span class="br0">&#41;</span><span class="sy0">*</span>s<span class="br0">&#41;</span><span class="sy0">/</span><span class="nu0">2</span><span class="sy0">;</span>
                o<span class="sy0">.</span><span class="kw7">y</span> = h <span class="sy0">-</span> <span class="br0">&#40;</span>o<span class="sy0">.</span><span class="kw7">y</span> <span class="sy0">-</span> min_y<span class="br0">&#41;</span> <span class="sy0">*</span> s <span class="sy0">-</span> <span class="br0">&#40;</span>h<span class="sy0">-</span><span class="br0">&#40;</span>max_y<span class="sy0">-</span>min_y<span class="br0">&#41;</span><span class="sy0">*</span>s<span class="br0">&#41;</span><span class="sy0">/</span><span class="nu0">2</span><span class="sy0">;</span>
                <span class="kw1">if</span> <span class="br0">&#40;</span>pt == coords<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">moveTo</span><span class="br0">&#40;</span>o<span class="sy0">.</span><span class="kw7">x</span><span class="sy0">,</span> o<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">;</span>
                <span class="kw1">else</span> <span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineTo</span><span class="br0">&#40;</span>o<span class="sy0">.</span><span class="kw7">x</span><span class="sy0">,</span> o<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="br0">&#125;</span>
            <span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineStyle</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<p>The result is a nicely centered world map (click image to see the swf):</p>
<p><a href="http://vis4.net/experiments/as3maps/tutorial1.html"><img class="aligncenter size-medium wp-image-1422" title="map_tut1" src="http://vis4.net/blog/wp-content/uploads/2010/04/map_tut1.png" alt="" width="640" /></a></p>
<p>The quick and dirty way has a few drawbacks and a lot of potential for improvements.</p>
<h3>Avoiding duplicate projection calculations</h3>
<p>Instead of calculating the projection twice for each point it would be better to store the points after the first projection. We could do this using normal arrays but I suggest the usage of my helper classes <a href="http://api.vis4.net/as3-vis4/index.html?net/vis4/geom/Polygon.html&#038;net/vis4/geom/class-list.html" class="tt">Polygon</a> and <a href="http://api.vis4.net/as3-vis4/index.html?net/vis4/geom/PointSet.html&#038;net/vis4/geom/class-list.html" class="tt">PointSet</a>. We can also replace the calculations of minimum and maximum coordinates using the <tt>boundingBox</tt> property of the polygons together with the <tt>Rectangle.union()</tt> method.</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="actionscript3"><pre class="de1"><span class="kw2">var</span> record<span class="sy0">:</span>ShpRecord<span class="sy0">,</span> poly<span class="sy0">:</span>ShpPolygon<span class="sy0">,</span> coords<span class="sy0">:</span><span class="kw5">Array</span><span class="sy0">,</span> pt<span class="sy0">:</span>ShpPoint<span class="sy0">;</span>
<span class="kw2">var</span> polygons<span class="sy0">:</span><span class="kw5">Array</span> = <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">;</span>
<span class="kw2">var</span> bounds<span class="sy0">:</span><span class="kw5">Rectangle</span> = <span class="kw1">new</span> <span class="kw5">Rectangle</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>record <span class="kw1">in</span> records<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>record<span class="sy0">.</span>shapeType == ShpType<span class="sy0">.</span>SHAPE_POLYGON<span class="br0">&#41;</span> <span class="br0">&#123;</span>
        poly = record<span class="sy0">.</span>shape <span class="kw1">as</span> ShpPolygon<span class="sy0">;</span>
        <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>coords <span class="kw1">in</span> poly<span class="sy0">.</span>rings<span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw2">var</span> out<span class="sy0">:</span>PointSet = <span class="kw1">new</span> PointSet<span class="sy0">;</span>
            <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>pt <span class="kw1">in</span> coords<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                out<span class="sy0">.</span><span class="kw7">push</span><span class="br0">&#40;</span>projection<span class="sy0">.</span>project<span class="br0">&#40;</span>deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">x</span><span class="br0">&#41;</span><span class="sy0">,</span> deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw1">new</span> <span class="kw5">Point</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="br0">&#125;</span>
            <span class="kw2">var</span> out_poly<span class="sy0">:</span>Polygon = <span class="kw1">new</span> Polygon<span class="br0">&#40;</span>out<span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="co1">// store polygon and update the bounding box</span>
            polygons<span class="sy0">.</span><span class="kw7">push</span><span class="br0">&#40;</span>out_poly<span class="br0">&#41;</span><span class="sy0">;</span>
            bounds = bounds<span class="sy0">.</span><span class="kw7">union</span><span class="br0">&#40;</span>out_poly<span class="sy0">.</span>boundingBox<span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre></div></div></div></div></div></div></div>


<h3>SolidPolygonRenderer and DataView</h3>
<p>Because the rendering of polygons and the following view calculations must be done for every map projection I thought it would be a good idea to put this functionality into separate classes. That makes our code shorter and also more readable. The polygon rendering is done by the <a href="http://api.vis4.net/as3-vis4/index.html?net/vis4/renderer/polygon/OutlinePolygonRenderer.html&#038;net/vis4/renderer/polygon/class-list.html" class="tt">OutlinePolygonRenderer</a> class, which implements the <a href="http://api.vis4.net/as3-vis4/index.html?net/vis4/renderer/polygon/IPolygonRenderer.html&#038;net/vis4/renderer/polygon/class-list.html" class="tt">IPolygonRenderer</a> interface to enable easy exchanges of renderer classes without having to change the rest of the code. The scaling and translation of the projected points is done by the <a href="http://api.vis4.net/as3-vis4/index.html?net/vis4/data/DataView.html&#038;net/vis4/data/class-list.html" class="tt">DataView</a> class, which works nicely together with the PolygonRenderer. After all, the rendering code has shrinked to the following few lines:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="actionscript3"><pre class="de1"><span class="kw2">var</span> renderer<span class="sy0">:</span>IPolygonRenderer = <span class="kw1">new</span> OutlinePolygonRenderer<span class="br0">&#40;</span>0x000000<span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">var</span> screen<span class="sy0">:</span><span class="kw5">Rectangle</span> = <span class="kw1">new</span> <span class="kw5">Rectangle</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span> <span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">stageWidth</span><span class="sy0">,</span> <span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">stageHeight</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">var</span> view<span class="sy0">:</span>DataView = <span class="kw1">new</span> DataView<span class="br0">&#40;</span>bounds<span class="sy0">,</span> screen<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>out_poly <span class="kw1">in</span> polygons<span class="br0">&#41;</span> renderer<span class="sy0">.</span><span class="kw7">render</span><span class="br0">&#40;</span>out_poly<span class="sy0">,</span> <span class="kw7">graphics</span><span class="sy0">,</span> view<span class="br0">&#41;</span><span class="sy0">;</span></pre></div></div></div></div></div></div></div>


<h3>Taking care of the projection limitations</h3>
<p>Some projections are limited to a specific latitude or longitude range (e.g. the mercator projection, which is not able to display latitudes of 90° because this would lead to infinite y values). This means we have to check each coordinate before we project it. To simplify this check I added the method <tt>Projection.testPointDeg()</tt> to the Projection api.</p>
<p>In the end, the complete rendering looks like this:</p>


<div class="wp-geshi-highlight-wrap5"><div class="wp-geshi-highlight-wrap4"><div class="wp-geshi-highlight-wrap3"><div class="wp-geshi-highlight-wrap2"><div class="wp-geshi-highlight-wrap"><div class="wp-geshi-highlight"><div class="actionscript3"><pre class="de1"><span class="co1">// see the previous tutorial</span>
<span class="kw2">var</span> records<span class="sy0">:</span><span class="kw5">Array</span> = ShpTools<span class="sy0">.</span>readRecords<span class="br0">&#40;</span>shp<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// step1: preprocessing and polygon creation</span>
<span class="kw2">var</span> projection<span class="sy0">:</span>Projection = <span class="kw1">new</span> Wagner5Projection<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
projection<span class="sy0">.</span>initialize<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw2">var</span> polygons<span class="sy0">:</span><span class="kw5">Array</span> = <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="sy0">;</span>
<span class="kw2">var</span> bounds<span class="sy0">:</span><span class="kw5">Rectangle</span> = <span class="kw1">new</span> <span class="kw5">Rectangle</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span><span class="kw2">var</span> record<span class="sy0">:</span>ShpRecord <span class="kw1">in</span> records<span class="br0">&#41;</span> <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span>record<span class="sy0">.</span>shapeType == ShpType<span class="sy0">.</span>SHAPE_POLYGON<span class="br0">&#41;</span> <span class="br0">&#123;</span>
        <span class="kw2">var</span> poly<span class="sy0">:</span>ShpPolygon = record<span class="sy0">.</span>shape <span class="kw1">as</span> ShpPolygon<span class="sy0">;</span>
        <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span><span class="kw2">var</span> coords<span class="sy0">:</span><span class="kw5">Array</span> <span class="kw1">in</span> poly<span class="sy0">.</span>rings<span class="br0">&#41;</span> <span class="br0">&#123;</span>
            <span class="kw2">var</span> out<span class="sy0">:</span>PointSet = <span class="kw1">new</span> PointSet<span class="sy0">;</span>
            <span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span><span class="kw2">var</span> pt<span class="sy0">:</span>ShpPoint <span class="kw1">in</span> coords<span class="br0">&#41;</span> <span class="br0">&#123;</span>
                <span class="kw1">if</span> <span class="br0">&#40;</span>projection<span class="sy0">.</span>testPointDeg<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">y</span><span class="sy0">,</span> pt<span class="sy0">.</span><span class="kw7">x</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
                    <span class="kw2">var</span> o<span class="sy0">:</span><span class="kw5">Point</span><span class="sy0">;</span>
                    o = projection<span class="sy0">.</span>project<span class="br0">&#40;</span>deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">x</span><span class="br0">&#41;</span><span class="sy0">,</span> deg2rad<span class="br0">&#40;</span>pt<span class="sy0">.</span><span class="kw7">y</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw1">new</span> <span class="kw5">Point</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
                    o<span class="sy0">.</span><span class="kw7">y</span> <span class="sy0">*</span>= <span class="sy0">-</span><span class="nu0">1</span><span class="sy0">;</span>
                    out<span class="sy0">.</span><span class="kw7">push</span><span class="br0">&#40;</span>o<span class="br0">&#41;</span><span class="sy0">;</span>
                <span class="br0">&#125;</span>
            <span class="br0">&#125;</span>
            <span class="kw2">var</span> out_poly<span class="sy0">:</span>Polygon = <span class="kw1">new</span> Polygon<span class="br0">&#40;</span>out<span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="co1">// store polygon for later rendering</span>
            polygons<span class="sy0">.</span><span class="kw7">push</span><span class="br0">&#40;</span>out_poly<span class="br0">&#41;</span><span class="sy0">;</span>
            <span class="co1">// update bounding box</span>
            bounds = bounds<span class="sy0">.</span><span class="kw7">union</span><span class="br0">&#40;</span>out_poly<span class="sy0">.</span>boundingBox<span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// step2: polygon rendering</span>
<span class="kw2">var</span> renderer<span class="sy0">:</span>IPolygonRenderer = <span class="kw1">new</span> OutlinePolygonRenderer<span class="br0">&#40;</span>0x000000<span class="sy0">,</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">var</span> screen<span class="sy0">:</span><span class="kw5">Rectangle</span> = <span class="kw1">new</span> <span class="kw5">Rectangle</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="sy0">,</span> <span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">stageWidth</span><span class="sy0">,</span> <span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">stageHeight</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw2">var</span> view<span class="sy0">:</span>DataView = <span class="kw1">new</span> DataView<span class="br0">&#40;</span>bounds<span class="sy0">,</span> screen<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="kw1">for</span> <span class="kw1">each</span> <span class="br0">&#40;</span>out_poly <span class="kw1">in</span> polygons<span class="br0">&#41;</span> renderer<span class="sy0">.</span><span class="kw7">render</span><span class="br0">&#40;</span>out_poly<span class="sy0">,</span> <span class="kw7">graphics</span><span class="sy0">,</span> view<span class="br0">&#41;</span><span class="sy0">;</span></pre></div></div></div></div></div></div></div>


<h3>Rendering of Sea and Graticule</h3>
<p>A great way to improve the visual understanding of a map projection is to integrate the sea boundary and a graticule. If you're interested in how to draw them in detail, please take a look at the <a href="http://vis4.net/blog/wp-content/uploads/2010/04/map-render-tutorial.zip">source files</a>.</p>
<p><a href="http://vis4.net/experiments/as3maps/tutorial2.html"><img src="http://vis4.net/blog/wp-content/uploads/2010/04/map_tut2.png" alt="" title="" class="aligncenter size-full wp-image-1462" width="640" /></a></p>
<div id="download">As usual you can <a href="http://vis4.net/blog/wp-content/uploads/2010/04/map-render-tutorial.zip">download a zip</a> file containing all files related to this tutorial.<br />
Additionally I uploaded and linked <a href="http://api.vis4.net/as3-vis4/">api docs</a> for the classes I used in this tutorial.</div>
<p>Hope this tutorial was useful to you, sorry for any grammar and spelling errors. If you like you can donate an amount of your choice via paypal. Thanks!</p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input name="cmd" type="hidden" value="_s-xclick" />
<input name="hosted_button_id" type="hidden" value="TR5UHDELVECTN" />
<input alt="PayPal - The safer, easier way to pay online!" name="submit" src="https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif" type="image" />
<img src="https://www.paypal.com/de_DE/i/scr/pixel.gif" border="0" alt="" style="visibility:hidden" width="1" height="1" /><br />
</form>
]]></content:encoded>
			<wfw:commentRss>http://vis4.net/blog/posts/rendering-world-maps-in-as3/?piwik_campaign=rss&#038;piwik_kwd=1248/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using memcached (User agent is rejected)
Database Caching 1/45 queries in 0.028 seconds using memcached
Object Caching 804/878 objects using memcached
Content Delivery Network via N/A

Served from: www.vis4.net @ 2012-02-05 22:34:39 -->
