This is the 9th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021


See the Data Visualization column for a series of articles


Reference:

  • Learn D3: Shapes
  • Shapes (d3-shape)
  • D3-shape (Unofficial Chinese Translation)

This article focuses on the Shapes modules.

A complete visualization work is composed of a variety of basic graphic symbols, for example, a pie chart is composed of multiple fan-shaped Arcs, a broken line chart is composed of multiple segmental lines, and an area chart is composed of one or more areas. Symbols symbols (such as stars, triangles, circles, etc.) may also be used for annotation in the figure.

SVG provides basic graphical symbols for constructing different elements, such as

elements for drawing rectangles (or rounded rectangles) and elements for drawing complex shapes. But setting the parameters of these elements “manually” to draw the desired shape (for example, the element requires setting the property D, whose value is a string of M, L, etc., and x-y point data) is tedious.

A variety of basic graphics generators are included in the D3-Shape module, which makes it easy to generate desired graphics based on abstract data.

A generator that can be used directly to generate graphics, typically generating values that can be used for the element attribute D:

  • Arcs sector/ring generator: Draw sector based on inside and outside radius and opening Angle

  • Lines segment generator: Based on coordinate points, directly connect a series of coordinate points to draw a polyline

  • Areas Area Generator: Similar to the line segment generator, but it draws Areas based on upper and lower boundaries

  • Links Connection generator: Based on two coordinate points, a smooth cubic Bezier curve is drawn as a line between two points

  • Symbols generator: Provides a series of Symbols commonly used in scatter plots to represent categories of data

Can not be used directly to generate graphics generators, need to be used with the generator above:

  • Pie Angle Generator: Returns an array of angles and uses this Angle data for the Arcs sector/ring generator

  • Curves Generator: Used in conjunction with the Lines segment generator, sets the interpolation between coordinate points (D3 has several built-in methods) to draw smoother Lines

  • Custom Curves Custom curve generator: Custom interpolation functions between coordinate points that are used in conjunction with the Lines segment generator

  • Generator: Sets Custom graphic symbols to the type of symbols through symbol.type

  • Stacks: This is how Stacks are calculated based on data that needs to be passed to the Areas area generator

Sector generator Arcs

The sector generator Arcs is used to generate a sector or ring, and then the pie chart or ring chart is formed by piecing together multiple sectors or rings.

Method of used3.arc()Create a fan generator.

It is both a method that can be called directly, passing as an input an object containing multiple set parameters (which are actually internally called by the corresponding method) to create a sector; It is also an object that has multiple methods for setting different parameters, and is typically used as a chain call.

💡 The result returned when the sector generator is called, depending on whether the sector generator has a parent context set. If the fan generator has a parent container (typically a selection set, such as a

element), the element is generated and added to the parent container; If there are no input arguments, a string is generated that can be used as an attribute value for attribute D of the element.

// Create a default sector generator
const arc = d3.arc();

// You need to pass the relevant setup parameters (radius and Angle) when calling the generator
// Suitable for dynamic generation of various shapes of the sector
arc({
  innerRadius: 0.outerRadius: 100.startAngle: 0.endAngle: Math.PI / 2
}); / / "M0, - 100 a100, 100,0,0,1,100,0 L0, 0 z"
Copy the code
// Create a sector generator and set the parameters
const arc = d3.arc()
    .innerRadius(0)
    .outerRadius(100)
    .startAngle(0)
    .endAngle(Math.PI / 2);

// When the fan generator has set the radius and Angle, it can be called directly to generate the path
arc(); / / "M0, - 100 a100, 100,0,0,1,100,0 L0, 0 z"
Copy the code

💡 The two creation and invocation methods can be mixed. For example, if you want to create a series of sectors with the same radius but different angles, you can create a sector generator with a preset radius and then pass the Angle information when the generator is called

// An array containing a series of fan angles (starting and ending angles in radians)
const angleArr = [
    {
        start: 0 * 2 * Math.PI,
        end: 1/4 * 2 * Math.PI
    },
    {
        // ...
    },
    // ...
]

