/* *
*
* (c) 2010-2021 Torstein Honsi
*
* License: www.highcharts.com/license
*
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
*
* */
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import A from '../../Core/Animation/AnimationUtilities.js';
var animObject = A.animObject;
import ColorMapComposition from '../ColorMapComposition.js';
import CU from '../CenteredUtilities.js';
import H from '../../Core/Globals.js';
var noop = H.noop;
import LegendSymbol from '../../Core/Legend/LegendSymbol.js';
import MapChart from '../../Core/Chart/MapChart.js';
var splitPath = MapChart.splitPath;
import MapPoint from './MapPoint.js';
import MapView from '../../Maps/MapView.js';
import Series from '../../Core/Series/Series.js';
import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
var
// indirect dependency to keep product size low
_a = SeriesRegistry.seriesTypes, ColumnSeries = _a.column, ScatterSeries = _a.scatter;
import SVGRenderer from '../../Core/Renderer/SVG/SVGRenderer.js';
import U from '../../Core/Utilities.js';
var extend = U.extend, find = U.find, fireEvent = U.fireEvent, getNestedProperty = U.getNestedProperty, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, merge = U.merge, objectEach = U.objectEach, pick = U.pick, splat = U.splat;
/* *
*
* Class
*
* */
/**
* @private
* @class
* @name Highcharts.seriesTypes.map
*
* @augments Highcharts.Series
*/
var MapSeries = /** @class */ (function (_super) {
__extends(MapSeries, _super);
function MapSeries() {
/* *
*
* Static Properties
*
* */
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.chart = void 0;
_this.data = void 0;
_this.group = void 0;
_this.joinBy = void 0;
_this.options = void 0;
_this.points = void 0;
_this.processedData = [];
return _this;
/* eslint-enable valid-jsdoc */
}
/* *
*
* Functions
*
* */
/* eslint-disable valid-jsdoc */
/**
* The initial animation for the map series. By default, animation is
* disabled. Animation of map shapes is not at all supported in VML
* browsers.
* @private
*/
MapSeries.prototype.animate = function (init) {
var _a = this, chart = _a.chart, group = _a.group, animation = animObject(this.options.animation);
if (chart.renderer.isSVG) {
// Initialize the animation
if (init) {
// Scale down the group and place it in the center
group.attr({
translateX: chart.plotLeft + chart.plotWidth / 2,
translateY: chart.plotTop + chart.plotHeight / 2,
scaleX: 0.001,
scaleY: 0.001
});
// Run the animation
}
else {
group.animate({
translateX: chart.plotLeft,
translateY: chart.plotTop,
scaleX: 1,
scaleY: 1
}, animation);
}
}
};
/**
* Animate in the new series. Depends on the drilldown.js module.
* @private
*/
MapSeries.prototype.animateDrilldown = function (init) {
var chart = this.chart, group = this.group;
if (chart.renderer.isSVG) {
// Initialize the animation
if (init) {
// Scale down the group and place it in the center. This is a
// regression from <= v9.2, when it animated from the old point.
group.attr({
translateX: chart.plotLeft + chart.plotWidth / 2,
translateY: chart.plotTop + chart.plotHeight / 2,
scaleX: 0.1,
scaleY: 0.1,
opacity: 0.01
});
// Run the animation
}
else {
group.animate({
translateX: chart.plotLeft,
translateY: chart.plotTop,
scaleX: 1,
scaleY: 1,
opacity: 1
}, this.chart.options.drilldown.animation);
if (chart.drilldown) {
chart.drilldown.fadeInGroup(this.dataLabelsGroup);
}
}
}
};
/**
* When drilling up, pull out the individual point graphics from the lower
* series and animate them into the origin point in the upper series.
* @private
*/
MapSeries.prototype.animateDrillupFrom = function () {
var chart = this.chart;
if (chart.renderer.isSVG) {
this.group.animate({
translateX: chart.plotLeft + chart.plotWidth / 2,
translateY: chart.plotTop + chart.plotHeight / 2,
scaleX: 0.1,
scaleY: 0.1,
opacity: 0.01
});
}
};
/**
* When drilling up, keep the upper series invisible until the lower series
* has moved into place.
* @private
*/
MapSeries.prototype.animateDrillupTo = function (init) {
ColumnSeries.prototype.animateDrillupTo.call(this, init);
};
MapSeries.prototype.clearBounds = function () {
this.points.forEach(function (point) {
delete point.bounds;
delete point.insetIndex;
delete point.projectedPath;
});
delete this.bounds;
};
/**
* Allow a quick redraw by just translating the area group. Used for zooming
* and panning in capable browsers.
* @private
*/
MapSeries.prototype.doFullTranslate = function () {
return Boolean(this.isDirtyData ||
this.chart.isResizing ||
this.chart.renderer.isVML ||
!this.hasRendered);
};
/**
* Draw the data labels. Special for maps is the time that the data labels
* are drawn (after points), and the clipping of the dataLabelsGroup.
* @private
*/
MapSeries.prototype.drawMapDataLabels = function () {
Series.prototype.drawDataLabels.call(this);
if (this.dataLabelsGroup) {
this.dataLabelsGroup.clip(this.chart.clipRect);
}
};
/**
* Use the drawPoints method of column, that is able to handle simple
* shapeArgs. Extend it by assigning the tooltip position.
* @private
*/
MapSeries.prototype.drawPoints = function () {
var _this = this;
var series = this, _a = this, chart = _a.chart, group = _a.group, _b = _a.transformGroups, transformGroups = _b === void 0 ? [] : _b, mapView = chart.mapView, renderer = chart.renderer;
if (!mapView) {
return;
}
// Set groups that handle transform during zooming and panning in order
// to preserve clipping on series.group
this.transformGroups = transformGroups;
if (!transformGroups[0]) {
transformGroups[0] = renderer.g().add(group);
}
mapView.insets.forEach(function (inset, i) {
if (!transformGroups[i + 1]) {
transformGroups.push(renderer.g().add(group));
}
});
// Draw the shapes again
if (this.doFullTranslate()) {
// Individual point actions.
this.points.forEach(function (point) {
var graphic = point.graphic, shapeArgs = point.shapeArgs;
// Points should be added in the corresponding transform group
point.group = transformGroups[typeof point.insetIndex === 'number' ?
point.insetIndex + 1 :
0];
// When the point has been moved between insets after
// MapView.update
if (graphic && graphic.parentGroup !== point.group) {
graphic.add(point.group);
}
// Restore state color on update/redraw (#3529)
if (shapeArgs && chart.hasRendered && !chart.styledMode) {
shapeArgs.fill = _this.pointAttribs(point, point.state).fill;
}
});
// Draw the points
ColumnSeries.prototype.drawPoints.apply(this);
// Add class names
this.points.forEach(function (point) {
var graphic = point.graphic;
if (graphic) {
var animate_1 = graphic.animate;
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'].toString().toLowerCase();
}
if (className) {
graphic.addClass(className);
}
// In styled mode, apply point colors by CSS
if (chart.styledMode) {
graphic.css(_this.pointAttribs(point, point.selected && 'select' || void 0));
}
graphic.animate = function (params, options, complete) {
var switchBack = false;
// When strokeWidth is animating
if (params['stroke-width']) {
var strokeWidth = pick(series.getStrokeWidth(series.options), 1 // Styled mode
), inheritedStrokeWidth = (strokeWidth /
(chart.mapView &&
chart.mapView.getScale() ||
1));
// For animating from inherit,
// .attr() reads the property as the starting point
if (graphic['stroke-width'] === 'inherit') {
graphic['stroke-width'] = inheritedStrokeWidth;
}
// For animating to inherit
if (params['stroke-width'] === 'inherit') {
params['stroke-width'] = inheritedStrokeWidth;
switchBack = true;
}
}
var ret = animate_1.call(graphic, params, options, switchBack ? function () {
// Switch back to "inherit" for zooming
// to work with the existing logic + complete
graphic.attr({
'stroke-width': 'inherit'
});
// Proceed
if (complete) {
complete.apply(this, arguments);
}
} : complete);
return ret;
};
}
});
}
// Apply the SVG transform
transformGroups.forEach(function (transformGroup, i) {
var view = i === 0 ? mapView : mapView.insets[i - 1], svgTransform = view.getSVGTransform(), strokeWidth = pick(_this.getStrokeWidth(_this.options), 1 // Styled mode
);
/*
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.
*/
var scale = svgTransform.scaleX;
var flipFactor = svgTransform.scaleY > 0 ? 1 : -1;
if (renderer.globalAnimation && chart.hasRendered) {
var startTranslateX_1 = Number(transformGroup.attr('translateX'));
var startTranslateY_1 = Number(transformGroup.attr('translateY'));
var startScale_1 = Number(transformGroup.attr('scaleX'));
var step = function (now, fx) {
var scaleStep = startScale_1 +
(scale - startScale_1) * fx.pos;
transformGroup.attr({
translateX: (startTranslateX_1 + (svgTransform.translateX - startTranslateX_1) * fx.pos),
translateY: (startTranslateY_1 + (svgTransform.translateY - startTranslateY_1) * fx.pos),
scaleX: scaleStep,
scaleY: scaleStep * flipFactor
});
transformGroup.element.setAttribute('stroke-width', strokeWidth / scaleStep);
};
transformGroup
.attr({ animator: 0 })
.animate({ animator: 1 }, { step: step });
// When dragging or first rendering, animation is off
}
else {
transformGroup.attr(svgTransform);
// 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.
transformGroup.element.setAttribute('stroke-width', strokeWidth / scale);
}
});
this.drawMapDataLabels();
};
/**
* Get the bounding box of all paths in the map combined.
*
*/
MapSeries.prototype.getProjectedBounds = function () {
if (!this.bounds && this.chart.mapView) {
var _a = this.chart.mapView, insets_1 = _a.insets, projection_1 = _a.projection, allBounds_1 = [];
// Find the bounding box of each point
(this.points || []).forEach(function (point) {
if (point.path || point.geometry) {
// @todo Try to puth these two conversions in
// MapPoint.applyOptions
if (typeof point.path === 'string') {
point.path = splitPath(point.path);
// Legacy one-dimensional array
}
else if (isArray(point.path) &&
point.path[0] === 'M') {
point.path = SVGRenderer.prototype.pathToSegments(point.path);
}
// The first time a map point is used, analyze its box
if (!point.bounds) {
var bounds = point.getProjectedBounds(projection_1);
if (bounds) {
point.labelrank = pick(point.labelrank,
// Bigger shape, higher rank
((bounds.x2 - bounds.x1) *
(bounds.y2 - bounds.y1)));
var midX_1 = bounds.midX, midY_1 = bounds.midY;
if (insets_1 && isNumber(midX_1) && isNumber(midY_1)) {
var inset = find(insets_1, function (inset) { return inset.isInside({
x: midX_1, y: midY_1
}); });
if (inset) {
// Project again, but with the inset
// projection
delete point.projectedPath;
bounds = point.getProjectedBounds(inset.projection);
if (bounds) {
inset.allBounds.push(bounds);
}
point.insetIndex = insets_1.indexOf(inset);
}
}
point.bounds = bounds;
}
}
if (point.bounds && point.insetIndex === void 0) {
allBounds_1.push(point.bounds);
}
}
});
this.bounds = MapView.compositeBounds(allBounds_1);
}
return this.bounds;
};
/**
* Return the stroke-width either from a series options or point options
* object. This function is used by both the map series where the
* `borderWidth` sets the stroke-width, and the mapline series where the
* `lineWidth` sets the stroke-width.
* @private
*/
MapSeries.prototype.getStrokeWidth = function (options) {
var pointAttrToOptions = this.pointAttrToOptions;
return options[pointAttrToOptions &&
pointAttrToOptions['stroke-width'] || 'borderWidth'];
};
/**
* Define hasData function for non-cartesian series. Returns true if the
* series has points at all.
* @private
*/
MapSeries.prototype.hasData = function () {
return !!this.processedXData.length; // != 0
};
/**
* 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.
* @private
*/
MapSeries.prototype.pointAttribs = function (point, state) {
var _a = point.series.chart, mapView = _a.mapView, styledMode = _a.styledMode;
var attr = styledMode ?
this.colorAttribs(point) :
ColumnSeries.prototype.pointAttribs.call(this, point, state);
// Individual stroke width
var pointStrokeWidth = this.getStrokeWidth(point.options);
// Handle state specific border or line width
if (state) {
var stateOptions = merge(this.options.states[state], point.options.states &&
point.options.states[state] ||
{});
pointStrokeWidth = this.getStrokeWidth(stateOptions);
}
if (pointStrokeWidth && mapView) {
pointStrokeWidth /= mapView.getScale();
}
// In order for dash style to avoid being scaled, set the transformed
// stroke width on the item
var seriesStrokeWidth = this.getStrokeWidth(this.options);
if (attr.dashstyle &&
mapView &&
isNumber(seriesStrokeWidth)) {
pointStrokeWidth = seriesStrokeWidth / mapView.getScale();
}
attr['stroke-width'] = pick(pointStrokeWidth,
// By default 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.
'inherit');
return attr;
};
/**
* @private
*/
MapSeries.prototype.updateData = function () {
// #16782
if (this.processedData) {
return false;
}
return _super.prototype.updateData.apply(this, arguments);
};
/**
* Extend setData to call processData and generatePoints immediately.
* @private
*/
MapSeries.prototype.setData = function (data, redraw, animation, updatePoints) {
if (redraw === void 0) { redraw = true; }
delete this.bounds;
_super.prototype.setData.call(this, data, false, void 0, updatePoints);
this.processData();
this.generatePoints();
if (redraw) {
this.chart.redraw(animation);
}
};
/**
* Extend processData 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. The results are stored in
* `processedData` in order to avoid mutating `data`.
* @private
*/
MapSeries.prototype.processData = function () {
var options = this.options, data = options.data, chartOptions = this.chart.options.chart, joinBy = this.joinBy, pointArrayMap = options.keys || this.pointArrayMap, dataUsed = [], mapMap = {};
var mapView = this.chart.mapView, mapDataObject = mapView && (
// Get map either from series or global
isObject(options.mapData, true) ?
mapView.getGeoMap(options.mapData) : mapView.geoMap), mapTransforms = this.chart.mapTransforms, mapPoint, props, i;
// Pick up transform definitions for chart
this.chart.mapTransforms = mapTransforms =
chartOptions.mapTransforms ||
mapDataObject && mapDataObject['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);
}
});
}
var mapData;
if (isArray(options.mapData)) {
mapData = options.mapData;
}
else if (mapDataObject && mapDataObject.type === 'FeatureCollection') {
this.mapTitle = mapDataObject.title;
mapData = H.geojson(mapDataObject, this.type, this);
}
// Reset processedData
this.processedData = [];
var processedData = this.processedData;
// 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)) {
processedData[i] = {
value: val
};
}
else if (isArray(val)) {
processedData[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') {
processedData[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) {
MapPoint.prototype.setNestedProperty(processedData[i], val[ix], pointArrayMap[j]);
}
else {
processedData[i][pointArrayMap[j]] =
val[ix];
}
}
}
}
else {
processedData[i] = data[i];
}
if (joinBy && joinBy[0] === '_i') {
processedData[i]._i = i;
}
});
}
if (mapData) {
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 (joinBy[1]) {
var joinKey_1 = joinBy[1];
processedData.forEach(function (pointOptions) {
var mapKey = getNestedProperty(joinKey_1, pointOptions);
if (mapMap[mapKey]) {
dataUsed.push(mapMap[mapKey]);
}
});
}
if (options.allAreas) {
// Register the point codes that actually hold data
if (joinBy[1]) {
var joinKey_2 = joinBy[1];
processedData.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. Searching a string is faster
// than Array.indexOf
var dataUsedString_1 = ('|' +
dataUsed
.map(function (point) {
return point && point[joinBy[0]];
})
.join('|') +
'|');
mapData.forEach(function (mapPoint) {
if (!joinBy[0] ||
dataUsedString_1.indexOf('|' + mapPoint[joinBy[0]] + '|') === -1) {
processedData.push(merge(mapPoint, { value: null }));
}
});
}
}
// The processedXData array is used by general chart logic for checking
// data length in various scanarios
this.processedXData = new Array(processedData.length);
return void 0;
};
/**
* Extend setOptions by picking up the joinBy option and applying it to a
* series property.
* @private
*/
MapSeries.prototype.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;
};
/**
* Add the path option for data points. Find the max value for color
* calculation.
* @private
*/
MapSeries.prototype.translate = function () {
var series = this, doFullTranslate = series.doFullTranslate(), mapView = this.chart.mapView, projection = mapView && mapView.projection;
// Recalculate box on updated data
if (this.chart.hasRendered && (this.isDirtyData || !this.hasRendered)) {
this.processData();
this.generatePoints();
delete this.bounds;
if (mapView &&
!mapView.userOptions.center &&
!isNumber(mapView.userOptions.zoom)) {
// Not only recalculate bounds but also fit view
mapView.fitToBounds(void 0, void 0, false); // #17012
}
else {
// If center and zoom is defined in user options, get bounds but
// don't change view
this.getProjectedBounds();
}
}
if (mapView) {
var mainSvgTransform_1 = mapView.getSVGTransform();
series.points.forEach(function (point) {
var svgTransform = (isNumber(point.insetIndex) &&
mapView.insets[point.insetIndex].getSVGTransform()) || mainSvgTransform_1;
// Record the middle point (loosely based on centroid),
// determined by the middleX and middleY options.
if (svgTransform &&
point.bounds &&
isNumber(point.bounds.midX) &&
isNumber(point.bounds.midY)) {
point.plotX = point.bounds.midX * svgTransform.scaleX +
svgTransform.translateX;
point.plotY = point.bounds.midY * svgTransform.scaleY +
svgTransform.translateY;
}
if (doFullTranslate) {
point.shapeType = 'path';
point.shapeArgs = {
d: MapPoint.getProjectedPath(point, projection)
};
}
});
}
fireEvent(series, 'afterTranslate');
};
/**
* 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
*
* @private
*/
MapSeries.defaultOptions = merge(ScatterSeries.defaultOptions, {
/**
* Whether the MapView takes this series into account when computing the
* default zoom and center of the map.
*
* @sample maps/series/affectsmapview/
* US map with world map backdrop
*
* @since 10.0.0
*
* @private
*/
affectsMapView: true,
animation: false,
dataLabels: {
crop: false,
formatter: function () {
var numberFormatter = this.series.chart.numberFormatter;
var value = this.point.value;
return isNumber(value) ? numberFormatter(value, -1) : '';
},
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" /* Palette.neutralColor3 */,
/**
* 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}
'
},
/**
* @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" /* Palette.neutralColor20 */,
/**
* 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,
/**
* @type {string}
* @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}
* @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" /* Palette.neutralColor20 */
},
inactive: {
opacity: 1
}
}
});
return MapSeries;
}(ScatterSeries));
extend(MapSeries.prototype, {
type: 'map',
axisTypes: ColorMapComposition.seriesMembers.axisTypes,
colorAttribs: ColorMapComposition.seriesMembers.colorAttribs,
colorKey: ColorMapComposition.seriesMembers.colorKey,
// When tooltip is not shared, this series (and derivatives) requires
// direct touch/hover. KD-tree does not apply.
directTouch: true,
// 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,
// No graph for the map series
drawGraph: noop,
drawLegendSymbol: LegendSymbol.drawRectangle,
forceDL: true,
getCenter: CU.getCenter,
getExtremesFromAll: true,
getSymbol: noop,
isCartesian: false,
parallelArrays: ColorMapComposition.seriesMembers.parallelArrays,
pointArrayMap: ColorMapComposition.seriesMembers.pointArrayMap,
pointClass: MapPoint,
// X axis and Y axis must have same translation slope
preserveAspectRatio: true,
searchPoint: noop,
trackerGroups: ColorMapComposition.seriesMembers.trackerGroups,
// Get axis extremes from paths, not values
useMapGeometry: true
});
ColorMapComposition.compose(MapSeries);
SeriesRegistry.registerSeriesType('map', MapSeries);
/* *
*
* Default Export
*
* */
export default MapSeries;
/* *
*
* API Options
*
* */
/**
* An array of objects containing a `geometry` or `path` definition and
* optionally additional properties to join in the `data` as per the `joinBy`
* option. GeoJSON and TopoJSON structures can also be passed directly into
* `mapData`.
*
* @sample maps/demo/category-map/
* Map data and joinBy
* @sample maps/series/mapdata-multiple/
* Multiple map sources
*
* @type {Array|Highcharts.GeoJSON|Highcharts.TopoJSON}
* @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|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
*/
/**
* For map and mapline series types, the geometry of a point.
*
* To achieve a better separation between the structure and the data,
* it is recommended to use `mapData` to define the geometry instead
* of defining it on the data points themselves.
*
* The geometry object is compatible to that of a `feature` in geoJSON, so
* features of geoJSON can be passed directly into the `data`, optionally
* after first filtering and processing it.
*
* @sample maps/series/data-geometry/
* Geometry defined in data
*
* @type {Object}
* @since 9.3.0
* @product highmaps
* @apioption series.map.data.geometry
*/
/**
* The geometry type. Can be one of `LineString`, `Polygon`, `MultiLineString`
* or `MultiPolygon`.
*
* @declare Highcharts.MapGeometryTypeValue
* @type {string}
* @since 9.3.0
* @product highmaps
* @validvalue ["LineString", "Polygon", "MultiLineString", "MultiPolygon"]
* @apioption series.map.data.geometry.type
*/
/**
* The geometry coordinates in terms of arrays of `[longitude, latitude]`, or
* a two dimensional array of the same. The dimensionality must comply with the
* `type`.
*
* @type {Array|Array>}
* @since 9.3.0
* @product highmaps
* @apioption series.map.data.geometry.coordinates
*/
/**
* 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.
*
* For providing true geographical shapes based on longitude and latitude, use
* the `geometry` option instead.
*
* @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