diff --git a/knip.config.ts b/knip.config.ts index b06dd59a3e9135..4e8539b31b37ea 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -22,6 +22,8 @@ const productionEntryPoints = [ 'static/app/chartcuterie/**/*.{js,ts,tsx}', // TODO: Remove when used 'static/app/views/seerExplorer/contexts/**/*.{js,ts,tsx}', + // TODO: Remove when integration into Explore has started + 'static/app/views/dashboards/widgets/heatMapWidget/**/*.{ts,tsx}', // TODO: Remove when used 'static/app/views/settings/organizationRepositories/connectProviderDropdown.tsx', 'static/app/views/settings/organizationRepositories/noIntegrationsEmptyState.tsx', diff --git a/static/app/views/dashboards/widgets/common/types.tsx b/static/app/views/dashboards/widgets/common/types.tsx index ae2963dcd268e0..3723853d528468 100644 --- a/static/app/views/dashboards/widgets/common/types.tsx +++ b/static/app/views/dashboards/widgets/common/types.tsx @@ -211,3 +211,113 @@ export interface CategoricalSeries { */ groupBy?: CategoricalGroupBy[] | null; } + +/** + * The type of values in a heatmap series. + * This is the broadest set of types supported - any value type that can come + * from the API. The plottable layer constrains this to plottable types. + */ +type HeatMapValueType = AttributeValueType; +export type HeatMapValueUnit = AttributeValueUnit; + +/** + * A single item in a heat map series. + */ +interface HeatMapItem { + /** + * The X-axis value + */ + xAxis: number; + /** + * The Y-axis value + */ + yAxis: number; + /** + * The Z-axis value. This can be null if the value is missing. + */ + zAxis: number | null; +} + +interface BoundedMeta { + /** + * The largest value of data on the axis + */ + end: number; + /** + * The name of the series. Corresponds to what it's plotting. Could be `"time"` or something like `"count()"` + */ + name: string; + /** + * The smallest value of data on the axis + */ + start: number; +} + +interface BucketedMeta { + /** + * The total count of buckets on this axis. Matches what was requested, if were requested + */ + bucketCount: number; + /** + * The size of the buckets on this axis. + */ + bucketSize: number; +} + +interface NamedMeta { + /** + * The name of the series. Corresponds to what it's plotting. Could be `"time"` or something like `"count()"` + */ + name: string; +} + +/** + * Metadata for a heat map series X-axis. Right now this axis is always time. + */ +interface HeatMapSeriesXAxisMeta extends NamedMeta, BoundedMeta, BucketedMeta {} + +/** + * Metadata for a heat map series Y axis. Right now this is the only axis that is configurable by the user, so it returns the value type and unit. + */ +interface HeatMapSeriesYAxisMeta extends NamedMeta, BoundedMeta, BucketedMeta { + /** + * The type of the values (e.g., "duration", "number") + */ + valueType: HeatMapValueType; + /** + * The unit of the values, if applicable. + */ + valueUnit: DataUnit | null; +} + +/** + * Metadata for a heat map series Z axis. Right now this is always a count. + */ +interface HeatMapSeriesZAxisMeta extends NamedMeta, BoundedMeta {} + +/** + * Metadata for a heat map series. + */ +interface HeatMapSeriesMeta { + xAxis: HeatMapSeriesXAxisMeta; + yAxis: HeatMapSeriesYAxisMeta; + zAxis: HeatMapSeriesZAxisMeta; +} + +/** + * A heat map data series for heat map visualizations. + */ +export interface HeatMapSeries { + /** + * Metadata about the series. + */ + meta: HeatMapSeriesMeta; + /** + * The data points in this series. + */ + values: HeatMapItem[]; + /** + * Represents the grouping information for the series, if applicable. + */ + groupBy?: CategoricalGroupBy[] | null; +} diff --git a/static/app/views/dashboards/widgets/heatMapWidget/fixtures/sampleLatencyHeatMap.ts b/static/app/views/dashboards/widgets/heatMapWidget/fixtures/sampleLatencyHeatMap.ts new file mode 100644 index 00000000000000..764a4724946a64 --- /dev/null +++ b/static/app/views/dashboards/widgets/heatMapWidget/fixtures/sampleLatencyHeatMap.ts @@ -0,0 +1,911 @@ +import {DurationUnit} from 'sentry/utils/discover/fields'; +import type {HeatMapSeries} from 'sentry/views/dashboards/widgets/common/types'; +export const sampleLatencyHeatMap: HeatMapSeries = { + meta: { + xAxis: { + name: 'time', + start: 1777802400000, + end: 1777982400000, + bucketCount: 50, + bucketSize: 3600, + }, + yAxis: { + name: 'value', + start: 0, + end: 1000, + bucketCount: 20, + bucketSize: 50, + valueType: 'duration', + valueUnit: DurationUnit.MILLISECOND, + }, + zAxis: { + name: 'count()', + start: 0, + end: 15, + }, + }, + values: [ + {xAxis: 1777802400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777802400000, yAxis: 50, zAxis: 1}, + {xAxis: 1777802400000, yAxis: 100, zAxis: 2}, + {xAxis: 1777802400000, yAxis: 150, zAxis: 3}, + {xAxis: 1777802400000, yAxis: 200, zAxis: 4}, + {xAxis: 1777802400000, yAxis: 250, zAxis: 4}, + {xAxis: 1777802400000, yAxis: 300, zAxis: 4}, + {xAxis: 1777802400000, yAxis: 350, zAxis: 3}, + {xAxis: 1777802400000, yAxis: 400, zAxis: 2}, + {xAxis: 1777802400000, yAxis: 450, zAxis: 1}, + {xAxis: 1777802400000, yAxis: 500, zAxis: 1}, + {xAxis: 1777802400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777802400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777802400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777802400000, yAxis: 700, zAxis: 0}, + {xAxis: 1777802400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777802400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777802400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777806000000, yAxis: 0, zAxis: 1}, + {xAxis: 1777806000000, yAxis: 50, zAxis: 1}, + {xAxis: 1777806000000, yAxis: 100, zAxis: 2}, + {xAxis: 1777806000000, yAxis: 150, zAxis: 3}, + {xAxis: 1777806000000, yAxis: 200, zAxis: 4}, + {xAxis: 1777806000000, yAxis: 250, zAxis: 4}, + {xAxis: 1777806000000, yAxis: 300, zAxis: 4}, + {xAxis: 1777806000000, yAxis: 350, zAxis: 3}, + {xAxis: 1777806000000, yAxis: 400, zAxis: 2}, + {xAxis: 1777806000000, yAxis: 450, zAxis: 1}, + {xAxis: 1777806000000, yAxis: 500, zAxis: 1}, + {xAxis: 1777806000000, yAxis: 550, zAxis: 1}, + {xAxis: 1777806000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777806000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777806000000, yAxis: 700, zAxis: 0}, + {xAxis: 1777806000000, yAxis: 750, zAxis: 0}, + {xAxis: 1777806000000, yAxis: 800, zAxis: 0}, + {xAxis: 1777806000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777809600000, yAxis: 0, zAxis: 1}, + {xAxis: 1777809600000, yAxis: 50, zAxis: 1}, + {xAxis: 1777809600000, yAxis: 100, zAxis: 2}, + {xAxis: 1777809600000, yAxis: 150, zAxis: 3}, + {xAxis: 1777809600000, yAxis: 200, zAxis: 4}, + {xAxis: 1777809600000, yAxis: 250, zAxis: 4}, + {xAxis: 1777809600000, yAxis: 300, zAxis: 4}, + {xAxis: 1777809600000, yAxis: 350, zAxis: 3}, + {xAxis: 1777809600000, yAxis: 400, zAxis: 2}, + {xAxis: 1777809600000, yAxis: 450, zAxis: 1}, + {xAxis: 1777809600000, yAxis: 500, zAxis: 1}, + {xAxis: 1777809600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777809600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777809600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777809600000, yAxis: 700, zAxis: 0}, + {xAxis: 1777809600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777809600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777809600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777813200000, yAxis: 0, zAxis: 1}, + {xAxis: 1777813200000, yAxis: 50, zAxis: 2}, + {xAxis: 1777813200000, yAxis: 100, zAxis: 3}, + {xAxis: 1777813200000, yAxis: 150, zAxis: 4}, + {xAxis: 1777813200000, yAxis: 200, zAxis: 6}, + {xAxis: 1777813200000, yAxis: 250, zAxis: 6}, + {xAxis: 1777813200000, yAxis: 300, zAxis: 6}, + {xAxis: 1777813200000, yAxis: 350, zAxis: 4}, + {xAxis: 1777813200000, yAxis: 400, zAxis: 3}, + {xAxis: 1777813200000, yAxis: 450, zAxis: 2}, + {xAxis: 1777813200000, yAxis: 500, zAxis: 1}, + {xAxis: 1777813200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777813200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777813200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777813200000, yAxis: 700, zAxis: 0}, + {xAxis: 1777813200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777813200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777813200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777816800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777816800000, yAxis: 50, zAxis: 2}, + {xAxis: 1777816800000, yAxis: 100, zAxis: 3}, + {xAxis: 1777816800000, yAxis: 150, zAxis: 4}, + {xAxis: 1777816800000, yAxis: 200, zAxis: 6}, + {xAxis: 1777816800000, yAxis: 250, zAxis: 6}, + {xAxis: 1777816800000, yAxis: 300, zAxis: 6}, + {xAxis: 1777816800000, yAxis: 350, zAxis: 4}, + {xAxis: 1777816800000, yAxis: 400, zAxis: 3}, + {xAxis: 1777816800000, yAxis: 450, zAxis: 2}, + {xAxis: 1777816800000, yAxis: 500, zAxis: 1}, + {xAxis: 1777816800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777816800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777816800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777816800000, yAxis: 700, zAxis: 0}, + {xAxis: 1777816800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777816800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777816800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777820400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777820400000, yAxis: 50, zAxis: 2}, + {xAxis: 1777820400000, yAxis: 100, zAxis: 3}, + {xAxis: 1777820400000, yAxis: 150, zAxis: 4}, + {xAxis: 1777820400000, yAxis: 200, zAxis: 6}, + {xAxis: 1777820400000, yAxis: 250, zAxis: 6}, + {xAxis: 1777820400000, yAxis: 300, zAxis: 6}, + {xAxis: 1777820400000, yAxis: 350, zAxis: 4}, + {xAxis: 1777820400000, yAxis: 400, zAxis: 3}, + {xAxis: 1777820400000, yAxis: 450, zAxis: 2}, + {xAxis: 1777820400000, yAxis: 500, zAxis: 1}, + {xAxis: 1777820400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777820400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777820400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777820400000, yAxis: 700, zAxis: 0}, + {xAxis: 1777820400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777820400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777820400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777824000000, yAxis: 0, zAxis: 1}, + {xAxis: 1777824000000, yAxis: 50, zAxis: 3}, + {xAxis: 1777824000000, yAxis: 100, zAxis: 5}, + {xAxis: 1777824000000, yAxis: 150, zAxis: 7}, + {xAxis: 1777824000000, yAxis: 200, zAxis: 9}, + {xAxis: 1777824000000, yAxis: 250, zAxis: 10}, + {xAxis: 1777824000000, yAxis: 300, zAxis: 9}, + {xAxis: 1777824000000, yAxis: 350, zAxis: 7}, + {xAxis: 1777824000000, yAxis: 400, zAxis: 5}, + {xAxis: 1777824000000, yAxis: 450, zAxis: 3}, + {xAxis: 1777824000000, yAxis: 500, zAxis: 1}, + {xAxis: 1777824000000, yAxis: 550, zAxis: 1}, + {xAxis: 1777824000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777824000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777824000000, yAxis: 700, zAxis: 1}, + {xAxis: 1777824000000, yAxis: 750, zAxis: 0}, + {xAxis: 1777824000000, yAxis: 800, zAxis: 0}, + {xAxis: 1777824000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777827600000, yAxis: 0, zAxis: 1}, + {xAxis: 1777827600000, yAxis: 50, zAxis: 3}, + {xAxis: 1777827600000, yAxis: 100, zAxis: 5}, + {xAxis: 1777827600000, yAxis: 150, zAxis: 7}, + {xAxis: 1777827600000, yAxis: 200, zAxis: 9}, + {xAxis: 1777827600000, yAxis: 250, zAxis: 10}, + {xAxis: 1777827600000, yAxis: 300, zAxis: 9}, + {xAxis: 1777827600000, yAxis: 350, zAxis: 7}, + {xAxis: 1777827600000, yAxis: 400, zAxis: 5}, + {xAxis: 1777827600000, yAxis: 450, zAxis: 3}, + {xAxis: 1777827600000, yAxis: 500, zAxis: 1}, + {xAxis: 1777827600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777827600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777827600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777827600000, yAxis: 700, zAxis: 1}, + {xAxis: 1777827600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777827600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777827600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777831200000, yAxis: 0, zAxis: 1}, + {xAxis: 1777831200000, yAxis: 50, zAxis: 3}, + {xAxis: 1777831200000, yAxis: 100, zAxis: 5}, + {xAxis: 1777831200000, yAxis: 150, zAxis: 7}, + {xAxis: 1777831200000, yAxis: 200, zAxis: 9}, + {xAxis: 1777831200000, yAxis: 250, zAxis: 10}, + {xAxis: 1777831200000, yAxis: 300, zAxis: 9}, + {xAxis: 1777831200000, yAxis: 350, zAxis: 7}, + {xAxis: 1777831200000, yAxis: 400, zAxis: 5}, + {xAxis: 1777831200000, yAxis: 450, zAxis: 3}, + {xAxis: 1777831200000, yAxis: 500, zAxis: 1}, + {xAxis: 1777831200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777831200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777831200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777831200000, yAxis: 700, zAxis: 1}, + {xAxis: 1777831200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777831200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777831200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777834800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777834800000, yAxis: 50, zAxis: 3}, + {xAxis: 1777834800000, yAxis: 100, zAxis: 5}, + {xAxis: 1777834800000, yAxis: 150, zAxis: 7}, + {xAxis: 1777834800000, yAxis: 200, zAxis: 9}, + {xAxis: 1777834800000, yAxis: 250, zAxis: 10}, + {xAxis: 1777834800000, yAxis: 300, zAxis: 9}, + {xAxis: 1777834800000, yAxis: 350, zAxis: 7}, + {xAxis: 1777834800000, yAxis: 400, zAxis: 5}, + {xAxis: 1777834800000, yAxis: 450, zAxis: 3}, + {xAxis: 1777834800000, yAxis: 500, zAxis: 1}, + {xAxis: 1777834800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777834800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777834800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777834800000, yAxis: 700, zAxis: 1}, + {xAxis: 1777834800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777834800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777834800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777838400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777838400000, yAxis: 50, zAxis: 3}, + {xAxis: 1777838400000, yAxis: 100, zAxis: 5}, + {xAxis: 1777838400000, yAxis: 150, zAxis: 7}, + {xAxis: 1777838400000, yAxis: 200, zAxis: 9}, + {xAxis: 1777838400000, yAxis: 250, zAxis: 10}, + {xAxis: 1777838400000, yAxis: 300, zAxis: 9}, + {xAxis: 1777838400000, yAxis: 350, zAxis: 7}, + {xAxis: 1777838400000, yAxis: 400, zAxis: 5}, + {xAxis: 1777838400000, yAxis: 450, zAxis: 3}, + {xAxis: 1777838400000, yAxis: 500, zAxis: 1}, + {xAxis: 1777838400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777838400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777838400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777838400000, yAxis: 700, zAxis: 1}, + {xAxis: 1777838400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777838400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777838400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777842000000, yAxis: 0, zAxis: 2}, + {xAxis: 1777842000000, yAxis: 50, zAxis: 4}, + {xAxis: 1777842000000, yAxis: 100, zAxis: 7}, + {xAxis: 1777842000000, yAxis: 150, zAxis: 11}, + {xAxis: 1777842000000, yAxis: 200, zAxis: 14}, + {xAxis: 1777842000000, yAxis: 250, zAxis: 15}, + {xAxis: 1777842000000, yAxis: 300, zAxis: 14}, + {xAxis: 1777842000000, yAxis: 350, zAxis: 11}, + {xAxis: 1777842000000, yAxis: 400, zAxis: 7}, + {xAxis: 1777842000000, yAxis: 450, zAxis: 4}, + {xAxis: 1777842000000, yAxis: 500, zAxis: 2}, + {xAxis: 1777842000000, yAxis: 550, zAxis: 1}, + {xAxis: 1777842000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777842000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777842000000, yAxis: 700, zAxis: 1}, + {xAxis: 1777842000000, yAxis: 750, zAxis: 0}, + {xAxis: 1777842000000, yAxis: 800, zAxis: 0}, + {xAxis: 1777842000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777845600000, yAxis: 0, zAxis: 2}, + {xAxis: 1777845600000, yAxis: 50, zAxis: 4}, + {xAxis: 1777845600000, yAxis: 100, zAxis: 7}, + {xAxis: 1777845600000, yAxis: 150, zAxis: 11}, + {xAxis: 1777845600000, yAxis: 200, zAxis: 14}, + {xAxis: 1777845600000, yAxis: 250, zAxis: 15}, + {xAxis: 1777845600000, yAxis: 300, zAxis: 14}, + {xAxis: 1777845600000, yAxis: 350, zAxis: 11}, + {xAxis: 1777845600000, yAxis: 400, zAxis: 7}, + {xAxis: 1777845600000, yAxis: 450, zAxis: 4}, + {xAxis: 1777845600000, yAxis: 500, zAxis: 2}, + {xAxis: 1777845600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777845600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777845600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777845600000, yAxis: 700, zAxis: 1}, + {xAxis: 1777845600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777845600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777845600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777849200000, yAxis: 0, zAxis: 2}, + {xAxis: 1777849200000, yAxis: 50, zAxis: 4}, + {xAxis: 1777849200000, yAxis: 100, zAxis: 7}, + {xAxis: 1777849200000, yAxis: 150, zAxis: 11}, + {xAxis: 1777849200000, yAxis: 200, zAxis: 14}, + {xAxis: 1777849200000, yAxis: 250, zAxis: 15}, + {xAxis: 1777849200000, yAxis: 300, zAxis: 14}, + {xAxis: 1777849200000, yAxis: 350, zAxis: 11}, + {xAxis: 1777849200000, yAxis: 400, zAxis: 7}, + {xAxis: 1777849200000, yAxis: 450, zAxis: 4}, + {xAxis: 1777849200000, yAxis: 500, zAxis: 2}, + {xAxis: 1777849200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777849200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777849200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777849200000, yAxis: 700, zAxis: 1}, + {xAxis: 1777849200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777849200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777849200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777852800000, yAxis: 0, zAxis: 2}, + {xAxis: 1777852800000, yAxis: 50, zAxis: 4}, + {xAxis: 1777852800000, yAxis: 100, zAxis: 7}, + {xAxis: 1777852800000, yAxis: 150, zAxis: 11}, + {xAxis: 1777852800000, yAxis: 200, zAxis: 14}, + {xAxis: 1777852800000, yAxis: 250, zAxis: 15}, + {xAxis: 1777852800000, yAxis: 300, zAxis: 14}, + {xAxis: 1777852800000, yAxis: 350, zAxis: 11}, + {xAxis: 1777852800000, yAxis: 400, zAxis: 7}, + {xAxis: 1777852800000, yAxis: 450, zAxis: 4}, + {xAxis: 1777852800000, yAxis: 500, zAxis: 2}, + {xAxis: 1777852800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777852800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777852800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777852800000, yAxis: 700, zAxis: 1}, + {xAxis: 1777852800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777852800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777852800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777856400000, yAxis: 0, zAxis: 2}, + {xAxis: 1777856400000, yAxis: 50, zAxis: 4}, + {xAxis: 1777856400000, yAxis: 100, zAxis: 7}, + {xAxis: 1777856400000, yAxis: 150, zAxis: 11}, + {xAxis: 1777856400000, yAxis: 200, zAxis: 14}, + {xAxis: 1777856400000, yAxis: 250, zAxis: 15}, + {xAxis: 1777856400000, yAxis: 300, zAxis: 14}, + {xAxis: 1777856400000, yAxis: 350, zAxis: 11}, + {xAxis: 1777856400000, yAxis: 400, zAxis: 7}, + {xAxis: 1777856400000, yAxis: 450, zAxis: 4}, + {xAxis: 1777856400000, yAxis: 500, zAxis: 2}, + {xAxis: 1777856400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777856400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777856400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777856400000, yAxis: 700, zAxis: 1}, + {xAxis: 1777856400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777856400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777856400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777860000000, yAxis: 0, zAxis: 2}, + {xAxis: 1777860000000, yAxis: 50, zAxis: 4}, + {xAxis: 1777860000000, yAxis: 100, zAxis: 7}, + {xAxis: 1777860000000, yAxis: 150, zAxis: 11}, + {xAxis: 1777860000000, yAxis: 200, zAxis: 14}, + {xAxis: 1777860000000, yAxis: 250, zAxis: 15}, + {xAxis: 1777860000000, yAxis: 300, zAxis: 14}, + {xAxis: 1777860000000, yAxis: 350, zAxis: 11}, + {xAxis: 1777860000000, yAxis: 400, zAxis: 7}, + {xAxis: 1777860000000, yAxis: 450, zAxis: 4}, + {xAxis: 1777860000000, yAxis: 500, zAxis: 2}, + {xAxis: 1777860000000, yAxis: 550, zAxis: 1}, + {xAxis: 1777860000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777860000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777860000000, yAxis: 700, zAxis: 1}, + {xAxis: 1777860000000, yAxis: 750, zAxis: 0}, + {xAxis: 1777860000000, yAxis: 800, zAxis: 0}, + {xAxis: 1777860000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777863600000, yAxis: 0, zAxis: 2}, + {xAxis: 1777863600000, yAxis: 50, zAxis: 4}, + {xAxis: 1777863600000, yAxis: 100, zAxis: 7}, + {xAxis: 1777863600000, yAxis: 150, zAxis: 11}, + {xAxis: 1777863600000, yAxis: 200, zAxis: 14}, + {xAxis: 1777863600000, yAxis: 250, zAxis: 15}, + {xAxis: 1777863600000, yAxis: 300, zAxis: 14}, + {xAxis: 1777863600000, yAxis: 350, zAxis: 11}, + {xAxis: 1777863600000, yAxis: 400, zAxis: 7}, + {xAxis: 1777863600000, yAxis: 450, zAxis: 4}, + {xAxis: 1777863600000, yAxis: 500, zAxis: 2}, + {xAxis: 1777863600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777863600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777863600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777863600000, yAxis: 700, zAxis: 1}, + {xAxis: 1777863600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777863600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777863600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777867200000, yAxis: 0, zAxis: 2}, + {xAxis: 1777867200000, yAxis: 50, zAxis: 4}, + {xAxis: 1777867200000, yAxis: 100, zAxis: 7}, + {xAxis: 1777867200000, yAxis: 150, zAxis: 11}, + {xAxis: 1777867200000, yAxis: 200, zAxis: 14}, + {xAxis: 1777867200000, yAxis: 250, zAxis: 15}, + {xAxis: 1777867200000, yAxis: 300, zAxis: 14}, + {xAxis: 1777867200000, yAxis: 350, zAxis: 11}, + {xAxis: 1777867200000, yAxis: 400, zAxis: 7}, + {xAxis: 1777867200000, yAxis: 450, zAxis: 4}, + {xAxis: 1777867200000, yAxis: 500, zAxis: 2}, + {xAxis: 1777867200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777867200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777867200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777867200000, yAxis: 700, zAxis: 1}, + {xAxis: 1777867200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777867200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777867200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777870800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777870800000, yAxis: 50, zAxis: 3}, + {xAxis: 1777870800000, yAxis: 100, zAxis: 5}, + {xAxis: 1777870800000, yAxis: 150, zAxis: 7}, + {xAxis: 1777870800000, yAxis: 200, zAxis: 9}, + {xAxis: 1777870800000, yAxis: 250, zAxis: 10}, + {xAxis: 1777870800000, yAxis: 300, zAxis: 9}, + {xAxis: 1777870800000, yAxis: 350, zAxis: 7}, + {xAxis: 1777870800000, yAxis: 400, zAxis: 5}, + {xAxis: 1777870800000, yAxis: 450, zAxis: 3}, + {xAxis: 1777870800000, yAxis: 500, zAxis: 1}, + {xAxis: 1777870800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777870800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777870800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777870800000, yAxis: 700, zAxis: 1}, + {xAxis: 1777870800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777870800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777870800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777874400000, yAxis: 0, zAxis: 2}, + {xAxis: 1777874400000, yAxis: 50, zAxis: 4}, + {xAxis: 1777874400000, yAxis: 100, zAxis: 7}, + {xAxis: 1777874400000, yAxis: 150, zAxis: 11}, + {xAxis: 1777874400000, yAxis: 200, zAxis: 14}, + {xAxis: 1777874400000, yAxis: 250, zAxis: 15}, + {xAxis: 1777874400000, yAxis: 300, zAxis: 14}, + {xAxis: 1777874400000, yAxis: 350, zAxis: 11}, + {xAxis: 1777874400000, yAxis: 400, zAxis: 7}, + {xAxis: 1777874400000, yAxis: 450, zAxis: 4}, + {xAxis: 1777874400000, yAxis: 500, zAxis: 2}, + {xAxis: 1777874400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777874400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777874400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777874400000, yAxis: 700, zAxis: 1}, + {xAxis: 1777874400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777874400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777874400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777878000000, yAxis: 0, zAxis: 2}, + {xAxis: 1777878000000, yAxis: 50, zAxis: 4}, + {xAxis: 1777878000000, yAxis: 100, zAxis: 7}, + {xAxis: 1777878000000, yAxis: 150, zAxis: 11}, + {xAxis: 1777878000000, yAxis: 200, zAxis: 14}, + {xAxis: 1777878000000, yAxis: 250, zAxis: 15}, + {xAxis: 1777878000000, yAxis: 300, zAxis: 14}, + {xAxis: 1777878000000, yAxis: 350, zAxis: 11}, + {xAxis: 1777878000000, yAxis: 400, zAxis: 7}, + {xAxis: 1777878000000, yAxis: 450, zAxis: 4}, + {xAxis: 1777878000000, yAxis: 500, zAxis: 2}, + {xAxis: 1777878000000, yAxis: 550, zAxis: 1}, + {xAxis: 1777878000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777878000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777878000000, yAxis: 700, zAxis: 1}, + {xAxis: 1777878000000, yAxis: 750, zAxis: 0}, + {xAxis: 1777878000000, yAxis: 800, zAxis: 0}, + {xAxis: 1777878000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777881600000, yAxis: 0, zAxis: 2}, + {xAxis: 1777881600000, yAxis: 50, zAxis: 4}, + {xAxis: 1777881600000, yAxis: 100, zAxis: 7}, + {xAxis: 1777881600000, yAxis: 150, zAxis: 11}, + {xAxis: 1777881600000, yAxis: 200, zAxis: 14}, + {xAxis: 1777881600000, yAxis: 250, zAxis: 15}, + {xAxis: 1777881600000, yAxis: 300, zAxis: 14}, + {xAxis: 1777881600000, yAxis: 350, zAxis: 11}, + {xAxis: 1777881600000, yAxis: 400, zAxis: 7}, + {xAxis: 1777881600000, yAxis: 450, zAxis: 4}, + {xAxis: 1777881600000, yAxis: 500, zAxis: 2}, + {xAxis: 1777881600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777881600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777881600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777881600000, yAxis: 700, zAxis: 1}, + {xAxis: 1777881600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777881600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777881600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777885200000, yAxis: 0, zAxis: 2}, + {xAxis: 1777885200000, yAxis: 50, zAxis: 4}, + {xAxis: 1777885200000, yAxis: 100, zAxis: 7}, + {xAxis: 1777885200000, yAxis: 150, zAxis: 11}, + {xAxis: 1777885200000, yAxis: 200, zAxis: 14}, + {xAxis: 1777885200000, yAxis: 250, zAxis: 15}, + {xAxis: 1777885200000, yAxis: 300, zAxis: 14}, + {xAxis: 1777885200000, yAxis: 350, zAxis: 11}, + {xAxis: 1777885200000, yAxis: 400, zAxis: 7}, + {xAxis: 1777885200000, yAxis: 450, zAxis: 4}, + {xAxis: 1777885200000, yAxis: 500, zAxis: 2}, + {xAxis: 1777885200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777885200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777885200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777885200000, yAxis: 700, zAxis: 1}, + {xAxis: 1777885200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777885200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777885200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777888800000, yAxis: 0, zAxis: 2}, + {xAxis: 1777888800000, yAxis: 50, zAxis: 4}, + {xAxis: 1777888800000, yAxis: 100, zAxis: 7}, + {xAxis: 1777888800000, yAxis: 150, zAxis: 11}, + {xAxis: 1777888800000, yAxis: 200, zAxis: 14}, + {xAxis: 1777888800000, yAxis: 250, zAxis: 15}, + {xAxis: 1777888800000, yAxis: 300, zAxis: 14}, + {xAxis: 1777888800000, yAxis: 350, zAxis: 11}, + {xAxis: 1777888800000, yAxis: 400, zAxis: 7}, + {xAxis: 1777888800000, yAxis: 450, zAxis: 4}, + {xAxis: 1777888800000, yAxis: 500, zAxis: 2}, + {xAxis: 1777888800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777888800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777888800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777888800000, yAxis: 700, zAxis: 1}, + {xAxis: 1777888800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777888800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777888800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777892400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777892400000, yAxis: 50, zAxis: 1}, + {xAxis: 1777892400000, yAxis: 100, zAxis: 2}, + {xAxis: 1777892400000, yAxis: 150, zAxis: 3}, + {xAxis: 1777892400000, yAxis: 200, zAxis: 6}, + {xAxis: 1777892400000, yAxis: 250, zAxis: 9}, + {xAxis: 1777892400000, yAxis: 300, zAxis: 11}, + {xAxis: 1777892400000, yAxis: 350, zAxis: 12}, + {xAxis: 1777892400000, yAxis: 400, zAxis: 11}, + {xAxis: 1777892400000, yAxis: 450, zAxis: 9}, + {xAxis: 1777892400000, yAxis: 500, zAxis: 6}, + {xAxis: 1777892400000, yAxis: 550, zAxis: 3}, + {xAxis: 1777892400000, yAxis: 600, zAxis: 2}, + {xAxis: 1777892400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777892400000, yAxis: 700, zAxis: 1}, + {xAxis: 1777892400000, yAxis: 750, zAxis: 1}, + {xAxis: 1777892400000, yAxis: 800, zAxis: 1}, + {xAxis: 1777892400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777896000000, yAxis: 0, zAxis: 1}, + {xAxis: 1777896000000, yAxis: 50, zAxis: 1}, + {xAxis: 1777896000000, yAxis: 100, zAxis: 2}, + {xAxis: 1777896000000, yAxis: 150, zAxis: 3}, + {xAxis: 1777896000000, yAxis: 200, zAxis: 6}, + {xAxis: 1777896000000, yAxis: 250, zAxis: 9}, + {xAxis: 1777896000000, yAxis: 300, zAxis: 11}, + {xAxis: 1777896000000, yAxis: 350, zAxis: 12}, + {xAxis: 1777896000000, yAxis: 400, zAxis: 11}, + {xAxis: 1777896000000, yAxis: 450, zAxis: 9}, + {xAxis: 1777896000000, yAxis: 500, zAxis: 6}, + {xAxis: 1777896000000, yAxis: 550, zAxis: 3}, + {xAxis: 1777896000000, yAxis: 600, zAxis: 2}, + {xAxis: 1777896000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777896000000, yAxis: 700, zAxis: 1}, + {xAxis: 1777896000000, yAxis: 750, zAxis: 1}, + {xAxis: 1777896000000, yAxis: 800, zAxis: 1}, + {xAxis: 1777896000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777899600000, yAxis: 0, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 50, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 100, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 150, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 200, zAxis: 4}, + {xAxis: 1777899600000, yAxis: 250, zAxis: 6}, + {xAxis: 1777899600000, yAxis: 300, zAxis: 9}, + {xAxis: 1777899600000, yAxis: 350, zAxis: 11}, + {xAxis: 1777899600000, yAxis: 400, zAxis: 12}, + {xAxis: 1777899600000, yAxis: 450, zAxis: 11}, + {xAxis: 1777899600000, yAxis: 500, zAxis: 9}, + {xAxis: 1777899600000, yAxis: 550, zAxis: 6}, + {xAxis: 1777899600000, yAxis: 600, zAxis: 4}, + {xAxis: 1777899600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 700, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 750, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 800, zAxis: 1}, + {xAxis: 1777899600000, yAxis: 850, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 0, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 50, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 100, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 150, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 200, zAxis: 4}, + {xAxis: 1777903200000, yAxis: 250, zAxis: 6}, + {xAxis: 1777903200000, yAxis: 300, zAxis: 9}, + {xAxis: 1777903200000, yAxis: 350, zAxis: 11}, + {xAxis: 1777903200000, yAxis: 400, zAxis: 12}, + {xAxis: 1777903200000, yAxis: 450, zAxis: 11}, + {xAxis: 1777903200000, yAxis: 500, zAxis: 9}, + {xAxis: 1777903200000, yAxis: 550, zAxis: 6}, + {xAxis: 1777903200000, yAxis: 600, zAxis: 4}, + {xAxis: 1777903200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 700, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 750, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 800, zAxis: 1}, + {xAxis: 1777903200000, yAxis: 850, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 50, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 100, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 150, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 200, zAxis: 4}, + {xAxis: 1777906800000, yAxis: 250, zAxis: 6}, + {xAxis: 1777906800000, yAxis: 300, zAxis: 9}, + {xAxis: 1777906800000, yAxis: 350, zAxis: 11}, + {xAxis: 1777906800000, yAxis: 400, zAxis: 12}, + {xAxis: 1777906800000, yAxis: 450, zAxis: 11}, + {xAxis: 1777906800000, yAxis: 500, zAxis: 9}, + {xAxis: 1777906800000, yAxis: 550, zAxis: 6}, + {xAxis: 1777906800000, yAxis: 600, zAxis: 4}, + {xAxis: 1777906800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 700, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 750, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 800, zAxis: 1}, + {xAxis: 1777906800000, yAxis: 850, zAxis: 1}, + {xAxis: 1777910400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777910400000, yAxis: 50, zAxis: 1}, + {xAxis: 1777910400000, yAxis: 100, zAxis: 2}, + {xAxis: 1777910400000, yAxis: 150, zAxis: 3}, + {xAxis: 1777910400000, yAxis: 200, zAxis: 6}, + {xAxis: 1777910400000, yAxis: 250, zAxis: 9}, + {xAxis: 1777910400000, yAxis: 300, zAxis: 11}, + {xAxis: 1777910400000, yAxis: 350, zAxis: 12}, + {xAxis: 1777910400000, yAxis: 400, zAxis: 11}, + {xAxis: 1777910400000, yAxis: 450, zAxis: 9}, + {xAxis: 1777910400000, yAxis: 500, zAxis: 6}, + {xAxis: 1777910400000, yAxis: 550, zAxis: 3}, + {xAxis: 1777910400000, yAxis: 600, zAxis: 2}, + {xAxis: 1777910400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777910400000, yAxis: 700, zAxis: 1}, + {xAxis: 1777910400000, yAxis: 750, zAxis: 1}, + {xAxis: 1777910400000, yAxis: 800, zAxis: 1}, + {xAxis: 1777910400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777914000000, yAxis: 0, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 50, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 100, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 150, zAxis: 3}, + {xAxis: 1777914000000, yAxis: 200, zAxis: 5}, + {xAxis: 1777914000000, yAxis: 250, zAxis: 7}, + {xAxis: 1777914000000, yAxis: 300, zAxis: 9}, + {xAxis: 1777914000000, yAxis: 350, zAxis: 10}, + {xAxis: 1777914000000, yAxis: 400, zAxis: 9}, + {xAxis: 1777914000000, yAxis: 450, zAxis: 7}, + {xAxis: 1777914000000, yAxis: 500, zAxis: 5}, + {xAxis: 1777914000000, yAxis: 550, zAxis: 3}, + {xAxis: 1777914000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 700, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 750, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 800, zAxis: 1}, + {xAxis: 1777914000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777917600000, yAxis: 0, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 50, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 100, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 150, zAxis: 3}, + {xAxis: 1777917600000, yAxis: 200, zAxis: 5}, + {xAxis: 1777917600000, yAxis: 250, zAxis: 7}, + {xAxis: 1777917600000, yAxis: 300, zAxis: 9}, + {xAxis: 1777917600000, yAxis: 350, zAxis: 10}, + {xAxis: 1777917600000, yAxis: 400, zAxis: 9}, + {xAxis: 1777917600000, yAxis: 450, zAxis: 7}, + {xAxis: 1777917600000, yAxis: 500, zAxis: 5}, + {xAxis: 1777917600000, yAxis: 550, zAxis: 3}, + {xAxis: 1777917600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 700, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 750, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 800, zAxis: 1}, + {xAxis: 1777917600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777921200000, yAxis: 0, zAxis: 1}, + {xAxis: 1777921200000, yAxis: 50, zAxis: 1}, + {xAxis: 1777921200000, yAxis: 100, zAxis: 2}, + {xAxis: 1777921200000, yAxis: 150, zAxis: 4}, + {xAxis: 1777921200000, yAxis: 200, zAxis: 6}, + {xAxis: 1777921200000, yAxis: 250, zAxis: 7}, + {xAxis: 1777921200000, yAxis: 300, zAxis: 8}, + {xAxis: 1777921200000, yAxis: 350, zAxis: 7}, + {xAxis: 1777921200000, yAxis: 400, zAxis: 6}, + {xAxis: 1777921200000, yAxis: 450, zAxis: 4}, + {xAxis: 1777921200000, yAxis: 500, zAxis: 2}, + {xAxis: 1777921200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777921200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777921200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777921200000, yAxis: 700, zAxis: 1}, + {xAxis: 1777921200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777921200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777921200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777924800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777924800000, yAxis: 50, zAxis: 1}, + {xAxis: 1777924800000, yAxis: 100, zAxis: 2}, + {xAxis: 1777924800000, yAxis: 150, zAxis: 4}, + {xAxis: 1777924800000, yAxis: 200, zAxis: 6}, + {xAxis: 1777924800000, yAxis: 250, zAxis: 7}, + {xAxis: 1777924800000, yAxis: 300, zAxis: 8}, + {xAxis: 1777924800000, yAxis: 350, zAxis: 7}, + {xAxis: 1777924800000, yAxis: 400, zAxis: 6}, + {xAxis: 1777924800000, yAxis: 450, zAxis: 4}, + {xAxis: 1777924800000, yAxis: 500, zAxis: 2}, + {xAxis: 1777924800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777924800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777924800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777924800000, yAxis: 700, zAxis: 1}, + {xAxis: 1777924800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777924800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777924800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777928400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777928400000, yAxis: 50, zAxis: 3}, + {xAxis: 1777928400000, yAxis: 100, zAxis: 5}, + {xAxis: 1777928400000, yAxis: 150, zAxis: 7}, + {xAxis: 1777928400000, yAxis: 200, zAxis: 9}, + {xAxis: 1777928400000, yAxis: 250, zAxis: 10}, + {xAxis: 1777928400000, yAxis: 300, zAxis: 9}, + {xAxis: 1777928400000, yAxis: 350, zAxis: 7}, + {xAxis: 1777928400000, yAxis: 400, zAxis: 5}, + {xAxis: 1777928400000, yAxis: 450, zAxis: 3}, + {xAxis: 1777928400000, yAxis: 500, zAxis: 1}, + {xAxis: 1777928400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777928400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777928400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777928400000, yAxis: 700, zAxis: 1}, + {xAxis: 1777928400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777928400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777928400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777932000000, yAxis: 0, zAxis: 1}, + {xAxis: 1777932000000, yAxis: 50, zAxis: 3}, + {xAxis: 1777932000000, yAxis: 100, zAxis: 5}, + {xAxis: 1777932000000, yAxis: 150, zAxis: 7}, + {xAxis: 1777932000000, yAxis: 200, zAxis: 9}, + {xAxis: 1777932000000, yAxis: 250, zAxis: 10}, + {xAxis: 1777932000000, yAxis: 300, zAxis: 9}, + {xAxis: 1777932000000, yAxis: 350, zAxis: 7}, + {xAxis: 1777932000000, yAxis: 400, zAxis: 5}, + {xAxis: 1777932000000, yAxis: 450, zAxis: 3}, + {xAxis: 1777932000000, yAxis: 500, zAxis: 1}, + {xAxis: 1777932000000, yAxis: 550, zAxis: 1}, + {xAxis: 1777932000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777932000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777932000000, yAxis: 700, zAxis: 1}, + {xAxis: 1777932000000, yAxis: 750, zAxis: 0}, + {xAxis: 1777932000000, yAxis: 800, zAxis: 0}, + {xAxis: 1777932000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777935600000, yAxis: 0, zAxis: 1}, + {xAxis: 1777935600000, yAxis: 50, zAxis: 3}, + {xAxis: 1777935600000, yAxis: 100, zAxis: 5}, + {xAxis: 1777935600000, yAxis: 150, zAxis: 7}, + {xAxis: 1777935600000, yAxis: 200, zAxis: 9}, + {xAxis: 1777935600000, yAxis: 250, zAxis: 10}, + {xAxis: 1777935600000, yAxis: 300, zAxis: 9}, + {xAxis: 1777935600000, yAxis: 350, zAxis: 7}, + {xAxis: 1777935600000, yAxis: 400, zAxis: 5}, + {xAxis: 1777935600000, yAxis: 450, zAxis: 3}, + {xAxis: 1777935600000, yAxis: 500, zAxis: 1}, + {xAxis: 1777935600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777935600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777935600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777935600000, yAxis: 700, zAxis: 1}, + {xAxis: 1777935600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777935600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777935600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777939200000, yAxis: 0, zAxis: 1}, + {xAxis: 1777939200000, yAxis: 50, zAxis: 1}, + {xAxis: 1777939200000, yAxis: 100, zAxis: 2}, + {xAxis: 1777939200000, yAxis: 150, zAxis: 4}, + {xAxis: 1777939200000, yAxis: 200, zAxis: 6}, + {xAxis: 1777939200000, yAxis: 250, zAxis: 7}, + {xAxis: 1777939200000, yAxis: 300, zAxis: 8}, + {xAxis: 1777939200000, yAxis: 350, zAxis: 7}, + {xAxis: 1777939200000, yAxis: 400, zAxis: 6}, + {xAxis: 1777939200000, yAxis: 450, zAxis: 4}, + {xAxis: 1777939200000, yAxis: 500, zAxis: 2}, + {xAxis: 1777939200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777939200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777939200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777939200000, yAxis: 700, zAxis: 1}, + {xAxis: 1777939200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777939200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777939200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777942800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777942800000, yAxis: 50, zAxis: 2}, + {xAxis: 1777942800000, yAxis: 100, zAxis: 3}, + {xAxis: 1777942800000, yAxis: 150, zAxis: 4}, + {xAxis: 1777942800000, yAxis: 200, zAxis: 5}, + {xAxis: 1777942800000, yAxis: 250, zAxis: 6}, + {xAxis: 1777942800000, yAxis: 300, zAxis: 5}, + {xAxis: 1777942800000, yAxis: 350, zAxis: 4}, + {xAxis: 1777942800000, yAxis: 400, zAxis: 3}, + {xAxis: 1777942800000, yAxis: 450, zAxis: 2}, + {xAxis: 1777942800000, yAxis: 500, zAxis: 1}, + {xAxis: 1777942800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777942800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777942800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777942800000, yAxis: 700, zAxis: 0}, + {xAxis: 1777942800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777942800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777942800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777946400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777946400000, yAxis: 50, zAxis: 2}, + {xAxis: 1777946400000, yAxis: 100, zAxis: 3}, + {xAxis: 1777946400000, yAxis: 150, zAxis: 4}, + {xAxis: 1777946400000, yAxis: 200, zAxis: 5}, + {xAxis: 1777946400000, yAxis: 250, zAxis: 6}, + {xAxis: 1777946400000, yAxis: 300, zAxis: 5}, + {xAxis: 1777946400000, yAxis: 350, zAxis: 4}, + {xAxis: 1777946400000, yAxis: 400, zAxis: 3}, + {xAxis: 1777946400000, yAxis: 450, zAxis: 2}, + {xAxis: 1777946400000, yAxis: 500, zAxis: 1}, + {xAxis: 1777946400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777946400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777946400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777946400000, yAxis: 700, zAxis: 0}, + {xAxis: 1777946400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777946400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777946400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777953600000, yAxis: 0, zAxis: 1}, + {xAxis: 1777953600000, yAxis: 50, zAxis: 2}, + {xAxis: 1777953600000, yAxis: 100, zAxis: 3}, + {xAxis: 1777953600000, yAxis: 150, zAxis: 4}, + {xAxis: 1777953600000, yAxis: 200, zAxis: 6}, + {xAxis: 1777953600000, yAxis: 250, zAxis: 6}, + {xAxis: 1777953600000, yAxis: 300, zAxis: 6}, + {xAxis: 1777953600000, yAxis: 350, zAxis: 4}, + {xAxis: 1777953600000, yAxis: 400, zAxis: 3}, + {xAxis: 1777953600000, yAxis: 450, zAxis: 2}, + {xAxis: 1777953600000, yAxis: 500, zAxis: 1}, + {xAxis: 1777953600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777953600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777953600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777953600000, yAxis: 700, zAxis: 0}, + {xAxis: 1777953600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777953600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777953600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777957200000, yAxis: 0, zAxis: 1}, + {xAxis: 1777957200000, yAxis: 50, zAxis: 1}, + {xAxis: 1777957200000, yAxis: 100, zAxis: 2}, + {xAxis: 1777957200000, yAxis: 150, zAxis: 3}, + {xAxis: 1777957200000, yAxis: 200, zAxis: 4}, + {xAxis: 1777957200000, yAxis: 250, zAxis: 4}, + {xAxis: 1777957200000, yAxis: 300, zAxis: 4}, + {xAxis: 1777957200000, yAxis: 350, zAxis: 3}, + {xAxis: 1777957200000, yAxis: 400, zAxis: 2}, + {xAxis: 1777957200000, yAxis: 450, zAxis: 1}, + {xAxis: 1777957200000, yAxis: 500, zAxis: 1}, + {xAxis: 1777957200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777957200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777957200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777957200000, yAxis: 700, zAxis: 0}, + {xAxis: 1777957200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777957200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777957200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777960800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777960800000, yAxis: 50, zAxis: 1}, + {xAxis: 1777960800000, yAxis: 100, zAxis: 2}, + {xAxis: 1777960800000, yAxis: 150, zAxis: 3}, + {xAxis: 1777960800000, yAxis: 200, zAxis: 4}, + {xAxis: 1777960800000, yAxis: 250, zAxis: 4}, + {xAxis: 1777960800000, yAxis: 300, zAxis: 4}, + {xAxis: 1777960800000, yAxis: 350, zAxis: 3}, + {xAxis: 1777960800000, yAxis: 400, zAxis: 2}, + {xAxis: 1777960800000, yAxis: 450, zAxis: 1}, + {xAxis: 1777960800000, yAxis: 500, zAxis: 1}, + {xAxis: 1777960800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777960800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777960800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777960800000, yAxis: 700, zAxis: 0}, + {xAxis: 1777960800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777960800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777960800000, yAxis: 850, zAxis: 0}, + {xAxis: 1777964400000, yAxis: 0, zAxis: 1}, + {xAxis: 1777964400000, yAxis: 50, zAxis: 1}, + {xAxis: 1777964400000, yAxis: 100, zAxis: 2}, + {xAxis: 1777964400000, yAxis: 150, zAxis: 3}, + {xAxis: 1777964400000, yAxis: 200, zAxis: 4}, + {xAxis: 1777964400000, yAxis: 250, zAxis: 4}, + {xAxis: 1777964400000, yAxis: 300, zAxis: 4}, + {xAxis: 1777964400000, yAxis: 350, zAxis: 3}, + {xAxis: 1777964400000, yAxis: 400, zAxis: 2}, + {xAxis: 1777964400000, yAxis: 450, zAxis: 1}, + {xAxis: 1777964400000, yAxis: 500, zAxis: 1}, + {xAxis: 1777964400000, yAxis: 550, zAxis: 1}, + {xAxis: 1777964400000, yAxis: 600, zAxis: 1}, + {xAxis: 1777964400000, yAxis: 650, zAxis: 1}, + {xAxis: 1777964400000, yAxis: 700, zAxis: 0}, + {xAxis: 1777964400000, yAxis: 750, zAxis: 0}, + {xAxis: 1777964400000, yAxis: 800, zAxis: 0}, + {xAxis: 1777964400000, yAxis: 850, zAxis: 0}, + {xAxis: 1777968000000, yAxis: 0, zAxis: 1}, + {xAxis: 1777968000000, yAxis: 50, zAxis: 1}, + {xAxis: 1777968000000, yAxis: 100, zAxis: 2}, + {xAxis: 1777968000000, yAxis: 150, zAxis: 3}, + {xAxis: 1777968000000, yAxis: 200, zAxis: 4}, + {xAxis: 1777968000000, yAxis: 250, zAxis: 4}, + {xAxis: 1777968000000, yAxis: 300, zAxis: 4}, + {xAxis: 1777968000000, yAxis: 350, zAxis: 3}, + {xAxis: 1777968000000, yAxis: 400, zAxis: 2}, + {xAxis: 1777968000000, yAxis: 450, zAxis: 1}, + {xAxis: 1777968000000, yAxis: 500, zAxis: 1}, + {xAxis: 1777968000000, yAxis: 550, zAxis: 1}, + {xAxis: 1777968000000, yAxis: 600, zAxis: 1}, + {xAxis: 1777968000000, yAxis: 650, zAxis: 1}, + {xAxis: 1777968000000, yAxis: 700, zAxis: 0}, + {xAxis: 1777968000000, yAxis: 750, zAxis: 0}, + {xAxis: 1777968000000, yAxis: 800, zAxis: 0}, + {xAxis: 1777968000000, yAxis: 850, zAxis: 0}, + {xAxis: 1777971600000, yAxis: 0, zAxis: 1}, + {xAxis: 1777971600000, yAxis: 50, zAxis: 1}, + {xAxis: 1777971600000, yAxis: 100, zAxis: 2}, + {xAxis: 1777971600000, yAxis: 150, zAxis: 3}, + {xAxis: 1777971600000, yAxis: 200, zAxis: 4}, + {xAxis: 1777971600000, yAxis: 250, zAxis: 4}, + {xAxis: 1777971600000, yAxis: 300, zAxis: 4}, + {xAxis: 1777971600000, yAxis: 350, zAxis: 3}, + {xAxis: 1777971600000, yAxis: 400, zAxis: 2}, + {xAxis: 1777971600000, yAxis: 450, zAxis: 1}, + {xAxis: 1777971600000, yAxis: 500, zAxis: 1}, + {xAxis: 1777971600000, yAxis: 550, zAxis: 1}, + {xAxis: 1777971600000, yAxis: 600, zAxis: 1}, + {xAxis: 1777971600000, yAxis: 650, zAxis: 1}, + {xAxis: 1777971600000, yAxis: 700, zAxis: 0}, + {xAxis: 1777971600000, yAxis: 750, zAxis: 0}, + {xAxis: 1777971600000, yAxis: 800, zAxis: 0}, + {xAxis: 1777971600000, yAxis: 850, zAxis: 0}, + {xAxis: 1777975200000, yAxis: 0, zAxis: 1}, + {xAxis: 1777975200000, yAxis: 50, zAxis: 1}, + {xAxis: 1777975200000, yAxis: 100, zAxis: 2}, + {xAxis: 1777975200000, yAxis: 150, zAxis: 3}, + {xAxis: 1777975200000, yAxis: 200, zAxis: 4}, + {xAxis: 1777975200000, yAxis: 250, zAxis: 4}, + {xAxis: 1777975200000, yAxis: 300, zAxis: 4}, + {xAxis: 1777975200000, yAxis: 350, zAxis: 3}, + {xAxis: 1777975200000, yAxis: 400, zAxis: 2}, + {xAxis: 1777975200000, yAxis: 450, zAxis: 1}, + {xAxis: 1777975200000, yAxis: 500, zAxis: 1}, + {xAxis: 1777975200000, yAxis: 550, zAxis: 1}, + {xAxis: 1777975200000, yAxis: 600, zAxis: 1}, + {xAxis: 1777975200000, yAxis: 650, zAxis: 1}, + {xAxis: 1777975200000, yAxis: 700, zAxis: 0}, + {xAxis: 1777975200000, yAxis: 750, zAxis: 0}, + {xAxis: 1777975200000, yAxis: 800, zAxis: 0}, + {xAxis: 1777975200000, yAxis: 850, zAxis: 0}, + {xAxis: 1777978800000, yAxis: 0, zAxis: 1}, + {xAxis: 1777978800000, yAxis: 50, zAxis: 1}, + {xAxis: 1777978800000, yAxis: 100, zAxis: 2}, + {xAxis: 1777978800000, yAxis: 150, zAxis: 3}, + {xAxis: 1777978800000, yAxis: 200, zAxis: 4}, + {xAxis: 1777978800000, yAxis: 250, zAxis: 4}, + {xAxis: 1777978800000, yAxis: 300, zAxis: 4}, + {xAxis: 1777978800000, yAxis: 350, zAxis: 3}, + {xAxis: 1777978800000, yAxis: 400, zAxis: 2}, + {xAxis: 1777978800000, yAxis: 450, zAxis: 1}, + {xAxis: 1777978800000, yAxis: 500, zAxis: 1}, + {xAxis: 1777978800000, yAxis: 550, zAxis: 1}, + {xAxis: 1777978800000, yAxis: 600, zAxis: 1}, + {xAxis: 1777978800000, yAxis: 650, zAxis: 1}, + {xAxis: 1777978800000, yAxis: 700, zAxis: 0}, + {xAxis: 1777978800000, yAxis: 750, zAxis: 0}, + {xAxis: 1777978800000, yAxis: 800, zAxis: 0}, + {xAxis: 1777978800000, yAxis: 850, zAxis: 0}, + ], +}; diff --git a/static/app/views/dashboards/widgets/heatMapWidget/heatMapWidgetVisualization.stories.tsx b/static/app/views/dashboards/widgets/heatMapWidget/heatMapWidgetVisualization.stories.tsx new file mode 100644 index 00000000000000..026be62247012c --- /dev/null +++ b/static/app/views/dashboards/widgets/heatMapWidget/heatMapWidgetVisualization.stories.tsx @@ -0,0 +1,107 @@ +import {Fragment} from 'react'; +import styled from '@emotion/styled'; + +import {CodeBlock} from '@sentry/scraps/code'; + +import * as Storybook from 'sentry/stories'; + +import {sampleLatencyHeatMap} from './fixtures/sampleLatencyHeatMap'; +import {HeatMap} from './plottables/heatMap'; +import {HeatMapWidgetVisualization} from './heatMapWidgetVisualization'; + +export default Storybook.story('HeatMapWidgetVisualization', story => { + story('Getting Started', () => { + return ( + +

+ is a dense data + visualization that plots three-dimensional data by using color as the third + axis. This naturally makes percentiles and problem areas visible, which makes it + useful for analysis of spiky or modal data. Right now we use Heat Maps in our + Metrics product, so the usage is limited. We expect the X-axis to be time, and + the Z-axis (the color axis) to be a count. The Y-axis can be any continuous + value. +

+ + + + +
+ ); + }); + + story('Basic Usage', () => { + return ( + +

+ accepts a{' '} + plottables prop, similar to{' '} + . At least one of the{' '} + plottables must be a HeatMap instance. +

+

+ + {` + + `} + +

+ +

+ The HeatMap class accepts a HeatMapSeries object. + Here's an example of a HeatMapSeries: +

+ + + {`{ + meta: { + xAxis: { + name: 'time', + start: 1777802400.0, + end: 1777824000.0, + bucketCount: 6, + bucketSize: 3600, + }, + yAxis: { + name: 'value', + start: 0.0, + end: 200.0, + bucketCount: 2, + bucketSize: 100.0, + valueType: 'integer', + valueUnit: null, + }, + zAxis: { + name: 'count()', + start: 0.0, + end: 1.0, + }, + }, + values: [ + {xAxis: 1777802400, yAxis: 0.0, zAxis: 1}, + {xAxis: 1777802400, yAxis: 100.0, zAxis: 1}, + {xAxis: 1777802400, yAxis: 200.0, zAxis: 1}, + {xAxis: 1777806000, yAxis: 0.0, zAxis: 0}, + {xAxis: 1777806000, yAxis: 100.0, zAxis: 0}, + {xAxis: 1777806000, yAxis: 200.0, zAxis: 0}, + {xAxis: 1777809600, yAxis: 0.0, zAxis: 1}, + {xAxis: 1777809600, yAxis: 100.0, zAxis: 1}, + {xAxis: 1777809600, yAxis: 200.0, zAxis: 1}, + {xAxis: 1777813200, yAxis: 0.0, zAxis: 1}, + {xAxis: 1777813200, yAxis: 100.0, zAxis: 1}, + {xAxis: 1777813200, yAxis: 200.0, zAxis: 1}, + ], +};`} + +
+ ); + }); +}); + +const LargeWidget = styled('div')` + position: relative; + width: 800px; + height: 400px; +`; diff --git a/static/app/views/dashboards/widgets/heatMapWidget/heatMapWidgetVisualization.tsx b/static/app/views/dashboards/widgets/heatMapWidget/heatMapWidgetVisualization.tsx new file mode 100644 index 00000000000000..794b647dad1193 --- /dev/null +++ b/static/app/views/dashboards/widgets/heatMapWidget/heatMapWidgetVisualization.tsx @@ -0,0 +1,145 @@ +import 'echarts/lib/chart/heatmap'; +import 'echarts/lib/component/visualMap'; + +import {useTheme} from '@emotion/react'; + +import {Flex} from '@sentry/scraps/layout'; + +import {BaseChart} from 'sentry/components/charts/baseChart'; +import {usePageFilters} from 'sentry/components/pageFilters/usePageFilters'; +import {NO_PLOTTABLE_VALUES} from 'sentry/views/dashboards/widgets/common/settings'; +import {plottablesCanBeVisualized} from 'sentry/views/dashboards/widgets/plottablesCanBeVisualized'; +import {formatXAxisTimestamp} from 'sentry/views/dashboards/widgets/timeSeriesWidget/formatters/formatXAxisTimestamp'; +import {formatYAxisValue} from 'sentry/views/dashboards/widgets/timeSeriesWidget/formatters/formatYAxisValue'; + +import {HeatMap} from './plottables/heatMap'; +import type {HeatMapPlottable} from './plottables/heatMapPlottable'; +import {HEATMAP_COLORS} from './settings'; + +interface HeatMapWidgetVisualizationProps { + /** + * An single `HeatMap` object to render on the chart, and any number of other compatible Heat Map plottables. + */ + plottables: [HeatMap, ...HeatMapPlottable[]]; + /** + * Experimental! Specify the Z-axis scale type. Logarithmic scales can be much more useful for values with a high range. + */ + scale?: 'linear' | 'log'; +} + +export function HeatMapWidgetVisualization(props: HeatMapWidgetVisualizationProps) { + const {plottables} = props; + const theme = useTheme(); + + const pageFilters = usePageFilters(); + const {start, end, period, utc} = pageFilters.selection.datetime; + + if (!plottablesCanBeVisualized(plottables)) { + throw new Error(NO_PLOTTABLE_VALUES); + } + + // TODO: Would be wise to guard against Y-axis type mismatches, we don't want + // to support multi-axis here. + + const {scale = 'linear'} = props; + + const series = plottables.flatMap(plottable => + plottable.toSeries({ + theme, + scale, + }) + ); + + const heatMapPlottable = plottables[0]; + + const yAxisDataType = heatMapPlottable.yAxisValueType; + const yAxisDataUnit = heatMapPlottable.yAxisValueUnit; + + const Zmax = + scale === 'log' ? Math.log1p(heatMapPlottable.Zend) : heatMapPlottable.Zend; + + return ( + + { + // NOTE: ECharts requires a `"category"` X-axis for heat maps, but we _know_ that we only support time as the X-axis. We need to parse the value here. + return formatXAxisTimestamp(parseFloat(value), { + utc: utc ?? undefined, + }); + }, + }, + axisPointer: { + show: false, + }, + splitArea: { + show: false, + }, + }} + yAxis={{ + type: 'category', + animation: false, + axisLabel: { + hideOverlap: true, + formatter: value => { + // NOTE: ECharts requires a `"category"` Y-axis for heat maps, but we _know_ that we only support continuous values for the Y-axis. We need to parse the value here. + return formatYAxisValue( + parseFloat(value), + yAxisDataType, + yAxisDataUnit ?? undefined + ); + }, + }, + axisPointer: { + show: false, + }, + splitArea: { + show: false, + }, + }} + options={{ + visualMap: [ + // Zero values are transparent (empty buckets) + { + type: 'piecewise', + show: false, + dimension: 2, + seriesIndex: 0, + pieces: [ + {value: 0, opacity: 0}, + {gt: 0, opacity: 1}, + ], + }, + // All values are plotted against a palette + { + type: 'continuous', + show: false, + dimension: 2, + seriesIndex: 0, + min: 0, + max: Zmax, + inRange: { + color: [...HEATMAP_COLORS], + }, + }, + ], + }} + start={start ? new Date(start) : undefined} + end={end ? new Date(end) : undefined} + period={period} + utc={utc ?? undefined} + /> + + ); +} diff --git a/static/app/views/dashboards/widgets/heatMapWidget/plottables/heatMap.tsx b/static/app/views/dashboards/widgets/heatMapWidget/plottables/heatMap.tsx new file mode 100644 index 00000000000000..e5404affeb04e6 --- /dev/null +++ b/static/app/views/dashboards/widgets/heatMapWidget/plottables/heatMap.tsx @@ -0,0 +1,72 @@ +import type {SeriesOption} from 'echarts'; + +import type {Theme} from 'sentry/utils/theme'; +import {ECHARTS_MISSING_DATA_VALUE} from 'sentry/utils/timeSeries/timeSeriesItemToEChartsDataPoint'; +import {isAPlottableTimeSeriesValueType} from 'sentry/views/dashboards/widgets/common/typePredicates'; +import type { + HeatMapSeries, + HeatMapValueUnit, +} from 'sentry/views/dashboards/widgets/common/types'; +import {FALLBACK_TYPE} from 'sentry/views/dashboards/widgets/timeSeriesWidget/settings'; + +import type {HeatMapPlottable, PlottableTimeSeriesValueType} from './heatMapPlottable'; + +export type HeatMapPlottingOptions = { + theme: Theme; + /** + * The Z-axis scale type. `'log'` applies `Math.log1p` to Z values so the + * color gradient distributes logarithmically. Useful when the Z range is + * very wide and low values would otherwise be invisible. + */ + scale?: 'linear' | 'log'; +}; + +export class HeatMap implements HeatMapPlottable { + readonly heatMapSeries: Readonly; + readonly Zstart: number; + readonly Zend: number; + + constructor(heatMapSeries: HeatMapSeries) { + this.heatMapSeries = heatMapSeries; + + this.Zstart = heatMapSeries.meta.zAxis.start; + this.Zend = heatMapSeries.meta.zAxis.end; + } + + get isEmpty(): boolean { + return this.heatMapSeries.values.every(item => item.zAxis === null); + } + + get yAxisValueType(): PlottableTimeSeriesValueType { + return isAPlottableTimeSeriesValueType(this.heatMapSeries.meta.yAxis.valueType) + ? this.heatMapSeries.meta.yAxis.valueType + : FALLBACK_TYPE; + } + + get yAxisValueUnit(): HeatMapValueUnit { + return this.heatMapSeries.meta.yAxis.valueUnit; + } + + toSeries(plottingOptions: HeatMapPlottingOptions): SeriesOption[] { + const {heatMapSeries} = this; + const {scale = 'linear'} = plottingOptions; + + return [ + { + name: 'heatmap', // Only one heat map is allowed per visualization, so this name doesn't have to be unique + type: 'heatmap', + data: heatMapSeries.values.map(item => { + const zValue = item.zAxis ?? ECHARTS_MISSING_DATA_VALUE; + return [ + item.xAxis, + item.yAxis, + scale === 'log' && typeof zValue === 'number' ? Math.log1p(zValue) : zValue, + ]; + }), + emphasis: { + disabled: true, + }, + }, + ]; + } +} diff --git a/static/app/views/dashboards/widgets/heatMapWidget/plottables/heatMapPlottable.ts b/static/app/views/dashboards/widgets/heatMapWidget/plottables/heatMapPlottable.ts new file mode 100644 index 00000000000000..1628e219f588c6 --- /dev/null +++ b/static/app/views/dashboards/widgets/heatMapWidget/plottables/heatMapPlottable.ts @@ -0,0 +1,42 @@ +import type {SeriesOption} from 'echarts'; + +import type {PLOTTABLE_TIME_SERIES_VALUE_TYPES} from 'sentry/views/dashboards/widgets/common/settings'; +import type {HeatMapValueUnit} from 'sentry/views/dashboards/widgets/common/types'; + +export type PlottableTimeSeriesValueType = + (typeof PLOTTABLE_TIME_SERIES_VALUE_TYPES)[number]; + +/** + * A `HeatMapPlottable` is any object that can be converted to an ECharts + * `Series` and therefore plotted on a heat map chart. This could be a data + * series, markers, or other visual elements. `HeatMapWidgetVisualization` uses + * `HeatMapPlottable` objects under the hood to convert data coming into + * the component via props into ECharts series. + */ +export interface HeatMapPlottable { + /** + * Largest value on the Z axis + */ + Zend: number; + /** + * Smallest value on the Z axis + */ + Zstart: number; + /** + * Whether this plottable has enough data to be visually represented. + */ + isEmpty: boolean; + /** + * Converts this plottable to ECharts series options. + * @param plottingOptions Plotting options depend on the specific implementation. + */ + toSeries(plottingOptions: unknown): SeriesOption[]; + /** + * Type of Y-axis data (different from Z-axis) + */ + yAxisValueType: PlottableTimeSeriesValueType; + /** + * Unit of the Y-axis data (different from Z-axis) + */ + yAxisValueUnit: HeatMapValueUnit; +} diff --git a/static/app/views/dashboards/widgets/heatMapWidget/settings.ts b/static/app/views/dashboards/widgets/heatMapWidget/settings.ts new file mode 100644 index 00000000000000..b0d08f59696352 --- /dev/null +++ b/static/app/views/dashboards/widgets/heatMapWidget/settings.ts @@ -0,0 +1,16 @@ +// Color scale interpolated across three design stops: #EEEFFF (low) → #7553FF +// (mid) → #990056 (high) Steps 1–5: segment 1, steps 6–10: segment 2 N.B. +// Missing values are not part of the palette here, they are filled in by the +// `HeatMapWidgetVisualization` component. +export const HEATMAP_COLORS = [ + '#eeefff', + '#d0c8ff', + '#b2a1ff', + '#937aff', + '#7553ff', + '#7c42dd', + '#8332bb', + '#8b219a', + '#921178', + '#990056', +] as const; diff --git a/static/app/views/dashboards/widgets/plottablesCanBeVisualized.ts b/static/app/views/dashboards/widgets/plottablesCanBeVisualized.ts index 567662972c1119..6d488c3af80012 100644 --- a/static/app/views/dashboards/widgets/plottablesCanBeVisualized.ts +++ b/static/app/views/dashboards/widgets/plottablesCanBeVisualized.ts @@ -1,8 +1,10 @@ import type {CategoricalPlottable} from 'sentry/views/dashboards/widgets/categoricalSeriesWidget/plottables/plottable'; import type {Plottable} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/plottable'; +import type {HeatMapPlottable} from './heatMapWidget/plottables/heatMapPlottable'; + export function plottablesCanBeVisualized( - plottables: CategoricalPlottable[] | Plottable[] + plottables: CategoricalPlottable[] | Plottable[] | HeatMapPlottable[] ) { return plottables.some(plottable => !plottable.isEmpty); }