15

I am playing around geo modules of D3.I have some experience with D3 but this is the first time I am trying out the geo modules. I have taken the following code (from https://github.com/alignedleft/d3-book/blob/master/chapter_12/04_fill.html) that originally displays US map(https://github.com/alignedleft/d3-book/edit/master/chapter_12/us-states.json) edit (files can now be found in the zip downloadable in https://github.com/alignedleft/d3-book/releases/tag/v1.0) in albers projection and modified to take a Geojson of India(indiastates1.json below). The code works well with the US file, but does not display anything with India json file. Am I missing something here. Any help is appreciated. I did change the projection to mercator though.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Setting path fills</title>
        <script type="text/javascript" src="../d3/d3.v3.js"></script>
        <style type="text/css">
            /* No style rules here yet */       
        </style>
    </head>
    <body>
        <script type="text/javascript">

            //Width and height
            var w = 500;
            var h = 300;

            //Define map projection
            var projection = d3.geo.mercator()
                                   .translate([w/2, h/2])
                                   .scale([500]);

            //Define path generator
            var path = d3.geo.path()
                             .projection(projection);

            //Create SVG element
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

            //Load in GeoJSON data
            d3.json("indiastates1.json", function(json) {

                //Bind data and create one path per GeoJSON feature
                svg.selectAll("path")
                   .data(json.features)
                   .enter()
                   .append("path")
                   .attr("d", path)
                   .style("fill", "steelblue");

            });

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

indiastates1.json

{"type":"FeatureCollection","features":[
{"type":"Feature","id":"IND","properties":{"name":"India"},"geometry":{"type":"Polygon","coordinates":[[[77.837451,35.49401],[78.912269,34.321936],[78.811086,33.506198],[79.208892,32.994395],[79.176129,32.48378],[78.458446,32.618164],[78.738894,31.515906],[79.721367,30.882715],[81.111256,30.183481],[80.476721,29.729865],[80.088425,28.79447],[81.057203,28.416095],[81.999987,27.925479],[83.304249,27.364506],[84.675018,27.234901],[85.251779,26.726198],[86.024393,26.630985],[87.227472,26.397898],[88.060238,26.414615],[88.174804,26.810405],[88.043133,27.445819],[88.120441,27.876542],[88.730326,28.086865],[88.814248,27.299316],[88.835643,27.098966],[89.744528,26.719403],[90.373275,26.875724],[91.217513,26.808648],[92.033484,26.83831],[92.103712,27.452614],[91.696657,27.771742],[92.503119,27.896876],[93.413348,28.640629],[94.56599,29.277438],[95.404802,29.031717],[96.117679,29.452802],[96.586591,28.83098],[96.248833,28.411031],[97.327114,28.261583],[97.402561,27.882536],[97.051989,27.699059],[97.133999,27.083774],[96.419366,27.264589],[95.124768,26.573572],[95.155153,26.001307],[94.603249,25.162495],[94.552658,24.675238],[94.106742,23.850741],[93.325188,24.078556],[93.286327,23.043658],[93.060294,22.703111],[93.166128,22.27846],[92.672721,22.041239],[92.146035,23.627499],[91.869928,23.624346],[91.706475,22.985264],[91.158963,23.503527],[91.46773,24.072639],[91.915093,24.130414],[92.376202,24.976693],[91.799596,25.147432],[90.872211,25.132601],[89.920693,25.26975],[89.832481,25.965082],[89.355094,26.014407],[88.563049,26.446526],[88.209789,25.768066],[88.931554,25.238692],[88.306373,24.866079],[88.084422,24.501657],[88.69994,24.233715],[88.52977,23.631142],[88.876312,22.879146],[89.031961,22.055708],[88.888766,21.690588],[88.208497,21.703172],[86.975704,21.495562],[87.033169,20.743308],[86.499351,20.151638],[85.060266,19.478579],[83.941006,18.30201],[83.189217,17.671221],[82.192792,17.016636],[82.191242,16.556664],[81.692719,16.310219],[80.791999,15.951972],[80.324896,15.899185],[80.025069,15.136415],[80.233274,13.835771],[80.286294,13.006261],[79.862547,12.056215],[79.857999,10.357275],[79.340512,10.308854],[78.885345,9.546136],[79.18972,9.216544],[78.277941,8.933047],[77.941165,8.252959],[77.539898,7.965535],[76.592979,8.899276],[76.130061,10.29963],[75.746467,11.308251],[75.396101,11.781245],[74.864816,12.741936],[74.616717,13.992583],[74.443859,14.617222],[73.534199,15.990652],[73.119909,17.92857],[72.820909,19.208234],[72.824475,20.419503],[72.630533,21.356009],[71.175273,20.757441],[70.470459,20.877331],[69.16413,22.089298],[69.644928,22.450775],[69.349597,22.84318],[68.176645,23.691965],[68.842599,24.359134],[71.04324,24.356524],[70.844699,25.215102],[70.282873,25.722229],[70.168927,26.491872],[69.514393,26.940966],[70.616496,27.989196],[71.777666,27.91318],[72.823752,28.961592],[73.450638,29.976413],[74.42138,30.979815],[74.405929,31.692639],[75.258642,32.271105],[74.451559,32.7649],[74.104294,33.441473],[73.749948,34.317699],[74.240203,34.748887],[75.757061,34.504923],[76.871722,34.653544],[77.837451,35.49401]]]}}
]}
Tschallacka
  • 27,901
  • 14
  • 88
  • 133
Ratnakar Vellanki
  • 219
  • 1
  • 2
  • 13

3 Answers3

12

You're (or the browser) just looking in the wrong place. I think that d3 automatically centres on the US with these geo projections. All you need to do is to use transform to move 'India' on to the svg viewport. Specifically, you need to translate the origin of the viewport to a location specified by coordinates in x,y pixels - or at least that's the way I think of it. To see India I tried

.attr("transform", "translate(-800,200)")

and it seemed to do the job.

It's pretty easy to pick these things up if you inspect the element, you can then use the path to give you some hints where to transform.

UPDATE

A far better approach is to this problem would be to compute the center and scale such as outlined in this question and answer. In particular the answer by Jan and Mike are both excellent. There is also an explaination of the code in this google groups discussion - the second last post.

Community
  • 1
  • 1
user1614080
  • 2,854
  • 1
  • 19
  • 22
  • Thanks a lot. Got it. I suspected on similar lines as well, as on chrome, because console did not throw any errors and when i cliked svg on elements, it seem to highlight it somewhere on the browser but did not know where ! So it was a matter of finding the right transform to relocate it. Did you go about trial and error in finding the right numbers for the transform ? – Ratnakar Vellanki Aug 25 '13 at 12:12
  • 1
    The path (i.e. the bit after d = that goes M150.158 128.215, 486.1589Z) tells you where it's displayed. The numbers in this made up example indicate that the path is starting a 150, in your example I think the first number was about 900, so I just shifted the viewport -800 and down a little bit as the inspector showed it just to the top of the screen. For a more complete answer try [this](http://stackoverflow.com/questions/14492284/center-a-map-in-d3-given-a-geojson-object) – user1614080 Aug 25 '13 at 21:30
2

This is a simple example showing how to centre your map projection around all the features in your feature collection (not just one or the default), it's based on the linked Mike Bostock answer, which is great but based on zooming on just one feature, and also d3.js geoJSON and bounds which shows that whole feature collections can be used.

The key changes to your code are:

  • Replacing the initial translate and scale with a neutral one:

       var projection = d3.geo.mercator()
           .scale(1)
           .translate([0, 0]);
    
  • After loading the json, adding code to dynamically update the projection based on the bounding box of the entire feature collection:

       var b = path.bounds( json ),
       s = .95 / Math.max((b[1][0] - b[0][0]) / w, (b[1][1] - b[0][1]) / h),
       t = [(w - s * (b[1][0] + b[0][0])) / 2, (h - s * (b[1][1] + b[0][1])) / 2];
    
       projection
            .scale(s)
            .translate(t);
    

So the updated complete example would look like this:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Setting path fills</title>
        <script type="text/javascript" src="../d3/d3.v3.js"></script>
        <style type="text/css">
            /* No style rules here yet */       
        </style>
    </head>
    <body>
        <script type="text/javascript">

            //Width and height
            var w = 500;
            var h = 300;

            //Define map projection
            var projection = d3.geo.mercator()
                                   .translate([0, 0])
                                   .scale(1);

            //Define path generator
            var path = d3.geo.path()
                             .projection(projection);

            //Create SVG element
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

            //Load in GeoJSON data
            d3.json("indiastates1.json", function(json) {

                // Calculate bounding box transforms for entire collection
                var b = path.bounds( json ),
                s = .95 / Math.max((b[1][0] - b[0][0]) / w, (b[1][1] - b[0][1]) / h),
                t = [(w - s * (b[1][0] + b[0][0])) / 2, (h - s * (b[1][1] + b[0][1])) / 2];

                // Update the projection    
                projection
                  .scale(s)
                  .translate(t);


                //Bind data and create one path per GeoJSON feature
                svg.selectAll("path")
                   .data(json.features)
                   .enter()
                   .append("path")
                   .attr("d", path)
                   .style("fill", "steelblue");

            });

        </script>
    </body>
</html>
Community
  • 1
  • 1
user56reinstatemonica8
  • 32,576
  • 21
  • 101
  • 125
  • Is it normal that it shows blank screen without errors in Firefox? Where `"indiastates1.json"` file should be placed? – reducing activity Mar 28 '21 at 20:34
  • No, that suggests it either failed to find your input and is running on nothing, or the input is corrupted somehow and Javascript is generating SVG but that SVG is failing to render. Use dev tools to look at the SVG output, and look in the network tab to see if your file did load. `indiastates1.json` should be replaced with a path to wherever your actual file is but also check the [documention for `d3.json`](https://github.com/d3/d3-fetch#json) because it might have changed in the 5 years since I wrote this (can't remember if this was before or after D3 switched to using `fetch` and promises) – user56reinstatemonica8 Mar 29 '21 at 10:00
0

This problem happens because US map is positioned so as to center US. You have probably loaded the India file but it may be out of the screen or very tiny to see. You can manually do the correction by changing values of scale and center and translate. but a better approach is automatically finding the scale and translation from the code

var b = path.bounds(#data#),
                s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
                 //scaled the bounding box 
                t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
// new projection
              projection = d3.geo.mercator()
                             .scale(s).translate(t);
              path = path.projection(projection);
DTNPerera
  • 95
  • 1
  • 12