// Create a sector generator with a preset radius. The start and end angles are passed in as parameters
arc = d3.arc()
    .innerRadius(210)
    .outerRadius(310)
    .startAngle(([startAngle, endAngle]) = > startAngle)
    .endAngle(([startAngle, endAngle]) = > endAngle)

const pathArr = []
angleArr.forEach(angle= > {
    pathArr.push(arc([angleArr.start, angleArr.end]))
})
Copy the code

The center coordinate of the sector generated by 💡 is (0, 0) by default. You can set the transform property for the parent container of the sector (usually the element

) and move it to the target position

💡 the tau tau tau symbol, which stands for 2π2 pi2π, is often found in official samples and documentation

The fan generator (arc below) has methods that generally return the fan generator itself for easy chain calls:

  • Arc.centroid () calculates the “midpoint” of the sector, That is, the radian is (startAngle+endAngle)/2(startAngle +endAngle)/2(startAngle +endAngle)/2 radius is (innerRadius+outerRadius)/2(innerRadius +outerRadius)/2(innerRadius +outerRadius)/2(innerRadius +outerRadius)/2(innerRadius +outerRadius)/2, generally add the text comment of the sector to this ⚠️. The point may not be inside the sector/ring

  • Arc.innerradius ([radius]) sets the innerRadius. The parameter can be a number or a function that returns a number. If there is no entry, the current inner radius is returned. A fan is generated if RADIUS =0, and a circle is generated if RADIUS is not zero

  • Arc.outerradius ([radius]) sets the outerRadius

    💡 If the inner radius is greater than the outer radius, then D3 will exchange the two; If the input parameter is negative, it is treated as 000

  • Arc.cornerradius ([radius]) Sets the radius of the round corner and the two outer corners for the sector; For the ring, set the inner and outer corners. But the radius of a fillet cannot be greater than (outerRadius−innerRadius)/2(outerradius-innerradius)/2(outerRadius−innerRadius)/2 and the radius of a fillet decreases when two adjacent fillets get too close together. Generally occurs in the inner rounded corner of a ring whose arc length is less than π\ PI π.

  • Arc.startangle ([Angle]) Sets the starting Angle of one side of the sector, in radians. It is 000 at the 12-point direction (the -y direction of the SVG coordinate system). If the parameter is negative, the starting Angle is counterclockwise.

  • Arc.endangle ([Angle]) sets the starting Angle of the other side of the sector as the endAngle.

    💡 if ∣ endAngle – startAngle ∣ p tau | endAngle – startAngle ge \ | \ tau ∣ endAngle – startAngle ∣ tau or generate a complete circle or ring

  • Arc.padangle ([Angle]) Sets the spacing Angle of the sector

  • Arc.padradius ([radius]) sets the interval radius of the sector

    💡 Interval Angle with interval radius calculate interval arc length padAngle×padRadiuspadAngle \times padRadiuspadAngle×padRadius Subtract the same interval arc length for both inside and outside of the ring, This results in the image on the right below (the adjacent edges of two rings are parallel); For circles with a fan or a smaller inner radius and a smaller opening Angle, only the interval arc length is subtracted from the outside so as to get the effect shown on the left side below (the inside of the fan or ring is connected to a point). But if the fan or ring opening Angle ∣ endAngle – startAngle ∣ p tau | endAngle – startAngle ge \ | \ tau ∣ endAngle – startAngle ∣ p tau (generates a complete circle, or circle) will ignore arc length apart

  • Arc.context ([context]) is used to set the parent container. If the fan generator has a parent container (typically a selection set, such as a

    element), the element is generated and added to the parent container; If there are no input arguments, a string is generated that can be used as an attribute value for attribute D of the element

💡 Data about pie or circle angles is not set “manually”, but rather a series of angles generated by the Pies Pie Angle Generator, which is then used in the pie generator.

Method of used3.pie()Create a Pie chart Angle generator.

The Pie chart Angle generator (Pie below) is called with the data you want to visualize (an array) as an input parameter

const data = [1.1.2.3.5.8.13.21];
const pie = d3.pie();
const pieArcData = pie(data)

/ / the result
/ / /
// {"data": 1, "value": 1, "index": 6, "startAngle": 6.050474740247008, "endAngle": 6.166830023713296, "padAngle": 0},
// {"data": 1, "value": 1, "index": 7, "startAngle": 6.166830023713296, "endAngle": 6.283185307179584, "padAngle": 0},
// {"data": 2, "value": 2, "index": 5, "startAngle": 5.817764173314431, "endAngle": 6.050474740247008, "padAngle": 0},
// {"data": 3, "value": 3, "index": 4, "startAngle": 5.468698322915565, "endAngle": 5.817764173314431, "padAngle": 0},
// {"data": 5, "value": 5, "index": 3, "startAngle": 4.886921905584122, "endAngle": 5.468698322915565, "padAngle": 0},
// {"data": 8, "value": 8, "index": 2, "startAngle": 3.956079637853813, "endAngle": 4.886921905584122, "padAngle": 0},
// {"data": 13, "value": 13, "index": 1, "startAngle": 2.443460952792061, "endAngle": 3.956079637853813, "padAngle": 0},
// {"data": 21, "value": 21, "index": 0, "startAngle": 0.000000000000000, "endAngle": 2.443460952792061, "padAngle": 0}
// ]
Copy the code

It then returns an array with the same length and the same order of elements, where each element (which is an object) corresponds to one data item in turn and contains the following attributes:

  • dataThe value of the data item
  • valueA value based on which the Pie chart generator calculates the Angle that the data item should occupy in a sector or ring
  • indexIndex of data items from0start
  • starAngleThe starting Angle corresponding to the data item in the sector or ring
  • endAngleThe end Angle corresponding to the data item in the sector or ring
  • padAngleThe Angle at which a sector or ring is spaced

These elements can then be used as Arc sector generatorsinnerRadiusouterRadiusSometimes the parent container is presetcontext), and then based on each elementstarAngle,endAnglepadAngleGenerate the corresponding sector

pieArcData.forEach((d) = > {
  arc(d); // Arc is a fan generator
})
Copy the code

The Pie Angle generator (Pie below) is both a method (which can accept data as an input parameter) and an object with multiple methods to set different parameters. These methods generally return the Pie Angle generator itself for easy chain calls:

  • Pie.value ([valueMapFunction]) sets the value that each data item is passed to the PIE chart Angle generator. By default, the entire data item is passed directly (because D3 assumes that each data item is a numeric value), i.e. the default valueMapFunction is as follows

    function value(d) {
      return d;
    }
    Copy the code

    ValueMapFunction takes data D of the data item (per row), index I of the data item, and the entire data table data as input parameters, which can then be transformed to return a value representing that data item, which is passed to the Pie chart Angle generator.

    const data = [
      {"number":  4."name": "Locke"},
      {"number":  8."name": "Reyes"},
      {"number": 15."name": "Ford"},
      {"number": 16."name": "Jarrah"},
      {"number": 23."name": "Shephard"},
      {"number": 42."name": "Kwon"}];const arcs = d3.pie()
        		  .value(d= > d.number) // Just accept the data item as an input and pass the value of the number attribute of the data item as the value of the Pie chart Angle generator
    Copy the code

    💡 valueMapFunction returns a value equivalent to the value attribute of each element in the array returned by the Pie Angle generator. The value of each data item corresponds to the data property of each element in the array returned by the Pie Angle generator.

    The entire table is passed directly to the Pie chart Angle generator, rather than being preprocessed first, so that in subsequent binding operations, all information for each data item can be bound to the corresponding element, preserving greater extendiability. However, items in the input table (per row) may have multiple attributes (for example, in the example above, each item contains both number and name attributes) rather than just numeric values, in which case you need to set pie.value(), similar to the original method map of the JS array.

  • Pie.sort ([compareFunction]) sets the priority of the Angle to which the data items are to be compared. If the function returns a negative value, then a is ranked before B, then; If the function returns a positive value, a comes after B.

    💡 The Pie Angle generator actually “sorts” the angles of the items without changing the order of the elements in the array (the sorted array returns the same order as the items in the table).

    💡 Implicitly sets pie.sortvalues (null) comparison function to null if pie.sort() is set to ignore sorting by value

    const data = [10.50.22.80.11.30.130];
    
    d3.pie()(data);
    / / /
    // 0: Object {data: 10, index: 6, value: 10, startAngle: 6.094501063720739, endAngle: 6.283185307179585, padAngle: 0}
    // 1: Object {data: 50, index: 2, value: 50, startAngle: 3.9623691126357747, endAngle: 4.9057903299300065, padAngle: 0} 1: Object {data: 50, index: 2, value: 50, startAngle: 3.9623691126357747, endAngle: 4.9057903299300065, padAngle: 0}
    // 2: Object {data: 22, index: 4, value: 22, startAngle: 5.471843060306545, endAngle: 5.886948395916008, padAngle: 0}
    // 3: Object {data: 80, index: 1, value: 80, startAngle: 2.4528951649650033, endAngle: 3.9623691126357747, padAngle: 0}
    // 4: Object {data: 11, index: 5, value: 11, startAngle: 5.886948395916008, endAngle: 6.094501063720739, padAngle: 0}
    // 5: Object {data: 30, index: 3, value: 30, startAngle: 4.9057903299300065, endAngle: 5.471843060306545, padAngle: 0}
    // 6: Object {data: 130, index: 0, value: 130, startAngle: 0, endAngle: 2.4528951649650033, padAngle: 0}
    // ]
    
    d3.pie().sort((a,b) = > a-b )(data);
    / / /
    // 0: Object {data: 10, index: 0, value: 10, startAngle: 0, endAngle: 0.18868424345884643, padAngle: 0}
    // 1: Object {data: 50, index: 4, value: 50, startAngle: 1.3773949772495788, endAngle: 2.320816194543811, padAngle: 0} 1: Object {data: 50, index: 4, value: 50, startAngle: 1.3773949772495788, endAngle: 2.320816194543811, padAngle: 0}
    // 2: Object {data: 22, index: 2, value: 22, startAngle: 0.3962369112635775, endAngle: 0.8113422468730396, padAngle: 0}
    // 3: Object {data: 80, index: 5, value: 80, startAngle: 2.320816194543811, endAngle: 3.8302901422145825, padAngle: 0}
    // 4: Object {data: 11, index: 1, value: 11, startAngle: 0.18868424345884643, endAngle: 0.3962369112635775, padAngle: 0}
    // 5: Object {data: 30, index: 3, value: 30, startAngle: 0.8113422468730396, endAngle: 1.3773949772495788, padAngle: 0}
    // 6: Object {data: 130, index: 6, value: 130, startAngle: 3.8302901422145825, endAngle: 6.283185307179586, padAngle: 0}
    // ]
    Copy the code
  • Pie.sortvalues ([compareFunction]) sets the priority of the Angle corresponding to the data item, The input parameter of the sorting equation compareFunction is the value valueA and valueB passed to the Pie chart Angle generator to represent the two data items to be compared

    💡 If pie.sortvalues () is set implicitly to null the pie.sort(null) comparison function ignores sorting by data item

  • Pie.startangle ([Angle]) Sets the starting Angle of the pie chart Angle generator (default is 0), which is called once to set the starting Angle of the first sector

    💡 The input parameter Angle can be arbitrary, but if you intend to apply the results of the Pie chart Angle generator to an Arc sector generator, you should use radians, such as Angle = math.pi /2

  • Pie.endangle ([Angle]) sets the starting Angle of the pie chart Angle generator (default is 2* math.pi), which is called once to set the endAngle of the last sector

  • Pie. PadAngle ([Angle]) set the spacing between adjacent segment Angle, is in the generation sector will set aside when the Angle of the corresponding space (interval Angle and the product of the number of data items, the maximum is | endAngle – startAngle |) used in the interval, The remaining Angle space is then allocated proportionally to each data item.