Showing posts with label topoJSON. Show all posts
Showing posts with label topoJSON. Show all posts

Saturday, June 14, 2014

Rambling

This post is content previously published on www.cathmhaol.com

I've a fine, felt hat
And a strong pair of brogues
I have rosin in my pocket for my bow
O my fiddle strings are new
And I've learned a tune or two
So, I'm well prepared to ramble and must go
Tommy Makem

Animated flight from LAX to LHR
with stops in LAS, DEN and ORD
In today's update, I'll describe how to draw routes on your cjl-earth.js map, so that you get something like this sample image, but first, I should explain the structure of the generated markup.

Within the map group (which is a 'g' tag - <g> - with an id attribute composed of the map object id and '-map'), are groups (using a 'g' tag - <g>) for the oceans, countries, markers, and routes. These 'route' groups each have the class 'route', and each group is created as it is read from the data, so you can style the individual 'routes' using the #map_object_id-routes g and the nth-of-type selectors as well as the g.route (an individual route) and path.travel-route (individual paths) selectors. Each route is composed of paths from the origin to the first destination and then from each subsequent 'destination' to the next, giving markup similar to the markup below. Because D3 is translating longitude and latitude to x and y coordinates in the SVG, all of this code is generated - you don't have to do any calculations.

Markup Example
<g id="cjl-globe-04748665224760771-routes">
 <g class="route">
  <path class="travel-route" d="M 88 203 L 93 200"></path>
  <path class="travel-route" d="M 93 200 L 107 195"></path>
  <path class="travel-route" d="M 107 195 L 130 192"></path>
  <path class="travel-route" d="M 130 192 L 250 179"></path>
 </g>
</g>

Now that we've covered what markup is generated, we're ready to begin.

