Raspberry Pi Pico Tips and Tricks

Wednesday 20 March 2013

A simple d3.js map explained

The following post is a portion of the D3 Tips and Tricks document which is free to download. To use this post in context, consider it with the others in the blog or just download the pdf  and / or the examples from the downloads page:-)

-------------------------------------------------------


Starting with a simple map

Our starting example will demonstrate the simple display of a World map. Our final result will looks like this;

The data file for the World map is one produced by Mike Bostock's as part of his TopoJSON work.

We'll move through the explanation of the code in a similar process to the one we went through when highlighting the function of the Sankey diagram. Where there are areas that we have covered before, I will gloss over some details on the understanding that you will have already seen them explained in an earlier section (most likely the basic line graph example).

Here is the full code;
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
  stroke: white;
  stroke-width: 0.25px;
  fill: grey;
}
</style>
<body>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<script src="js/topojson.v0.min.js"></script>
<script>
var width = 960,
    height = 500;

var projection = d3.geo.mercator()
    .center([0, 5 ])
    .scale(900)
    .rotate([-180,0]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var path = d3.geo.path()
    .projection(projection);

var g = svg.append("g");

// load and display the World
d3.json("json/world-110m2.json", function(error, topology) {
    g.selectAll("path")
      .data(topojson.object(topology, topology.objects.countries)
          .geometries)
    .enter()
      .append("path")
      .attr("d", path)
});

</script>
</body>
</html>

 One of the first things that struck me when I first saw the code to draw a map was how small it was (the amount of code, not the World).  It's a measure of the degree of abstraction that D3 is able to provide to the process of getting data from a raw format to the scree that such a complicated task can be condensed to such an apparently small amount of code. Of course that doesn't tell the whole story. Like a duck on a lake, above the water all is serene and calm while below the water the feet are paddling like fury. In this case, our code looks serene because D3 is doing all the hard work :-).

The first block of our code is the start of the file and sets up our HTML.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
 This leads into our style declarations.
path {
  stroke: white;
  stroke-width: 0.25px;
  fill: grey;
}
We only state the properties of the path components which will make up our countries. Obviously we will fill them with grey and have a thin (`0.25px`) line around each one.

The next block of code loads the JavaScript files.
</style>
<body>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<script src="js/topojson.v0.min.js"></script>
<script>
In this case it's d3 and topojson. We load `topojson.v0.min.js` as a separate file because it's still fairly new. In other words it hasn't been incorporated into the main d3.js code base (that's an assumption on my part since it might exist in isolation or perhaps end up as a plug-in). Whatever the case, for the time being, it exists as a separate file.

Then we get into the JavaScript. The first thing we do is define the size of our map.
var width = 960,
    height = 500;
Then we get into one of the simple, but cool parts of making any map. Setting up the view.
var projection = d3.geo.mercator()
    .center([0, 5 ])
    .scale(900)
    .rotate([-180,0]);
The projection is the way that the geographic coordinate system is adjusted for display on our flat screen. The screen is after all a two dimensional space and we are trying to present a three dimensional object. This is a big deal to cartographers in the sense that selecting a geographic projection for a map is an exercise in compromise. You can make it look pretty, but in doing so you can grievously distort the land size / shape. On the other hand you might make it more accurate, in size / shape but people will have trouble recognising it because they're so used to the standard Mercator projection. For example, the awesome Waterman Butterfly

There are a lot of alternative projections available. Please have a browse on the wiki where you will find a huge range of options (66 at time of writing).

In our case we've gone with the conservative Mercator option.

Then we define three aspects of the projection. Center, scale and rotate.

 center

If center is specified, this sets the projection’s center to the specified location as two-element array of longitude and latitude in degrees and returns the projection. If center is not specified the default of (0°,0°)  is used.

Our example is using `[0, 5 ]` which I have selected as being in the middle (`0`) for longitude (left to right) and `5` degrees North of the equator (North is positive values of latitude, South is negative values). This was purely to make it look aesthetically pleasing. Here's the result of setting the center to `[100,30]`.
The map has been centered on 100 degrees West and 30 degrees North. Of course, it's also been pushed to the left without the right hand side of the map scrolling around. We'll get to that in a moment.

