Support for polygons with holes #803

Closed
jystic opened this Issue Jun 20, 2013 · 8 comments

Projects

None yet

3 participants

@jystic
jystic commented Jun 20, 2013

The canvas vector renderer does not currently support rendering polygons with holes.

There is mention in the renderer of needing to "render polygon to sketch canvas first" but I think the solution is simpler than that unless I'm missing something.

Canvas supports holes in polygons by calling closePath() multiple times for a single beginPath().

I tried this out and it works great for the dataset which I have (jystic@20b05f5)

Note the the commit above probably breaks scenario 1 & 2, I just tested out polygons which had a fill and a stroke.

polygon-with-holes

@tschaub
Member
tschaub commented Jun 20, 2013

Awesome @jystic! We need to confirm that this works for overlapping polygons (with potentially overlapping holes), but it looks very promising.

@jystic
jystic commented Jun 21, 2013

So I tried a few things with overlapping polygons. As per the stackoverflow article I linked above, canvas requires that the outer ring is defined in a clockwise direction, while the inner rings (holes) must be defined in an anti-clockwise direction.

polygons-clockwise
Download sample.kml

  1. This polygon has two inner rings which are defined in the counter-clockwise direction (rendered correctly)
  2. This polygon has two inner rings which are defined in the clockwise direction (rendered incorrectly)
  3. This is just a simple polygon with only an outer ring that crosses itself (rendered correctly)
@tschaub
Member
tschaub commented Jun 21, 2013

We can add a wind method that is used in geometry construction, ensuring that outer rings are clockwise and inner rings are counter-clockwise.

@jystic
jystic commented Jun 21, 2013

I just played with the kml parser and if I fix up the polygons to be defined in the correct direction everything works great.

polygons-fixed

Here's the two bits of code I hacked in to the kml parser to get it working:

'outerBoundaryIs': function(node, coordinates) {
  this.readChildNodes(node, coordinates);

  var edge = 0;
  var x1, y1, x2, y2;

  var points = coordinates[coordinates.length - 1];

  x1 = points[points.length - 1][0];
  y1 = points[points.length - 1][1];

  for (var i = 0, ii = points.length; i < ii; i++) {
    x2 = points[i][0];
    y2 = points[i][1];

    edge += (x2 - x1) * (y2 + y1);

    x1 = x2;
    y1 = y2;
  }

  // counter-clockwise (needs reversing)
  if (edge < 0) {
    points.reverse();
  }
},

The main part of the routine is obviously the same and should be in a separate function somewhere.

'innerBoundaryIs': function(node, coordinates) {
  this.readChildNodes(node, coordinates);

  var edge = 0;
  var x1, y1, x2, y2;

  var points = coordinates[coordinates.length - 1];

  x1 = points[points.length - 1][0];
  y1 = points[points.length - 1][1];

  for (var i = 0, ii = points.length; i < ii; i++) {
    x2 = points[i][0];
    y2 = points[i][1];

    edge += (x2 - x1) * (y2 + y1);

    x1 = x2;
    y1 = y2;
  }

  // counter-clockwise (needs reversing)
  if (edge > 0) {
    points.reverse();
  }
},

The code is based on this algorithm from stackoverflow. Pretty elegant I must say :)

@tschaub
Member
tschaub commented Jun 22, 2013

@jystic let's deal with the winding order in #809. After that is complete & merged, you could turn this into a pull request for hole-rendering support.

@jystic
jystic commented Jun 24, 2013

Sounds good to me

@twpayne
Contributor
twpayne commented Jan 6, 2014

Note that polygons with holes are supported in the vector-api branch. I don't know about master.

@twpayne
Contributor
twpayne commented Jan 17, 2014

Fixed with #1545.

@twpayne twpayne closed this Jan 17, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment