6

I'm trying to make a Choropleth with d3.js but I got stucked just at the beginning. I found a Shapefile and generated GeoJSON and TopoJson files from it just like here. The map uses Albers-Siberia projection. What I found about this projection:

Projection: Albers Equal-Area Conic

  • Units: Meters
  • Spheroid: Krasovsky
  • Central meridian: 105
  • Standard Parallel 1: 52
  • Standard Parallel 2: 64
  • Reference Latitude: 0
  • False Easting: 18500000
  • False Northing: 0

PROJ.4: +proj=aea +lat_1=52 +lat_2=64 +lat_0=0 +lon_0=105 +x_0=18500000 +y_0=0 +ellps=krass +units=m +towgs84=28,-130,-95,0,0,0,0 +no_defs

MapInfo: "Albers-Siberia", 9, 1001, 7, 105, 0, 64, 52, 18500000, 0.

So I got this code finally and it make nothing (and even freez up), what's wrong?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Choropleth</title>
    <script type="text/javascript" src="d3/d3.v3.js"></script>
    <script type="text/javascript" src="d3/queue.v1.min.js"></script>
    <script type="text/javascript" src="d3/topojson.v0.min.js"></script>
</head>
<body>
    <h1>My Choropleth</h1>
    <script type="text/javascript">

        var width = 960,
            height = 500;

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

        var pr = d3.geo.albers()
            .center([105,0])
            .parallels([52, 64])
            .scale(1000);


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

        d3.json("map_rus_topo.json", function(error, map) {
         svg.append("path")
          .datum(topojson.object(map, map.objects.map_rus))
          .attr("d", path);
        });

    </script>
</body>

You can find all JSON files here.
And one more question: How can I reference to value of region field in my TopoJson file.

JJD
  • 50,076
  • 60
  • 203
  • 339
KoGor
  • 237
  • 5
  • 12

2 Answers2

17

The first problem is that your GeoJSON file isn’t in degrees [longitude°, latitude°], otherwise known as EPSG:4326 or WGS 84. To convert your GeoJSON file to WGS 84, you first need to create a projection file, say albers.prj so that you can tell OGR what the source projection is.

+proj=aea +lat_1=52 +lat_2=64 +lat_0=0 +lon_0=105 +x_0=18500000 +y_0=0 +ellps=krass +units=m +towgs84=28,-130,-95,0,0,0,0 +no_defs

Then, “unproject” the GeoJSON file by converting it to WGS 84:

ogr2ogr -f GeoJSON -s_srs albers.prj -t_srs EPSG:4326 map_rus_wgs84_geo.json map_rus_geo.json

Now you can convert to TopoJSON in WGS 84, rather than projected coordinates. I’ve also taken the liberty of doing some simplification:

topojson -o map_rus_wgs84_topo.json -s 1e-7 -- russia=map_rus_wgs84_geo.json

The second problem is that your projection definition in D3 is incorrect. The d3.geo.albers projection has a default rotate and center that’s designed for a U.S.-centered map, so in addition to defining the center you’ll also need to override the default rotation. In fact, the +lon_0 (central meridian) projection parameter maps to the projection’s rotation, not the projection’s center. Giving:

var projection = d3.geo.albers()
    .rotate([-105, 0])
    .center([-10, 65])
    .parallels([52, 64])
    .scale(700)
    .translate([width / 2, height / 2]);

(I fudged with the center parameter to put Russia at the center of the viewport. You can compute this automatically if you prefer.) You should now see something like this:

Albers Siberia

It’s also possible to work with projected (Cartesian) coordinates in TopoJSON, and then define a d3.geo.path with a null (identity) projection, but I’ll leave that for a separate question.

