This commit changes the albersUsa projection so that it uses a custom projection
stream rather than a custom projection function. The projection stream passes
geometry to all three constituent projections, which are then clipped to their
respective viewports. The result is that geometry that appears in multiple
projections (such as a graticule) is now rendered correctly in each, with
clipping! In addition, the inverse projection is much easier to compute because
the viewport regions of each projection are explicitly defined.
This removes the use of interpolateString if the start and end values passed to
d3.interpolate are different types; the interpolation behavior should be based
solely on the end value. Now if you interpolate an undefined attribute (the
empty string) to a number, it will use number interpolation as expected.
This also fixes interpolateString such that it always returns a string, even if
both the start and end value are coercible to numbers.
Fixes#1241. With d3.scale.pow, d3.scale.sqrt and d3.scale.log, the underlying
implementation is a linear scale. Due to the transformation a linear domain and
the limits of floating-point precision, the reported domain could differ
slightly from what was set. Now, the set domain is preserved exactly.
Since tests are now run in their own sandbox, assert.deepEqual was not
properly testing the returned Date objects for equality, as they weren't
instances of the same Date class used by the test itself, causing type
inference to fail. It was always returning true, even for different
dates.
The behaviour of d3.geo.bounds has been modified slightly, such that the
minimum and maximum longitudes denote minimum and maximum meridians when
going from left to right in a standard equirectangular projection.
If the maximum longitude is in fact numerically smaller than the
meridian, this simply means that the bounds cross the antimeridian.
This fixes several issues:
* The bounding box is now a true minimum-width bounding box.
* Line segments that cross the antimeridian are now correctly handled.
* Similarly, polygons that cross the antimeridian are now correctly
handled.
Secondly, inflection points are now detected, so that the latitudinal
range is extended in the case where the great arc contains the great
circle’s inflection point.
Lastly, support for polygons has been improved, e.g. those that wind
around the poles, and those that have a counter-clockwise winding order.
Note that as part of the winding order detection, the performance of
d3.geo.area had to be reduced somewhat (by as much as 25%). This is
because the trick to avoid atan2 calls fails for winding order detection
in the case where a counter-clockwise area strays into a positive
quadrant. We can reinstate the trick for area-only calculations in
future; I think correct winding order detection is more important at the
moment.
See #1154.
This commit fixes d3.select so that it no longer propagates data from the
document element to the selected node when a selector is used. This commit also
fixes d3.select and d3.selectAll such that the returned selection’s group’s
parent node is set to the document element.
In HSL space, grayscale colors can now have undefined hue rather than assuming a
hue of 0°; likewise black and white can have undefined saturation rather than
assuming 0%. In HCL space, black can now have undefined hue and chroma. (For
non-black grayscale colors, including white, hue and chroma are implied by the
D65 standard referent.)
When interpolating between colors with undefined hue, saturation or chroma, the
defined value is used when available. For example, when interpolating from black
to blue in HCL space, the intermediate colors are now dark blues (#241178)
rather than dark purples (#600054).
Fixes#833.
Data-in and data-out. Although I could see it being useful to access the
computed coordinates, but I think in the common case, these will be implemented
as simple accessors.
Since smash.load uses a separate context, any exceptions it throws use a
different Error class, and thus the assert.throws fails. So, instead, just allow
any exception type to be thrown.
This corrects the handling of lines that are long enough to have two
visible or invisible endpoints, but still cross the small circle and
thus have an invisible or visible intermediate segment.
Fixes#1127.
You can now pass a format specifier to scale.tickFormat (for linear, pow and
identity scales). If the format specifier doesn't have a defined precision, the
precision will be set automatically by the scale, returning the appropriate
format. This provides a convenient, declarative way of specifying a format whose
precision will be automatically set by the scale.
This works with axes, too! For example, `axis.ticks(10, "%")` will now use a
percentage format rather than the default format, while still computing the
appropriate precision.
This commit also includes a fix to make d3.format more robust when unreasonable
precisions are specified. Rather than throwing an error, the nearest reasonable
value is used instead.
Fixes#912.
This adds d3.geo.transverseMercator and removes the custom default scales from
the mercator and equirectangular projections. Also, this commit removes the
built-in 2π scale factor from the mercator projection, simplifying the
implementation and making it consistent with transverseMercator and GDAL. This
is a partial fix for #1133; see also d3/d3-plugins#55.
Previous versions of JSDOM erroneously returned null rather than the empty
string, but this has now been fixed. Note: this depends on tmpvar/jsdom#582
which has not yet been merged to master.
The previous implementation of format, which only supported arrays as input, is
retained as d3_dsv.formatRows; for backwards-compatibility, d3_dsv.format allows
both arrays and objects as input (to be removed in 4.0). This change makes
format and formatRows symmetric with parse and parseRows, respectively.
To compute the set of fields from all objects, two passes are required. Fields
are listed in discovery order, so that in the common case where all fields are
defined on all objects, the order of columns in the generated DSV will match the
property iteration order of the first object.
This supersedes #1106 and fixes#509; thank you to @smcgivern and @hammer for
suggesting this feature.
Similar to selection.select(function), the before argument to selection.insert
can now be specified as a function, so that the caller can specify the before
node dynamically. Supersedes #1071.
You can now pass a map type to d3.nest’s map function. For example, if you pass
d3.map as the second argument to nest.map(array, type), the returned value will
be an instance of d3.map rather than a bar object. In addition to offering
better protection against built-in property names, d3.map offers more convenient
iteration methods.
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.
Now that D3 supports great-arc interpolation automatically for projections,
there is no need for a great arc feature generator, as a simple LineString
geometry object is equivalent. For the same reason, d3.geo.graticule does not
need to use supersampling on meridians, since meridians are great arcs.
This commit also adds d3.geo.distance(a, b) for computing the length of the
great arc between points a and b in radians, as a replacement for the
deprecated d3.geo.greatArc method, and a faster alternative to constructing a
d3.geo.interpolate solely to measure distance.
Fixes#1021#1024.
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.
This allows us to generate the clip outline for whole-earth projections
with arbitrary rotations.
Internally, this is interpreted as a polygon with null coordinates.
This reverts commit 7712dcd59e.
Unfortunately, this caused empty polygons to be interpreted as "the
whole sphere", which is undesirable. Also, it is more useful to have a
definite graticule outline, at least for a graticule that doesn't cover
the whole sphere.
This fixes various problems that were occurring due to winding numbers
being tricky on a finite sphere.
This also includes some cleanup and fixes for degenerate points.
1. We no longer perform an equality check on GeoJSON polygon endpoints,
since we know that the first and last points should be equal.
2. However, there are special cases where a polygon just touches the
clip edge, generating two coincident intersection points. Here we avoid
generating a coincident point, and continue as normal.
3. Another special case is where we have coincident intersection points
e.g. due to a self-intersecting polygon. Here we continue calculating
the winding number as if this is a closed polygon (so we know whether to
insert a polygon around the whole clip edge).
It's not always meaningful to define the centroid of a collection because
collections are heterogenous: they can contain points, lines, polygons, or all
these types together. The previous code ignored objects that had dimensionality
lower than the collection's maximum dimensionality; for example, ignoring lines
and points if the collection contains polygons. While this may be what you want,
this feels magical and it's simpler to just define the centroid as undefined.
I'd prefer to force the user to state what they want explicitly, say by creating
a MultiPolygon that merges together a collection's polygons.
For example, "%-d" formats the day with no padding, "%_d" formats the day with
space-padding equivalent to "%e", and "%0d" formats the day with zero-padding
equivalent to "%d".
Rather than computing the ending value when the transition starts, the ending
value is computed when the transition is scheduled. This gives more predictable
behavior and makes it easier to debug evaluation errors since they occur
immediately (during user code) rather than inside a d3_timer callback.
The behavior of attrTween and styleTween are unchanged, since the interpolator
can only be constructed once the starting value is known. This commit also
removes d3.tween; I may add this back in a future commit, but I think there is
probably a better way to specify an interpolator for transitions.
The start event is now fired before any tweens are evaluated, such that you can
do any necessary preprocessing work beforehand. The end event no longer causes
the transition id to be inherited, since we can do that more elegantly now using
transition.transition.
This commit moves easing and event listeners down to the node's transition
object, allowing these fields to be modified via post-selection. This removes
the last vestige of private state for transitions.
This required a few changes to tests that depended on the order of callbacks for
tweens and events. Because timers are called back in reverse order, tweens are
now initialized in reverse order, and listeners are now invoked in order.
It is now possible to reselect elements with scheduled transitions and redefine
associated tweens; this enables "post-selection" to customize the behavior of
reusable components undergoing transitions, such as an axis. This commit also
makes it much easier to sequence transitions.
Previously, a transition's tweens were stored privately by the transition and
could only be accessed through the transition. This made it impossible to modify
transitions created by components: the transition is not accessible externally,
and cannot be reselected from the document. Consider the following snippet:
g.select(".x.axis")
.call(xAxis)
.selectAll("text")
.attr("dy", null);
If `g` is a selection, then this code alters the appearance of the axis as
expected. However, if `g` is a transition, then transition.selectAll creates a
new concurrent transition, and now multiple tweens compete to set the "dy"
attribute. Oy!
Under the new design, an element's scheduled tweens are stored semi-privately on
the node (in the existing node.__transition__). Transition parameters can thus
be reselected and modified by transitions that share the same id. If you now
reselect a transitioning element, you modify the transition rather creating a
competing transition; this should be less surprising and allow greater control.
As a side-effect of this change, it is no longer possible to schedule concurrent
transitions on the same element, even with the same id: only one transition may
be active on a given element at any time. (Note that you can still schedule
multiple future transitions on the same element, and concurrent transitions on
different elements.) For example, you could previously schedule overlapping
transitions with different easing functions, delays or durations, provided you
were careful to avoid conflict. This seems like a relatively obscure use-case
compared to modifying a transition, so I believe this is a reasonable change.
This commit also changes transition.transition, such that the returned
transition starts at the end of the originating transition, rather than
overlapping. This makes it much easier to schedule sequenced transitions without
the complexity of transition.each("end") and d3.select(this).
Also, transitions are now simply arrays of nodes, consistent with selections!
And d3.xhr now detects if the data has a type attribute; if so, this is used to
set the Content-Type header if one has not already been specified. In
conjunction with d3.urlencode, it is now easy to post encoded parameters.