Let's begin by deciding whether we want to use some sort of a marker - like an airplane or storm symbol - to animate the route(s) you're going to define or if you just want a line. If you want a simple line, the display will be controlled by a style - for example, <style type="text/css">.travel-route { fill:none; stroke:#ff0000; stroke-width:1px; }</style> will make the paths defined in your route appear as a thin, red line.

If you want to move a marker along the route, you'll need to provide the SVG equivalent of the image in a marker object. Marker objects have a 'd' property that contains the pen instructions ordinarily contained in the 'd' attribute of the path tag, and may optionally have an 'orient' property for those symbols that have an orientation (e.g. an airplane or other object that has a 'front'). If orient is set to true, the object is rotated so that the 'top' is facing in the direction of movement, like an airplane that 'flies' along a route. Markers are not scaled unless specifically requested by providing the 'scale' property, so the image that's provided should be the appropriate size when loaded; however, if the map is 'zoomed' by user interaction, the markers are 'zoomed' as well and will maintain their relative size.

Note: you may also choose to have the two types of animation combined, so that a travel path is revealed as your marker moves along the route; however, this is only possible if the marker is provided and the 'combineAnimation' flag is set - see the documentation regarding the travel method in the cjl-earth.js library for more information.

After you've decided which sort of animation you'd like, decide how long the animation should run and whether you want it to loop or run once.

Once all your design decisions are made, decide how you want to provide the data to the function. Here again, you have two choices: directly or via AJAX. With this final decision, you're ready to write the code to display the routes by calling the 'travel' method...something like the following.

Providing data using AJAX with no marker earth.travel('/ReST/api/routes?origin=LAX&amp;destination=LHR', null, 2000, true);

or

Providing data directly with a marker earth.travel( [
 {origin:[-118.408075, 33.942536],
  destination:[
    [-115.15225, 36.080056],
    [-104.673178, 39.861656],
    [-87.904842, 41.978603],
    [0.461389, 51.4775]
   ]
 }
], {d:"m25.21488,3.93375c-0.44355,0 -0.84275,0.18332 -1.17933,0.51592c-0.33397,0.33267 -0.61055,0.80884 -0.84275,1.40377c-0.45922,1.18911 -0.74362,2.85964 -0.89755,4.86085c-0.15655,1.99729 -0.18263,4.32223 -0.11741,6.81118c-5.51835,2.26427 -16.7116,6.93857 -17.60916,7.98223c-1.19759,1.38937 -0.81143,2.98095 -0.32874,4.03902l18.39971,-3.74549c0.38616,4.88048 0.94192,9.7138 1.42461,13.50099c-1.80032,0.52703 -5.1609,1.56679 -5.85232,2.21255c-0.95496,0.88711 -0.95496,3.75718 -0.95496,3.75718l7.53,-0.61316c0.17743,1.23545 0.28701,1.95767 0.28701,1.95767l0.01304,0.06557l0.06002,0l0.13829,0l0.0574,0l0.01043,-0.06557c0,0 0.11218,-0.72222 0.28961,-1.95767l7.53164,0.61316c0,0 0,-2.87006 -0.95496,-3.75718c-0.69044,-0.64577 -4.05363,-1.68813 -5.85133,-2.21516c0.48009,-3.77545 1.03061,-8.58921 1.42198,-13.45404l18.18207,3.70115c0.48009,-1.05806 0.86881,-2.64965 -0.32617,-4.03902c-0.88969,-1.03062 -11.81147,-5.60054 -17.39409,-7.89352c0.06524,-2.52287 0.04175,-4.88024 -0.1148,-6.89989l0,-0.00476c-0.15655,-1.99844 -0.44094,-3.6683 -0.90277,-4.8561c-0.22699,-0.59493 -0.50356,-1.07111 -0.83754,-1.40377c-0.33658,-0.3326 -0.73578,-0.51592 -1.18194,-0.51592l0,0l-0.00001,0l0,0l0.00002,0.00001z", orient:true, scale:0.3}, 2000, true, true);

One final note - the travel method does not play well with the transition method, which is covered in the last blog entry, A different point of view, nor does it work well with projections that are designed to rotate, such as the 'orthographic' projection.

Additional Resources:

Saturday, May 31, 2014

A different point of view

This post is content previously published on www.cathmhaol.com

In the post preceding this, Around the world in eighty days, I discussed how to add a spinning globe to show global data collections. In the time since that post went live, I've made a few changes - all of which will be in the library on github - as soon as I get that up.

First, the library has been hardened so that if the core functionality is not available, the library will not work, but it also will not throw an error that breaks other JavaScript you have on your page. This should have been done earlier, and I apologize that it was wasn't - that is simply poor writing.

Second, the library has been extended by a new feature that allows developers to transition between projection styles. This transition, rather than being a sharp, jarring redraw of the image, is an animation that makes use of the d3 tweening function. I've tried to make sure that I'm only supporting styles that transition well to other styles, but I suspect there are some projection pairs that do not transition well. As you experiment to see which styles work best for you, I'd appreciate a little 'heads up' about the styles you find don't work well together.

Overall, though, you should now be able to transition between a Orthographic (globe) shape and an Equirectangular shape, for example…

…with just one line of code along the lines of…
var earth = new Cathmhaol.Earth('map', '/prototypes/earth/world-110m.js');
document.getElementById('trn').onclick = function() { earth.transition('Equirectangular'); }

That's all there is to it - just call the transition method and pass in the name of the projection you want to transition to (and optionally, how long you want the transition to take in milliseconds - the default is 750).

As before, you can see how this works by visiting the prototype page, which now has a special 'transition' button that will run the animation. The transition button on the prototype page will toggle between the two styles - Orthographic and Equirectangular - but if you're feeling really adventurous you could build a drop down (select) using the array returned by a supportedTypes method call and allow the user to select the transitions - it just takes a little creativity.

Again, you will soon be able to find the unminified files used in this project in my cjl github repo, or you can get them all bundled up from the download tent at cathmhaol.com - which also contains the HTML for the prototype, or you can get the required files through the links on the prototype page.

Still to come for this library:

  • a method that lets you set up and show animated routes between an origin and destination, e.g., a flight path
  • the ability to use a different shape for location markers rather than a circle
As always, feel free to drop me an email or ping me on Twitter with your ideas and/or how you've used what you've seen here.

Saturday, May 17, 2014

Around the world in eighty days

This post is content previously published on www.cathmhaol.com

One of the projects I've been working on is developing a map that will visually represent data. This seems easy enough, but when practice meets theory, it hasn't been.

My project is fairly straightforward. Take a collection of quantifiable global information - it could be nearly any quantifiable information - e.g., the amount of money moving in or out of world banks, kilowatt hours consumed, number of mobile users, or the number of take-off and landing events at world airports - and tie it to a location like a city. Then represent the relative activity with a marker on a map. So basically, translating data that we might find in a table into a visual format.


CountryCityActivity
USATL68343
USORD59692
USDFW56496
USLAX51396
CNPEK48226
USCLT44583
USDEN44438
USLAS41164
USIAH39808
GBLHR37680


Beyond the basic requirement - to visually represent the data - there were a few other requirements I had set for myself. I didn't want the map to just be a two-dimensional projection - like a Mercator - I wanted it be a globe that rotates.

One option was a WebGL map - something like Akamai's GNET globe - but that doesn't work in Safari, practically eliminating iOS devices. Another option was planetary.js - which works most everywhere but didn't have the functionality I needed, mainly because it uses an HTML canvas instead of an SVG. I was left to build what I needed.

Even though the planetary.js library was slightly inadequate, it gave clues that led me to the library published as D3.js - which made building the exact tool I needed easy. Additionally, there are several online examples and tutorials that explain how to use D3 and topoJSON to create maps, making it a simple matter to generate the map. Add in the ability to zoom, for which there are hooks in the D3 library and a little finesse in JavaScript to convince the library that touch events should be handled like mouse events, and I had a nearly fully-functional globe with countries in a short amount of time.

Next came the markers...

First, we need some way of identifying the latitude and longitude of cities...or in our example data, airports. To accomplish this, I built a massive JSON file that contains nearly every airport, the country it is in, and the coordinates (which is airports.json in one of my GitHub repos, along with coordinates of most major cities). With this, it's a simple matter of mapping an airport to a specific point on the grid.

Once I had the coordinates, I needed some way to represent the relative size...which is really just a matter of creating a scale based on the smallest and largest numbers in the dataset. Here's where it gets a little tricky...because we want the markers to do something - be animated in some way. Transitions and animations are pretty time consuming, but luckily D3 again fills the gap. By using two somewhat clever transitions and fiddling with the timing, there are two slightly different animations - pulse and ping - that give the markers extra visual appeal.

Of course, the visual appeal of the rotating globe doesn't really solve all the problems, even with the ability to stop the globe and drag it to turn it or even zoom in or out. People see the animated markers and can see that traffic is heavier in ATL than in LHR, but the actual data is now hidden. To expose the data, I added a scrollable, sortable table to the library.

By specifying the columns or properties of the data elements that you want to display, a table is generated and attached to the same DOM node that contains the SVG with the map. By again using D3, we quickly make the table sortable - meaning users can click on the headers to sort by that property, e.g., clicking on the Country header to sort by country. Since there is likely little real estate available when a globe is present, I limit the table to around 10 rows and make it scrollable - the CSS to accomplish this is generated by the library and appended to the DOM when the table is generated.

You can find prototypes for both the marked map and the scrollable table at products.cathmhaol.com. You can find the D3 plugin that will do all this in my GitHub D3 plugins repo. Feel free to fork the repo, or if you'd rather, let me know what you'd like to see changed - maybe your requests will make it into the official repo.