Harry Stevens
Harry Stevens

Reputation: 1423

How do you convert TopoJSON linestring to polygon?

I have a TopoJSON file containing the boundaries of various districts in Uttar Pradesh, India. When you load the data on a map, you see only the outlines of the districts; the districts themselves are not filled.

I believe the problem is that each district is of type GeometryCollection that has its own geometries made up of a series of LineStrings.

Instead, I want each district to be of type Polygon that just has arcs.

For example, the first object is:

    {
        "type": "GeometryCollection",
        "geometries": [{
            "type": "GeometryCollection",
            "properties": {
                "district_number": 1,
                "district_name": "Ghaziabad"
            },
            "geometries": [{
                "type": "LineString",
                "arcs": [0]
            }, {
                "type": "LineString",
                "arcs": [1]
            }, {
                "type": "LineString",
                "arcs": [2]
            }, {
                "type": "LineString",
                "arcs": [3]
            }, {
                "type": "LineString",
                "arcs": [4]
            }, {
                "type": "LineString",
                "arcs": [5]
            }]
    }

I think I want to convert it, and every other object, to:

    {
        "type": "Polygon",
        "properties": {
            "district_number": 1,
            "district_name": "Ghaziabad"
        },
        "arcs": [[0,1,2,3,4,5]]
    }

I could fix it manually, but that seems insane. Is there a better way?

Update

So I figured out how to convert the object into the result I thought I wanted, but I got some very wacky polygons. Here is my (very clunky) code. Thanks to Saeed Adel Mehraban for some guidance with this.

   d3.json('map.topojson',function(error,data){ // get my json that needs to be converted

    var arr = data.objects.collection.geometries; // this is the relevant array
    var newArr = [];  // in order to map each object, i need to put each one into a new array as a single-item array

    arr.forEach(function(d,i){
      var curr = [d];
      newArr.push(curr);
    })

    newArr.forEach(function(e,i){ // now that i have my new array, i want to convert each object that contains a LineString into a Polygon

      var output = e.map(function(d){

        var arcsArr = []; // an empty array to push each single value of the LineString arcs into
        return {
          "type": "Polygon", // change the type to polygon
          "properties": d.properties, // keep the properties
          "arcs": d.geometries.map(function(g) { // a single key-value pair for arcs, made up of the individual arcs from the LineString
            arcsArr.push(g.arcs[0]);
            return [arcsArr]; // the array of arcs must be in another array
          })
        };

      });

      var output = output[0]; // get only the first item in the output array, which is the object i have modified
      output.arcs = output.arcs[0]; // and change the arcs so we're only taking the first array (as we've duplicated the arrays)
      $('body').append(JSON.stringify(output)+','); // append the whole thing to the body so I can copy it and paste it into the appropriate part of the JSON
    });
  });

This "worked" in the sense that my LineStrings were indeed converted to Polygons, retaining the original border. But the polygons themselves are a nightmare, with straight lines crisscrossing the map at all kinds of angles.

Is there something like a command line tool that can convert boundaries made of LineStrings into Polygons?

Upvotes: 1

Views: 1203

Answers (2)

Andrew Mellor
Andrew Mellor

Reputation: 216

I believe I ran into the same problem being described.

This is Zambia drawn as a svg polyline foreach arc (red being the first arc listed, and magenta being the last):

Zambia drawn as a svg polyline foreach arc

However when attempting to create a polygon by concatenating the arcs:

concatenated arcs

What happened was the arcs for every object were listed clockwise, but the points in every individual arc were listed counterclockwise. Without seeing the topojson that OP is using I cannot 100% confirm this, but I suspect that this was the case.

I solved this by reversing the points in an arc before pushing them to the array of points to draw the polygon and now all is well:

enter image description here

Upvotes: 1

Mehraban
Mehraban

Reputation: 3324

Maybe a map function like below? (I write that with simplistic assumption about data schema. I can't guarantee that it works for complex linestrings since I'm not familiar with topojson format. But it works with your provided data)

var foo = [
  {
    "type": "GeometryCollection",
    "geometries": [{
      "type": "GeometryCollection",
      "properties": {
        "district_number": 1,
        "district_name": "Ghaziabad"
      },
      "geometries": [{
        "type": "LineString",
        "arcs": [0]
      }, {
        "type": "LineString",
        "arcs": [1]
      }, {
        "type": "LineString",
        "arcs": [2]
      }, {
        "type": "LineString",
        "arcs": [3]
      }, {
        "type": "LineString",
        "arcs": [4]
      }, {
        "type": "LineString",
        "arcs": [5]
      }]
    }]
  }
];
var bar = foo.map(function(d) {
  return {
    "type": "Polygon",
    "properties": d.geometries[0].properties,
    "arc": d.geometries.map(function(g1) {
      return g1.geometries.map(function(g) {
        return g.arcs[0];
      });
    })
  };
});

console.log(bar);

Upvotes: 0

Related Questions