Fil / d3-geo-voronoi

Voronoi / Delaunay tessellations on the sphere
ISC License
229 stars 24 forks source link

Unexpected Singularities in Projection #48

Closed DrYerzinia closed 2 years ago

DrYerzinia commented 2 years ago

I use this library to take a "point cloud" of data on a sphere and connect them to show the data as a continuous mesh. I am seeing weird singularities at the poles and a circle connecting them as shown here.

image

This image is generated by using globe.gl and plotting a polygon for each triangle in the triangles element of the output of geoDelaunay. I am giving it a set of points in lat long. I create points by converting the lat-long to Euclidian for the polygons.

I would expect as is shown in this post (https://www.redblobgames.com/x/1842-delaunay-voronoi-sphere/) that after projection and triangulation the only hole after triangulation would be at the point at infinity and that would be patched up at the end.

I took a look into how the projecting is being done and don't see a problem, but am not an expert on this kind of thing. Will look into it more myself, but if anyone has any ideas let me know.

Fil commented 2 years ago

Can you share some code, maybe? Triangles generated by this library should not leave any hole. Here's an example: https://observablehq.com/@recifs/spherical-delaunay-triangulation

DrYerzinia commented 2 years ago

This is the code I'm using. The data is being pulled from an HDF5. I convert it to Cartesians, and then used those points when I use the triangle indexes from the geoDelaunay.

  const geometry = new THREE.BufferGeometry();

  let ts = f.get('unix_timestamps').value;
  let lats =  f.get('latitude').value;
  let lngs = f.get('longitude').value;
  let vals = f.get('ta').value;

  let pointsSpherical = [];
  let pointsCartesian = [];
  let vertexColors = []
  for (let i = 0; i < ts.length; i++){
    pointsSpherical.push([lats[i], lngs[i]])
    pointsCartesian.push(world.getCoords(lats[i], lngs[i], 20 / constants.EARTH_RADIUS_KM));
    vertexColors.push(Util.evaluate_cmap(vals[i], defaultColormap, false));
  }

  del = geoDelaunay(pointsSpherical);
  const numTri = del.triangles.length;

  let positions = new Float32Array(numTri * 9);
  let colors = new Float32Array(numTri * 9);

  for (let i = 0; i < numTri; i++) {

    const pointIndicies = del.triangles[i];

    for (let j = 0; j < 3; j++){

      positions[i*9 + j*3 + 0] = pointsCartesian[pointIndicies[j]].x;
      positions[i*9 + j*3 + 1] = pointsCartesian[pointIndicies[j]].y;
      positions[i*9 + j*3 + 2] = pointsCartesian[pointIndicies[j]].z;

      colors[i*9 + j*3 + 0] = vertexColors[pointIndicies[j]][0];
      colors[i*9 + j*3 + 1] = vertexColors[pointIndicies[j]][1];
      colors[i*9 + j*3 + 2] = vertexColors[pointIndicies[j]][2];

    }
  }

  // itemSize = 3 because there are 3 values (components) per vertex
  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3) );
  geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
  const material = new THREE.MeshBasicMaterial({vertexColors: true, side: THREE.DoubleSide});
  const mesh = new THREE.Mesh(geometry, material);
DrYerzinia commented 2 years ago

My first suspicion here is that the original input points don't map to the indices in the triangle array. I have other data associated with those points so if that is the case I would need a way to map those indices back to the original ones. I'm looking though the code though and am not seeing how that would be the case.

DrYerzinia commented 2 years ago

So this is choking my browser to run, but you can sort of see the issue here. https://observablehq.com/@dryerzinia/spherical-delaunay-triangulation image This should actually be a more uniform grid.

DrYerzinia commented 2 years ago

For reference here is the Delaunay triangulation computed via quickhull3d which takes the convex hull of the 3D points. The input data is similar to what I created above which is the set of points that represents the centers of the H3 grid at resolution 4 as input. image I believe your method will run faster however which is why I am trying to figure out why this distortion occurs.

Fil commented 2 years ago

Oh I see.

h3.cellToLatLng returns coordinates as [lat, lon], but d3 expects [lon, lat].

You can fix the notebook by adding .reverse:

h3.cellToLatLng(children[j]).reverse()

DrYerzinia commented 2 years ago

This fixed it. lol