title: How to fix geojson to satisfy the needs of a mongodb 2dsphere index
body: <p>I have ~400K documents in a mongo collection, all with geometry of <code>type:Polygon</code>.  It is not possible to add a <code>2dsphere</code> index to the data as it currently stands because the geometry apparently has self-intersections.</p>
  
  <p>In the past we had a hacky workaround which was to compute the bounding box of the geometry on a mongoose save hook and then index that rather than the geometry itself, but we would like to simplify things and just use the actual geometry.</p>
  
  <p><strong>So far I have tried using turf</strong> as follows (this is the body of a function called <code>fix</code>):</p>
  
  <pre><code>let geom = turf.polygon(geometry.coordinates);
  geom = turf.simplify(geom, { tolerance: 1e-7 }); 
  geom = turf.cleanCoords(geom); 
  geom = turf.unkinkPolygon(geom);
  geom = turf.combine(geom);
  return geom.features[0].geometry;
  </code></pre>
  
  <p>The most important function there is the <code>unkinkPolygons</code> which I hoped would do exactly what I wanted, i.e. make the geometry nice enough to be indexed.  The <code>simplify</code> is possibly not helpful but I added it in for good measure. The <code>clean</code> is there because <code>unkink</code> complained about its input, and the <code>combine</code> is there to turn an array of <code>Polygon</code>s into a single <code>MultiPolygon</code>.  Actually, <code>unkink</code> still wasn't happy with it's inputs, so I had to write a hacky function as follows that jitters duplicated vertices, this modifies the <code>geom</code> before passing to <code>unkink</code>:</p>
  
  <pre><code>function jitterDups(geom) {
    let coords = geom.geometry.coordinates;
    let points = new Set();
    for (let ii = 0; ii &lt; coords.length; ii++) {
      // last coords is allowed to match first, not sure if it must match.
      let endsMatch = coords[ii][0].join(",") === coords[ii][coords[ii].length - 1].join(",");
  
      for (let jj = 0; jj &lt; coords[ii].length - (endsMatch ? 1 : 0); jj++) {
        let str = coords[ii][jj].join(",");
  
        while (points.has(str)) {
          coords[ii][jj][0] += 1e-8; // if you make this too small it doesn't do the job
          if (jj === 0 &amp;&amp; endsMatch) {
            coords[ii][coords[ii].length - 1][0] = coords[ii][jj][0];
          }
          str = coords[ii][jj].join(",");
        }
        points.add(str);
      }
    }
  }
  </code></pre>
  
  <p>However, even after all of that mongo still complains.  </p>
  
  <p>Here is some sample raw <code>Polygon</code> input:</p>
  
  <pre><code>{ type: "Polygon", coordinates: [ [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027535925691804, 51.5122814221859 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027638484531731, 51.5122996934574 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027672409315982, 51.5123868001613 ], [ -0.027667905522642, 51.5123866344944 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.02764931654289, 51.512375566682 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.027542009179339, 51.5122867222457 ] ], [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ], [ -0.027542009179339, 51.5122867222457 ] ] ] }
  </code></pre>
  
  <p>And that same data after it has passed through the above fixing pipeline:</p>
  
  <pre><code>{ type: "MultiPolygon", coordinates: [ [ [ [ -0.027560309178214, 51.5123001412876 ], [ -0.02754202882236209, 51.51228674396312 ], [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.02754202884162257, 51.51228674398443 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ] ] ], [ [ [ -0.02754202884162257, 51.51228674398443 ], [ -0.02754202882236209, 51.51228674396312 ], [ -0.027541999179339, 51.5122867222457 ], [ -0.02754202884162257, 51.51228674398443 ] ] ] ] }
  </code></pre>
  
  <p>And here is the relevant bit of the error that is spat out by the index creation:</p>
  
  <pre><code>Edges 0 and 9 cross.
  Edge locations in degrees: [-0.0275603, 51.5123001]-[-0.0275420, 51.5122867] and [-0.0275420, 51.5122867]-[-0.0275579, 51.5122984]
  "code" : 16755,
  "codeName" : "Location16755"
  </code></pre>
  
  <p><strong>My question is:</strong> is there a bug in <code>turf</code>, or is it not doing what I need here in terms of keeping mongo happy?  Also is there any documentation on exactly what the <code>2dshpere</code> index needs in terms of "fixing"?  Also, does anyone have suggestions as to what other tools I might use to fix the data, e.g. <a href="https://github.com/mbloch/mapshaper/wiki/Command-Reference" rel="noreferrer">mapshaper</a> or PostGIS's <a href="https://postgis.net/docs/ST_MakeValid.html" rel="noreferrer">ST_MakeValid</a>.</p>
  
  <p>Note that once the existing data is fixed I also need a solution for fixing new data on the fly (ideally something that works nice with node).</p>
  
  <p>Mongo Version: 3.4.14 (or any later 3.x)</p>