CesiumGS / cesium

An open-source JavaScript library for world-class 3D globes and maps :earth_americas:
https://cesium.com/cesiumjs/
Apache License 2.0
13.02k stars 3.51k forks source link

Valid GeoJSON crashes Cesium ("Invalid array length") #12216

Closed Fedec96 closed 2 months ago

Fedec96 commented 2 months ago

What happened?

I receive a valid GeoJSON geometry which cannot be rendered on Cesium. I tried to convert it to Entity and also tried to use GeoJsonDataSource. The incriminated code for reference:

const data = {
  type: "Polygon",
  coordinates: [
    [
      [21.280205, -84.583176],
      [8.256063, -85.434074],
      [-9.347993, -85.946365],
      [-29.513454, -85.98816],
      [-47.776035, -85.546135],
      [-61.55078, -84.74101],
      [-71.1919, -83.710815],
      [-77.725555, -82.5886],
      [-78.26145, -82.645424],
      [-89.556595, -83.550964],
      [-113.51692, -84.281715],
      [-129.65762, -84.145615],
      [-142.52821, -83.64484],
      [-150.33168, -83.09725],
      [-156.74371, -82.43531],
      [-160.9363, -81.8499],
      [-164.67131, -81.18087],
      [-167.3054, -80.593056],
      [-169.61964, -79.9683],
      [-169.81206, -79.91075],
      [-171.68597, -79.2953],
      [-173.56128, -78.55666],
      [-175.02722, -77.86415],
      [-176.55356, -76.99529],
      [-177.79204, -76.13829],
      [-179.1311, -74.996124],
      [-180.0, -74.0675201974177],
      [-180.0, -90.0],
      [0.0, -90.0],
      [180.0, -90.0],
      [180.0, -74.0675201974177],
      [179.73425, -73.78351],
      [178.4335, -72.00047],
      [177.22516, -69.820854],
      [176.38747, -67.922035],
      [172.48814, -68.307625],
      [168.4704, -68.59659],
      [164.3651, -68.784836],
      [160.208, -68.86968],
      [156.03867, -68.85006],
      [151.89645, -68.72602],
      [147.95389, -68.50878],
      [147.87831, -68.59033],
      [146.26059, -70.20061],
      [143.3978, -72.54696],
      [141.24571, -74.00381],
      [139.0332, -75.30195],
      [137.23401, -76.23959],
      [135.26419, -77.165665],
      [133.56697, -77.89019],
      [131.60173, -78.65341],
      [129.81068, -79.28467],
      [127.8049, -79.92548],
      [127.61365, -79.98318],
      [125.48649, -80.58774],
      [122.69854, -81.28585],
      [119.790924, -81.91508],
      [115.62604, -82.66951],
      [110.787834, -83.371056],
      [102.82585, -84.222885],
      [91.85955, -84.98044],
      [70.24746, -85.658905],
      [40.15074, -85.47838],
      [21.280205, -84.583176]
    ]
  ]
};

const dataSource = Cesium.GeoJsonDataSource.load(data);
viewer.dataSources.add(dataSource);
viewer.zoomTo(dataSource);

This is the same geometry drawn on geojson.io (no errors):

image2

The error from Cesium:

image

