vasturiano / three-globe

WebGL Globe Data Visualization as a ThreeJS reusable 3D object
https://vasturiano.github.io/three-globe/example/links/
MIT License
1.2k stars 146 forks source link

Multipolygon holes not rendered correctly #37

Open baxxos opened 3 years ago

baxxos commented 3 years ago

Is there any way to render GeoJSON multipolygons which contain holes using the polygons layer? I just can't make this work - a very simple example is 2 triangles nested into each other (one of them is poportionally smaller):

const polygonData = {
  "type": "FeatureCollection",
  "features": [
    {
      "properties": {},
      "type": "Feature",
      "geometry": {
        "type": "MultiPolygon",
        "coordinates": [
          [
            [
              [-40, -15],
              [-30, -5],
              [-40, -5],
              [-40, -15]
            ],
            [
              [-39, -12],
              [-32, -6],
              [-39, -6],
              [-39, -12]
            ]
          ]
        ]
      }
    }
  ]
}

...

globeInstance.polygonsData(polygonData.features)

Expected result: image (taken from https://geojson.io/, you can also try https://geojson.tools/ - the result is identical)

Actual results: imageimage

baxxos commented 3 years ago

Lowering the polygonCapCurvatureResolution reveals that, in fact, an inverse polygon is rendered - i.e. the multipolygon hole is filled and the area that's supposed to be filled originally is empty: image

vasturiano commented 3 years ago

@baxxos thanks for reaching out.

Your polygons may be reverse wound, which would lead to plotting the outer side of the globe.

Could you try and inverse the order of your coordinates, to see if you get a different result?

baxxos commented 3 years ago

@vasturiano thanks for the fast reply.

You're right, inverting the coordinates order of both the polygon and its inner hole mitigates the isssue:

image

However, some things are still unclear to me:

vasturiano commented 3 years ago

@baxxos you're certainly not alone in this confusion. This module follows the convention used in world-atlas, natural earth, PostGIS and D3, since these are tools very frequently used to generate and handle geographical polygons. These tools all use the convention of outer-boundary: clockwise and inner-holes: counter-clockwise.

What is unfortunate is that this is the exact opposite of what the GeoJSON standard (RFC 7946) dictates. So there you have it, given the discrepancy between theory and practice, this repo makes the pragmatic decision to follow what's most commonly used.

You can see here a comment from mbostock articulating some of the back story behind this.

baxxos commented 2 years ago

@vasturiano thanks for the detailed reply - it helped me to understand what's actually going on with my GeoJSON data.

The only thing left is the incorrect outlining of polygons with holes (see the screenshot in my previous comment). However, in the meantime, I've come up with the following workaround for my use case:

  1. Generate GeoJSON data to be projected on the globe (D3.js ecosystem polygon data shape)
  2. Transform the GeoJSON multipolygons into 2D paths using d3.geoPath together with a high-resolution equirectangular projection - d3.geoEquirectangular
  3. Draw (fill, stroke..) the paths onto a dynamically created HTML canvas element
  4. Use the canvas to create a new Three.js texture
  5. Overlay the texture on the one you already use for the main three-globe globe object or create a new, slightly larger sphere geometry object and use the texture on it (depends on the particular use case)

This approach has much better rendering performance since the engine doesn't have to render any of the complex GeoJSON shapes. However, it might not be suitable for everyone since it blocks the way to some additional features (e. g. mouse interaction or animations).

I'll leave the decision to close this issue to you.

vasturiano commented 2 years ago

@baxxos regarding your issue about the stroke in the polygon holes, I've tracked down the bug and fixed it in this deeper module: https://github.com/vasturiano/three-geojson-geometry/commit/800de3361a59e5067fa85d77c96422a2cba42c18

There was an incorrect calculation in the holes indexing. The fix is published in the newest version, so if you upgrade your nested dependencies it should solve your case.