UNPKG

@amcharts/amcharts4

Version:
1,290 lines 65.3 kB
/** * Map module. */ import { __extends } from "tslib"; /** * ============================================================================ * IMPORTS * ============================================================================ * @hidden */ import { SerialChart, SerialChartDataItem } from "./SerialChart"; import { Disposer } from "../../core/utils/Disposer"; import { InterfaceColorSet } from "../../core/utils/InterfaceColorSet"; import { MapSeries } from "../map/MapSeries"; import { MapImage } from "../map/MapImage"; import { MapPolygon } from "../map/MapPolygon"; import { MapPolygonSeries } from "../map/MapPolygonSeries"; import { Projection } from "../map/projections/Projection"; import { Circle } from "../../core/elements/Circle"; import { SmallMap } from "../map/SmallMap"; import * as $mapUtils from "../map/MapUtils"; import { keyboard } from "../../core/utils/Keyboard"; import { registry } from "../../core/Registry"; import { options } from "../../core/Options"; import * as $math from "../../core/utils/Math"; import * as $utils from "../../core/utils/Utils"; import * as $ease from "../../core/utils/Ease"; import * as $iter from "../../core/utils/Iterator"; import * as $type from "../../core/utils/Type"; import * as $geo from "../map/Geo"; import { GraticuleSeries } from "../map/GraticuleSeries"; import { getInteraction } from "../../core/interaction/Interaction"; /** * ============================================================================ * DATA ITEM * ============================================================================ * @hidden */ /** * Defines a [[DataItem]] for [[MapChart]]. * * @see {@link DataItem} */ var MapChartDataItem = /** @class */ (function (_super) { __extends(MapChartDataItem, _super); /** * Constructor */ function MapChartDataItem() { var _this = _super.call(this) || this; _this.className = "MapChartDataItem"; _this.applyTheme(); return _this; } return MapChartDataItem; }(SerialChartDataItem)); export { MapChartDataItem }; /** * ============================================================================ * MAIN CLASS * ============================================================================ * @hidden */ /** * Creates a map. * * @see {@link IMapChartEvents} for a list of available Events * @see {@link IMapChartAdapters} for a list of available Adapters * @see {@link https://www.amcharts.com/docs/v4/chart-types/map/} for documentation */ var MapChart = /** @class */ (function (_super) { __extends(MapChart, _super); /** * Constructor */ function MapChart() { var _this = // Init _super.call(this) || this; /** * A ratio to be used when scaling the map shapes. * * @readonly */ _this.scaleRatio = 1; /** * Default duration of zoom animations (ms). */ _this.zoomDuration = 1000; /** * Default zooming animation easing function. */ _this.zoomEasing = $ease.cubicOut; /** * Smallest available zoom level. The map will not allow to zoom out past * this setting. * * NOTE: Should be power of 2. * * @default 1 */ _this.minZoomLevel = 1; /** * Biggest available zoom level. The map will not allow to zoom in past * this setting. * * NOTE: Should be power of 2. * * @default 32 */ _this.maxZoomLevel = 32; /** * [_prevZoomGeoPoint description] * * @todo Description */ _this._prevZoomGeoPoint = { latitude: 0, longitude: 0 }; _this.className = "MapChart"; // Set default projection _this.projection = new Projection(); _this.setPropertyValue("deltaLatitude", 0); _this.setPropertyValue("deltaLongitude", 0); _this.setPropertyValue("deltaGamma", 0); _this.maxPanOut = 0.7; _this.homeZoomLevel = 1; _this.zoomStep = 2; _this.layout = "absolute"; _this.centerMapOnZoomOut = true; // Set padding _this.padding(0, 0, 0, 0); $utils.used(_this.backgroundSeries); // so that the map would render in a hidden div too _this.minWidth = 10; _this.minHeight = 10; _this.events.once("inited", _this.handleAllInited, _this, false); // Create a container for map series var seriesContainer = _this.seriesContainer; seriesContainer.visible = false; seriesContainer.inert = true; seriesContainer.resizable = true; seriesContainer.events.on("transformed", _this.handleMapTransform, _this, false); seriesContainer.events.on("doublehit", _this.handleDoubleHit, _this, false); seriesContainer.events.on("dragged", _this.handleDrag, _this, false); seriesContainer.zIndex = 0; seriesContainer.dragWhileResize = true; //seriesContainer.background.fillOpacity = 0; seriesContainer.adapter.add("scale", function (scale, target) { return $math.fitToRange(scale, _this.minZoomLevel, _this.maxZoomLevel); }); // Set up events //this.events.on("validated", this.updateExtremes, this); //this.events.on("datavalidated", this.handleAllValidated, this, false); //this.events.on("datavalidated", this.updateExtremes, this, false); _this.events.on("maxsizechanged", function (event) { if (event.previousWidth == 0 || event.previousHeight == 0) { _this.updateExtremes(); _this.updateCenterGeoPoint(); } }, undefined, false); // Set up main chart container, e.g. set backgrounds and events to monitor // size changes, etc. var chartContainer = _this.chartContainer; chartContainer.parent = _this; chartContainer.zIndex = -1; _this._disposers.push(_this.events.on("maxsizechanged", function () { if (_this.inited) { if (_this._mapAnimation) { _this._mapAnimation.stop(); } var allInited_1 = true; _this.series.each(function (series) { series.updateTooltipBounds(); if (!series.inited || series.dataInvalid) { allInited_1 = false; } }); if (allInited_1) { _this.updateScaleRatio(); } _this.zoomToGeoPoint(_this._zoomGeoPointReal, _this.zoomLevel, true, 0); } }, undefined, false)); var chartContainerBg = chartContainer.background; chartContainerBg.fillOpacity = 0; chartContainerBg.events.on("down", function (e) { _this.seriesContainer.dragStart(e.target.interactions.downPointers.getIndex(0)); }, _this); chartContainerBg.events.on("up", function (e) { _this.seriesContainer.dragStop(); }, _this); chartContainerBg.events.on("doublehit", _this.handleDoubleHit, _this); chartContainerBg.focusable = true; chartContainer.events.on("down", _this.handleMapDown, _this, false); _this.addDisposer(seriesContainer.events.on("down", function () { // Cancel any move inertia if there is one var inertia = _this.seriesContainer.interactions.inertias.getKey("move"); if (inertia) { inertia.done(); } })); // Add description to background _this.background.fillOpacity = 0; // Add keyboard events for panning _this._disposers.push(getInteraction().body.events.on("keyup", function (ev) { if (_this.topParent.hasFocused) { var key = keyboard.getEventKey(ev.event); if (!_this._zoomControl || !_this._zoomControl.thumb.isFocused) { switch (key) { case "up": _this.pan({ x: 0, y: 0.1 }); break; case "down": _this.pan({ x: 0, y: -0.1 }); break; case "left": _this.pan({ x: 0.1, y: 0 }); break; case "right": _this.pan({ x: -0.1, y: 0 }); break; } } } }, _this)); _this.mouseWheelBehavior = "zoom"; var interaction = getInteraction(); _this._disposers.push(interaction.body.events.on("down", _this.handlePanDown, _this)); _this._disposers.push(interaction.body.events.on("up", _this.handlePanUp, _this)); //this._disposers.push(interaction.body.events.on("track", this.handlePanMove, this)); var panSprite = _this.seriesContainer.createChild(Circle); panSprite.radius = 10; panSprite.inert = true; panSprite.isMeasured = false; panSprite.events.on("transformed", _this.handlePanMove, _this, false); panSprite.interactionsEnabled = false; panSprite.opacity = 0; panSprite.x = 0; panSprite.y = 0; _this.panSprite = panSprite; _this.panBehavior = "move"; /* this.panSprite.inertiaOptions.setKey("move", { "time": 100, "duration": 1000, "factor": 3, "easing": $ease.sinOut });*/ // Apply theme _this.applyTheme(); return _this; } /** * @ignore */ MapChart.prototype.handlePanDown = function (event) { var svgPoint = $utils.documentPointToSvg(event.pointer.point, this.htmlContainer); if (svgPoint.x > 0 && svgPoint.y > 0 && svgPoint.x < this.svgContainer.width && svgPoint.y < this.svgContainer.height) { // Get local point this._downPointOrig = $utils.documentPointToSprite(event.pointer.point, this.seriesContainer); this.panSprite.moveTo(this._downPointOrig); this.panSprite.dragStart(event.pointer); this._downDeltaLongitude = this.deltaLongitude; this._downDeltaLatitude = this.deltaLatitude; } }; /** * @ignore */ MapChart.prototype.handlePanUp = function (event) { if (this._downPointOrig) { this.panSprite.dragStop(event.pointer, true); } this._downPointOrig = undefined; }; /** * @ignore */ MapChart.prototype.handlePanMove = function () { if (!this.seriesContainer.isResized) { if (getInteraction().areTransformed([this.panSprite.interactions, this.seriesContainer.interactions])) { return; } var d3Projection = this.projection.d3Projection; var panBehavior = this.panBehavior; if (panBehavior != "move" && panBehavior != "none" && this._downPointOrig && d3Projection.rotate) { var rotation = d3Projection.rotate(); var dln = rotation[0]; var dlt = rotation[1]; var dlg = rotation[2]; d3Projection.rotate([0, 0, 0]); var downGeoLocal = this.projection.invert(this._downPointOrig); var local = { x: this.panSprite.pixelX, y: this.panSprite.pixelY }; var geoLocal = void 0; if (local) { geoLocal = this.projection.invert(local); } d3Projection.rotate([dln, dlt, dlg]); if (geoLocal) { if (panBehavior == "rotateLat" || panBehavior == "rotateLongLat") { this.deltaLatitude = this._downDeltaLatitude + geoLocal.latitude - downGeoLocal.latitude; } if (panBehavior == "rotateLong" || panBehavior == "rotateLongLat") { this.deltaLongitude = this._downDeltaLongitude + geoLocal.longitude - downGeoLocal.longitude; } } } } }; /** * @ignore */ MapChart.prototype.handleAllInited = function () { var _this = this; var inited = true; this.seriesContainer.visible = true; this.series.each(function (series) { if (!series.inited || series.dataInvalid) { inited = false; } }); if (inited) { this.updateCenterGeoPoint(); this.updateScaleRatio(); this.goHome(0); } else { // TODO verify that this is correct var disposer_1 = registry.events.once("exitframe", function () { _this.removeDispose(disposer_1); _this.handleAllInited(); }, this, false); this.addDisposer(disposer_1); } }; /** * @ignore */ MapChart.prototype.updateZoomGeoPoint = function () { var seriesPoint = $utils.svgPointToSprite({ x: this.innerWidth / 2 + this.pixelPaddingLeft, y: this.innerHeight / 2 + this.pixelPaddingTop }, this.series.getIndex(0)); var geoPoint = this.projection.invert(seriesPoint); this._zoomGeoPointReal = geoPoint; }; /** * @ignore */ MapChart.prototype.updateCenterGeoPoint = function () { var maxLeft; var maxRight; var maxTop; var maxBottom; if (this.backgroundSeries) { var features = this.backgroundSeries.getFeatures(); if (features.length > 0) { var bounds = this.projection.d3Path.bounds(features[0].geometry); maxLeft = bounds[0][0]; maxTop = bounds[0][1]; maxRight = bounds[1][0]; maxBottom = bounds[1][1]; } } else { this.series.each(function (series) { var bbox = series.group.node.getBBox(); if (maxLeft > bbox.x || !$type.isNumber(maxLeft)) { maxLeft = bbox.x; } if (maxRight < bbox.x + bbox.width || !$type.isNumber(maxRight)) { maxRight = bbox.x + bbox.width; } if (maxTop > bbox.y || !$type.isNumber(maxTop)) { maxTop = bbox.y; } if (maxBottom < bbox.y + bbox.height || !$type.isNumber(maxBottom)) { maxBottom = bbox.y + bbox.height; } }); } this.seriesMaxLeft = maxLeft; this.seriesMaxRight = maxRight; this.seriesMaxTop = maxTop; this.seriesMaxBottom = maxBottom; this.seriesWidth = maxRight - maxLeft; this.seriesHeight = maxBottom - maxTop; if (this.seriesWidth > 0 && this.seriesHeight > 0) { this.chartContainer.visible = true; this._centerGeoPoint = this.projection.invert({ x: maxLeft + (maxRight - maxLeft) / 2, y: maxTop + (maxBottom - maxTop) / 2 }); if (!this._zoomGeoPointReal || !$type.isNumber(this._zoomGeoPointReal.latitude)) { this._zoomGeoPointReal = this._centerGeoPoint; } } else { this.chartContainer.visible = false; } }; /** * Prevents map to be dragged out of the container area * @ignore */ MapChart.prototype.handleDrag = function () { var d = this.zoomLevel * this.scaleRatio; var ww = this.seriesWidth * d; var hh = this.seriesHeight * d; var seriesContainer = this.seriesContainer; var maxLeft = this.seriesMaxLeft * d; var maxRight = this.seriesMaxRight * d; var maxTop = this.seriesMaxTop * d; var maxBottom = this.seriesMaxBottom * d; var x = seriesContainer.pixelX; var y = seriesContainer.pixelY; var maxPanOut = this.maxPanOut; var minX = Math.min(this.maxWidth * (1 - maxPanOut) - ww - maxLeft, -maxLeft); if (x < minX) { x = minX; } var maxX = Math.max(this.maxWidth * maxPanOut - maxLeft, this.maxWidth - maxRight); if (x > maxX) { x = maxX; } var minY = Math.min(this.maxHeight * (1 - maxPanOut) - hh - maxTop, -maxTop); if (y < minY) { y = minY; } var maxY = Math.max(this.maxHeight * maxPanOut - maxTop, this.maxHeight - maxBottom); if (y > maxY) { y = maxY; } seriesContainer.moveTo({ x: x, y: y }, undefined, undefined, true); this._zoomGeoPointReal = this.zoomGeoPoint; }; /** * Sets defaults that instantiate some objects that rely on parent, so they * cannot be set in constructor. */ MapChart.prototype.applyInternalDefaults = function () { _super.prototype.applyInternalDefaults.call(this); // Add a default screen reader title for accessibility // This will be overridden in screen reader if there are any `titles` set if (!$type.hasValue(this.readerTitle)) { this.readerTitle = this.language.translate("Map"); } if (!$type.hasValue(this.background.readerTitle)) { this.background.role = "application"; this.background.readerTitle = this.language.translate("Use plus and minus keys on your keyboard to zoom in and out"); } }; /** * Handles event when a pointer presses down on the map, e.g. user presses * down mouse or touches the map on a screen. * * Stops all animations currently going on. */ MapChart.prototype.handleMapDown = function () { if (this._mapAnimation) { this._mapAnimation.stop(); } }; /** * Handles the event when user doubleclicks or dooubletaps the map: zooms * in on the reference point. * * @param event Original event */ MapChart.prototype.handleDoubleHit = function (event) { var svgPoint = $utils.documentPointToSvg(event.point, this.htmlContainer, this.svgContainer.cssScale); var geoPoint = this.svgPointToGeo(svgPoint); this.zoomIn(geoPoint); }; /** * Handles mouse wheel event, e.g. user rotates mouse wheel while over the * map: zooms in or out depending on the direction of the wheel turn. * * @param event Original event */ MapChart.prototype.handleWheel = function (event) { // Cancel any move inertia if there is one var inertia = this.seriesContainer.interactions.inertias.getKey("move"); if (inertia) { inertia.done(); } var svgPoint = $utils.documentPointToSvg(event.point, this.htmlContainer, this.svgContainer.cssScale); var geoPoint = this.svgPointToGeo(svgPoint); if (event.shift.y < 0) { this.zoomIn(geoPoint, undefined, this.interactions.mouseOptions.sensitivity); } else { this.zoomOut(geoPoint, undefined, this.interactions.mouseOptions.sensitivity); } }; Object.defineProperty(MapChart.prototype, "mouseWheelBehavior", { /** * @return mouse wheel behavior */ get: function () { return this.getPropertyValue("mouseWheelBehavior"); }, /** * Specifies what should chart do if when mouse wheel is rotated. * * @see {@link https://www.amcharts.com/docs/v4/reference/sprite/#mouseOptions_property} More information about `mouseOptions` * @param mouse wheel behavior * @default zoomX */ set: function (value) { if (this.setPropertyValue("mouseWheelBehavior", value)) { if (value != "none") { this._mouseWheelDisposer = this.chartContainer.events.on("wheel", this.handleWheel, this, false); this._disposers.push(this._mouseWheelDisposer); } else { if (this._mouseWheelDisposer) { this._mouseWheelDisposer.dispose(); } this.chartContainer.wheelable = false; } } }, enumerable: true, configurable: true }); Object.defineProperty(MapChart.prototype, "panBehavior", { /** * @returns Behavior */ get: function () { return this.getPropertyValue("panBehavior"); }, /** * What "dragging" map does. * * Available values: * * `"move"` (default): changes position of the map. * * `"rotateLat"`: changes `deltaLatitude` (rotates the globe vertically). * * `"rotateLong"`: changes `deltaLongitude` (rotates the globe horizontally). * * `"rotateLongLat"`: changes both `deltaLongitude` and `deltaLatitude` (rotates the globe in any direction). * * @default "move" * @since 4.3.0 * @param value Behavior */ set: function (value) { if (this.setPropertyValue("panBehavior", value)) { var seriesContainer = this.seriesContainer; this.panSprite.draggable = false; seriesContainer.draggable = false; switch (value) { case "move": seriesContainer.draggable = true; break; default: this.panSprite.draggable = true; break; } } }, enumerable: true, configurable: true }); Object.defineProperty(MapChart.prototype, "centerMapOnZoomOut", { /** * @returns If the map should be centered when zooming out. */ get: function () { return this.getPropertyValue("centerMapOnZoomOut"); }, /** * Specifies if the map should be centered when zooming out * @default true * @since 4.7.12 */ set: function (value) { this.setPropertyValue("centerMapOnZoomOut", value); }, enumerable: true, configurable: true }); Object.defineProperty(MapChart.prototype, "projection", { /** * @return Projection */ get: function () { return this.getPropertyValue("projection"); }, /** * Projection to use for the map. * * Available projections: * * Albers * * AlbersUSA * * AzimuthalEqualArea * * Eckert6 * * EqualEarth * * Mercator * * Miller * * NaturalEarth * * Orthographic * * Stereographic * * ```TypeScript * map.projection = new am4maps.projections.Mercator(); * ``` * ```JavaScript * map.projection = new am4maps.projections.Mercator(); * ``` * ```JSON * { * // ... * "projection": "Mercator" * // ... * } * ``` * * @see {@link https://www.amcharts.com/docs/v4/chart-types/map/#Setting_projection} More about projections * @param projection Projection */ set: function (projection) { var _this = this; if (this.setPropertyValue("projection", projection)) { this.invalidateProjection(); projection.chart = this; if (this._backgroundSeries) { this._backgroundSeries.invalidate(); } if (this.inited) { this.updateExtremes(); } this.series.each(function (series) { series.events.once("validated", function () { _this.updateCenterGeoPoint(); _this.updateScaleRatio(); _this.goHome(0); }); }); } }, enumerable: true, configurable: true }); /** * Validates (processes) data items. * * @ignore Exclude from docs */ MapChart.prototype.validateDataItems = function () { _super.prototype.validateDataItems.call(this); this.updateExtremes(); }; /** * Calculates the longitudes and latitudes of the most distant points from * the center in all four directions: West, East, North, and South. * * @ignore Exclude from docs */ MapChart.prototype.updateExtremes = function () { var east; var north; var west; var south; this.series.each(function (series) { if (series.ignoreBounds || (series instanceof GraticuleSeries && series.fitExtent)) { } else { if (series.north > north || !$type.isNumber(north)) { north = series.north; } if (series.south < south || !$type.isNumber(south)) { south = series.south; } if (series.west < west || !$type.isNumber(west)) { west = series.west; } if (series.east > east || !$type.isNumber(east)) { east = series.east; } } }); var features = []; var foundGraticule = false; // if we gave graticule, get features of these series only for faster fitSize this.series.each(function (series) { if (series instanceof GraticuleSeries && !series.fitExtent) { features = series.getFeatures(); foundGraticule = true; } }); if (!foundGraticule) { this.series.each(function (series) { if (series.ignoreBounds || (series instanceof GraticuleSeries && series.fitExtent)) { } else { features = features.concat(series.getFeatures()); } }); } var w = $math.max(50, this.innerWidth); var h = $math.max(50, this.innerHeight); var d3Projection = this.projection.d3Projection; if (features.length > 0 && d3Projection && (this.east != east || this.west != west || this.north != north || this.south != south)) { this.east = east; this.west = west; this.north = north; this.south = south; if (d3Projection.rotate) { var rotation = d3Projection.rotate(); var deltaLong = rotation[0]; var deltaLat = rotation[1]; var deltaGamma = rotation[2]; this.deltaLongitude = deltaLong; this.deltaLatitude = deltaLat; this.deltaGamma = deltaGamma; } var geoJSON = { "type": "FeatureCollection", features: features }; var initialScale = d3Projection.scale(); d3Projection.fitSize([w, h], geoJSON); if (d3Projection.scale() != initialScale) { this.invalidateDataUsers(); } this.series.each(function (series) { if (series instanceof GraticuleSeries) { series.invalidateData(); } }); if (this._backgroundSeries) { var polygon = this._backgroundSeries.mapPolygons.getIndex(0); if (polygon) { polygon.multiPolygon = $mapUtils.getBackground(this.north, this.east, this.south, this.west); } } this._fitWidth = w; this._fitHeight = h; } if (!this._zoomGeoPointReal || !$type.isNumber(this._zoomGeoPointReal.latitude)) { this.goHome(0); } }; /** * (Re)calculates a ratio which should be used to scale the actual map so * that it fits perfectly into available space. Helps to avoid redrawing of all the map if container size changes * @ignore */ MapChart.prototype.updateScaleRatio = function () { var scaleRatio; this.updateCenterGeoPoint(); var hScale = this.innerWidth / this.seriesWidth; var vScale = this.innerHeight / this.seriesHeight; scaleRatio = $math.min(hScale, vScale); if ($type.isNaN(scaleRatio) || scaleRatio == Infinity) { scaleRatio = 1; } if (scaleRatio != this.scaleRatio) { this.scaleRatio = scaleRatio; $iter.each(this.series.iterator(), function (series) { series.scale = scaleRatio; series.updateTooltipBounds(); }); this.backgroundSeries.scale = scaleRatio; this.dispatch("scaleratiochanged"); } }; /** * Converts a point within map container to geographical (lat/long) * coordinates. * * @param point Source point * @return Geo-point */ MapChart.prototype.svgPointToGeo = function (point) { var series = this.series.getIndex(0); if (series) { var seriesPoint = $utils.svgPointToSprite(point, series); return this.seriesPointToGeo(seriesPoint); } }; /** * Converts geographical (lat/long) coordinates to an X/Y point within map's * container. * * @param point Source geo-point * @return Point */ MapChart.prototype.geoPointToSVG = function (point) { var series = this.series.getIndex(0); if (series) { var seriesPoint = this.geoPointToSeries(point); return $utils.spritePointToSvg(seriesPoint, series); } }; /** * Converts a point (X/Y) within actual objects of the map to geographical * (lat/long) coordinates. * * @param point Source point * @return Geo-point */ MapChart.prototype.seriesPointToGeo = function (point) { return this.projection.invert(point); }; /** * Converts geographical (lat/long) coordinates to an X/Y point within * actual elements/objects of the maps. * * @param point Source geo-point * @return Point */ MapChart.prototype.geoPointToSeries = function (point) { return this.projection.convert(point); }; Object.defineProperty(MapChart.prototype, "geodata", { /** * @return GeoJSON data */ get: function () { return this._geodata; }, /** * Map data in GeoJSON format. * * The Map supports the following GeoJSON objects: `Point`, `LineString`, * `Polygon`, `MultiPoint`, `MultiLineString`, and `MultiPolygon`. * * @see {@link http://geojson.org/} Official GeoJSON format specification * @param geoJSON GeoJSON data */ set: function (geodata) { if (geodata != this._geodata) { this._geodata = geodata; if (this.reverseGeodata) { this.processReverseGeodata(this._geodata); } this.invalidateData(); this.dataUsers.each(function (dataUser) { for (var i = dataUser.data.length - 1; i >= 0; i--) { if (dataUser.data[i].madeFromGeoData == true) { dataUser.data.splice(i, 1); } } dataUser.disposeData(); dataUser.invalidateData(); }); } }, enumerable: true, configurable: true }); Object.defineProperty(MapChart.prototype, "reverseGeodata", { /** * @returns Reverse the order of geodata coordinates? */ get: function () { return this.getPropertyValue("reverseGeodata"); }, /** * Indicates whether GeoJSON geodata supplied to the chart uses * ESRI (clockwise) or non-ESRI (counter-clockwise) order of the polygon * coordinates. * * `MapChart` supports only ESRI standard, so if your custom maps appears * garbled, try setting `reverseGeodata = true`. * * @default false * @since 4.10.11 * @param value Reverse the order of geodata coordinates? */ set: function (value) { if (this.setPropertyValue("reverseGeodata", value) && this._geodata) { this.processReverseGeodata(this._geodata); } }, enumerable: true, configurable: true }); /** * Reverses the order of polygons on a GeoJSON data. * * @since 4.10.11 * @param geodata Source geodata */ MapChart.prototype.processReverseGeodata = function (geodata) { for (var i = 0; i < geodata.features.length; i++) { var feature = geodata.features[i]; for (var x = 0; x < feature.geometry.coordinates.length; x++) { if (feature.geometry.type == "MultiPolygon") { for (var y = 0; y < feature.geometry.coordinates[x].length; y++) { feature.geometry.coordinates[x][y].reverse(); } } else { feature.geometry.coordinates[x].reverse(); } } } }; /** * Zooms the map to particular zoom level and centers on a latitude/longitude * coordinate. * * @param point Center coordinate * @param zoomLevel Zoom level * @param center Center on the given coordinate? * @param duration Duration for zoom animation (ms) * @return Zoom animation */ MapChart.prototype.zoomToGeoPoint = function (point, zoomLevel, center, duration, mapObject) { var _this = this; if (!point) { var hasData_1 = false; this.series.each(function (series) { if (series.dataItems.length > 0) { hasData_1 = true; } }); if (hasData_1) { point = this.zoomGeoPoint; } else { return; } } if (!point || !$type.isNumber(point.longitude) || !$type.isNumber(point.latitude)) { return; } this._zoomGeoPointReal = point; zoomLevel = $math.fitToRange(zoomLevel, this.minZoomLevel, this.maxZoomLevel); var seriesPoint = this.projection.convert(point); if (seriesPoint) { var svgPoint = this.geoPointToSVG(point); var mapPoint = $utils.svgPointToSprite(svgPoint, this); if (center) { mapPoint = { x: this.innerWidth / 2, y: this.innerHeight / 2 }; } if (!$type.isNumber(duration)) { duration = this.zoomDuration; } var x = mapPoint.x - seriesPoint.x * zoomLevel * this.scaleRatio; var y = mapPoint.y - seriesPoint.y * zoomLevel * this.scaleRatio; if (!mapObject && zoomLevel < this.zoomLevel && this.centerMapOnZoomOut && zoomLevel < 1.5) { x = this.innerWidth / 2 - (this.seriesMaxLeft + (this.seriesMaxRight - this.seriesMaxLeft) / 2) * zoomLevel * this.scaleRatio; y = this.innerHeight / 2 - (this.seriesMaxTop + (this.seriesMaxBottom - this.seriesMaxTop) / 2) * zoomLevel * this.scaleRatio; } this._mapAnimation = this.seriesContainer.animate([{ property: "scale", to: zoomLevel }, { property: "x", from: this.seriesContainer.pixelX, to: x }, { property: "y", from: this.seriesContainer.pixelY, to: y }], duration, this.zoomEasing); this._disposers.push(this._mapAnimation.events.on("animationended", function () { _this._zoomGeoPointReal = _this.zoomGeoPoint; })); this.seriesContainer.validatePosition(); return this._mapAnimation; } }; /** * Zooms the map to a particular map object. * * @param mapObject Target map object * @param zoomLevel Zoom level * @param center Center on the given coordinate? * @param duration Duration for zoom animation (ms) * @return Zoom animation */ MapChart.prototype.zoomToMapObject = function (mapObject, zoomLevel, center, duration) { if (center == undefined) { center = true; } var inertia = this.seriesContainer.interactions.inertias.getKey("move"); if (inertia) { inertia.done(); } if (mapObject instanceof MapImage) { if ($type.isNaN(zoomLevel)) { zoomLevel = 5; } return this.zoomToGeoPoint({ latitude: mapObject.latitude, longitude: mapObject.longitude }, zoomLevel, center, duration, true); } var dataItem = mapObject.dataItem; if (dataItem && $type.isNumber(dataItem.zoomLevel)) { zoomLevel = dataItem.zoomLevel; } if (mapObject instanceof MapPolygon) { var dataItem_1 = mapObject.dataItem; var bbox = mapObject.polygon.bbox; if (bbox.width == 0 || bbox.height == 0) { bbox = mapObject.polygon.group.getBBox(); } if (!$type.isNumber(zoomLevel)) { zoomLevel = Math.min(this.seriesWidth / bbox.width, this.seriesHeight / bbox.height); } var geoPoint = void 0; if (dataItem_1 && $type.hasValue(dataItem_1.zoomGeoPoint)) { geoPoint = dataItem_1.zoomGeoPoint; } else { // this is more accurate var polygonPoint = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 }; var seriesPoint = $utils.spritePointToSprite(polygonPoint, mapObject.polygon, mapObject.series); geoPoint = this.seriesPointToGeo(seriesPoint); } return this.zoomToGeoPoint(geoPoint, zoomLevel, true, duration, true); } }; /** * Zooms the map to a particular viewport. * * The `north`, `east`, `south`, and `west` define boundaries of the * imaginary viewort we want to zoom the map to. * * `level` is not actual zoom level. The map will determine the zoom level * required to accommodated such zoom, and will adjust it by `level` if set. * * @param north Latitude of the North-most boundary * @param east Longitude of the East-most boundary * @param south Latitude of the South-most boundary * @param west Longitude of the West-most boundary * @param level Adjust zoom level * @param center Center on the given coordinate? * @param duration Duration for zoom animation (ms) * @return Zoom animation */ MapChart.prototype.zoomToRectangle = function (north, east, south, west, level, center, duration) { if ($type.isNaN(level)) { level = 1; } var w = $math.min(west, east); var e = $math.max(west, east); west = w; east = e; var splitLongitude = $math.normalizeAngle(180 - this.deltaLongitude); if (splitLongitude > 180) { splitLongitude -= 360; } var newLong = west + (east - west) / 2; var d = (west - east); if (west < splitLongitude && east > splitLongitude) { newLong += 180; d = $math.normalizeAngle(east - west - 360); } var zoomLevel = level * Math.min((this.south - this.north) / (south - north), Math.abs((this.west - this.east) / d)); return this.zoomToGeoPoint({ latitude: north + (south - north) / 2, longitude: newLong }, zoomLevel, center, duration, true); }; /** * Zooms in the map, optionally centering on particular latitude/longitude * point. * * @param geoPoint Optional center point * @param duration Duration for zoom animation (ms) * @return Zoom animation */ MapChart.prototype.zoomIn = function (geoPoint, duration, sensitivity) { if (sensitivity === void 0) { sensitivity = 1; } var step = 1 + (this.zoomStep - 1) * sensitivity; if (step < 1) { step = 1; } return this.zoomToGeoPoint(geoPoint, this.zoomLevel * step, false, duration); }; /** * Zooms out the map, optionally centering on particular latitude/longitude * point. * * @param geoPoint Optional center point * @param duration Duration for zoom animation (ms) * @return Zoom animation */ MapChart.prototype.zoomOut = function (geoPoint, duration, sensitivity) { if (sensitivity === void 0) { sensitivity = 1; } var step = 1 + (this.zoomStep - 1) * sensitivity; if (step < 1) { step = 1; } return this.zoomToGeoPoint(geoPoint, this.zoomLevel / step, false, duration); }; /** * Pans the maps using relative coordinates. E.g.: * * ```JSON * { * x: 0.1, * y: -0.1 * } * ``` * * The above will move the map by 10% to the right, and by 10% upwards. * * @param shift Vertical and horizontal shift * @param duration Pan animation duration (ms) */ MapChart.prototype.pan = function (shift, duration) { var point = this.geoPointToSVG(this.zoomGeoPoint); point.x += this.pixelWidth * shift.x; point.y += this.pixelHeight * shift.y; this.zoomToGeoPoint(this.svgPointToGeo(point), this.zoomLevel, true, duration, true); }; Object.defineProperty(MapChart.prototype, "zoomGeoPoint", { /** * Current lat/long coordinates for the center of the viewport. (default * zoom reference point) * * @readonly * @return Coordinates */ get: function () { var point = $utils.spritePointToSvg({ x: this.pixelWidth / 2, y: this.pixelHeight / 2 }, this); return this.svgPointToGeo(point); }, enumerable: true, configurable: true }); Object.defineProperty(MapChart.prototype, "zoomLevel", { /** * @return Zoom level */ get: function () { return this.seriesContainer.scale; }, /** * Current zoom level. * * @readonly * @return Zoom level */ set: function (value) { this.seriesContainer.scale = value; }, enumerable: true, configurable: true }); /** * Dispatches events after some map transformation, like pan or zoom. * * @ignore */ MapChart.prototype.handleMapTransform = function () { if (this.zoomLevel != this._prevZoomLevel) { this.dispatch("zoomlevelchanged"); this._prevZoomLevel = this.zoomLevel; this.svgContainer.readerAlert(this.language.translate("Zoom level changed to %1", this.language.locale, $type.castString(this.zoomLevel))); } if (this.zoomGeoPoint && (this._prevZoomGeoPoint.latitude != this.zoomGeoPoint.latitude || this._prevZoomGeoPoint.longitude != this.zoomGeoPoint.longitude)) { this.dispatch("mappositionchanged"); } }; Object.defineProperty(MapChart.prototype, "smallMap", { /** * @return Small map */ get: function () { if (!this._smallMap) { var smallMap = new SmallMap(); this.smallMap = smallMap; } return this._smallMap; }, /** * A [[SmallMap]] to be used on the map. * * Please note, that accessing this property will NOT create a small map * if it has not yet been created. (except in JSON) * * ```TypeScript * // Create a small map * map.smallMap = new am4maps.SmallMap(); * ``` * ```JavaScript * // Create a small map * map.smallMap = new am4maps.SmallMap(); * ``` * ```JSON * { * // ... * "smallMap": {} * // ... * } * ``` * * @param smallMap Small map */ set: function (smallMap) { if (this._smallMap) { this.removeDispose(this._smallMap); } this._smallMap = smallMap; this._smallMap.chart = this; smallMap.parent = this.chartContainer; }, enumerable: true, configurable: true }); Object.defineProperty(MapChart.prototype, "zoomControl", { /** * @return Zoom control */ get: function () { return this._zoomControl; }, /** * A [[ZoomControl]] to be used on the map. * * Please note, that accessing this property will NOT create a zoom control * if it has not yet been created. (except in JSON) * * ```TypeScript * // Create a zoom control * map.zoomControl = new am4maps.ZoomControl(); * ``` * ```JavaScript * // Create a zoom control * map.zoomControl = new am4maps.ZoomControl(); * ``` * ```JSON * { * // ... * "zoomControl": {} * // ... * } * ``` * * @param zoomControl Zoom control */ set: function (zoomControl) { if (this._zoomControl) { this.removeDispose(this._zoomControl); } this._zoomControl = zoomControl; zoomControl.chart = this; zoomControl.parent = this.chartContainer; zoomControl.plusButton.exportable = false; zoomControl.minusButton.exportable = false; }, enumerable: true, configurable: true }); /** * Creates and returns a map series of appropriate type. * * @return Map series */ MapChart.prototype.createSeries = function () { return new MapSeries(); }; Object.defineProperty(MapChart.prototype, "deltaLongitude", { /** * @return Rotation */ get: function () { return this.getPropertyValue("deltaLongitude"); }, /** * Degrees to rotate the map around vertical axis (Y). * * E.g. if set to -160, the longitude 20 will become a new center, creating * a Pacific-centered map. * * @see {@link https://www.amcharts.com/docs/v4/chart-types/map/#Map_rotation} For more info on map rotation. * @param value Rotation */ set: function (value) { value = $math.round(value, 3); if (this.setPropertyValue("deltaLongitude", $geo.wrapAngleTo180(value))) { this.rotateMap(); this.updateZoomGeoPoint(); } }, enumerable: true, configurable: true }); Object.defineProperty(MapChart.prototype, "deltaLatitude", { /** * @return Rotation */ get: function () { return this.getPropertyValue("deltaLatitude"); }, /** * Degrees to rotate the map around horizontal axis (X). * * E.g. setting this to 90 will put Antarctica directly in the center of * the map. * * @see {@link https://www.amcharts.com/docs/v4/chart-types/map/#Map_rotation} For more info on map rotation. * @since 4.3.0 * @param value Rotation */ set: function (value) { value = $math.round(value, 3); if (this.setPropertyValue("deltaLatitude", value)) { this.rotateMap(); this.upd