RangeError: Invalid array length
    at PolygonGeometryLibrary.subdivideRhumbLine (blob:https://sandcastle.cesium.com/c200042e-fdc5-4f55-90d1-941627c88a74:28170:26)
    at createGeometryFromPositions2 (blob:https://sandcastle.cesium.com/c200042e-fdc5-4f55-90d1-941627c88a74:39329:58)
    at PolygonOutlineGeometry.createGeometry (blob:https://sandcastle.cesium.com/c200042e-fdc5-4f55-90d1-941627c88a74:39700:32)
    at createPolygonOutlineGeometry (blob:https://sandcastle.cesium.com/c200042e-fdc5-4f55-90d1-941627c88a74:39754:43)
    at blob:https://sandcastle.cesium.com/c200042e-fdc5-4f55-90d1-941627c88a74:60625:36
    at async Promise.all (index 0)
    at async onMessageHandler (blob:https://sandcastle.cesium.com/c200042e-fdc5-4f55-90d1-941627c88a74:21870:24)

Reproduction steps

No response

Sandcastle example

https://sandcastle.cesium.com/index.html#c=jVbbbtw2EP0VYZ9cwKZneBmSrmMUcIICRW+Ai750+6DsKqlQrWSsZAdOkH/voSgn4tYF6gfv7pyZ4ZkruRv6car29VRXr6pP276qpqf75qrabn4duqf3Q7/dnCfpbhiO+7avp2a8qv5Ikur5A180Kx1IkzuvLoJVLhj28uf5Fzwo7YTEJNgpayx5u4IvojLWx7jg0YoRt8Z1VI6NdXZRCIHX7i+sV94LGZdxZ4VN4UBYOUc+ZH7eMvEa9qw4ckyoUZ4pcGHt4V47/CUFjfBCcbpHeMJ2QQU0dRFdAHsnLrrsH0SiFArMkLJEnenpwL4kwEiAOC+LAs6SEwULWjpozkeItaE8wZEyhiVknCLiKXFBVoznHIM1zhQJYiEVTS4gq2BjLFGrxLPhDHOg4EvcK0O5eqRcxHcpcYTHSAoUfFRRgjmFA2uSBWbyJXmUT4KLPuM6utLcI7vCOsWOUqEUUp7unSLtdUouKh3EnuTWC4yMm48XFaPTZfSpPaImm3E24RSPEHJKjrcwB5WyNsgJZZDEO00c0aDev6QT8bGSvyj8D93/cUxi6o3VqVGRNB+M4wIO6Ix5yLxWRGRLY6+0RhsDTgXTFJwtcFEmeJuqhH6IWlMxowyfFoOd0igB/YJ2L3AIracFdhEDVaBWYWlwRn2wwUgBkwKhjAZBh61BND+ZIH6BHVFh69DxaE63+NZCeg1j+aDjQlyIoffDCQyJ4WfeZEwBC5YHuWTtE0eSIuXWKBPnvYX0YK/FgpnF3rXOL61FCKIwNhFxmbmvHRLKscinQb2wiTm3rTaxTKhxIGbnpYjKsmBsSvM0VZKHDiFGuC9gzCRh9PLQiTO2oKbTSNO8j9LIBitFL2l4JBuXgdfOhhMUC17cAiO5JYwrBlMcl3UTfHHZsMaOjiFvo3RvuVDExZiBSFEveMTqLLyzU4KK2WXbSyxnhAndh3rbvGmxUstlx6QxGzhz2fVah+L8iGbDVbHAEVlYk08tYr2VfM+JQ9rXxpYU6PrlmrSgsab+8i2d4fkD/z5/u+23/W5+FTy2zYfmiHdB33yobpuxfTio32fZ2Xazm3/fDv1Ut31zxDMhPx/GXdM3Pw17vCEWk7tnibq7ffPzG/16ZjS1h6aD5VX1ru7GZpbVfXuop3bovwo/f7MilJ4pd8PDcdeA1OL9+2b4YRz6118g1Q31/iypJtMcg/pqOap6n+H8e6X0cRgOvw0n2OZ8cz1OT11z85zH79rD/XCcqodjd6bU5dQc7rv0Lrp8+7D7u5nUbhyTYVK9vlybXu/bx6rdv3ohedWuq8cRyLuHrrtrPzbbzc31JfT/ZZqia/v3vzw2x65+Smp/8c2PWaiUur7Ez5ctp2Ho3tbHE8//AA

Environment

Browser: Microsoft Edge 128.0.2739.79 (Official build) (64-bit) CesiumJS Version: 1.121.1 Operating System: Linux Ubuntu 22.04.5 LTS x86_64

Fedec96 commented 2 months ago

Tried to change the entity's arc type:

const dataSource = Cesium.GeoJsonDataSource.load(data);

dataSource.then((render) => {
  for (let i = 0; i < render.entities.values.length; i++) {
    const entity = render.entities.values[i];
    if (entity.polygon) {
      entity.polygon.arcType = Cesium.ArcType.GEODESIC;
    }
  }
  viewer.dataSources.add(dataSource);
});

The error is different:

image

Error
    at new DeveloperError (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:64:13)
    at Cartesian3.normalize (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:859:17)
    at blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:19224:23
    at computeTriangleAttributes (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:19245:7)
    at splitLongitudeTriangles (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:19512:9)
    at GeometryPipeline.splitLongitude (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:20994:13)
    at geometryPipeline (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:21253:36)
    at PrimitivePipeline.combineGeometry (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:21542:24)
    at combineGeometry (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:21924:47)
    at onMessageHandler (blob:https://sandcastle.cesium.com/40f95b44-e0f4-46f1-b23d-819881da1a16:21870:30)
ggetz commented 2 months ago

Hi @Fedec96, thanks for reporting this issue!

I believe this is an instance of a bug we're already tracking, https://github.com/CesiumGS/cesium/issues/4801. I'm going to close your issue to keep the discussion in one place. If you have any further input on this, please post it there instead.

From that issue, a potential workaround could be to set up the Viewer with the scene3DOnly option.

Fedec96 commented 2 months ago

Thanks for the feedback @ggetz! Setting scene3Donly to true prevents Cesium from crashing, but no geometry is drawn on the globe. I'll keep an eye on #4801.

Fedec96 commented 2 months ago

@ggetz & others: I'm gonna leave this here for other souls that have this problem in production: while this is not ideal, this is the function I used to patch the crash. While it's not formally correct, it prevents crashes while not heavily impacting visuals:

import type { Polygon, Position } from "geojson";

const adjustPolygonCoordinates = (coordinates: Position[][]): Position[][] => {
  const maxLatitude = 89.9;
  const minLatitude = -89.9;

  return coordinates.map((ring) =>
    ring.map(([lon, lat]) => {
      const adjustedLat = Math.max(Math.min(lat, maxLatitude), minLatitude);

      let adjustedLon = lon;
      if (lon < -180) {
        adjustedLon = lon + 360;
      } else if (lon > 180) {
        adjustedLon = lon - 360;
      }

      return [adjustedLon, adjustedLat];
    })
  );
};

/**
 * Adjusts a polygon's coordinates to avoid rendering issues caused by extreme
 * latitudes (near the poles) and longitudes (crossing the International Date
 * Line).
 *
 * The problem: GeoJSON polygons that have coordinates near the poles (at
 * latitudes of -90 or 90) can cause issues in rendering engines like Cesium.
 * Additionally, coordinates that cross the International Date Line
 * (IDL, at longitude ±180°) might introduce invalid geometries due to improper
 * longitude wrapping.
 *
 * Approach:
 *
 * - Latitude adjustment: Coordinates with latitudes beyond -89.9° and 89.9°
 * are adjusted to avoid issues at the poles
 * - Longitude wrapping: Longitudes outside the range of [-180, 180] are
 * normalized by wrapping around the globe to ensure continuity
 *
 * @param geometry The polygon geometry.
 *
 * @returns The adjusted polygon.
 */
export const adjustPolygon = (geometry: Polygon): Polygon => ({
  ...geometry,
  coordinates: adjustPolygonCoordinates(geometry.coordinates),
});