mbostock
  • 51,423
  • 13
  • 175
  • 129
  • That's just Great! Thank you, for so fast and full answer. I am new to all this stuff, my first time working with maps. – KoGor Apr 18 '13 at 17:03
  • Additionally, can you explain please what make --russia= in this code: > topojson -o map_rus_wgs84_topo.json -s 1e-7 -- russia=map_rus_wgs84_geo.json. And I don't get it about center coordinates, the link U gave is about auto computing scale and translate params, or I somthing missed. – KoGor Apr 18 '13 at 17:14
  • 1
    See the [TopoJSON command-line reference](https://github.com/mbostock/topojson/wiki/Command-Line-Reference) for the full explanation. Above, I’m using `-o` to specify the output file name, `-s` to specify the simplification threshold in steradians, and then the input files follow the `--` separator. There’s only one input file (`map_rus_wgs84_geo.json`), and by prefixing with with `russia=`, I can set the name of the object in the generated topology. Which is why in the linked example, I refer to `russia.objects.russia`. – mbostock Apr 18 '13 at 21:02
  • 1
    As for the projection’s center coordinates, [-10°, 65°], I just made them up based on what looked good. The center coordinates are rotated along with everything else, so generally the center longitude (here -10°) is going to be close to zero with a conic projection. Likewise the center latitude is going to be approximately in the same range as your parallels (52° and 64°) to minimize distortion from the conic projection. – mbostock Apr 18 '13 at 21:04
  • Thx again, now all clear. And about prefixing there is no description (examples) of such possibility in [TopoJSON Command-line reference](https://github.com/mbostock/topojson/wiki/Command-Line-Reference), so I guess adding it would be nice. – KoGor Apr 19 '13 at 08:08
  • 1
    Thanks; added a description. – mbostock Apr 19 '13 at 20:06
  • I'm trying to drill down deeper and found problem with encoding. When I try to execute command `topojson -o output_topo.json -p -s 1e-7 -- name=input_geo.json` I got TopoJSON file with all undefined symbols instead of non-english. How can I control encoding during generating TopoJSON file or it is impossible? – KoGor Apr 21 '13 at 18:55
  • 2
    The topojson command-line tool assumes GeoJSON input is UTF-8 and Shapefile input is Windows1252, and always generates UTF-8 output. You can use the `--shapefile-encoding` if your shapefile input is in a different encoding, but this is extremely rare. If your GeoJSON input is not in UTF-8, use `ogr2ogr -lco ENCODING=UTF-8` to fix it. – mbostock Apr 22 '13 at 15:27
  • I tried to resave my GeoJSON file with UTF-8 encoding simply in notepad, but after executing `topojson -o output_topo.json -p -s 1e-7 -- name=input_geo.json` I got syntax error during parsing. It says I got a couple of crap symbols just at the beginning of the file, but file begins correct (as I can see in notepad) with `{"type": "FeatureCollection",…` I just don't understand what's going on, dark magic? =) I suppose it is not about TopoJSON, but about wrong encoding in shapefile, but why it still fails, if I fixed it manually in notepad… – KoGor Apr 22 '13 at 20:45
  • Mike, need your help again =). I got TopoJSON file: `…"objects":{"russia":{"type":"GeometryCollection","geometries":[{"type":"Polygon","arcs":[[0]],"properties":{"AREA":29809500,"PERIMETER":21822.4,"region":"XYZ"}}…` Now I need to get to fields in properties. I tried this: `.datum(topojson.object(map, map.objects.russia).geometries) … .style("fill", function(d) { if (d.properties.region == "XYZ") {return "red"} else {return "gray"} })` But it doesn't work, what I'm doing wrong? (And why 4 space don't make block of code here?) – KoGor Apr 29 '13 at 12:30
  • Please ask a separate question rather than replying with unrelated questions to this thread. – mbostock Apr 29 '13 at 21:52
0

Adding an answer to this thanks to @mbostock's response. My d3.js map was not showing up or displaying correctly with any projection, then realised through this thread that it was because it didn't conform to the WGS-84 Geographic Coordinate System. According to Datawrapper, TopoJSON or GeoJSON needs to be in the WGS-84 coordinate system.

Thankfully, there is a much quicker and easier way to fix this in 2022. Just upload your geographic file(s) to Mapshaper, open the console, and type in proj wgs84 and press 'enter'. To verify that the projection has changed, type in info and press enter again. You should find the following line in the code that appears: Proj.4: +proj=longlat +datum=WGS8 4. This won't work in every case - it depends on the coordinate system your shapefiles originally used. But it's worth a try.

The preview of your map in Mapshaper may change after the conversion, but as long as you project the map correctly within the d3.js Javascript code, the end result will not be distorted. I had to refer back to where I downloaded my shapefiles to get the info needed for the d3 projection function.

I hope this helps others in the same boat.

Sidders
  • 124
  • 10