Previously, null nodes were passed to the comparator and were indistinguishable
from non-null nodes with no bound data. For consistency with other selection
operators that skip null nodes, it seems preferrable to put null nodes at the
end of the selection rather than passing them to the comparator. Fixes#881.
This incorporates two enhancements:
1. A simpler formulation for the area of a spherical triangle with one
vertex at the south pole.
2. Instead of calling atan2 for every point, we use the identity:
∑ arg(z) = arg(∏ z),
where z is a complex number, and only call atan2 once for each
polygon, instead opting for a cheaper complex number multiplication
for each point.
This is around 3x faster than the old implementation as measured by the
included benchmark.
Fixes#997. The enter, update and exit selections are now initialized as fixed-
length arrays rather than dynamically populated with nulls. If duplicate keys
are used for either data or selected elements, only the first datum or element
is considered and subsequent data or elements are ignored.
3D rotations are not commutative, so we must perform an inverse rotation
to move something from origin [0, 0] to [λ, φ]. Normally we are moving
origin [λ, φ] to [0, 0].
The south pole rotation could result in line segments that go through
the poles. These are characterised by a 180° longitudinal difference.
These cases were not handled properly by the approximation algorithm,
but it now handles both north and south pole cases.
Rather than doing something like picking the last seen point, return
an undefined centroid if it is ambiguous.
Users can decide what to do, e.g. when picking an origin for a rotating
globe, using the last coordinate in a MultiPoint or LineString seems
reasonable in an ambiguous situation.