scale

If scale is specified, this sets the projection’s scale factor to the specified value. If scale is not specified, returns the current scale factor which defaults to 150. It's important to note that scale factors are not consistent across projections.

Our current map uses a scale of 900. Again, this has been set for aesthetics. Keeping our center of `[100,30]`, if we increase our scale to `2000` this is the result.

rotate

If rotation is specified, this sets the projection’s three-axis rotation to the specified angles for yaw, pitch and roll (equivalently longitude, latitude and roll) in degrees and returns the projection. If rotation is not specified, it sets the values to [0, 0, 0]. If the specified rotation has only two values, rather than three, the roll is assumed to be 0°.

In our map we have specified `[-180,0]` so we can assume a roll value of zero. Likewise we have rotated our map by -180 degrees in longitude. This has been done specifically to place the map with the center on the anti-meridian (The international date line in the middle of the Pacific ocean). If we return the value to `[0,0]`(with our original values of `scale` and `center` this is the result.
In this case the centre of the map lines up with the meridian.

The next block of code sets our svg window;
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);
The follow portion of code creates a new geographic path generator;
var path = d3.geo.path()
    .projection(projection);
The path generator (`d3.geo.path()`) is used to spcify a projection type (`.projection`) which was defined earlier as a Mercator projection via the variable `projection`. (I'm not entirely sure, but it is possible that I have just set some kind of record for use of the word 'projection' in a sentence.)

We then declare `g` as our appended svg.
var g = svg.append("g");
The last block of JavaScript draws our map.
d3.json("json/world-110m2.json", function(error, topology) {
    g.selectAll("path")
      .data(topojson.object(topology, topology.objects.countries)
          .geometries)
    .enter()
      .append("path")
      .attr("d", path)
});
We load the TopoJSON file with the coordinates for our World map (`world-110m2.json`). Then we declare that we are going to act on all the `path` elements in the graphic (`g.selectAll("path")`).

Then we pull the data that defines the countries from the TopoJSON file (`.data(topojson.object(topology, topology.objects.countries).geometries)`).  We add it to the data that we're going to display (`.enter()`) and then we append that data as `path` elements (`.append("path")`).

The last html block closes off our tags and we have a map!
The code and data for this example can be found as World Map Centered on the Pacific  on bl.ocks.org.

Zooming and panning a map

With our map displayed nicely we need to be able to move it about to explore it fully
.
To do this we can provide the functionality to zoom and pan it using the mouse.

Towards the end of the script, just before the close off of the script at the `</script>` tag we can add in the following code;
var zoom = d3.behavior.zoom()
    .on("zoom",function() {
        g.attr("transform","translate("+ 
            d3.event.translate.join(",")+")scale("+d3.event.scale+")");
        g.selectAll("path")  
            .attr("d", path.projection(projection)); 
});

svg.call(zoom)
This block of code introduces the `behavior`s functions. Using the `d3.behavior.zoom` function creates event listeners (which are like hidden functions standing by to look out for a specific type of activity on the computer and in this case mouse actions) to handle zooming and panning gestures on a container element (in this case our map). More information on the range of zoom options is available on the [D3 Wiki](https://github.com/mbostock/d3/wiki/Zoom-Behavior).

We begin by declaring the `zoom` function as `d3.behavior.zoom`.

Then we instruct the computer that when it 'sees' a 'zoom' event to carry out another function (`.on("zoom",function() {`).

That function firstly gathers the (correctly formatted) `translate` and `scale` attributes in...
        g.attr("transform","translate("+ 
            d3.event.translate.join(",")+")scale("+d3.event.scale+")");
 ... and then applies them to all the path elements (which are the shapes of the countries) via...
        g.selectAll("path")  
            .attr("d", path.projection(projection)); 
 Lastly we call the zoom function.
svg.call(zoom)
Then we relax and explore our map!
The code and data for this example can be found as World Map with zoom and pan on bl.ocks.org.

 Displaying points on a map

Displaying maps and exploring them is pretty entertaining, but as anyone who has participated in the improvement of our geographic understanding of our world via projects such as [Open Street Map](http://www.openstreetmap.org/) will tell you, there's a whole new level of cool to be attained by adding to a map.

With that in mind, our next task is to add some simple detail in the form of points that show the location of cities.

To do this we will load in a csv file with data that identifies our cities and includes latitude and longitude details. Our file is called `cities.csv` and looks like this;

code,city,country,lat,lon
ZNZ,ZANZIBAR,TANZANIA,-6.13,39.31
TYO,TOKYO,JAPAN,35.68,139.76
AKL,AUCKLAND,NEW ZEALAND,-36.85,174.78
BKK,BANGKOK,THAILAND,13.75,100.48
DEL,DELHI,INDIA,29.01,77.38
SIN,SINGAPORE,SINGAPOR,1.36,103.75
BSB,BRASILIA,BRAZIL,-15.67,-47.43
RIO,RIO DE JANEIRO,BRAZIL,-22.90,-43.24
YTO,TORONTO,CANADA,43.64,-79.40
IPC,EASTER ISLAND,CHILE,-27.11,-109.36
SEA,SEATTLE,USA,47.61,-122.33


While we're only going to use the latitude and longitude for our current work, the additional details could just as easily be used for labeling or tooltips.

We need to place our code carefully in this case because while you might have some flexibility in getting the right result with a locally hosted version of a map, there is a possibility that with a version hosted in the outside World (*gasp* the internet) you could strike trouble.

The code to load the cities should be placed inside the function that is loading the World map as indicated below;
d3.json("json/world-110m2.json", function(error, topology) {
    g.selectAll("path")
      .data(topojson.object(topology, topology.objects.countries)
          .geometries)
    .enter()
      .append("path")
      .attr("d", path)
                           // <== Put the new code block here
});
Here's the new code;
    d3.csv("data/cities.csv", function(error, data) {
        g.selectAll("circle")
           .data(data)
           .enter()
           .append("circle")
           .attr("cx", function(d) {
                   return projection([d.lon, d.lat])[0];
           })
           .attr("cy", function(d) {
                   return projection([d.lon, d.lat])[1];
           })
           .attr("r", 5)
           .style("fill", "red");
We'll go through the code and then explain the quirky thing about it.

First of all we load the `cities.csv` file (`d3.csv("data/cities.csv", function(error, data) {`). Then we select all the circle elements (` g.selectAll("circle")`), assign our data (`.data(data)`), enter our data (` .enter()`) and then add in circles (`.append("circle")`).

Then we set the x and y position for the circles based on the longitude (`([d.lon, d.lat])[0]`) and latitude (`([d.lon, d.lat])[1]`) information in the csv file.

Finally we assign a radius of 5 pixels and fill the circles with red.

The quirky thing about the new code block is that we have to put it inside the code block that loads the world data (`d3.json("json/world-110m2.json", function(error, topology) {`). We could place the two blocks one after the others (load / draw the world data, then load / draw the circles). And this will probably work if you run the file from your local computer. But when you host the files on the internet, it takes too long to load the world data compared to the city data and the end result is that the city data gets drawn before the world data and this is the result.
To avoid the problem we place the loading of the city data *into* the code that loads the World data. That way the city data doesn't get loaded until the World data is loaded and then the circles get drawn on top of the world instead of under it :-).
The code and data for this example can be found as World map with zoom / pan and cities on bl.ocks.org.
The topojson file is here; and the cities.csv file is here (while you can find these from the github page, it isn't particularly obvious where they are).

The above description (and heaps of other stuff) is in the D3 Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).

71 comments:

  1. Where are the downloads for this post? I can't find a source for topojson or the data source.

    ReplyDelete
    Replies
    1. Well spotted! While the main html and cities.csv file are in the github link at the bottom, it's not at all obvious where to get the topojson file. The link is here (https://gist.github.com/d3noob/5193723/raw/world-110m2.json), but I will make it more obvious in the post. Thanks for pointing it out.

      Delete
  2. Hi! Thanks so much for this tutorial. Is there a way to set a maximum zoom out on the map so users can't zoom out further than that range? thank you!

    ReplyDelete
    Replies
    1. No problem. I think what you're looking for has been best demonstrated by Mike Bostock here http://bl.ocks.org/mbostock/4987520 I'm pretty sure that's what you're looking for. Enjoy!

      Delete
  3. If I wanted to highlight the country itself, how would that be achievable?

    ReplyDelete
    Replies
    1. Good question. I haven't tried this myself, but there's plenty of good examples about. A combination of http://bl.ocks.org/mbostock/4180634, http://bost.ocks.org/mike/map/ and http://bl.ocks.org/mbostock/4183330 would steer you in the right direction. I believe the key point would be understanding the structure of your topojson file and the select process to apply the fill. I.e. If you know how to select the specific country you want (the structure that is) you should then be able to fill it. Good luck.

      Delete
  4. Hi. I recently read through your d3 tips and tricks. I am new to the vis world, and as part of my work need to learn various different web vis apps, the learning curve has been steep in most cases and your d3 tips and tricks helped greatly.

    QUESTION: I am required to use maps as well in vis. The trouble I am having at the moment is that I need to map a specific state(of Australia), in better detail than the world-110m.json provides. Any idea on how I get a more detail map?

    Cheers

    ReplyDelete
    Replies
    1. Thanks for your kind words on the book. I'm glad it was useful.
      As to an answer to your question. I'd start with the excellent overview by Mike Bostock (http://bost.ocks.org/mike/map/) although you're probably already got a good understanding of the basics. I have used the instructions here (http://vallandingham.me/shapefile_to_geojson.html) from Jim Vallandingham. to turn a shape file into GeoJSON before and I think that is what you're going to want to do. You should be able to get a suitable shape file from http://www.ga.gov.au/products/servlet/controller?event=DEFINE_PRODUCTS or http://www.diva-gis.org/gdata or http://www.aec.gov.au/Electorates/gis/gis_datadownload.htm .
      Hopefully that should set you on your way.

      Delete
    2. Ok Thanks for the help, will look into those ASAP

      Delete
    3. Also just another question. I have also used(so far) tableau public and highcharts.

      What is your take on tableau. IMO it is a great tool if it does what you are looking for, so for example geographic data is very easy to deal with, but the only thing I don't really like is the fact that if you are really looking for customized viz, Tableau probably can't provide as much as D3 or similar tools. I also liked the fact that there is no scripting involved(could be good in a business situation, allowing amateurs to learn fast) and I have heard its really popular out there in the real world :P

      Delete
    4. Interesting comments on Tableau. I'm sorry to say that I've never used it. From a brief look, it appears to fill a niche that you've described very well. Much simpler to use with no scripting, but not as much flexability as a 'lower level' tool like d3.js. Each will have it's part to play and I suppose it's just a matter of your end use requirement.

      Delete
    5. Would you be able to explain this line for me, I don't quite understand it as you have listed above.

      topojson.object(topology,topology.objects.countries)
      .geometries

      As I go to import my own json file into my script, I am face with the error type error: topology.objects is undefined.

      However when I use the world-110m map its fine. And I am not sure why. Is topology.objects.countries somehow exclusive to the world-110m.json?

      Delete
    6. Ahh... This would most likely be as a result of the construction of your geojson or topojson files having a different structure. I can't claim to be an expert on JSON, but I started to understand it a bit better once I started to think like this http://www.d3noob.org/2013/06/understanding-javascript-object.html
      If this is the problem, I'm afraid you will have to be a little patient and open both your JSON files and check their structure. Experiment a bit by reducing them to as simple a content as possible so that their structure becomes obvious. Then you will have to translate this to your topojson.xxx.etc call. The process of understanding your data is really useful and will help greatly in the long run.

      Delete
    7. Ok so I managed to reduce the .json down as much as possible to check structure. it looks like this
      "type": "FeatureCollection","features": [{ "type": "Feature","properties": { "FID": 0.000000 }, "geometry": { "type":"MultiPolygon", "coordinates":[[[[141.415967, -15.837696 ]....and the coordinates go on.

      Once I did that I translated it across into my topojson call...

      d3.json("json/QLDCairns.json", function(error, topology) {
      g.selectAll("path")
      .data(topojson.feature(topology,topology.xxxxx.xxxxx)
      .features)

      I tried various combinations topology.geometry.coordinates. But I don't really understand how the call works. I search google but it the search results were a bit off topic. Any ideas? BTW sorry for all the question, with your help I have made so much progress today, appreciated

      Delete
    8. Good to hear that you're making progress. I'm not familiar enough with the call and your data to provide any insightful solutions. but on the surface it looks like you're really close. I would recommend opening a question on Stack Overflow at this stage to see if there is a guru who can spot it quicker. Try to include as much information as possible and if possible have your code in a gist or similar so that they can get good context. You look close at this point!

      Delete
  5. When I paste in the full code example, and then adjust the location of the json file (per first post), I am getting an error. What does it mean?:

    d3.v3.min.js (line 1)
    TypeError: topology is undefined
    .data(topojson.object(topology, topology.objects.countries)

    ReplyDelete
    Replies
    1. It's most likely because you don't have the topojson.min.js file on your system sowhere. In retrospect, I don't know that I elaborated on it as well as I could have in the post above. You need an additional javascript file called topojson.min.js and you need to load it the same way that you load d3.js. You can get it from here (https://github.com/mbostock/topojson) (actually I see that the version at the link isn't the minimised version, but use it and change your code to load topojson.js). Hopefully that will sort the problem out. Good luck.

      Delete
  6. Thanks for sharing the tutorial, good stuff!

    ReplyDelete
  7. Good day,

    I follow this tutorial and i found its working, now my query is that how could i create a heatmap effect on the circle based on the data

    ReplyDelete
  8. thank you for the tutorial, i follow this, it works great but i have some query.. how could i create a heatmap base on the data that was given to me.. let say the data is the circle...

    thanks and happy coding

    ReplyDelete
    Replies
    1. I like your question :-). A neat trick (and I call it a trick because I think there would be more accurate ways to do it) might be to place your dots on the map with differing degrees of opacity, colour and radius depending on the magnitude of the data. That way when your circles started overlapping the opacity values combine. This might provide the effect you're looking for? If you get an effect that you're happy with post a link here so that other readers can admire your work :-). Good luck.

      Delete
    2. Good day,
      I have another problem with this.. What if i am going to remove the circle on each country. instead of circle i want to fill each country with color, let in JAPAN. Japan has circle on it. i will remove that circle and fill the whole area of Japan into blue or other color.. how could i do that . do i have to modify my json file? or can i declare it inside the script? i dont have idea how to do it..

      Delete
    3. Another good question.
      I don't know that I'm clever enough to figure out how to do it with dynamic data (for instance if you are given a lat/long, then colour the country in which it falls) , however, I think the best alternative would be to follow the advice of the master (Mike Bostock) in his tutorial (Let's make a map) here http://bost.ocks.org/mike/map/#styling-polygons. For the example above this would mean identifying the json structure that identified each country and applying the fill based on the country. This may take a bit of thinking before you get it right, but at the end I suspect you will have learned some pretty good knowledge about how geographic data is structured. I'm sorry I can't provide a straight answer, but I haven't done this before, but at least the above description is where I would start. Good luck!

      Delete
  9. I have topology.objects is undefined and I am using the world-110m2.json map. It is weird.

    ReplyDelete
    Replies
    1. Hmm.. Yes... Perhaps post a question with an example of your code and data on Stack Overflow?

      Delete
  10. try this.. d3.json("Script/topojson/examples/world-110m.json", function (error, world) {
    svg.selectAll("path", ".graticule")

    .data(topojson.feature(world, world.objects.countries).features)

    .enter()
    .append("path")

    .attr("d", path);

    ReplyDelete
  11. Awesome tutorial! I am trying to clone in JSFiddle but can't seem to get it to work. Any suggestions?

    http://jsfiddle.net/scottieb/8tTnV/

    ReplyDelete
    Replies
    1. Have you used JSFiddle before? I have only looked (not played) with JSFiddle (I should write a section on it for the book) but I can't see where your code can pickup the world-110m2.json file. You might want to see ow that can be done and go from there. You might want to download the example files from here as an alternative http://bl.ocks.org/d3noob/5193723

      Delete
  12. good day,

    I am having trouble with the zooming using the drop down list in d3. how could i zoom country based on selected value on the drop down. is there any idea

    ReplyDelete
  13. Good day,

    How could i zoom country based on the selected value from drop down list using dispatch method of d3? any suggestion

    ReplyDelete
    Replies
    1. Sorry for the delay in replying. I see a recent question here (http://stackoverflow.com/questions/20260405/zoom-map-based-on-selected-value-on-the-drop-down-list) that might help. Good luck.

      Delete
  14. I think Mike modified either the d3 or the topojson file in the src. Your example is not working anymore.

    ReplyDelete
    Replies
    1. Yikes! Thanks for the heads up. I will have a dig about and see what the story is. Cheers

      Delete
    2. Righto. The problem seems to be related to a change in d3.js. I will let things lie for a couple of days in case it's a bug that just needs to be sorted out, but if not, I will look at updating my examples since I notice they are using an outdated version of topojson at the very least. Thanks again for the heads up!

      Delete
    3. A pleasure Sir. I wonder why they dont make sure ompatibility is ensured when they modify d3.js

      Delete
    4. In fairness, I should probably reference a static version of d3 and topojson in my code. I cant gripe, the price was right ;-)

      Delete
    5. In fairness, I should probably reference a static version of d3 and topojson in my code. I cant gripe, the price was right ;-)

      Delete
  15. @Manuel.. how come it is not working. it working on my machine.. please let me know the problem that you encounter? d3 is not working or json? maybe the problem occurs in your browser version.. json is not working on IE8 and lower..its better in firefox,safari,chrome

    ReplyDelete
    Replies
    1. Thanks Rajan. None of the three links on bl.ocks.org that have the examples on them for the maps are working correctly in chrome or firexo for me either (http://bl.ocks.org/d3noob/5193723, http://bl.ocks.org/d3noob/5189284, http://bl.ocks.org/d3noob/5189184) I have presumed the problem is rrelated to a change in d3.js since I can recreate the sites exactly, but use an older version of d3.js and it works correctly. On your machine are you using a local copy of d3? or the latest on-line version?

      Delete
  16. Aaaand now is working again.

    ReplyDelete
    Replies
    1. Ahh.. and I see there was an update to d3.js 10 hours ago. I think the mystery is solved! Cheers

      Delete
    2. And here's some details of the fix
      https://groups.google.com/forum/#!topic/d3-js/K9EAmmzNUzc

      Delete
  17. Thanks for the tutorial!

    I have a question:
    I would like to assign a link to each single point
    So that you can click on any point

    I hope someone can help ?
    Thanks Tom

    ReplyDelete
    Replies
    1. Great question Tom.
      Yes, you can and it's fairly simple. You just need to append the city circles to an html link.
      I have taken the opportunity to amend the live example on bl.ocks.org (http://bl.ocks.org/d3noob/5193723) and have added in the code that will append the link and it also takes the city name from the csv file and does a Google search on it.
      Have a look over that and play with it a bit till you get a feel for how it works.
      Thanks again for a great question.

      Delete
    2. And while I remember, there is a section in the book (https://leanpub.com/D3-Tips-and-Tricks) that explains the linking thing a bit more fully. Here is a link to an on-line version https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-adding-web-links-to-d3js-objects

      Delete
  18. you mean a tooltip is what you need?

    ReplyDelete
  19. Thanks for the quick reply!
    I'll try to understand this
    lg Tom

    ReplyDelete
  20. I would like to create a map similar to this: http://techslides.com/demos/d3/worldmap-template.html

    Only I would like to select each country: press top and forward to a new page. (Link).

    Is there a similar example that I can understand?
    Or can someone help me with an idea?

    Thank you
    Tom

    ReplyDelete
    Replies
    1. I'd recommend starting with the oracle Mike Bostock's example page "Let's make a map" (http://bost.ocks.org/mike/map/). Take it slowly and aim to understand the steps he goes through. Once you're finished, take a look at the code in the example you provided from techslides and you will see possibilities opening up from the knowledge you have learned from Mike's tutorial. D3.js is a framework where you can fairly easily get something up and running as a derivative of a previous work (and there are a LOT of examples (http://christopheviau.com/d3list/gallery.html)) but once you get into the realm of creating something unique, you need to have a deeper understanding of what is going on in the code. And unfortunately there's no substitute for sitting down and working through the problem. IMHO it's a worthwhile journey :-)

      Delete
  21. Hi and thank you for this awesome tutorial!! Its great!
    I was wondering if you could help me with a simple problem. I have created the map with cities like in your tutorial, changed all to orthographic projection. Now the problem goes because I want to rotate the earth with points also. Is it possible?
    Thanks for your answer and have a good day.

    ReplyDelete
    Replies
    1. I'm sure it would be possible, but it's not something that I've ever tried, so I'm afraid that I won't have anything to help with. I've always been impressed with the faux globe made by Derek Watkins (http://bl.ocks.org/dwtkns/4686432) it looks really pretty and includes rotating labels for cities. It might help as a good example.

      Delete
  22. Excellent tutorial, thanks! It took me a while to realise though that I had linked v1 of topojson and not v0, and v1 doesn't use topojson.object any more. If anyone has the same problem, replace

    .data(topojson.object(topology, topology.objects.countries)
    .geometries)

    with

    .data(topojson.feature(topology, topology.objects.countries)
    .features)

    and it should work fine.

    ReplyDelete
    Replies
    1. Many thanks. That's a great piece of information.

      Delete
    2. Thanks a lot!!! this post saved me!!

      Delete
  23. Hello,
    I would like to show the name of the cities beside the red points. How can I do this?

    ReplyDelete
    Replies
    1. Good question. it's just a matter of adding a block of code into the section where the red dots for the cities are drawn which adds text elements. The location for the elements should be just offset from the dots and it will all work automagically! An example of what you describe is here (http://bl.ocks.org/d3noob/401237468c9e38cea8c7).

      Delete
  24. Hello,
    Is it possible to add some points with blue color?
    I want to draw some points with one more colors simultaneously using two or more data files. How can I do this?

    ReplyDelete
    Replies
    1. Apologies for the extremely late answer to your question. I expect that you have moved on, but I'll try and provide some guidance for anyone else who might read.
      Good question. It would be possible to make the colours transition in a sequence by cycling through a palette, but I sense that this probably isn't what you're looking for. Instead I would encode a specific colour with the `cities.csv` file. This would mean an additional column (labelled 'colour'?) where we could specify the colour that we wanted to make that specific cioty. Then when we placed the circle on the map we could specify the colour using that variable as the attribute. That would look quite neat I think.
      Again, apologies for the delay.

      Delete
  25. Pretty late to ask questions to you on your code written 3 years back. What is the use of http://d3js.org/topojson.v0.min.js?? is it to draw the map background.
    If yes,
    1. Can we include this in our dashboard.

    ReplyDelete
    Replies
    1. Never too late... The only problem is me trying to remember :-).
      "TopoJSON is an extension of GeoJSON that encodes topology. Rather than representing geometries discretely, geometries in TopoJSON files are stitched together from shared line segments called arcs. Arcs are sequences of points, while line strings and polygons are defined as sequences of arcs. Each arc is defined only once, but can be referenced several times by different shapes, thus reducing redundancy and decreasing the file size." So it doesn't draw the map. It's a mechanism for encoding ad reading highly compressed geodata.
      Best place to check it out is here https://github.com/mbostock/topojson/wiki
      Licence details here https://github.com/mbostock/topojson/blob/master/LICENSE

      Delete
  26. https://bost.ocks.org/mike/map/ I followed this article and created topojson file which has places and subunits.
    ogr2ogr -f GeoJSON -where "ADM0_A3 IN ('IND')" subunits.json ne_10m_admin_0_map_subunits.shp

    ogr2ogr -f GeoJSON -where "ISO_A2 = 'IN' AND SCALERANK < 8" places.json ne_10m_populated_places.shp

    topojson -o in.json --id-property SU_A3 --properties name=NAME -- subunits.json places.json

    Then I tried to read your article and use the data but was unable to find the countries properties. So I tried to stick to what is in that article and used:
    console.log(topojson.feature(uk, uk.objects.subunits));
    g.selectAll("path")
    .data([topojson.feature(uk, uk.objects.subunits)]) //note I added []
    .enter()
    .append("path")
    .attr("d", d3.geo.path().projection(d3.geo.mercator()));

    this at least shows the map on india in black but I am unable to center it. Then I will also try to scale and add zoom as per your article.

    the below seems to have no effect.
    //I guess India is in 37.6 N to 80.4 N lat, 68o 7’ E to 97o 25’ E lon
    var projection = d3.geo.mercator()
    .center([178, 60])
    .scale(500)
    .rotate([-180, 0]);
    //.translate([width / 2, height / 2]);

    I was unable to make progress using other article so tried yours.

    ReplyDelete
    Replies
    1. Hmm... That's an interesting question. Formatting data for maps is often 'tricky' because of the difficulty in determining the structure of the JSON used. I suspect that this will be the situation that you find yourself in. I would reccomend that you parse your topojson file back to as small an amount of information as possible and use this to understand the structure and to test in the code. In theory you should be able to find a really small amount of code for each example and then compare the structure between the two. This may help in understanding the differences and make the process of selecting one code block or the other to persevere with. Good luck.

      Delete
  27. Great Example. But I have this error message at the below line of code. Any suggestions are appreciated. Thanks.
    0x800a138f - JavaScript runtime error: Unable to get property 'objects' of undefined or null reference
    d3.json("JOSN/Regions.JSON", function (error, us) {
    g.append("g")
    .attr("id", "countries")
    .selectAll("path")
    .data(topojson.feature(us, us.objects.countries).features)
    .enter()
    .append("path")
    .attr("id", function (d) { return d.id; })
    .attr("d", path)
    .on("click", country_clicked);
    });

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. Hmm... My first thought is that the JSON importing portion (`d3.json("JOSN/Regions.JSON", function (error, us) {`) might be causing problems (can it see the data, is it reading the file). It might be alright, but what you have there is unfamiliar to me. The second thing to check is whether or not the data referenced later is being pulled out correctly. I will have to leave the checking of this down to you as the dataset is yours. My advice for trying to trouble shoot these types of problems is always to make the problem as simple as possible. If you can start with a really uncomplicated piece of code and build up from there to eventually find the sticking point. Good luck

      Delete
  28. Hi, i need exactly this map but a little bit bigger (1280px x 640px). How can i change the size of the map? I tryed to change the width and height vars, but this doesnt work for width...

    I hope u can help me

    ReplyDelete
    Replies
    1. Ahh... This is a good question. I think that what you might be seeing is the difference between setting the size of the map on the page and scaling the map. As well as setting the size to a lager setting as you have done, adjust the scale (it certainly works for me).

      Delete
  29. maybe you can change the scale of the map..

    ReplyDelete
  30. I have a completely different kind of n00b question but I see you were able to make it work with your bl.ocks.org link. How do you get your gist to display the map as it should be (with out the topo.json which is converted through gist)? You link to your topo.json but it isn't loaded to the same gist directory? Appreciate any help!

    ReplyDelete
    Replies
    1. Believe it or not, the map gets displayed automagically byt github! see here for details https://github.com/blog/1528-there-s-a-map-for-that

      Delete