import { MapChartDefaultTheme } from "./MapChartDefaultTheme"; import { SerialChart } from "../../core/render/SerialChart"; import { Rectangle } from "../../core/render/Rectangle"; import { geoPath } from "d3-geo"; import { Color } from "../../core/util/Color"; import { registry } from "../../core/Registry"; import * as $math from "../../core/util/Math"; import * as $array from "../../core/util/Array"; import * as $type from "../../core/util/Type"; import * as $mapUtils from "./MapUtils"; import * as $object from "../../core/util/Object"; import * as $utils from "../../core/util/Utils"; export class MapChart extends SerialChart { constructor() { super(...arguments); Object.defineProperty(this, "_downTranslateX", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_downTranslateY", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_downRotationX", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_downRotationY", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_downRotationZ", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_pLat", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "_pLon", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "_movePoints", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "_downZoomLevel", { enumerable: true, configurable: true, writable: true, value: 1 }); Object.defineProperty(this, "_doubleDownDistance", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "_dirtyGeometries", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "_geometryColection", { enumerable: true, configurable: true, writable: true, value: { type: "GeometryCollection", geometries: [] } }); Object.defineProperty(this, "_centerLocation", { enumerable: true, configurable: true, writable: true, value: null }); Object.defineProperty(this, "_za", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_rxa", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_rya", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_txa", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_tya", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_mapBounds", { enumerable: true, configurable: true, writable: true, value: [[0, 0], [0, 0]] }); Object.defineProperty(this, "_geoCentroid", { enumerable: true, configurable: true, writable: true, value: { longitude: 0, latitude: 0 } }); Object.defineProperty(this, "_geoBounds", { enumerable: true, configurable: true, writable: true, value: { left: 0, right: 0, top: 0, bottom: 0 } }); Object.defineProperty(this, "_prevGeoBounds", { enumerable: true, configurable: true, writable: true, value: { left: 0, right: 0, top: 0, bottom: 0 } }); Object.defineProperty(this, "_dispatchBounds", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "_wheelDp", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_pw", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_ph", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_mapFitted", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "_centerX", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "_centerY", { enumerable: true, configurable: true, writable: true, value: 0 }); } _makeGeoPath() { const projection = this.get("projection"); const path = geoPath(); path.projection(projection); this.setPrivateRaw("geoPath", path); } /** * Returns a geoPoint of the current zoom position. * * You can later use it to restore zoom position, e.g.: `chart.zoomToGeoPoint(geoPoint, zoomLevel, true)`. * * @since 5.2.19 */ geoPoint() { return this.invert(this.seriesContainer.toGlobal({ x: this.width() / 2, y: this.height() / 2 })); } /** * Returns coordinates to geographical center of the map. */ geoCentroid() { return this._geoCentroid; } /** * Returns geographical bounds of the map. */ geoBounds() { return this._geoBounds; } _handleSetWheel() { const wheelX = this.get("wheelX"); const wheelY = this.get("wheelY"); const chartContainer = this.chartContainer; if (wheelX != "none" || wheelY != "none") { if (this._wheelDp) { this._wheelDp.dispose(); } this._wheelDp = chartContainer.events.on("wheel", (event) => { const wheelEasing = this.get("wheelEasing"); const wheelSensitivity = this.get("wheelSensitivity", 1); const wheelDuration = this.get("wheelDuration", 0); const wheelEvent = event.originalEvent; // Ignore wheel event if it is happening on a non-chart element, e.g. if // some page element is over the chart. if ($utils.isLocalEvent(wheelEvent, this)) { wheelEvent.preventDefault(); } else { return; } const point = chartContainer._display.toLocal(event.point); if ((wheelY == "zoom")) { this._handleWheelZoom(wheelEvent.deltaY, point); } else if (wheelY == "rotateY") { this._handleWheelRotateY(wheelEvent.deltaY / 5 * wheelSensitivity, wheelDuration, wheelEasing); } else if (wheelY == "rotateX") { this._handleWheelRotateX(wheelEvent.deltaY / 5 * wheelSensitivity, wheelDuration, wheelEasing); } if ((wheelX == "zoom")) { this._handleWheelZoom(wheelEvent.deltaX, point); } else if (wheelX == "rotateY") { this._handleWheelRotateY(wheelEvent.deltaX / 5 * wheelSensitivity, wheelDuration, wheelEasing); } else if (wheelX == "rotateX") { this._handleWheelRotateX(wheelEvent.deltaX / 5 * wheelSensitivity, wheelDuration, wheelEasing); } }); this._disposers.push(this._wheelDp); } else { if (this._wheelDp) { this._wheelDp.dispose(); } } } _prepareChildren() { super._prepareChildren(); const projection = this.get("projection"); const w = this.innerWidth(); const h = this.innerHeight(); const previousGeometries = this._geometryColection.geometries; if (this.isDirty("projection")) { this._makeGeoPath(); this.markDirtyProjection(); this._fitMap(); projection.scale(this.getPrivate("mapScale") * this.get("zoomLevel", 1)); if (projection.rotate) { projection.rotate([this.get("rotationX", 0), this.get("rotationY", 0), this.get("rotationZ", 0)]); } let prev = this._prevSettings.projection; if (prev && prev != projection) { let hw = w / 2; let hh = h / 2; if (prev.invert) { let centerLocation = prev.invert([hw, hh]); if (centerLocation) { let xy = projection(centerLocation); if (xy) { let translate = projection.translate(); let xx = hw - ((xy[0] - translate[0])); let yy = hh - ((xy[1] - translate[1])); projection.translate([xx, yy]); this.setRaw("translateX", xx); this.setRaw("translateY", yy); } } } } } if (this.isDirty("wheelX") || this.isDirty("wheelY")) { this._handleSetWheel(); } if (this._dirtyGeometries) { this._geometryColection.geometries = []; this.series.each((series) => { $array.pushAll(this._geometryColection.geometries, series._geometries); }); this._fitMap(); } if (previousGeometries.length != 0 && (w != this._pw || h != this._ph || this._dirtyGeometries)) { if (w > 0 && h > 0) { let hw = w / 2; let hh = h / 2; projection.fitSize([w, h], this._geometryColection); const newScale = projection.scale(); this.setPrivateRaw("mapScale", newScale); projection.scale(newScale * this.get("zoomLevel", 1)); if (this._centerLocation) { let xy = projection(this._centerLocation); if (xy) { let translate = projection.translate(); let xx = hw - ((xy[0] - translate[0])); let yy = hh - ((xy[1] - translate[1])); projection.translate([xx, yy]); this.setRaw("translateX", xx); this.setRaw("translateY", yy); this._centerX = translate[0]; this._centerY = translate[1]; } } this.markDirtyProjection(); const geoPath = this.getPrivate("geoPath"); this._mapBounds = geoPath.bounds(this._geometryColection); } } this._pw = w; this._ph = h; if (this.isDirty("zoomControl")) { const previous = this._prevSettings.zoomControl; const zoomControl = this.get("zoomControl"); if (zoomControl !== previous) { this._disposeProperty("zoomControl"); if (previous) { previous.dispose(); } if (zoomControl) { zoomControl.setPrivate("chart", this); this.children.push(zoomControl); } this.setRaw("zoomControl", zoomControl); } } if (this.isDirty("zoomLevel")) { projection.scale(this.getPrivate("mapScale") * this.get("zoomLevel", 1)); this.markDirtyProjection(); this.series.each((series) => { if (series.isType("MapPointSeries")) { if (series.get("autoScale")) { $array.each(series.dataItems, (dataItem) => { const bullets = dataItem.bullets; if (bullets) { $array.each(bullets, (bullet) => { const sprite = bullet.get("sprite"); if (sprite) { sprite.set("scale", this.get("zoomLevel")); } }); } }); } } }); } if (this.isDirty("translateX") || this.isDirty("translateY")) { projection.translate([this.get("translateX", this.width() / 2), this.get("translateY", this.height() / 2)]); this.markDirtyProjection(); } if (projection.rotate) { if (this.isDirty("rotationX") || this.isDirty("rotationY") || this.isDirty("rotationZ")) { projection.rotate([this.get("rotationX", 0), this.get("rotationY", 0), this.get("rotationZ", 0)]); this.markDirtyProjection(); } } if (this.isDirty("pinchZoom") || this.get("panX") || this.get("panY")) { this._setUpTouch(); } } _fitMap() { const projection = this.get("projection"); let w = this.innerWidth(); let h = this.innerHeight(); if (w > 0 && h > 0) { projection.fitSize([w, h], this._geometryColection); this.setPrivateRaw("mapScale", projection.scale()); const translate = projection.translate(); this.setRaw("translateX", translate[0]); this.setRaw("translateY", translate[1]); this._centerX = translate[0]; this._centerY = translate[1]; const geoPath = this.getPrivate("geoPath"); this._mapBounds = geoPath.bounds(this._geometryColection); this._geoCentroid = $mapUtils.getGeoCentroid(this._geometryColection); const bounds = $mapUtils.getGeoBounds(this._geometryColection); this._geoBounds = bounds; if (this._geometryColection.geometries.length > 0) { bounds.left = $math.round(this._geoBounds.left, 3); bounds.right = $math.round(this._geoBounds.right, 3); bounds.top = $math.round(this._geoBounds.top, 3); bounds.bottom = $math.round(this._geoBounds.bottom, 3); const prevGeoBounds = this._prevGeoBounds; if (prevGeoBounds && !$utils.sameBounds(bounds, prevGeoBounds)) { this._dispatchBounds = true; this._prevGeoBounds = bounds; } } this._mapFitted = true; } } /** * Returns geographical coordinates for calculated or manual center of the * map. */ homeGeoPoint() { let homeGeoPoint = this.get("homeGeoPoint"); if (!homeGeoPoint) { const geoPath = this.getPrivate("geoPath"); const bounds = geoPath.bounds(this._geometryColection); const left = bounds[0][0]; const top = bounds[0][1]; const right = bounds[1][0]; const bottom = bounds[1][1]; homeGeoPoint = this.invert({ x: left + (right - left) / 2, y: top + (bottom - top) / 2 }); } return homeGeoPoint; } /** * Repositions the map to the "home" zoom level and center coordinates. * * @see {@link https://www.amcharts.com/docs/v5/charts/map-chart/map-pan-zoom/#Resetting_position_level} for more info * @param duration Animation duration in milliseconds */ goHome(duration) { this.zoomToGeoPoint(this.homeGeoPoint(), this.get("homeZoomLevel", 1), true, duration, this.get("homeRotationX"), this.get("homeRotationY")); } _updateChildren() { const projection = this.get("projection"); if (projection.invert) { let w = this.innerWidth(); let h = this.innerHeight(); if (w > 0 && h > 0) { this._centerLocation = projection.invert([this.innerWidth() / 2, this.innerHeight() / 2]); } } super._updateChildren(); } _afterChanged() { super._afterChanged(); if (this._dispatchBounds) { this._dispatchBounds = false; const type = "geoboundschanged"; if (this.events.isEnabled(type)) { this.events.dispatch(type, { type: type, target: this }); } } } _setUpTouch() { if (!this.chartContainer._display.cancelTouch) { this.chartContainer._display.cancelTouch = (this.get("pinchZoom") || this.get("panX") || this.get("panY")) ? true : false; } } /** * @ignore */ markDirtyGeometries() { this._dirtyGeometries = true; this.markDirty(); } /** * @ignore */ markDirtyProjection() { this.series.each((series) => { series.markDirtyProjection(); }); } _afterNew() { this._defaultThemes.push(MapChartDefaultTheme.new(this._root)); this._settings.themeTags = $utils.mergeTags(this._settings.themeTags, ["map"]); this.children.push(this.bulletsContainer); super._afterNew(); this._makeGeoPath(); this.chartContainer.children.push(this.seriesContainer); if (this.get("translateX") == null) { this.set("translateX", this.width() / 2); } if (this.get("translateY") == null) { this.set("translateY", this.height() / 2); } // Setting trasnparent background so that full body of the plot container // is interactive this.chartContainer.set("interactive", true); this.chartContainer.set("interactiveChildren", false); this.chartContainer.set("background", Rectangle.new(this._root, { themeTags: ["map", "background"], fill: Color.fromHex(0x000000), fillOpacity: 0 })); this._disposers.push(this.chartContainer.events.on("pointerdown", (event) => { this._handleChartDown(event); })); this._disposers.push(this.chartContainer.events.on("globalpointerup", (event) => { this._handleChartUp(event); })); this._disposers.push(this.chartContainer.events.on("globalpointermove", (event) => { this._handleChartMove(event); })); let license = false; for (let i = 0; i < registry.licenses.length; i++) { if (registry.licenses[i].match(/^AM5M.{5,}/i)) { license = true; } } if (!license) { this._root._showBranding(); } this._setUpTouch(); } _handleChartDown(event) { this._downZoomLevel = this.get("zoomLevel", 1); const downPoints = this.chartContainer._downPoints; let count = $object.keys(downPoints).length; if (count == 1) { // workaround to solve a problem when events are added to some children of chart container (rotation stops working) let downPoint = downPoints[1]; if (!downPoint) { downPoint = downPoints[0]; } if (downPoint && (downPoint.x == event.point.x && downPoint.y == event.point.y)) { count = 0; } } if (count > 0) { this._downTranslateX = this.get("translateX"); this._downTranslateY = this.get("translateY"); this._downRotationX = this.get("rotationX"); this._downRotationY = this.get("rotationY"); this._downRotationZ = this.get("rotationZ"); const downId = this.chartContainer._getDownPointId(); if (downId) { let movePoint = this._movePoints[downId]; if (movePoint) { this.chartContainer._downPoints[downId] = movePoint; } } } else if (count == 0) { let bg = this.chartContainer.get("background"); if (bg) { bg.events.enableType("click"); } if (this.get("panX") || this.get("panY")) { if (this._za) { this._za.stop(); } if (this._txa) { this._txa.stop(); } if (this._tya) { this._tya.stop(); } if (this._rxa) { this._rxa.stop(); } if (this._rya) { this._rya.stop(); } const downPoint = this.chartContainer._display.toLocal(event.point); this._downTranslateX = this.get("translateX"); this._downTranslateY = this.get("translateY"); this._downRotationX = this.get("rotationX"); this._downRotationY = this.get("rotationY"); this._downRotationZ = this.get("rotationZ"); let projection = this.get("projection"); if (projection.invert) { let l0 = projection.invert([downPoint.x, downPoint.y]); let l1 = projection.invert([downPoint.x + 1, downPoint.y + 1]); if (l0 && l1) { this._pLon = Math.abs(l1[0] - l0[0]); this._pLat = Math.abs(l1[1] - l0[1]); } } } } } /** * Converts screen coordinates (X and Y) within chart to latitude and * longitude. * * @param point Screen coordinates * @return Geographical coordinates */ invert(point) { let projection = this.get("projection"); if (projection.invert) { const ll = projection.invert([point.x, point.y]); if (ll) { return { longitude: ll[0], latitude: ll[1] }; } } return { longitude: 0, latitude: 0 }; } /** * Converts latitude/longitude to screen coordinates (X and Y). * * @param point Geographical coordinates * @param rotationX X rotation of a map if different from current * @param rotationY Y rotation of a map if different from current * * @return Screen coordinates */ convert(point, rotationX, rotationY) { let projection = this.get("projection"); let xy; if (!projection.rotate) { rotationX = undefined; rotationY = undefined; } if (rotationX != null || rotationY != null) { if (rotationX == null) { rotationX = 0; } if (rotationY == null) { rotationY = 0; } let rotation = projection.rotate(); projection.rotate([rotationX, rotationY, 0]); xy = projection([point.longitude, point.latitude]); projection.rotate(rotation); } else { xy = projection([point.longitude, point.latitude]); } if (xy) { return { x: xy[0], y: xy[1] }; } return { x: 0, y: 0 }; } _handleChartUp(_event) { this.chartContainer._downPoints = {}; } _handlePinch() { const chartContainer = this.chartContainer; let i = 0; let downPoints = []; let movePoints = []; $object.each(chartContainer._downPoints, (k, point) => { downPoints[i] = point; let movePoint = this._movePoints[k]; if (movePoint) { movePoints[i] = movePoint; } i++; }); if (downPoints.length > 1 && movePoints.length > 1) { const display = chartContainer._display; let downPoint0 = downPoints[0]; let downPoint1 = downPoints[1]; let movePoint0 = movePoints[0]; let movePoint1 = movePoints[1]; if (downPoint0 && downPoint1 && movePoint0 && movePoint1) { downPoint0 = display.toLocal(downPoint0); downPoint1 = display.toLocal(downPoint1); movePoint0 = display.toLocal(movePoint0); movePoint1 = display.toLocal(movePoint1); let initialDistance = Math.hypot(downPoint1.x - downPoint0.x, downPoint1.y - downPoint0.y); let currentDistance = Math.hypot(movePoint1.x - movePoint0.x, movePoint1.y - movePoint0.y); let level = currentDistance / initialDistance * this._downZoomLevel; level = $math.fitToRange(level, this.get("minZoomLevel", 1), this.get("maxZoomLevel", 32)); let moveCenter = { x: movePoint0.x + (movePoint1.x - movePoint0.x) / 2, y: movePoint0.y + (movePoint1.y - movePoint0.y) / 2 }; let downCenter = { x: downPoint0.x + (downPoint1.x - downPoint0.x) / 2, y: downPoint0.y + (downPoint1.y - downPoint0.y) / 2 }; let tx = this._downTranslateX || 0; let ty = this._downTranslateY || 0; let zoomLevel = this._downZoomLevel; let xx = moveCenter.x - (-tx + downCenter.x) / zoomLevel * level; let yy = moveCenter.y - (-ty + downCenter.y) / zoomLevel * level; this.set("zoomLevel", level); this.set("translateX", xx); this.set("translateY", yy); } } } _handleChartMove(event) { const chartContainer = this.chartContainer; let downPoint = chartContainer._getDownPoint(); const downPointId = chartContainer._getDownPointId(); const originalEvent = event.originalEvent; const pointerId = originalEvent.pointerId; if (this.get("pinchZoom")) { if (pointerId) { this._movePoints[pointerId] = event.point; if ($object.keys(chartContainer._downPoints).length > 1) { this._handlePinch(); return; } } } if (downPointId && pointerId && pointerId != downPointId) { return; } else { if (downPoint) { const panX = this.get("panX"); const panY = this.get("panY"); if (panX != "none" || panY != "none") { const display = chartContainer._display; let local = display.toLocal(event.point); downPoint = display.toLocal(downPoint); let x = this._downTranslateX; let y = this._downTranslateY; if (Math.hypot(downPoint.x - local.x, downPoint.y - local.y) > 5) { let bg = chartContainer.get("background"); if (bg) { bg.events.disableType("click"); } if ($type.isNumber(x) && $type.isNumber(y)) { let projection = this.get("projection"); const zoomLevel = this.get("zoomLevel", 1); const maxPanOut = this.get("maxPanOut", 0.4); const bounds = this._mapBounds; const w = this.width(); const h = this.height(); const ww = bounds[1][0] - bounds[0][0]; const hh = bounds[1][1] - bounds[0][1]; if (panX == "translateX") { x += local.x - downPoint.x; const cx = w / 2 - (w / 2 - this._centerX) * zoomLevel; x = Math.min(x, cx + ww * maxPanOut * zoomLevel); x = Math.max(x, cx - ww * maxPanOut * zoomLevel); } if (panY == "translateY") { y += local.y - downPoint.y; const cy = h / 2 - (h / 2 - this._centerY) * zoomLevel; y = Math.min(y, cy + hh * maxPanOut * zoomLevel); y = Math.max(y, cy - hh * maxPanOut * zoomLevel); } this.set("translateX", x); this.set("translateY", y); if (projection.invert) { let downLocation = projection.invert([downPoint.x, downPoint.y]); if (location && downLocation) { if (panX == "rotateX") { this.set("rotationX", this._downRotationX - (downPoint.x - local.x) * this._pLon); } if (panY == "rotateY") { this.set("rotationY", this._downRotationY + (downPoint.y - local.y) * this._pLat); } } } } } } } } } _handleWheelRotateY(delta, duration, easing) { this._rya = this.animate({ key: "rotationY", to: this.get("rotationY", 0) - delta, duration: duration, easing: easing }); } _handleWheelRotateX(delta, duration, easing) { this._rxa = this.animate({ key: "rotationX", to: this.get("rotationX", 0) - delta, duration: duration, easing: easing }); } _handleWheelZoom(delta, point) { let step = this.get("zoomStep", 2); let zoomLevel = this.get("zoomLevel", 1); let newZoomLevel = zoomLevel; if (delta > 0) { newZoomLevel = zoomLevel / step; } else if (delta < 0) { newZoomLevel = zoomLevel * step; } if (newZoomLevel != zoomLevel) { this.zoomToPoint(point, newZoomLevel); } } /** * Zoom the map to geographical bounds. * * @param geoBounds Bounds * @param duration Animation duration in milliseconds * @param rotationX X rotation of a map at the end of zoom * @param rotationY Y rotation of a map at the end of zoom */ zoomToGeoBounds(geoBounds, duration, rotationX, rotationY) { if (geoBounds.right < geoBounds.left) { geoBounds.right = 180; geoBounds.left = -180; } const geoPath = this.getPrivate("geoPath"); const mapBounds = geoPath.bounds(this._geometryColection); let p0 = this.convert({ longitude: geoBounds.left, latitude: geoBounds.top }, rotationX, rotationY); let p1 = this.convert({ longitude: geoBounds.right, latitude: geoBounds.bottom }, rotationX, rotationY); if (p0.y < mapBounds[0][1]) { p0.y = mapBounds[0][1]; } if (p1.y > mapBounds[1][1]) { p1.y = mapBounds[1][1]; } let zl = this.get("zoomLevel", 1); let bounds = { left: p0.x, right: p1.x, top: p0.y, bottom: p1.y }; let seriesContainer = this.seriesContainer; let zoomLevel = .9 * Math.min(seriesContainer.innerWidth() / (bounds.right - bounds.left) * zl, seriesContainer.innerHeight() / (bounds.bottom - bounds.top) * zl); let x = bounds.left + (bounds.right - bounds.left) / 2; let y = bounds.top + (bounds.bottom - bounds.top) / 2; let geoPoint = this.invert({ x, y }); if (rotationX != null || rotationY != null) { this.rotate(rotationX, rotationY); } return this.zoomToGeoPoint(geoPoint, zoomLevel, true, duration); } /** * Zooms the map to specific screen point. * * @param point Point * @param level Zoom level * @param center Center the map * @param duration Duration of the animation in milliseconds */ zoomToPoint(point, level, center, duration) { if (level) { level = $math.fitToRange(level, this.get("minZoomLevel", 1), this.get("maxZoomLevel", 32)); } if (!$type.isNumber(duration)) { duration = this.get("animationDuration", 0); } const easing = this.get("animationEasing"); const zoomLevel = this.get("zoomLevel", 1); if (this.get("centerMapOnZoomOut") && level == this.get("homeZoomLevel", 1)) { point = this.convert(this.homeGeoPoint()); center = true; } let x = point.x; let y = point.y; let tx = this.get("translateX", 0); let ty = this.get("translateY", 0); let cx = x; let cy = y; if (center) { cx = this.width() / 2; cy = this.height() / 2; } let xx = cx - ((x - tx) / zoomLevel * level); let yy = cy - ((y - ty) / zoomLevel * level); this._txa = this.animate({ key: "translateX", to: xx, duration: duration, easing: easing }); this._tya = this.animate({ key: "translateY", to: yy, duration: duration, easing: easing }); this._za = this.animate({ key: "zoomLevel", to: level, duration: duration, easing: easing }); if (zoomLevel != level) { this._root.readerAlert(this._t("Zoom level changed to %1", this._root.locale, $type.numberToString(level))); } return this._za; } /** * Zooms the map to specific geographical point. * * @param geoPoint Point * @param level Zoom level * @param center Center the map * @param duration Duration of the animation in milliseconds * @param rotationX X rotation of a map at the end of zoom * @param rotationY Y rotation of a map at the end of zoom * */ zoomToGeoPoint(geoPoint, level, center, duration, rotationX, rotationY) { let xy = this.convert(geoPoint, rotationX, rotationY); if (rotationX != null || rotationY != null) { this.rotate(rotationX, rotationY, duration); } if (xy) { return this.zoomToPoint(xy, level, center, duration); } } rotate(rotationX, rotationY, duration) { const projection = this.get("projection"); if (!projection.rotate) { } else { if (!$type.isNumber(duration)) { duration = this.get("animationDuration", 0); } const easing = this.get("animationEasing"); if (rotationX != null) { this.animate({ key: "rotationX", to: rotationX, duration: duration, easing: easing }); } if (rotationY != null) { this.animate({ key: "rotationY", to: rotationY, duration: duration, easing: easing }); } } } /** * Zooms the map in. */ zoomIn() { return this.zoomToPoint({ x: this.width() / 2, y: this.height() / 2 }, this.get("zoomLevel", 1) * this.get("zoomStep", 2)); } /** * Zooms the map out. */ zoomOut() { return this.zoomToPoint({ x: this.width() / 2, y: this.height() / 2 }, this.get("zoomLevel", 1) / this.get("zoomStep", 2)); } _clearDirty() { super._clearDirty(); this._dirtyGeometries = false; this._mapFitted = false; } /** * Returns area of a mapPolygon in square pixels. */ getArea(dataItem) { const geoPath = this.getPrivate("geoPath"); const geometry = dataItem.get("geometry"); if (geometry) { return geoPath.area(geometry); } return 0; } } Object.defineProperty(MapChart, "className", { enumerable: true, configurable: true, writable: true, value: "MapChart" }); Object.defineProperty(MapChart, "classNames", { enumerable: true, configurable: true, writable: true, value: SerialChart.classNames.concat([MapChart.className]) }); //# sourceMappingURL=MapChart.js.map