| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071 |
- /* *
- *
- * (c) 2010-2020 Torstein Honsi
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- 'use strict';
- import H from '../Core/Globals.js';
- import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
- import Point from '../Core/Series/Point.js';
- import SVGRenderer from '../Core/Renderer/SVG/SVGRenderer.js';
- import U from '../Core/Utilities.js';
- var extend = U.extend, fireEvent = U.fireEvent, getNestedProperty = U.getNestedProperty, isArray = U.isArray, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick, seriesType = U.seriesType, splat = U.splat;
- import '../Core/Options.js';
- import '../Series/ScatterSeries.js';
- import '../Core/Series/Series.js';
- import '../Mixins/ColorMapSeries.js';
- var colorMapPointMixin = H.colorMapPointMixin, colorMapSeriesMixin = H.colorMapSeriesMixin, noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes;
- /**
- * @private
- * @class
- * @name Highcharts.seriesTypes.map
- *
- * @augments Highcharts.Series
- */
- seriesType('map', 'scatter',
- /**
- * The map series is used for basic choropleth maps, where each map area has
- * a color based on its value.
- *
- * @sample maps/demo/all-maps/
- * Choropleth map
- *
- * @extends plotOptions.scatter
- * @excluding marker, cluster
- * @product highmaps
- * @optionparent plotOptions.map
- */
- {
- animation: false,
- dataLabels: {
- crop: false,
- formatter: function () {
- return this.point.value;
- },
- inside: true,
- overflow: false,
- padding: 0,
- verticalAlign: 'middle'
- },
- /**
- * @ignore-option
- *
- * @private
- */
- marker: null,
- /**
- * The color to apply to null points.
- *
- * In styled mode, the null point fill is set in the
- * `.highcharts-null-point` class.
- *
- * @sample maps/demo/all-areas-as-null/
- * Null color
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- *
- * @private
- */
- nullColor: '#f7f7f7',
- /**
- * Whether to allow pointer interaction like tooltips and mouse events
- * on null points.
- *
- * @type {boolean}
- * @since 4.2.7
- * @apioption plotOptions.map.nullInteraction
- *
- * @private
- */
- stickyTracking: false,
- tooltip: {
- followPointer: true,
- pointFormat: '{point.name}: {point.value}<br/>'
- },
- /**
- * @ignore-option
- *
- * @private
- */
- turboThreshold: 0,
- /**
- * Whether all areas of the map defined in `mapData` should be rendered.
- * If `true`, areas which don't correspond to a data point, are rendered
- * as `null` points. If `false`, those areas are skipped.
- *
- * @sample maps/plotoptions/series-allareas-false/
- * All areas set to false
- *
- * @type {boolean}
- * @default true
- * @product highmaps
- * @apioption plotOptions.series.allAreas
- *
- * @private
- */
- allAreas: true,
- /**
- * The border color of the map areas.
- *
- * In styled mode, the border stroke is given in the `.highcharts-point`
- * class.
- *
- * @sample {highmaps} maps/plotoptions/series-border/
- * Borders demo
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @default '#cccccc'
- * @product highmaps
- * @apioption plotOptions.series.borderColor
- *
- * @private
- */
- borderColor: '#cccccc',
- /**
- * The border width of each map area.
- *
- * In styled mode, the border stroke width is given in the
- * `.highcharts-point` class.
- *
- * @sample maps/plotoptions/series-border/
- * Borders demo
- *
- * @type {number}
- * @default 1
- * @product highmaps
- * @apioption plotOptions.series.borderWidth
- *
- * @private
- */
- borderWidth: 1,
- /**
- * @default value
- * @apioption plotOptions.map.colorKey
- */
- /**
- * What property to join the `mapData` to the value data. For example,
- * if joinBy is "code", the mapData items with a specific code is merged
- * into the data with the same code. For maps loaded from GeoJSON, the
- * keys may be held in each point's `properties` object.
- *
- * The joinBy option can also be an array of two values, where the first
- * points to a key in the `mapData`, and the second points to another
- * key in the `data`.
- *
- * When joinBy is `null`, the map items are joined by their position in
- * the array, which performs much better in maps with many data points.
- * This is the recommended option if you are printing more than a
- * thousand data points and have a backend that can preprocess the data
- * into a parallel array of the mapData.
- *
- * @sample maps/plotoptions/series-border/
- * Joined by "code"
- * @sample maps/demo/geojson/
- * GeoJSON joined by an array
- * @sample maps/series/joinby-null/
- * Simple data joined by null
- *
- * @type {string|Array<string>}
- * @default hc-key
- * @product highmaps
- * @apioption plotOptions.series.joinBy
- *
- * @private
- */
- joinBy: 'hc-key',
- /**
- * Define the z index of the series.
- *
- * @type {number}
- * @product highmaps
- * @apioption plotOptions.series.zIndex
- */
- /**
- * @apioption plotOptions.series.states
- *
- * @private
- */
- states: {
- /**
- * @apioption plotOptions.series.states.hover
- */
- hover: {
- /** @ignore-option */
- halo: null,
- /**
- * The color of the shape in this state.
- *
- * @sample maps/plotoptions/series-states-hover/
- * Hover options
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highmaps
- * @apioption plotOptions.series.states.hover.color
- */
- /**
- * The border color of the point in this state.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highmaps
- * @apioption plotOptions.series.states.hover.borderColor
- */
- /**
- * The border width of the point in this state
- *
- * @type {number}
- * @product highmaps
- * @apioption plotOptions.series.states.hover.borderWidth
- */
- /**
- * The relative brightness of the point when hovered, relative
- * to the normal point color.
- *
- * @type {number}
- * @product highmaps
- * @default 0.2
- * @apioption plotOptions.series.states.hover.brightness
- */
- brightness: 0.2
- },
- /**
- * @apioption plotOptions.series.states.normal
- */
- normal: {
- /**
- * @productdesc {highmaps}
- * The animation adds some latency in order to reduce the effect
- * of flickering when hovering in and out of for example an
- * uneven coastline.
- *
- * @sample {highmaps} maps/plotoptions/series-states-animation-false/
- * No animation of fill color
- *
- * @apioption plotOptions.series.states.normal.animation
- */
- animation: true
- },
- /**
- * @apioption plotOptions.series.states.select
- */
- select: {
- /**
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @default #cccccc
- * @product highmaps
- * @apioption plotOptions.series.states.select.color
- */
- color: '#cccccc'
- },
- inactive: {
- opacity: 1
- }
- }
- // Prototype members
- }, merge(colorMapSeriesMixin, {
- type: 'map',
- getExtremesFromAll: true,
- useMapGeometry: true,
- forceDL: true,
- searchPoint: noop,
- // When tooltip is not shared, this series (and derivatives) requires
- // direct touch/hover. KD-tree does not apply.
- directTouch: true,
- // X axis and Y axis must have same translation slope
- preserveAspectRatio: true,
- pointArrayMap: ['value'],
- // Extend setOptions by picking up the joinBy option and applying it
- // to a series property
- setOptions: function (itemOptions) {
- var options = Series.prototype.setOptions.call(this, itemOptions), joinBy = options.joinBy, joinByNull = joinBy === null;
- if (joinByNull) {
- joinBy = '_i';
- }
- joinBy = this.joinBy = splat(joinBy);
- if (!joinBy[1]) {
- joinBy[1] = joinBy[0];
- }
- return options;
- },
- // Get the bounding box of all paths in the map combined.
- getBox: function (paths) {
- var MAX_VALUE = Number.MAX_VALUE, maxX = -MAX_VALUE, minX = MAX_VALUE, maxY = -MAX_VALUE, minY = MAX_VALUE, minRange = MAX_VALUE, xAxis = this.xAxis, yAxis = this.yAxis, hasBox;
- // Find the bounding box
- (paths || []).forEach(function (point) {
- if (point.path) {
- if (typeof point.path === 'string') {
- point.path = H.splitPath(point.path);
- // Legacy one-dimensional array
- }
- else if (point.path[0] === 'M') {
- point.path = SVGRenderer.prototype.pathToSegments(point.path);
- }
- var path = point.path || [], pointMaxX = -MAX_VALUE, pointMinX = MAX_VALUE, pointMaxY = -MAX_VALUE, pointMinY = MAX_VALUE, properties = point.properties;
- // The first time a map point is used, analyze its box
- if (!point._foundBox) {
- path.forEach(function (seg) {
- var x = seg[seg.length - 2];
- var y = seg[seg.length - 1];
- if (typeof x === 'number' && typeof y === 'number') {
- pointMinX = Math.min(pointMinX, x);
- pointMaxX = Math.max(pointMaxX, x);
- pointMinY = Math.min(pointMinY, y);
- pointMaxY = Math.max(pointMaxY, y);
- }
- });
- // Cache point bounding box for use to position data
- // labels, bubbles etc
- point._midX = (pointMinX + (pointMaxX - pointMinX) * pick(point.middleX, properties &&
- properties['hc-middle-x'], 0.5));
- point._midY = (pointMinY + (pointMaxY - pointMinY) * pick(point.middleY, properties &&
- properties['hc-middle-y'], 0.5));
- point._maxX = pointMaxX;
- point._minX = pointMinX;
- point._maxY = pointMaxY;
- point._minY = pointMinY;
- point.labelrank = pick(point.labelrank, (pointMaxX - pointMinX) * (pointMaxY - pointMinY));
- point._foundBox = true;
- }
- maxX = Math.max(maxX, point._maxX);
- minX = Math.min(minX, point._minX);
- maxY = Math.max(maxY, point._maxY);
- minY = Math.min(minY, point._minY);
- minRange = Math.min(point._maxX - point._minX, point._maxY - point._minY, minRange);
- hasBox = true;
- }
- });
- // Set the box for the whole series
- if (hasBox) {
- this.minY = Math.min(minY, pick(this.minY, MAX_VALUE));
- this.maxY = Math.max(maxY, pick(this.maxY, -MAX_VALUE));
- this.minX = Math.min(minX, pick(this.minX, MAX_VALUE));
- this.maxX = Math.max(maxX, pick(this.maxX, -MAX_VALUE));
- // If no minRange option is set, set the default minimum zooming
- // range to 5 times the size of the smallest element
- if (xAxis && typeof xAxis.options.minRange === 'undefined') {
- xAxis.minRange = Math.min(5 * minRange, (this.maxX - this.minX) / 5, xAxis.minRange || MAX_VALUE);
- }
- if (yAxis && typeof yAxis.options.minRange === 'undefined') {
- yAxis.minRange = Math.min(5 * minRange, (this.maxY - this.minY) / 5, yAxis.minRange || MAX_VALUE);
- }
- }
- },
- // Define hasData function for non-cartesian series.
- // Returns true if the series has points at all.
- hasData: function () {
- return !!this.processedXData.length; // != 0
- },
- getExtremes: function () {
- // Get the actual value extremes for colors
- var _a = Series.prototype.getExtremes
- .call(this, this.valueData), dataMin = _a.dataMin, dataMax = _a.dataMax;
- // Recalculate box on updated data
- if (this.chart.hasRendered && this.isDirtyData) {
- this.getBox(this.options.data);
- }
- if (isNumber(dataMin)) {
- this.valueMin = dataMin;
- }
- if (isNumber(dataMax)) {
- this.valueMax = dataMax;
- }
- // Extremes for the mock Y axis
- return { dataMin: this.minY, dataMax: this.maxY };
- },
- // Translate the path, so it automatically fits into the plot area box
- translatePath: function (path) {
- var series = this, xAxis = series.xAxis, yAxis = series.yAxis, xMin = xAxis.min, xTransA = xAxis.transA, xMinPixelPadding = xAxis.minPixelPadding, yMin = yAxis.min, yTransA = yAxis.transA, yMinPixelPadding = yAxis.minPixelPadding, ret = []; // Preserve the original
- // Do the translation
- if (path) {
- path.forEach(function (seg) {
- if (seg[0] === 'M') {
- ret.push([
- 'M',
- (seg[1] - (xMin || 0)) * xTransA + xMinPixelPadding,
- (seg[2] - (yMin || 0)) * yTransA + yMinPixelPadding
- ]);
- }
- else if (seg[0] === 'L') {
- ret.push([
- 'L',
- (seg[1] - (xMin || 0)) * xTransA + xMinPixelPadding,
- (seg[2] - (yMin || 0)) * yTransA + yMinPixelPadding
- ]);
- }
- else if (seg[0] === 'C') {
- ret.push([
- 'C',
- (seg[1] - (xMin || 0)) * xTransA + xMinPixelPadding,
- (seg[2] - (yMin || 0)) * yTransA + yMinPixelPadding,
- (seg[3] - (xMin || 0)) * xTransA + xMinPixelPadding,
- (seg[4] - (yMin || 0)) * yTransA + yMinPixelPadding,
- (seg[5] - (xMin || 0)) * xTransA + xMinPixelPadding,
- (seg[6] - (yMin || 0)) * yTransA + yMinPixelPadding
- ]);
- }
- else if (seg[0] === 'Q') {
- ret.push([
- 'Q',
- (seg[1] - (xMin || 0)) * xTransA + xMinPixelPadding,
- (seg[2] - (yMin || 0)) * yTransA + yMinPixelPadding,
- (seg[3] - (xMin || 0)) * xTransA + xMinPixelPadding,
- (seg[4] - (yMin || 0)) * yTransA + yMinPixelPadding
- ]);
- }
- else if (seg[0] === 'Z') {
- ret.push(['Z']);
- }
- });
- }
- return ret;
- },
- // Extend setData to join in mapData. If the allAreas option is true,
- // all areas from the mapData are used, and those that don't correspond
- // to a data value are given null values.
- setData: function (data, redraw, animation, updatePoints) {
- var options = this.options, chartOptions = this.chart.options.chart, globalMapData = chartOptions && chartOptions.map, mapData = options.mapData, joinBy = this.joinBy, pointArrayMap = options.keys || this.pointArrayMap, dataUsed = [], mapMap = {}, mapPoint, mapTransforms = this.chart.mapTransforms, props, i;
- // Collect mapData from chart options if not defined on series
- if (!mapData && globalMapData) {
- mapData = typeof globalMapData === 'string' ?
- H.maps[globalMapData] :
- globalMapData;
- }
- // Pick up numeric values, add index
- // Convert Array point definitions to objects using pointArrayMap
- if (data) {
- data.forEach(function (val, i) {
- var ix = 0;
- if (isNumber(val)) {
- data[i] = {
- value: val
- };
- }
- else if (isArray(val)) {
- data[i] = {};
- // Automatically copy first item to hc-key if there is
- // an extra leading string
- if (!options.keys &&
- val.length > pointArrayMap.length &&
- typeof val[0] === 'string') {
- data[i]['hc-key'] = val[0];
- ++ix;
- }
- // Run through pointArrayMap and what's left of the
- // point data array in parallel, copying over the values
- for (var j = 0; j < pointArrayMap.length; ++j, ++ix) {
- if (pointArrayMap[j] &&
- typeof val[ix] !== 'undefined') {
- if (pointArrayMap[j].indexOf('.') > 0) {
- Point.prototype.setNestedProperty(data[i], val[ix], pointArrayMap[j]);
- }
- else {
- data[i][pointArrayMap[j]] =
- val[ix];
- }
- }
- }
- }
- if (joinBy && joinBy[0] === '_i') {
- data[i]._i = i;
- }
- });
- }
- this.getBox(data);
- // Pick up transform definitions for chart
- this.chart.mapTransforms = mapTransforms =
- chartOptions && chartOptions.mapTransforms ||
- mapData && mapData['hc-transform'] ||
- mapTransforms;
- // Cache cos/sin of transform rotation angle
- if (mapTransforms) {
- objectEach(mapTransforms, function (transform) {
- if (transform.rotation) {
- transform.cosAngle = Math.cos(transform.rotation);
- transform.sinAngle = Math.sin(transform.rotation);
- }
- });
- }
- if (mapData) {
- if (mapData.type === 'FeatureCollection') {
- this.mapTitle = mapData.title;
- mapData = H.geojson(mapData, this.type, this);
- }
- this.mapData = mapData;
- this.mapMap = {};
- for (i = 0; i < mapData.length; i++) {
- mapPoint = mapData[i];
- props = mapPoint.properties;
- mapPoint._i = i;
- // Copy the property over to root for faster access
- if (joinBy[0] && props && props[joinBy[0]]) {
- mapPoint[joinBy[0]] = props[joinBy[0]];
- }
- mapMap[mapPoint[joinBy[0]]] = mapPoint;
- }
- this.mapMap = mapMap;
- // Registered the point codes that actually hold data
- if (data && joinBy[1]) {
- var joinKey_1 = joinBy[1];
- data.forEach(function (pointOptions) {
- var mapKey = getNestedProperty(joinKey_1, pointOptions);
- if (mapMap[mapKey]) {
- dataUsed.push(mapMap[mapKey]);
- }
- });
- }
- if (options.allAreas) {
- this.getBox(mapData);
- data = data || [];
- // Registered the point codes that actually hold data
- if (joinBy[1]) {
- var joinKey_2 = joinBy[1];
- data.forEach(function (pointOptions) {
- dataUsed.push(getNestedProperty(joinKey_2, pointOptions));
- });
- }
- // Add those map points that don't correspond to data, which
- // will be drawn as null points
- dataUsed = ('|' + dataUsed.map(function (point) {
- return point && point[joinBy[0]];
- }).join('|') + '|'); // Faster than array.indexOf
- mapData.forEach(function (mapPoint) {
- if (!joinBy[0] ||
- dataUsed.indexOf('|' + mapPoint[joinBy[0]] + '|') === -1) {
- data.push(merge(mapPoint, { value: null }));
- // #5050 - adding all areas causes the update
- // optimization of setData to kick in, even though
- // the point order has changed
- updatePoints = false;
- }
- });
- }
- else {
- this.getBox(dataUsed); // Issue #4784
- }
- }
- Series.prototype.setData.call(this, data, redraw, animation, updatePoints);
- },
- // No graph for the map series
- drawGraph: noop,
- // We need the points' bounding boxes in order to draw the data labels,
- // so we skip it now and call it from drawPoints instead.
- drawDataLabels: noop,
- // Allow a quick redraw by just translating the area group. Used for
- // zooming and panning in capable browsers.
- doFullTranslate: function () {
- return (this.isDirtyData ||
- this.chart.isResizing ||
- this.chart.renderer.isVML ||
- !this.baseTrans);
- },
- // Add the path option for data points. Find the max value for color
- // calculation.
- translate: function () {
- var series = this, xAxis = series.xAxis, yAxis = series.yAxis, doFullTranslate = series.doFullTranslate();
- series.generatePoints();
- series.data.forEach(function (point) {
- // Record the middle point (loosely based on centroid),
- // determined by the middleX and middleY options.
- if (isNumber(point._midX) && isNumber(point._midY)) {
- point.plotX = xAxis.toPixels(point._midX, true);
- point.plotY = yAxis.toPixels(point._midY, true);
- }
- if (doFullTranslate) {
- point.shapeType = 'path';
- point.shapeArgs = {
- d: series.translatePath(point.path)
- };
- }
- });
- fireEvent(series, 'afterTranslate');
- },
- // Get presentational attributes. In the maps series this runs in both
- // styled and non-styled mode, because colors hold data when a colorAxis
- // is used.
- pointAttribs: function (point, state) {
- var attr = point.series.chart.styledMode ?
- this.colorAttribs(point) :
- seriesTypes.column.prototype.pointAttribs.call(this, point, state);
- // Set the stroke-width on the group element and let all point
- // graphics inherit. That way we don't have to iterate over all
- // points to update the stroke-width on zooming.
- attr['stroke-width'] = pick(point.options[(this.pointAttrToOptions &&
- this.pointAttrToOptions['stroke-width']) || 'borderWidth'], 'inherit');
- return attr;
- },
- // Use the drawPoints method of column, that is able to handle simple
- // shapeArgs. Extend it by assigning the tooltip position.
- drawPoints: function () {
- var series = this, xAxis = series.xAxis, yAxis = series.yAxis, group = series.group, chart = series.chart, renderer = chart.renderer, scaleX, scaleY, translateX, translateY, baseTrans = this.baseTrans, transformGroup, startTranslateX, startTranslateY, startScaleX, startScaleY;
- // Set a group that handles transform during zooming and panning in
- // order to preserve clipping on series.group
- if (!series.transformGroup) {
- series.transformGroup = renderer.g()
- .attr({
- scaleX: 1,
- scaleY: 1
- })
- .add(group);
- series.transformGroup.survive = true;
- }
- // Draw the shapes again
- if (series.doFullTranslate()) {
- // Individual point actions.
- if (chart.hasRendered && !chart.styledMode) {
- series.points.forEach(function (point) {
- // Restore state color on update/redraw (#3529)
- if (point.shapeArgs) {
- point.shapeArgs.fill = series.pointAttribs(point, point.state).fill;
- }
- });
- }
- // Draw them in transformGroup
- series.group = series.transformGroup;
- seriesTypes.column.prototype.drawPoints.apply(series);
- series.group = group; // Reset
- // Add class names
- series.points.forEach(function (point) {
- if (point.graphic) {
- var className = '';
- if (point.name) {
- className +=
- 'highcharts-name-' +
- point.name.replace(/ /g, '-').toLowerCase();
- }
- if (point.properties &&
- point.properties['hc-key']) {
- className +=
- ' highcharts-key-' +
- point.properties['hc-key'].toLowerCase();
- }
- if (className) {
- point.graphic.addClass(className);
- }
- // In styled mode, apply point colors by CSS
- if (chart.styledMode) {
- point.graphic.css(series.pointAttribs(point, point.selected && 'select' || void 0));
- }
- }
- });
- // Set the base for later scale-zooming. The originX and originY
- // properties are the axis values in the plot area's upper left
- // corner.
- this.baseTrans = {
- originX: (xAxis.min -
- xAxis.minPixelPadding / xAxis.transA),
- originY: (yAxis.min -
- yAxis.minPixelPadding / yAxis.transA +
- (yAxis.reversed ? 0 : yAxis.len / yAxis.transA)),
- transAX: xAxis.transA,
- transAY: yAxis.transA
- };
- // Reset transformation in case we're doing a full translate
- // (#3789)
- this.transformGroup.animate({
- translateX: 0,
- translateY: 0,
- scaleX: 1,
- scaleY: 1
- });
- // Just update the scale and transform for better performance
- }
- else {
- scaleX = xAxis.transA / baseTrans.transAX;
- scaleY = yAxis.transA / baseTrans.transAY;
- translateX = xAxis.toPixels(baseTrans.originX, true);
- translateY = yAxis.toPixels(baseTrans.originY, true);
- // Handle rounding errors in normal view (#3789)
- if (scaleX > 0.99 &&
- scaleX < 1.01 &&
- scaleY > 0.99 &&
- scaleY < 1.01) {
- scaleX = 1;
- scaleY = 1;
- translateX = Math.round(translateX);
- translateY = Math.round(translateY);
- }
- /* Animate or move to the new zoom level. In order to prevent
- flickering as the different transform components are set out
- of sync (#5991), we run a fake animator attribute and set
- scale and translation synchronously in the same step.
- A possible improvement to the API would be to handle this in
- the renderer or animation engine itself, to ensure that when
- we are animating multiple properties, we make sure that each
- step for each property is performed in the same step. Also,
- for symbols and for transform properties, it should induce a
- single updateTransform and symbolAttr call. */
- transformGroup = this.transformGroup;
- if (chart.renderer.globalAnimation) {
- startTranslateX = transformGroup.attr('translateX');
- startTranslateY = transformGroup.attr('translateY');
- startScaleX = transformGroup.attr('scaleX');
- startScaleY = transformGroup.attr('scaleY');
- transformGroup
- .attr({ animator: 0 })
- .animate({
- animator: 1
- }, {
- step: function (now, fx) {
- transformGroup.attr({
- translateX: (startTranslateX +
- (translateX - startTranslateX) * fx.pos),
- translateY: (startTranslateY +
- (translateY - startTranslateY) * fx.pos),
- scaleX: (startScaleX +
- (scaleX - startScaleX) *
- fx.pos),
- scaleY: (startScaleY +
- (scaleY - startScaleY) * fx.pos)
- });
- }
- });
- // When dragging, animation is off.
- }
- else {
- transformGroup.attr({
- translateX: translateX,
- translateY: translateY,
- scaleX: scaleX,
- scaleY: scaleY
- });
- }
- }
- /* Set the stroke-width directly on the group element so the
- children inherit it. We need to use setAttribute directly,
- because the stroke-widthSetter method expects a stroke color also
- to be set. */
- if (!chart.styledMode) {
- group.element.setAttribute('stroke-width', (pick(series.options[(series.pointAttrToOptions &&
- series.pointAttrToOptions['stroke-width']) || 'borderWidth'], 1 // Styled mode
- ) / (scaleX || 1)));
- }
- this.drawMapDataLabels();
- },
- // Draw the data labels. Special for maps is the time that the data
- // labels are drawn (after points), and the clipping of the
- // dataLabelsGroup.
- drawMapDataLabels: function () {
- Series.prototype.drawDataLabels.call(this);
- if (this.dataLabelsGroup) {
- this.dataLabelsGroup.clip(this.chart.clipRect);
- }
- },
- // Override render to throw in an async call in IE8. Otherwise it chokes
- // on the US counties demo.
- render: function () {
- var series = this, render = Series.prototype.render;
- // Give IE8 some time to breathe.
- if (series.chart.renderer.isVML && series.data.length > 3000) {
- setTimeout(function () {
- render.call(series);
- });
- }
- else {
- render.call(series);
- }
- },
- // The initial animation for the map series. By default, animation is
- // disabled. Animation of map shapes is not at all supported in VML
- // browsers.
- animate: function (init) {
- var chart = this.chart, animation = this.options.animation, group = this.group, xAxis = this.xAxis, yAxis = this.yAxis, left = xAxis.pos, top = yAxis.pos;
- if (chart.renderer.isSVG) {
- if (animation === true) {
- animation = {
- duration: 1000
- };
- }
- // Initialize the animation
- if (init) {
- // Scale down the group and place it in the center
- group.attr({
- translateX: left + xAxis.len / 2,
- translateY: top + yAxis.len / 2,
- scaleX: 0.001,
- scaleY: 0.001
- });
- // Run the animation
- }
- else {
- group.animate({
- translateX: left,
- translateY: top,
- scaleX: 1,
- scaleY: 1
- }, animation);
- }
- }
- },
- // Animate in the new series from the clicked point in the old series.
- // Depends on the drilldown.js module
- animateDrilldown: function (init) {
- var toBox = this.chart.plotBox, level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1], fromBox = level.bBox, animationOptions = this.chart.options.drilldown.animation, scale;
- if (!init) {
- scale = Math.min(fromBox.width / toBox.width, fromBox.height / toBox.height);
- level.shapeArgs = {
- scaleX: scale,
- scaleY: scale,
- translateX: fromBox.x,
- translateY: fromBox.y
- };
- this.points.forEach(function (point) {
- if (point.graphic) {
- point.graphic
- .attr(level.shapeArgs)
- .animate({
- scaleX: 1,
- scaleY: 1,
- translateX: 0,
- translateY: 0
- }, animationOptions);
- }
- });
- }
- },
- drawLegendSymbol: LegendSymbolMixin.drawRectangle,
- // When drilling up, pull out the individual point graphics from the
- // lower series and animate them into the origin point in the upper
- // series.
- animateDrillupFrom: function (level) {
- seriesTypes.column.prototype
- .animateDrillupFrom.call(this, level);
- },
- // When drilling up, keep the upper series invisible until the lower
- // series has moved into place
- animateDrillupTo: function (init) {
- seriesTypes.column.prototype
- .animateDrillupTo.call(this, init);
- }
- // Point class
- }), extend({
- // Extend the Point object to split paths
- applyOptions: function (options, x) {
- var series = this.series, point = Point.prototype.applyOptions.call(this, options, x), joinBy = series.joinBy, mapPoint;
- if (series.mapData && series.mapMap) {
- var joinKey = joinBy[1];
- var mapKey = Point.prototype.getNestedProperty.call(point, joinKey);
- mapPoint = typeof mapKey !== 'undefined' &&
- series.mapMap[mapKey];
- if (mapPoint) {
- // This applies only to bubbles
- if (series.xyFromShape) {
- point.x = mapPoint._midX;
- point.y = mapPoint._midY;
- }
- extend(point, mapPoint); // copy over properties
- }
- else {
- point.value = point.value || null;
- }
- }
- return point;
- },
- // Stop the fade-out
- onMouseOver: function (e) {
- U.clearTimeout(this.colorInterval);
- if (this.value !== null || this.series.options.nullInteraction) {
- Point.prototype.onMouseOver.call(this, e);
- }
- else {
- // #3401 Tooltip doesn't hide when hovering over null points
- this.series.onMouseOut(e);
- }
- },
- // eslint-disable-next-line valid-jsdoc
- /**
- * Highmaps only. Zoom in on the point using the global animation.
- *
- * @sample maps/members/point-zoomto/
- * Zoom to points from butons
- *
- * @requires modules/map
- *
- * @function Highcharts.Point#zoomTo
- */
- zoomTo: function () {
- var point = this, series = point.series;
- series.xAxis.setExtremes(point._minX, point._maxX, false);
- series.yAxis.setExtremes(point._minY, point._maxY, false);
- series.chart.redraw();
- }
- }, colorMapPointMixin));
- /**
- * A map data object containing a `path` definition and optionally additional
- * properties to join in the data as per the `joinBy` option.
- *
- * @sample maps/demo/category-map/
- * Map data and joinBy
- *
- * @type {Array<Highcharts.SeriesMapDataOptions>|*}
- * @product highmaps
- * @apioption series.mapData
- */
- /**
- * A `map` series. If the [type](#series.map.type) option is not specified, it
- * is inherited from [chart.type](#chart.type).
- *
- * @extends series,plotOptions.map
- * @excluding dataParser, dataURL, marker
- * @product highmaps
- * @apioption series.map
- */
- /**
- * An array of data points for the series. For the `map` series type, points can
- * be given in the following ways:
- *
- * 1. An array of numerical values. In this case, the numerical values will be
- * interpreted as `value` options. Example:
- * ```js
- * data: [0, 5, 3, 5]
- * ```
- *
- * 2. An array of arrays with 2 values. In this case, the values correspond to
- * `[hc-key, value]`. Example:
- * ```js
- * data: [
- * ['us-ny', 0],
- * ['us-mi', 5],
- * ['us-tx', 3],
- * ['us-ak', 5]
- * ]
- * ```
- *
- * 3. An array of objects with named values. The following snippet shows only a
- * few settings, see the complete options set below. If the total number of
- * data points exceeds the series'
- * [turboThreshold](#series.map.turboThreshold),
- * this option is not available.
- * ```js
- * data: [{
- * value: 6,
- * name: "Point2",
- * color: "#00FF00"
- * }, {
- * value: 6,
- * name: "Point1",
- * color: "#FF00FF"
- * }]
- * ```
- *
- * @type {Array<number|Array<string,(number|null)>|null|*>}
- * @product highmaps
- * @apioption series.map.data
- */
- /**
- * Individual color for the point. By default the color is either used
- * to denote the value, or pulled from the global `colors` array.
- *
- * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
- * @product highmaps
- * @apioption series.map.data.color
- */
- /**
- * Individual data label for each point. The options are the same as
- * the ones for [plotOptions.series.dataLabels](
- * #plotOptions.series.dataLabels).
- *
- * @sample maps/series/data-datalabels/
- * Disable data labels for individual areas
- *
- * @type {Highcharts.DataLabelsOptions}
- * @product highmaps
- * @apioption series.map.data.dataLabels
- */
- /**
- * The `id` of a series in the [drilldown.series](#drilldown.series)
- * array to use for a drilldown for this point.
- *
- * @sample maps/demo/map-drilldown/
- * Basic drilldown
- *
- * @type {string}
- * @product highmaps
- * @apioption series.map.data.drilldown
- */
- /**
- * An id for the point. This can be used after render time to get a
- * pointer to the point object through `chart.get()`.
- *
- * @sample maps/series/data-id/
- * Highlight a point by id
- *
- * @type {string}
- * @product highmaps
- * @apioption series.map.data.id
- */
- /**
- * When data labels are laid out on a map, Highmaps runs a simplified
- * algorithm to detect collision. When two labels collide, the one with
- * the lowest rank is hidden. By default the rank is computed from the
- * area.
- *
- * @type {number}
- * @product highmaps
- * @apioption series.map.data.labelrank
- */
- /**
- * The relative mid point of an area, used to place the data label.
- * Ranges from 0 to 1\. When `mapData` is used, middleX can be defined
- * there.
- *
- * @type {number}
- * @default 0.5
- * @product highmaps
- * @apioption series.map.data.middleX
- */
- /**
- * The relative mid point of an area, used to place the data label.
- * Ranges from 0 to 1\. When `mapData` is used, middleY can be defined
- * there.
- *
- * @type {number}
- * @default 0.5
- * @product highmaps
- * @apioption series.map.data.middleY
- */
- /**
- * The name of the point as shown in the legend, tooltip, dataLabel
- * etc.
- *
- * @sample maps/series/data-datalabels/
- * Point names
- *
- * @type {string}
- * @product highmaps
- * @apioption series.map.data.name
- */
- /**
- * For map and mapline series types, the SVG path for the shape. For
- * compatibily with old IE, not all SVG path definitions are supported,
- * but M, L and C operators are safe.
- *
- * To achieve a better separation between the structure and the data,
- * it is recommended to use `mapData` to define that paths instead
- * of defining them on the data points themselves.
- *
- * @sample maps/series/data-path/
- * Paths defined in data
- *
- * @type {string}
- * @product highmaps
- * @apioption series.map.data.path
- */
- /**
- * The numeric value of the data point.
- *
- * @type {number|null}
- * @product highmaps
- * @apioption series.map.data.value
- */
- /**
- * Individual point events
- *
- * @extends plotOptions.series.point.events
- * @product highmaps
- * @apioption series.map.data.events
- */
- ''; // adds doclets above to the transpiled file
|