d3 / d3-contour

Compute contour polygons using marching squares.

Home Page:https://d3js.org/d3-contour

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Option to remove points along straight lines?

mbostock opened this issue · comments

We currently emit one point per pixel in the grid, which leads to many extra coordinates along straight edges. For example this:

{
  "type": "MultiPolygon",
  "value": 0.5,
  "coordinates": [
    [
      [[6, 7.5], [6, 6.5], [6, 5.5], [6, 4.5], [6, 3.5], [5.5, 3], [4.5, 3],
       [3.5, 3], [3, 3.5], [3, 4.5], [3, 5.5], [3, 6.5], [3, 7.5], [3.5, 8],
       [4.5, 8], [5.5, 8], [6, 7.5]]
    ]
  ]
}

Could be reduce to this:

{
  "type": "MultiPolygon",
  "value": 0.5,
  "coordinates": [
    [
      [[6, 7.5], [6, 3.5], [5.5, 3], [3.5, 3], [3, 3.5], [3, 7.5], [3.5, 8], [5.5, 8], [6, 7.5]]
    ]
  ]
}

The tricky thing is that for geographic coordinates (as in GeoTIFF Contours II) we want to retain the intersecting points along the antimeridian, which may not be possible if we eliminate all the straight line points. Also, parallels are not great arcs, so removing these points changes the interpretation in spherical coordinates.

Is it possible to add an option to remove the rectangular outer border when it's part of the generated path?
RaumZeit/MarchingSquares.js provided a noFrame option to remove it.

Contour simplification could be an option of d3-contour, but I believe it's enough to leave this to topojson as in https://observablehq.com/d/4fe65b865ccf545d

The tricky thing is for geographic coordinates

Shameless plug: for spatial contours we can use d3-geo-voronoi’s geoContour.

Regarding noFrame it seems that the solution is to filter out the multipolygons that have exactly 1 polygon with 1 ring with an area equal to n * m - 1/2, like so:

      p.coordinates.length === 1 &&
      p.coordinates[0].length === 1 &&
      2 * d3.polygonArea(p.coordinates[0][0]) === 2 * n * m - 1

The solution works both with smoothed/non smoothed contours. It seems to work for all n, m but we can fortify it against calculation drift by testing d3.polygonArea(p.coordinates[0][0]) > n * m - 3/4

The test is fast enough (exits immediately on most polygons, and 2*area is computed anyway), so it could be added at no cost as a property to the polygons as a property polygon.sphere or polygon.isFrame, leaving the API otherwise unchanged. It can also be left to the user as shown here.

Re: "noFrame", I think that returning the area (since we've computed it) as p.area could be the simplest solution. Beyond "filtering out frames", it can be used to compute stats. (Implemented in #47)

Re: contour simplification, I propose to leave that to topojson.

All in all, closing this issue. Feel free to comment/reopen etc as necessary.

I don’t think #47 is sufficiently helpful here: the points along straight lines occur not only when a contour polygon covers the entire frame, but whenever any contour polygon abuts the frame. In other words the darker blue polygons in the #47 example also have many points along straight lines that could be removed.

I think what I want here is to detect perfectly horizontal (x1 === x2) or perfectly vertical (y1 === y2) line segments and remove the extraneous intermediate points. But per #14 (comment) this should be optional.

Yes, #47 was only about the "noFrame" sub-issue.