Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Straight lines in GeoJSON sometimes crash with normalized result is not a number #8464

Closed
OmarShehata opened this issue Dec 13, 2019 · 3 comments · Fixed by #8787
Closed

Comments

@OmarShehata
Copy link
Contributor

OmarShehata commented Dec 13, 2019

The GeoJSON below crashes in CesiumJS:

{"type":"LineString","coordinates":[ [0.0, 0.0], [10.0, 0.0], [10.0, 5.0] ]}

image

Visualized in geojson.io.

Sandcastle link. Error produced:

DeveloperError: normalized result is not a number
Error
    at new DeveloperError (http://localhost:8080/Source/Workers/Check-da037458.js:40:23)
    at Function.Cartesian3.normalize (http://localhost:8080/Source/Workers/Cartesian2-2a723276.js:408:23)
    at Ellipsoid.geodeticSurfaceNormal (http://localhost:8080/Source/Workers/Cartesian2-2a723276.js:1800:31)
    at Ellipsoid.cartesianToCartographic (http://localhost:8080/Source/Workers/Cartesian2-2a723276.js:1890:26)
    at Object.GeometryPipeline.projectTo2D (http://localhost:8080/Source/Workers/GeometryPipeline-14f03628.js:980:40)
    at geometryPipeline (http://localhost:8080/Source/Workers/PrimitivePipeline-a698899f.js:261:63)
    at Object.PrimitivePipeline.combineGeometry (http://localhost:8080/Source/Workers/PrimitivePipeline-a698899f.js:367:30)
    at combineGeometry (http://localhost:8080/Source/Workers/combineGeometry.js:6:63)
    at callAndWrap (http://localhost:8080/Source/Workers/createTaskProcessorWorker.js:39:35)
    at http://localhost:8080/Source/Workers/createTaskProcessorWorker.js:84:34

But notice if you preturb the line just slightly, it renders fine:

{"type":"LineString","coordinates":[ [0.0, 0.0], [10.0, 0.0], [10.0, 5.0001] ]}
@dennisadams
Copy link
Contributor

After some digging I think I've got it.
It's an edge case in the generation of rhumb arcs in PolylinePipeline.js.

In generateRhumbArc, first the number of wanted points on each line segment is computed based on distance and granularity. This is used to create an empty positions array.
Then generateCartesianRhumbArc is called to compute the actual positions of the points, and before doing so it computes again the number of points on the line segment, to see how many positions it must generate.
However, the second computation of the number of points is slightly different (see below). So in this edge case, there is one position "missing" in the positions array, which gradually turns into undefineds and then into destructive NaNs.

Now for the details, with @OmarShehata's specific example in mind:
First, generateRhumbArc computes the number of points on each segment:

var c0 = ellipsoid.cartesianToCartographic(
positions[0],
scratchCartographic0
);
var c1;
for (i = 0; i < length - 1; i++) {
c1 = ellipsoid.cartesianToCartographic(
positions[i + 1],
scratchCartographic1
);
numPoints += PolylinePipeline.numberOfPointsRhumbLine(c0, c1, granularity);
c0 = Cartographic.clone(c1, scratchCartographic0);
}

The first segment has 10 points and the second has 6. If you put a breakpoint inside numberOfPointsRhumbLine, you can see this 6 is the result of Math.ceil(5.000000000000001).
Then generateCartesianRhumbArc is called for each segment, and starts by computing again the number of points:
var first = ellipsoid.scaleToGeodeticSurface(p0, scaleFirst);
var last = ellipsoid.scaleToGeodeticSurface(p1, scaleLast);
var start = ellipsoid.cartesianToCartographic(first, carto1);
var end = ellipsoid.cartesianToCartographic(last, carto2);
var numPoints = PolylinePipeline.numberOfPointsRhumbLine(
start,
end,
granularity
);

The first segment still has 10 points, but this time the second one has only 5 points. Have another look at the breakpoint inside numberOfPointsRhumbLine and you'll see this 5 is the result of Math.ceil(4.999999999999999).
So the 6th position is never generated and will remain empty in the positions array.

Why is there a difference? In the first time, the Cartesian3 end points are converted to Cartographics before the computation. But in the second time, the Cartesian3s are scaled with scaleToGeodeticSurface before they are converted. This extra scaling makes a really tiny difference to some coordinates, but it turns out to be critical in this case.

A simple solution that worked for me is to remove the scaling part (which is really redundant anyway), and instead to set the height of the converted Cartographics to 0. (Notice that numberOfPointsRhumbLine doesn't account for heights; perhaps it should, but that's another issue.)
A better solution would obviously be to compute the number of points for each segment only once and pass it over to generateCartesianRhumbArc.

@OmarShehata
Copy link
Contributor Author

Thanks so much for digging into this @dennisadams ! It would be great if you can open a PR with that fix you suggested.

@dennisadams
Copy link
Contributor

Great, I'll try to get it done by tomorrow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants