@mstone6969/graph 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -14
- package/dist/ExcelWriter-3ZSYUH4T.cjs +154 -0
- package/dist/GraphType.js +24 -38
- package/dist/PdfWriter-6HJFYMCO.cjs +205 -0
- package/dist/catalog-YWX6FHRM.cjs +8 -0
- package/dist/catalog.js +5 -1
- package/dist/charts/bars/index.js +2 -12
- package/dist/charts/comparison/EffectForestPlot.js +1 -1
- package/dist/charts/comparison/index.js +1 -8
- package/dist/charts/demography/Nomogram.js +3 -3
- package/dist/charts/density/HexbinChart.js +6 -4
- package/dist/charts/diagrams/ArchitectureCanvas.js +68 -0
- package/dist/charts/diagrams/ArchitectureDiagram.js +331 -0
- package/dist/charts/diagrams/ArchitectureLayout.js +106 -0
- package/dist/charts/diagrams/ArchitectureLinkLabels.js +78 -0
- package/dist/charts/diagrams/ArchitectureModel.js +108 -0
- package/dist/charts/diagrams/ArchitectureRouter.js +348 -0
- package/dist/charts/diagrams/ArchitectureSamples.js +312 -0
- package/dist/charts/diagrams/ArchitectureShapes.js +130 -0
- package/dist/charts/diagrams/ArchitectureText.js +25 -0
- package/dist/charts/diagrams/DiagramText.js +38 -0
- package/dist/charts/diagrams/EntityRelationshipDiagram.js +465 -0
- package/dist/charts/diagrams/ErCrowsFoot.js +33 -0
- package/dist/charts/diagrams/ErSamples.js +205 -0
- package/dist/charts/diagrams/ErSchema.js +122 -0
- package/dist/charts/diagrams/LogicCircuit.js +154 -0
- package/dist/charts/diagrams/LogicCircuitLayout.js +600 -0
- package/dist/charts/diagrams/LogicCircuitSamples.js +66 -0
- package/dist/charts/diagrams/LogicGateDiagram.js +216 -0
- package/dist/charts/diagrams/LogicGateSymbol.js +113 -0
- package/dist/charts/diagrams/NetworkTopologyDiagram.js +247 -0
- package/dist/charts/diagrams/SequenceDiagram.js +351 -0
- package/dist/charts/diagrams/SequenceLayout.js +422 -0
- package/dist/charts/diagrams/SequenceModel.js +221 -0
- package/dist/charts/diagrams/TopologyLayout.js +143 -0
- package/dist/charts/diagrams/architectureEnums.js +50 -0
- package/dist/charts/diagrams/index.js +26 -0
- package/dist/charts/diagrams/logicEnums.js +42 -0
- package/dist/charts/diagrams/sequenceEnums.js +40 -0
- package/dist/charts/distribution/HistogramChart.js +25 -3
- package/dist/charts/distribution/index.js +1 -3
- package/dist/charts/earthscience/HovmollerDiagram.js +477 -0
- package/dist/charts/earthscience/HovmollerEnums.js +42 -0
- package/dist/charts/earthscience/HovmollerField.js +180 -0
- package/dist/charts/earthscience/HovmollerSample.js +40 -0
- package/dist/charts/earthscience/HovmollerSpeed.js +71 -0
- package/dist/charts/earthscience/index.js +10 -2
- package/dist/charts/engineering/ConstellationDiagram.js +26 -6
- package/dist/charts/engineering/EyeDiagram.js +2 -1
- package/dist/charts/engineering/PoleZeroPlot.js +32 -8
- package/dist/charts/financial/RelativeStrengthRotation.js +434 -0
- package/dist/charts/financial/RelativeStrengthRotationMath.js +71 -0
- package/dist/charts/financial/RelativeStrengthRotationSamples.js +27 -0
- package/dist/charts/financial/index.js +5 -3
- package/dist/charts/flow/index.js +1 -5
- package/dist/charts/frameworks/AnsoffMatrix.js +523 -0
- package/dist/charts/frameworks/FrameworkText.js +98 -0
- package/dist/charts/frameworks/NpsGauge.js +279 -0
- package/dist/charts/frameworks/NpsSurvey.js +58 -0
- package/dist/charts/frameworks/PerceptualMap.js +419 -0
- package/dist/charts/frameworks/PerceptualProjection.js +142 -0
- package/dist/charts/frameworks/PositioningSamples.js +191 -0
- package/dist/charts/frameworks/RaciMatrix.js +458 -0
- package/dist/charts/frameworks/RaciPlan.js +81 -0
- package/dist/charts/frameworks/RaciSamples.js +76 -0
- package/dist/charts/frameworks/SwotMatrix.js +358 -0
- package/dist/charts/frameworks/SwotSamples.js +85 -0
- package/dist/charts/frameworks/VanWestendorpChart.js +187 -0
- package/dist/charts/frameworks/VanWestendorpSurvey.js +197 -0
- package/dist/charts/frameworks/index.js +26 -0
- package/dist/charts/frameworks/raciEnums.js +30 -0
- package/dist/charts/frameworks/swotEnums.js +34 -0
- package/dist/charts/genomics/CircosPlot.js +1 -1
- package/dist/charts/geoflows/GeoFlowChart.js +2 -2
- package/dist/charts/geoflows/GeoFlowSample.js +2 -1
- package/dist/charts/geomaps/FloorPlanMap.js +324 -0
- package/dist/charts/geomaps/FloorPlanModel.js +179 -0
- package/dist/charts/geomaps/FloorPlanSamples.js +416 -0
- package/dist/charts/geomaps/GeoLand.js +2 -2
- package/dist/charts/geomaps/GeoSamples.js +2 -1
- package/dist/charts/geomaps/GraticuleMap.js +2 -2
- package/dist/charts/geomaps/RoomLabelFit.js +200 -0
- package/dist/charts/geomaps/TissotMap.js +2 -2
- package/dist/charts/geomaps/index.js +8 -4
- package/dist/charts/hierarchy/SunburstChart.js +17 -10
- package/dist/charts/hierarchy/index.js +2 -11
- package/dist/charts/indicators/BulletChart.js +8 -2
- package/dist/charts/indicators/SpeedometerChart.js +1 -1
- package/dist/charts/indicators/ThermometerChart.js +1 -1
- package/dist/charts/lifescience/LoessFit.js +113 -0
- package/dist/charts/lifescience/MaPlot.js +337 -0
- package/dist/charts/lifescience/MaPlotSamples.js +35 -0
- package/dist/charts/lifescience/OncoGlyph.js +137 -0
- package/dist/charts/lifescience/OncoPrint.js +373 -0
- package/dist/charts/lifescience/OncoPrintMatrix.js +107 -0
- package/dist/charts/lifescience/OncoPrintSamples.js +142 -0
- package/dist/charts/lifescience/PedigreeChart.js +306 -0
- package/dist/charts/lifescience/PedigreeLayout.js +261 -0
- package/dist/charts/lifescience/PedigreeRows.js +70 -0
- package/dist/charts/lifescience/PedigreeSamples.js +161 -0
- package/dist/charts/lifescience/PedigreeSymbol.js +47 -0
- package/dist/charts/lifescience/RamachandranPlot.js +364 -0
- package/dist/charts/lifescience/RamachandranRegions.js +2712 -0
- package/dist/charts/lifescience/RamachandranSamples.js +235 -0
- package/dist/charts/lifescience/index.js +25 -0
- package/dist/charts/lifescience/pedigreeEnums.js +21 -0
- package/dist/charts/lifescience/ramachandranEnums.js +34 -0
- package/dist/charts/linesareas/DualAxisChart.js +1 -1
- package/dist/charts/linesareas/LineEnums.js +1 -15
- package/dist/charts/linesareas/MultiLineChart.js +1 -1
- package/dist/charts/linesareas/TrendChart.js +15 -5
- package/dist/charts/linesareas/index.js +2 -6
- package/dist/charts/massive/MassiveChart.js +3 -2
- package/dist/charts/mathematical/FourierSeriesChart.js +7 -1
- package/dist/charts/mathematical/index.js +2 -6
- package/dist/charts/matrix/GridPlot.js +27 -2
- package/dist/charts/matrix/TilegramChart.js +2 -2
- package/dist/charts/mlcharts/AttentionGrid.js +79 -0
- package/dist/charts/mlcharts/AttentionHeatmap.js +329 -0
- package/dist/charts/mlcharts/AttentionModel.js +132 -0
- package/dist/charts/mlcharts/AttentionSamples.js +82 -0
- package/dist/charts/mlcharts/CellNumber.js +8 -0
- package/dist/charts/mlcharts/CurveDiagnosticSamples.js +95 -0
- package/dist/charts/mlcharts/CurvePickMark.js +31 -0
- package/dist/charts/mlcharts/DecisionBoundaryChart.js +316 -0
- package/dist/charts/mlcharts/DecisionSamples.js +60 -0
- package/dist/charts/mlcharts/DecisionSurface.js +130 -0
- package/dist/charts/mlcharts/DistinctTicks.js +13 -0
- package/dist/charts/mlcharts/ElbowMath.js +153 -0
- package/dist/charts/mlcharts/ElbowPlot.js +273 -0
- package/dist/charts/mlcharts/ExplainabilitySamples.js +70 -0
- package/dist/charts/mlcharts/FeatureImportanceChart.js +150 -0
- package/dist/charts/mlcharts/ImportanceRanking.js +98 -0
- package/dist/charts/mlcharts/LearningCurveChart.js +276 -0
- package/dist/charts/mlcharts/LearningCurveMath.js +92 -0
- package/dist/charts/mlcharts/PartialDependence.js +92 -0
- package/dist/charts/mlcharts/PartialDependencePlot.js +225 -0
- package/dist/charts/mlcharts/WideLabel.js +28 -0
- package/dist/charts/mlcharts/curveDiagnosticEnums.js +37 -0
- package/dist/charts/mlcharts/index.js +32 -0
- package/dist/charts/proportion/index.js +5 -9
- package/dist/charts/scatter/CategoryScatterBase.js +1 -4
- package/dist/charts/scatter/ConnectedScatter.js +7 -4
- package/dist/charts/scatter/MarginalScatter.js +13 -8
- package/dist/charts/scatter/index.js +4 -17
- package/dist/charts/scientific/SpectrogramChart.js +9 -3
- package/dist/charts/scientific/index.js +2 -2
- package/dist/charts/signals/index.js +3 -13
- package/dist/charts/statistics/LorenzCurve.js +7 -4
- package/dist/charts/threebars/Candlestick3DChart.js +18 -2
- package/dist/charts/threeglobe/GlobeChart.js +2 -2
- package/dist/charts/threeglobe/GlobeSamples.js +2 -1
- package/dist/charts/threeglobe/RingGlobe.js +1 -1
- package/dist/charts/threenetwork/ConeTreeChart.js +2 -1
- package/dist/charts/threenetwork/Gantt3DChart.js +3 -2
- package/dist/charts/threesurfaces/Ridgeline3DChart.js +22 -4
- package/dist/charts/threevolume/LorenzAttractor.js +19 -0
- package/dist/charts/threevolume/VolumeRaycastChart.js +4 -3
- package/dist/charts/threevolume/volumeEnums.js +1 -0
- package/dist/charts/timeline/index.js +2 -10
- package/dist/chunk-753UBLV2.cjs +33 -0
- package/dist/chunk-VX54A7KA.cjs +17 -0
- package/dist/{chunk-W5GAAAZY.cjs → chunk-WAODDPYX.cjs} +24874 -17620
- package/dist/chunk-Z3JPIJSC.cjs +338 -0
- package/dist/core/CartesianChart.js +87 -11
- package/dist/core/Chart.js +63 -15
- package/dist/core/Chart2D.js +54 -17
- package/dist/core/Chart3D.js +56 -20
- package/dist/core/ChartExportError.js +2 -0
- package/dist/core/ChartExporter.js +50 -8
- package/dist/core/ColumnCheck.js +53 -0
- package/dist/core/Context2D.js +29 -15
- package/dist/core/Context3D.js +7 -2
- package/dist/core/ExcelWriter.js +152 -0
- package/dist/core/ExportText.js +28 -0
- package/dist/core/MarkOrigin.js +88 -0
- package/dist/core/MarkStyle.js +294 -0
- package/dist/core/Palette.js +27 -7
- package/dist/core/PdfPage.js +13 -0
- package/dist/core/PdfWriter.js +245 -0
- package/dist/core/Scales.js +52 -2
- package/dist/core/SceneOverlay.js +4 -4
- package/dist/core/TextFit.js +37 -0
- package/dist/core/Theme.js +28 -1
- package/dist/core/enums.js +33 -1
- package/dist/core/pathMorph.js +15 -0
- package/dist/data/SampleData.js +1 -17
- package/dist/data/WorldSample.js +23 -0
- package/dist/index.cjs +810 -691
- package/dist/index.d.cts +8390 -2925
- package/dist/index.d.ts +8392 -2927
- package/dist/index.js +106 -56
- package/dist/react/ChartView.js +18 -8
- package/dist/react/GraphHandle.js +2 -0
- package/dist/react/components.js +74 -116
- package/package.json +13 -3
- package/dist/catalog-4UP2YFNR.cjs +0 -7
- package/dist/charts/bars/BulletBar.js +0 -184
- package/dist/charts/bars/ErrorBarColumn.js +0 -147
- package/dist/charts/bars/HistogramBar.js +0 -217
- package/dist/charts/bars/MarimekkoBar.js +0 -191
- package/dist/charts/bars/ParetoBar.js +0 -143
- package/dist/charts/comparison/PanelChart.js +0 -70
- package/dist/charts/comparison/SmallMultipleBars.js +0 -134
- package/dist/charts/comparison/SpanChart.js +0 -118
- package/dist/charts/comparison/TrellisChart.js +0 -158
- package/dist/charts/distribution/RidgeSlabs3DChart.js +0 -169
- package/dist/charts/financial/Candlestick3D.js +0 -157
- package/dist/charts/flow/BumpFlow.js +0 -212
- package/dist/charts/flow/StreamFlow.js +0 -174
- package/dist/charts/geomaps/ThiessenMap.js +0 -106
- package/dist/charts/hierarchy/ConeTree3D.js +0 -135
- package/dist/charts/hierarchy/Hierarchy3DChart.js +0 -27
- package/dist/charts/hierarchy/SpherePack3D.js +0 -81
- package/dist/charts/hierarchy/SunburstTower3D.js +0 -93
- package/dist/charts/hierarchy/TreemapSkyline3D.js +0 -98
- package/dist/charts/linesareas/ConnectedScatterTime.js +0 -206
- package/dist/charts/linesareas/SmallMultipleLines.js +0 -153
- package/dist/charts/mathematical/ParametricSurface3D.js +0 -234
- package/dist/charts/mathematical/StrangeAttractor3D.js +0 -251
- package/dist/charts/proportion/ConeFunnel.js +0 -126
- package/dist/charts/proportion/ExtrudedPieChart.js +0 -123
- package/dist/charts/scatter/BeeswarmScatter.js +0 -151
- package/dist/charts/scatter/BinnedScatterBase.js +0 -51
- package/dist/charts/scatter/ContourScatter.js +0 -116
- package/dist/charts/scatter/GridBinScatter.js +0 -98
- package/dist/charts/scatter/HexbinScatter.js +0 -74
- package/dist/charts/scatter/StripScatter.js +0 -107
- package/dist/charts/scatter/SunflowerScatter.js +0 -109
- package/dist/charts/signals/ConstellationChart.js +0 -153
- package/dist/charts/signals/CorrelogramChart.js +0 -171
- package/dist/charts/signals/EyeDiagramChart.js +0 -155
- package/dist/charts/signals/PoleZeroChart.js +0 -164
- package/dist/charts/signals/SpectrogramChart.js +0 -79
- package/dist/charts/timeline/CalendarSkylineChart.js +0 -128
- package/dist/charts/timeline/ScheduleBlocksChart.js +0 -119
- package/dist/charts/timeline/SpiralTimeline.js +0 -154
- package/dist/charts/timeline/StreamTimeline.js +0 -141
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { CartesianChart } from "../../core/CartesianChart.js";
|
|
2
|
+
import { ChartFamily, DataShape, Dimension } from "../../core/enums.js";
|
|
3
|
+
import { ZoomAxis } from "../../core/ChartViewport.js";
|
|
4
|
+
import { SampleData } from "../../data/SampleData.js";
|
|
5
|
+
import { VanWestendorpCurves, VanWestendorpPoint, VanWestendorpSurvey, VanWestendorpTrace } from "./VanWestendorpSurvey.js";
|
|
6
|
+
import { extent, line, scaleLinear } from "d3";
|
|
7
|
+
// src/charts/frameworks/VanWestendorpChart.ts
|
|
8
|
+
var LANE = 15;
|
|
9
|
+
var MOST_LANES = 2;
|
|
10
|
+
var CHAR = 6.2;
|
|
11
|
+
var money = (value) => value.toLocaleString("en", { maximumFractionDigits: 2 });
|
|
12
|
+
/** Synthetic survey, invented for the sample: anchored on a person's price sense, heaped on multiples of 5 as stated prices are. */
|
|
13
|
+
var syntheticResponses = (seed) => {
|
|
14
|
+
const draws = SampleData.distribution(seed, 1500, 1).map((row) => (row.value - 40) / 8);
|
|
15
|
+
const stated = (value) => Math.max(0, Math.round(value / 5) * 5);
|
|
16
|
+
return Array.from({ length: 300 }, (_, i) => {
|
|
17
|
+
const z = draws.slice(i * 5, i * 5 + 5), anchor = 40 + 13 * Math.max(-2, Math.min(2, z[0]));
|
|
18
|
+
const tooCheap = stated(anchor * (.5 + .08 * z[1])), cheap = Math.max(tooCheap, stated(anchor * (.78 + .1 * z[2])));
|
|
19
|
+
const expensive = Math.max(cheap, stated(anchor * (1.2 + .12 * z[3])));
|
|
20
|
+
return {
|
|
21
|
+
tooCheap,
|
|
22
|
+
cheap,
|
|
23
|
+
expensive,
|
|
24
|
+
tooExpensive: Math.max(expensive, stated(anchor * (1.6 + .16 * z[4])))
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Van Westendorp's Price Sensitivity Meter: reads an acceptable price range and a stress-free price off four
|
|
30
|
+
* cumulative curves of stated price thresholds. PMC and PME bound the range, OPP is Too Cheap × Too Expensive
|
|
31
|
+
* and IPP is Cheap × Expensive. See P. van Westendorp, "NSS Price Sensitivity Meter", ESOMAR Congress, 1976.
|
|
32
|
+
* Shows stated acceptability, not demand, so it does not by itself give a revenue-maximising price.
|
|
33
|
+
*/
|
|
34
|
+
var VanWestendorpChart = class extends CartesianChart {
|
|
35
|
+
static meta = {
|
|
36
|
+
id: "van-westendorp",
|
|
37
|
+
name: "Van Westendorp price sensitivity meter",
|
|
38
|
+
family: ChartFamily.Statistical,
|
|
39
|
+
dimension: Dimension.Two,
|
|
40
|
+
shape: DataShape.Table,
|
|
41
|
+
description: "Finds the acceptable price range and indifference point from four cumulative price-perception curves.",
|
|
42
|
+
tags: [
|
|
43
|
+
"pricing",
|
|
44
|
+
"survey",
|
|
45
|
+
"psm",
|
|
46
|
+
"price sensitivity"
|
|
47
|
+
],
|
|
48
|
+
sample: syntheticResponses,
|
|
49
|
+
variants: {
|
|
50
|
+
curves: [VanWestendorpCurves.Direct, VanWestendorpCurves.Inverted],
|
|
51
|
+
trace: [VanWestendorpTrace.Interpolated, VanWestendorpTrace.Stepped],
|
|
52
|
+
band: [true, false]
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
#placed = [];
|
|
56
|
+
#lanes = 0;
|
|
57
|
+
defaults() {
|
|
58
|
+
return {
|
|
59
|
+
tooCheap: "tooCheap",
|
|
60
|
+
cheap: "cheap",
|
|
61
|
+
expensive: "expensive",
|
|
62
|
+
tooExpensive: "tooExpensive",
|
|
63
|
+
curves: VanWestendorpCurves.Direct,
|
|
64
|
+
trace: VanWestendorpTrace.Interpolated,
|
|
65
|
+
band: true,
|
|
66
|
+
validate: true,
|
|
67
|
+
xLabel: "Price",
|
|
68
|
+
yLabel: "Respondents (%)"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** The computed curves' source: read `crossings()` from it for the four prices. */
|
|
72
|
+
get survey() {
|
|
73
|
+
const { data, tooCheap, cheap, expensive, tooExpensive, validate } = this.options;
|
|
74
|
+
return new VanWestendorpSurvey(data, {
|
|
75
|
+
tooCheap,
|
|
76
|
+
cheap,
|
|
77
|
+
expensive,
|
|
78
|
+
tooExpensive
|
|
79
|
+
}, validate);
|
|
80
|
+
}
|
|
81
|
+
/** The share axis is fixed at 0 to 100 and shares the plot with the label lanes. */
|
|
82
|
+
zoomAxes() {
|
|
83
|
+
return ZoomAxis.X;
|
|
84
|
+
}
|
|
85
|
+
/** Labels go in lanes above 100%, one more each time two would touch. Crowded ones give up their prices, which tooltips keep. */
|
|
86
|
+
#place(crossings, x, width) {
|
|
87
|
+
const clusters = crossings.reduce((all, crossing) => {
|
|
88
|
+
const last = all.at(-1);
|
|
89
|
+
if (last && x(crossing.price) - x(last[0].price) < 1) last.push(crossing);
|
|
90
|
+
else all.push([crossing]);
|
|
91
|
+
return all;
|
|
92
|
+
}, []);
|
|
93
|
+
const names = (cluster) => cluster.map((crossing) => crossing.point).join(" · ");
|
|
94
|
+
const lay = (label) => {
|
|
95
|
+
const ends = [];
|
|
96
|
+
return {
|
|
97
|
+
placed: clusters.flatMap((cluster) => {
|
|
98
|
+
const text = label(cluster), span = text.length * CHAR;
|
|
99
|
+
const left = Math.max(0, Math.min(width - span, x(cluster[0].price) - span / 2));
|
|
100
|
+
const free = ends.findIndex((end) => left >= end + 6), lane = free < 0 ? ends.length : free;
|
|
101
|
+
ends[lane] = left + span;
|
|
102
|
+
return cluster.map((crossing, i) => ({
|
|
103
|
+
...crossing,
|
|
104
|
+
label: i ? "" : text,
|
|
105
|
+
lane
|
|
106
|
+
}));
|
|
107
|
+
}),
|
|
108
|
+
lanes: ends.length
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const priced = lay((cluster) => `${names(cluster)} ${money(cluster[0].price)}`);
|
|
112
|
+
const { placed, lanes } = priced.lanes <= MOST_LANES ? priced : lay(names);
|
|
113
|
+
this.#placed = placed;
|
|
114
|
+
this.#lanes = lanes;
|
|
115
|
+
}
|
|
116
|
+
get #top() {
|
|
117
|
+
return this.#lanes ? this.#lanes * LANE + 4 : 0;
|
|
118
|
+
}
|
|
119
|
+
scales(ctx) {
|
|
120
|
+
const { width, height } = ctx.plot;
|
|
121
|
+
const { survey } = this;
|
|
122
|
+
const [low = 0, high = 1] = extent(survey.prices);
|
|
123
|
+
const x = scaleLinear().domain(low === high ? [Math.max(0, low - 1), high + 1] : [low, high]).nice().range([0, width]);
|
|
124
|
+
this.#place(survey.crossings(this.options.curves, this.options.trace), x, width);
|
|
125
|
+
return {
|
|
126
|
+
x,
|
|
127
|
+
y: scaleLinear().domain([0, 100]).range([height, this.#top])
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
plotMarks(ctx, { x, y }) {
|
|
131
|
+
const { width, height } = ctx.plot;
|
|
132
|
+
const { theme, palette } = ctx;
|
|
133
|
+
const placed = this.#placed.filter((crossing) => x(crossing.price) >= 0 && x(crossing.price) <= width);
|
|
134
|
+
const { survey } = this;
|
|
135
|
+
const curves = survey.curves(this.options.curves, this.options.trace).filter((curve) => curve.vertices.length);
|
|
136
|
+
const low = this.#placed.find((crossing) => crossing.point === VanWestendorpPoint.PMC), high = this.#placed.find((crossing) => crossing.point === VanWestendorpPoint.PME);
|
|
137
|
+
const range = this.options.band && low && high ? [{
|
|
138
|
+
low: Math.min(low.price, high.price),
|
|
139
|
+
high: Math.max(low.price, high.price)
|
|
140
|
+
}] : [];
|
|
141
|
+
const band = ctx.marks("band", "rect", range).attr("fill", theme.grid).attr("opacity", .55);
|
|
142
|
+
ctx.set(band, {
|
|
143
|
+
x: (d) => x(d.low),
|
|
144
|
+
width: (d) => Math.max(0, x(d.high) - x(d.low)),
|
|
145
|
+
y: this.#top,
|
|
146
|
+
height: height - this.#top
|
|
147
|
+
});
|
|
148
|
+
ctx.hover(band, (d) => ({
|
|
149
|
+
title: "Acceptable price range",
|
|
150
|
+
rows: [["PMC", money(d.low)], ["PME", money(d.high)]]
|
|
151
|
+
}));
|
|
152
|
+
const trace = line().x((vertex) => x(vertex.price)).y((vertex) => y(vertex.share));
|
|
153
|
+
const paths = ctx.marks("curves", "path", curves, (curve) => curve.name).attr("fill", "none").attr("stroke-width", 2).attr("stroke-linejoin", "round").attr("stroke", (curve) => palette.color(curve.name));
|
|
154
|
+
ctx.set(paths, { d: (curve) => trace(curve.vertices) });
|
|
155
|
+
ctx.drawOn(paths);
|
|
156
|
+
ctx.hover(paths, (curve) => ({
|
|
157
|
+
title: curve.name,
|
|
158
|
+
color: palette.color(curve.name),
|
|
159
|
+
rows: [["Respondents", survey.respondents]]
|
|
160
|
+
}));
|
|
161
|
+
const row = (crossing) => 11 + (this.#lanes - 1 - crossing.lane) * LANE;
|
|
162
|
+
const drops = ctx.marks("drops", "line", placed, (crossing) => crossing.point).attr("stroke", theme.axis).attr("stroke-dasharray", "3 3");
|
|
163
|
+
ctx.set(drops, {
|
|
164
|
+
x1: (d) => x(d.price),
|
|
165
|
+
x2: (d) => x(d.price),
|
|
166
|
+
y1: (d) => row(d) + 3,
|
|
167
|
+
y2: height
|
|
168
|
+
});
|
|
169
|
+
const dots = ctx.marks("crossings", "circle", placed, (crossing) => crossing.point).attr("fill", theme.ink).attr("stroke", theme.surface).attr("stroke-width", 2);
|
|
170
|
+
ctx.set(dots, {
|
|
171
|
+
cx: (d) => x(d.price),
|
|
172
|
+
cy: (d) => y(d.share),
|
|
173
|
+
r: 5
|
|
174
|
+
}, { r: 0 }, 60);
|
|
175
|
+
ctx.hover(dots, (d) => ({
|
|
176
|
+
title: d.name,
|
|
177
|
+
rows: [["Price", money(d.price)], ["Respondents", `${money(d.share)}%`]]
|
|
178
|
+
}));
|
|
179
|
+
const labels = ctx.marks("crossing-labels", "text", placed.filter((crossing) => crossing.label), (crossing) => crossing.point).attr("stroke", theme.surface).attr("stroke-width", 3).attr("paint-order", "stroke").attr("text-anchor", "middle").attr("font-weight", 600).text((d) => d.label);
|
|
180
|
+
ctx.text(labels, 11, "ink");
|
|
181
|
+
ctx.set(labels, {
|
|
182
|
+
x: (d) => Math.max(d.label.length * CHAR / 2, Math.min(width - d.label.length * CHAR / 2, x(d.price))),
|
|
183
|
+
y: row
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
export { VanWestendorpChart };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { bisectLeft, bisectRight } from "d3";
|
|
2
|
+
// src/charts/frameworks/VanWestendorpSurvey.ts
|
|
3
|
+
/** Which pair of middle curves the chart plots and intersects. Both are in published use. */
|
|
4
|
+
var VanWestendorpCurves = /* @__PURE__ */ function(VanWestendorpCurves) {
|
|
5
|
+
/** Cheap and Expensive as asked: PMC is Too Cheap × Expensive, PME is Too Expensive × Cheap. */
|
|
6
|
+
VanWestendorpCurves["Direct"] = "direct";
|
|
7
|
+
/** Not Cheap (100 − Cheap) and Not Expensive (100 − Expensive): PMC is Too Cheap × Not Cheap, PME is Too Expensive × Not Expensive. */
|
|
8
|
+
VanWestendorpCurves["Inverted"] = "inverted";
|
|
9
|
+
return VanWestendorpCurves;
|
|
10
|
+
}({});
|
|
11
|
+
/** How the survey's discrete prices join up. */
|
|
12
|
+
var VanWestendorpTrace = /* @__PURE__ */ function(VanWestendorpTrace) {
|
|
13
|
+
VanWestendorpTrace["Interpolated"] = "interpolated";
|
|
14
|
+
VanWestendorpTrace["Stepped"] = "stepped";
|
|
15
|
+
return VanWestendorpTrace;
|
|
16
|
+
}({});
|
|
17
|
+
var VanWestendorpPoint = /* @__PURE__ */ function(VanWestendorpPoint) {
|
|
18
|
+
VanWestendorpPoint["PMC"] = "PMC";
|
|
19
|
+
VanWestendorpPoint["OPP"] = "OPP";
|
|
20
|
+
VanWestendorpPoint["IPP"] = "IPP";
|
|
21
|
+
VanWestendorpPoint["PME"] = "PME";
|
|
22
|
+
return VanWestendorpPoint;
|
|
23
|
+
}({});
|
|
24
|
+
var SNAP = 1e-9;
|
|
25
|
+
var NAMES = {
|
|
26
|
+
["PMC"]: "Point of marginal cheapness",
|
|
27
|
+
["OPP"]: "Optimal price point",
|
|
28
|
+
["IPP"]: "Indifference price point",
|
|
29
|
+
["PME"]: "Point of marginal expensiveness"
|
|
30
|
+
};
|
|
31
|
+
var price = (cell) => {
|
|
32
|
+
const value = typeof cell === "number" ? cell : typeof cell === "string" && cell.trim() ? Number(cell) : NaN;
|
|
33
|
+
return Number.isFinite(value) && value >= 0 ? value : null;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Van Westendorp's Price Sensitivity Meter (1976) from raw survey answers. Each curve is the share of
|
|
37
|
+
* respondents placing a price in that category: Too Cheap and Cheap at or above the price, Expensive and
|
|
38
|
+
* Too Expensive at or below it. Every crossing pairs a rising curve with a falling one, so their gap never
|
|
39
|
+
* falls and a crossing is one price, or a flat stretch whose middle is taken.
|
|
40
|
+
*/
|
|
41
|
+
var VanWestendorpSurvey = class VanWestendorpSurvey {
|
|
42
|
+
#answers;
|
|
43
|
+
#prices;
|
|
44
|
+
/** Respondents answering nothing usable or, when validating, out of order. */
|
|
45
|
+
dropped;
|
|
46
|
+
respondents;
|
|
47
|
+
/** With `validate`, keeps only complete answers ordered Too Cheap ≤ Cheap ≤ Expensive ≤ Too Expensive. */
|
|
48
|
+
constructor(rows, columns, validate = true) {
|
|
49
|
+
const all = Array.isArray(rows) ? rows : [];
|
|
50
|
+
const kept = all.map((row) => {
|
|
51
|
+
const read = (column) => row !== null && typeof row === "object" ? price(row[column]) : null;
|
|
52
|
+
return [
|
|
53
|
+
read(columns.tooCheap),
|
|
54
|
+
read(columns.cheap),
|
|
55
|
+
read(columns.expensive),
|
|
56
|
+
read(columns.tooExpensive)
|
|
57
|
+
];
|
|
58
|
+
}).filter((row) => validate ? row.every((value, i) => value !== null && (!i || row[i - 1] <= value)) : row.some((value) => value !== null));
|
|
59
|
+
const column = (i) => kept.map((row) => row[i]).filter((value) => value !== null).sort((a, b) => a - b);
|
|
60
|
+
this.#answers = {
|
|
61
|
+
tooCheap: column(0),
|
|
62
|
+
cheap: column(1),
|
|
63
|
+
expensive: column(2),
|
|
64
|
+
tooExpensive: column(3)
|
|
65
|
+
};
|
|
66
|
+
this.#prices = [...new Set(Object.values(this.#answers).flat())].sort((a, b) => a - b);
|
|
67
|
+
this.respondents = kept.length;
|
|
68
|
+
this.dropped = all.length - kept.length;
|
|
69
|
+
}
|
|
70
|
+
get prices() {
|
|
71
|
+
return this.#prices;
|
|
72
|
+
}
|
|
73
|
+
#falling(answers, trace) {
|
|
74
|
+
const share = (count) => count / answers.length * 100;
|
|
75
|
+
return !answers.length ? [] : this.#prices.flatMap((at) => {
|
|
76
|
+
const reached = {
|
|
77
|
+
price: at,
|
|
78
|
+
share: share(answers.length - bisectLeft(answers, at))
|
|
79
|
+
};
|
|
80
|
+
return trace === "stepped" ? [reached, {
|
|
81
|
+
price: at,
|
|
82
|
+
share: share(answers.length - bisectRight(answers, at))
|
|
83
|
+
}] : [reached];
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
#rising(answers, trace) {
|
|
87
|
+
const share = (count) => count / answers.length * 100;
|
|
88
|
+
return !answers.length ? [] : this.#prices.flatMap((at) => {
|
|
89
|
+
const reached = {
|
|
90
|
+
price: at,
|
|
91
|
+
share: share(bisectRight(answers, at))
|
|
92
|
+
};
|
|
93
|
+
return trace === "stepped" ? [{
|
|
94
|
+
price: at,
|
|
95
|
+
share: share(bisectLeft(answers, at))
|
|
96
|
+
}, reached] : [reached];
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
#slots(form, trace) {
|
|
100
|
+
const flip = (vertices) => vertices.map((vertex) => ({
|
|
101
|
+
price: vertex.price,
|
|
102
|
+
share: 100 - vertex.share
|
|
103
|
+
}));
|
|
104
|
+
const { tooCheap, cheap, expensive, tooExpensive } = this.#answers;
|
|
105
|
+
const cheapShare = this.#falling(cheap, trace), expensiveShare = this.#rising(expensive, trace);
|
|
106
|
+
const inverted = form === "inverted";
|
|
107
|
+
return {
|
|
108
|
+
tooCheap: this.#falling(tooCheap, trace),
|
|
109
|
+
tooExpensive: this.#rising(tooExpensive, trace),
|
|
110
|
+
up: inverted ? flip(cheapShare) : expensiveShare,
|
|
111
|
+
down: inverted ? flip(expensiveShare) : cheapShare
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** The four curves, cheapest first, over the same prices. */
|
|
115
|
+
curves(form = "direct", trace = "interpolated") {
|
|
116
|
+
const { tooCheap, tooExpensive, up, down } = this.#slots(form, trace);
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
name: "Too cheap",
|
|
120
|
+
vertices: tooCheap
|
|
121
|
+
},
|
|
122
|
+
...form === "inverted" ? [{
|
|
123
|
+
name: "Not cheap",
|
|
124
|
+
vertices: up
|
|
125
|
+
}, {
|
|
126
|
+
name: "Not expensive",
|
|
127
|
+
vertices: down
|
|
128
|
+
}] : [{
|
|
129
|
+
name: "Cheap",
|
|
130
|
+
vertices: down
|
|
131
|
+
}, {
|
|
132
|
+
name: "Expensive",
|
|
133
|
+
vertices: up
|
|
134
|
+
}],
|
|
135
|
+
{
|
|
136
|
+
name: "Too expensive",
|
|
137
|
+
vertices: tooExpensive
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
/** The four published intersections, ordered by price. A curve with no answers has no crossing. */
|
|
142
|
+
crossings(form = "direct", trace = "interpolated") {
|
|
143
|
+
const { tooCheap, tooExpensive, up, down } = this.#slots(form, trace);
|
|
144
|
+
return [
|
|
145
|
+
[
|
|
146
|
+
"PMC",
|
|
147
|
+
up,
|
|
148
|
+
tooCheap
|
|
149
|
+
],
|
|
150
|
+
[
|
|
151
|
+
"OPP",
|
|
152
|
+
tooExpensive,
|
|
153
|
+
tooCheap
|
|
154
|
+
],
|
|
155
|
+
[
|
|
156
|
+
"IPP",
|
|
157
|
+
up,
|
|
158
|
+
down
|
|
159
|
+
],
|
|
160
|
+
[
|
|
161
|
+
"PME",
|
|
162
|
+
tooExpensive,
|
|
163
|
+
down
|
|
164
|
+
]
|
|
165
|
+
].flatMap(([point, rising, falling]) => {
|
|
166
|
+
const hit = VanWestendorpSurvey.#cross(rising, falling);
|
|
167
|
+
return hit ? [{
|
|
168
|
+
point,
|
|
169
|
+
name: NAMES[point],
|
|
170
|
+
...hit
|
|
171
|
+
}] : [];
|
|
172
|
+
}).sort((a, b) => a.price - b.price);
|
|
173
|
+
}
|
|
174
|
+
/** Both lists share their prices, vertex by vertex. */
|
|
175
|
+
static #cross(rising, falling) {
|
|
176
|
+
if (!rising.length || !falling.length) return null;
|
|
177
|
+
const gaps = rising.map((vertex, i) => {
|
|
178
|
+
const gap = vertex.share - falling[i].share;
|
|
179
|
+
return Math.abs(gap) < SNAP ? 0 : gap;
|
|
180
|
+
});
|
|
181
|
+
const first = gaps.findIndex((gap) => gap >= 0);
|
|
182
|
+
if (first < 0) return null;
|
|
183
|
+
if (!first || !gaps[first]) {
|
|
184
|
+
const last = Math.max(first, gaps.findLastIndex((gap) => gap <= 0));
|
|
185
|
+
return {
|
|
186
|
+
price: (rising[first].price + rising[last].price) / 2,
|
|
187
|
+
share: rising[first].share
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const before = rising[first - 1], after = rising[first], t = -gaps[first - 1] / (gaps[first] - gaps[first - 1]);
|
|
191
|
+
return {
|
|
192
|
+
price: before.price + (after.price - before.price) * t,
|
|
193
|
+
share: before.share + (after.share - before.share) * t
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
export { VanWestendorpCurves, VanWestendorpPoint, VanWestendorpSurvey, VanWestendorpTrace };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FrameworkText } from "./FrameworkText.js";
|
|
2
|
+
import { RaciHeader, RaciIssue, RaciLetter, RaciMark } from "./raciEnums.js";
|
|
3
|
+
import { RaciPlan } from "./RaciPlan.js";
|
|
4
|
+
import { RaciSamples } from "./RaciSamples.js";
|
|
5
|
+
import { RaciMatrix } from "./RaciMatrix.js";
|
|
6
|
+
import { SwotItemMark, SwotLayout, SwotOrder, SwotQuadrant, SwotSizing } from "./swotEnums.js";
|
|
7
|
+
import { SwotSamples } from "./SwotSamples.js";
|
|
8
|
+
import { SwotMatrix } from "./SwotMatrix.js";
|
|
9
|
+
import { NpsBand, NpsSurvey } from "./NpsSurvey.js";
|
|
10
|
+
import { NpsGauge, NpsIndicator, NpsSweep } from "./NpsGauge.js";
|
|
11
|
+
import { VanWestendorpCurves, VanWestendorpPoint, VanWestendorpSurvey, VanWestendorpTrace } from "./VanWestendorpSurvey.js";
|
|
12
|
+
import { VanWestendorpChart } from "./VanWestendorpChart.js";
|
|
13
|
+
import { PositioningSamples } from "./PositioningSamples.js";
|
|
14
|
+
import { AnsoffDisplay, AnsoffMarketOrder, AnsoffMatrix, AnsoffStage, AnsoffStrategy } from "./AnsoffMatrix.js";
|
|
15
|
+
import { PerceptualMethod, PerceptualProjection, PerceptualRole } from "./PerceptualProjection.js";
|
|
16
|
+
import { PerceptualMap } from "./PerceptualMap.js";
|
|
17
|
+
// src/charts/frameworks/index.ts
|
|
18
|
+
var frameworksCatalog = [
|
|
19
|
+
SwotMatrix,
|
|
20
|
+
RaciMatrix,
|
|
21
|
+
NpsGauge,
|
|
22
|
+
VanWestendorpChart,
|
|
23
|
+
AnsoffMatrix,
|
|
24
|
+
PerceptualMap
|
|
25
|
+
];
|
|
26
|
+
export { AnsoffDisplay, AnsoffMarketOrder, AnsoffMatrix, AnsoffStage, AnsoffStrategy, FrameworkText, NpsBand, NpsGauge, NpsIndicator, NpsSurvey, NpsSweep, PerceptualMap, PerceptualMethod, PerceptualProjection, PerceptualRole, PositioningSamples, RaciHeader, RaciIssue, RaciLetter, RaciMark, RaciMatrix, RaciPlan, RaciSamples, SwotItemMark, SwotLayout, SwotMatrix, SwotOrder, SwotQuadrant, SwotSamples, SwotSizing, VanWestendorpChart, VanWestendorpCurves, VanWestendorpPoint, VanWestendorpSurvey, VanWestendorpTrace, frameworksCatalog };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/charts/frameworks/raciEnums.ts
|
|
2
|
+
/** RACI responsibility assignment: who does the work, who answers for it, who is asked, who is told. */
|
|
3
|
+
var RaciLetter = /* @__PURE__ */ function(RaciLetter) {
|
|
4
|
+
RaciLetter["Responsible"] = "R";
|
|
5
|
+
RaciLetter["Accountable"] = "A";
|
|
6
|
+
RaciLetter["Consulted"] = "C";
|
|
7
|
+
RaciLetter["Informed"] = "I";
|
|
8
|
+
return RaciLetter;
|
|
9
|
+
}({});
|
|
10
|
+
/** Faults in a task's assignments, from the rule of exactly one A and at least one R. */
|
|
11
|
+
var RaciIssue = /* @__PURE__ */ function(RaciIssue) {
|
|
12
|
+
RaciIssue["NoAccountable"] = "no-accountable";
|
|
13
|
+
RaciIssue["SeveralAccountable"] = "several-accountable";
|
|
14
|
+
RaciIssue["NoResponsible"] = "no-responsible";
|
|
15
|
+
return RaciIssue;
|
|
16
|
+
}({});
|
|
17
|
+
/** How role names sit above their columns. */
|
|
18
|
+
var RaciHeader = /* @__PURE__ */ function(RaciHeader) {
|
|
19
|
+
RaciHeader["Wrapped"] = "wrapped";
|
|
20
|
+
RaciHeader["Angled"] = "angled";
|
|
21
|
+
RaciHeader["Vertical"] = "vertical";
|
|
22
|
+
return RaciHeader;
|
|
23
|
+
}({});
|
|
24
|
+
/** How an assignment is drawn. The letter is always text. */
|
|
25
|
+
var RaciMark = /* @__PURE__ */ function(RaciMark) {
|
|
26
|
+
RaciMark["Badge"] = "badge";
|
|
27
|
+
RaciMark["Tile"] = "tile";
|
|
28
|
+
return RaciMark;
|
|
29
|
+
}({});
|
|
30
|
+
export { RaciHeader, RaciIssue, RaciLetter, RaciMark };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/charts/frameworks/swotEnums.ts
|
|
2
|
+
/** The four SWOT headings, in reading order. */
|
|
3
|
+
var SwotQuadrant = /* @__PURE__ */ function(SwotQuadrant) {
|
|
4
|
+
SwotQuadrant["Strengths"] = "Strengths";
|
|
5
|
+
SwotQuadrant["Weaknesses"] = "Weaknesses";
|
|
6
|
+
SwotQuadrant["Opportunities"] = "Opportunities";
|
|
7
|
+
SwotQuadrant["Threats"] = "Threats";
|
|
8
|
+
return SwotQuadrant;
|
|
9
|
+
}({});
|
|
10
|
+
/** How the quadrants are arranged. */
|
|
11
|
+
var SwotLayout = /* @__PURE__ */ function(SwotLayout) {
|
|
12
|
+
SwotLayout["Grid"] = "grid";
|
|
13
|
+
SwotLayout["Columns"] = "columns";
|
|
14
|
+
SwotLayout["Rows"] = "rows";
|
|
15
|
+
return SwotLayout;
|
|
16
|
+
}({});
|
|
17
|
+
/** Whether stacked quadrants share height equally or by how much text each holds. */
|
|
18
|
+
var SwotSizing = /* @__PURE__ */ function(SwotSizing) {
|
|
19
|
+
SwotSizing["Equal"] = "equal";
|
|
20
|
+
SwotSizing["Content"] = "content";
|
|
21
|
+
return SwotSizing;
|
|
22
|
+
}({});
|
|
23
|
+
/** What leads each item: a bullet, or a reference code such as S1 for citing it in a TOWS analysis. */
|
|
24
|
+
var SwotItemMark = /* @__PURE__ */ function(SwotItemMark) {
|
|
25
|
+
SwotItemMark["Bullet"] = "bullet";
|
|
26
|
+
SwotItemMark["Code"] = "code";
|
|
27
|
+
return SwotItemMark;
|
|
28
|
+
}({});
|
|
29
|
+
var SwotOrder = /* @__PURE__ */ function(SwotOrder) {
|
|
30
|
+
SwotOrder["Input"] = "input";
|
|
31
|
+
SwotOrder["Weight"] = "weight";
|
|
32
|
+
return SwotOrder;
|
|
33
|
+
}({});
|
|
34
|
+
export { SwotItemMark, SwotLayout, SwotOrder, SwotQuadrant, SwotSizing };
|
|
@@ -143,7 +143,7 @@ var CircosPlot = class extends RadialChart {
|
|
|
143
143
|
});
|
|
144
144
|
});
|
|
145
145
|
const reach = (d) => outer + (d.major ? 5 : 3);
|
|
146
|
-
const lines = ctx.marks("ticks", "line", ticks, (d) => d.key).attr("stroke", ctx.theme.inkMuted).attr("stroke-width", (d) => d.major ? 1 : .6);
|
|
146
|
+
const lines = ctx.marks("axis-ticks", "line", ticks, (d) => d.key).attr("stroke", ctx.theme.inkMuted).attr("stroke-width", (d) => d.major ? 1 : .6);
|
|
147
147
|
const xy = (a, r) => point(a, r).split(" ").map(Number);
|
|
148
148
|
ctx.set(lines, {
|
|
149
149
|
x1: (d) => xy(d.a, outer)[0],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Chart2D } from "../../core/Chart2D.js";
|
|
2
2
|
import { LegendPosition } from "../../core/enums.js";
|
|
3
3
|
import { Scales } from "../../core/Scales.js";
|
|
4
|
-
import {
|
|
4
|
+
import { WorldSample } from "../../data/WorldSample.js";
|
|
5
5
|
import { GeoViewport } from "../geomaps/GeoViewport.js";
|
|
6
6
|
import { GeoFlowFrame, GeoFlowProjection, isAzimuthal } from "./GeoFlowFrame.js";
|
|
7
7
|
import { geoGraticule10, max } from "d3";
|
|
@@ -129,7 +129,7 @@ var GeoFlowChart = class extends Chart2D {
|
|
|
129
129
|
this.plotGeo(ctx, frame);
|
|
130
130
|
}
|
|
131
131
|
get land() {
|
|
132
|
-
return this.options.basemap ?? (countries ??=
|
|
132
|
+
return this.options.basemap ?? (countries ??= WorldSample.countries(1));
|
|
133
133
|
}
|
|
134
134
|
drawBasemap(ctx, frame, graticules = true) {
|
|
135
135
|
const { theme } = ctx;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SampleData } from "../../data/SampleData.js";
|
|
2
|
+
import { WorldSample } from "../../data/WorldSample.js";
|
|
2
3
|
import { geoCentroid, geoDistance } from "d3";
|
|
3
4
|
// src/charts/geoflows/GeoFlowSample.ts
|
|
4
5
|
/** Broad region of a place, from its coordinates; demo data only. */
|
|
@@ -237,7 +238,7 @@ var GeoFlowSample = class {
|
|
|
237
238
|
}
|
|
238
239
|
/** `{ name, lon, lat, value, series }`: one point per country, at its centroid. */
|
|
239
240
|
static places(seed) {
|
|
240
|
-
return
|
|
241
|
+
return WorldSample.countries(seed).features.map((country) => {
|
|
241
242
|
const [lon, lat] = geoCentroid(country);
|
|
242
243
|
return {
|
|
243
244
|
name: String(country.properties?.name ?? ""),
|