UNPKG

@syncfusion/ej2-maps

Version:

The Maps component is used to visualize the geographical data and represent the statistical data of a particular geographical area on earth with user interactivity, and provides various customizing options

790 lines (774 loc) 160 kB
/* eslint-disable max-len */ import { Maps, Orientation, ITouches, ZoomSettings } from '../../index'; import { Point, getElementByID, Size, PathOption, Rect, convertGeoToPoint, CircleOption, convertTileLatLongToPoint, measureTextElement } from '../utils/helper'; import { RectOption, createTooltip, calculateScale, getTouchCenter, getTouches, targetTouches, Coordinate } from '../utils/helper'; import { MapLocation, zoomAnimate, smoothTranslate , measureText, textTrim, clusterTemplate, marker, getProcessedMarginValue } from '../utils/helper'; import { markerTemplate, removeElement, getElement, clusterSeparate, markerColorChoose, calculatePolygonPath } from '../utils/helper'; import { markerShapeChoose } from '../utils/helper'; import { isNullOrUndefined, EventHandler, Browser, remove, createElement, animationMode } from '@syncfusion/ej2-base'; import { MarkerSettings, LayerSettings, changeBorderWidth, IMarkerRenderingEventArgs, markerRendering} from '../index'; import { IMapZoomEventArgs, IMapPanEventArgs, IMinMaxLatitudeLongitude, GeoPosition } from '../model/interface'; import { pan } from '../model/constants'; import { getValueFromObject } from '../utils/helper'; import { PanDirection } from '../utils/enum'; import { FontModel, DataLabelSettingsModel, BorderModel, ZoomToolbarButtonSettingsModel, ZoomToolbarTooltipSettingsModel, ZoomToolbarSettingsModel, PolygonSettingModel } from '../model/base-model'; import { MapsTooltip } from './tooltip'; /** * Zoom module used to process the zoom for maps */ export class Zoom { private maps: Maps; /** @private */ public toolBarGroup: Element; private currentToolbarEle: Element; /** @private */ public zoomingRect: Rect; /** @private */ public selectionColor: string; private fillColor: string; private zoomElements: Element; private panElements: Element; /** @private */ public isPanModeEnabled: boolean = false; /** @private */ public mouseEnter: boolean = false; /** @private */ public baseTranslatePoint: Point; private wheelEvent: string; private cancelEvent: string; /** @private */ public currentScale: number; /** @private */ public isTouch: boolean = false; /** @private */ public rectZoomingStart: boolean = false; /** @private */ public touchStartList: ITouches[] | TouchList; /** @private */ public touchMoveList: ITouches[] | TouchList; /** @private */ public previousTouchMoveList: ITouches[] | TouchList; /** @private */ public mouseDownPoints: Point; /** @private */ public mouseMovePoints: Point; /** @private */ public isDragZoom: boolean; /** @private */ public currentLayer: LayerSettings; private panColor: string; private clearTimeout: number; /** @private */ public zoomColor: string; /** @private */ public browserName: string = Browser.info.name; /** @private */ public isPointer: boolean = Browser.isPointer; private handled: boolean = false; private fingers: number; /** @private */ public firstMove: boolean; /** @private */ public isPanningInProgress: boolean = false; private isPan: boolean = false; private isZoomFinal: boolean = false; private isZoomSelection: boolean = false; private interaction: string; private lastScale: number; private pinchFactor: number = 1; // eslint-disable-next-line @typescript-eslint/no-explicit-any private startTouches: any[] = []; private index: number; private templateCount: number; private pinchDistance: number; /** @private */ public startDistance: number; /** @private */ public touchCenter: Point; /** @private */ public pinchStartLatLong: object; /** @private */ public isCancellation: boolean = false; private pinchTileZoomScale: number = 1; private tileZoomLevel: number = 1; private pinchZoomScale: number = 1; private isPinchZooming: boolean = false; /** @private */ public mouseDownLatLong: object = { x: 0, y: 0 }; /** @private */ public mouseMoveLatLong: object = { x: 0, y: 0 }; /** @private */ public isSingleClick: boolean = false; /** @private */ public layerCollectionEle: Element; constructor(maps: Maps) { this.maps = maps; this.wheelEvent = this.browserName === 'mozilla' ? (this.isPointer ? 'mousewheel' : 'DOMMouseScroll') : 'mousewheel'; this.cancelEvent = this.isPointer ? 'pointerleave' : 'mouseleave'; this.selectionColor = this.maps.zoomSettings.toolbarSettings.buttonSettings.selectionColor; this.fillColor = this.maps.zoomSettings.toolbarSettings.buttonSettings.color; this.addEventListener(); } /** * To perform zooming for maps. * * @param {Point} position - Specifies the position. * @param {number} newZoomFactor - Specifies the zoom factor. * @param {string} type - Specifies the type. * @param {boolean} isMouseWheel - Indicates whether the zoom operation was triggered by the mouse wheel. * @returns {void} * @private */ public performZooming(position: Point, newZoomFactor: number, type: string, isMouseWheel: boolean = false): void { const map: Maps = this.maps; map.previousProjection = newZoomFactor <= 1.5 ? undefined : map.projectionType; map.defaultState = false; map.initialCheck = false; map.markerZoomedState = map.isMarkerZoomCompleted = false; map.zoomPersistence = map.enablePersistence; const prevLevel: number = map.tileZoomLevel; const maxZoom: number = map.zoomSettings.maxZoom; const minZoom: number = map.zoomSettings.minZoom; newZoomFactor = maxZoom >= newZoomFactor ? newZoomFactor : maxZoom; let isToolbarPerform: boolean = true; switch (type.toLowerCase()) { case 'zoomin': isToolbarPerform = newZoomFactor <= this.maps.zoomSettings.maxZoom; break; case 'zoomout': isToolbarPerform = newZoomFactor >= this.maps.zoomSettings.minZoom; break; } if (isToolbarPerform) { const scale: number = map.previousScale = map.scale; const prevTilePoint: Point = map.tileTranslatePoint; if ((!map.isTileMap) && ((type === 'ZoomIn' ? newZoomFactor >= minZoom && newZoomFactor <= maxZoom : newZoomFactor >= minZoom) || map.isReset)) { const availSize: Rect = map.mapAreaRect; // eslint-disable-next-line @typescript-eslint/no-explicit-any const minBounds: any = map.baseMapRectBounds['min'] as any; // eslint-disable-next-line @typescript-eslint/no-explicit-any const maxBounds: any = map.baseMapRectBounds['max'] as any; let mapTotalWidth: number = Math.abs(minBounds['x'] - maxBounds['x']); let mapTotalHeight: number = Math.abs(minBounds['y'] - maxBounds['y']); let translatePointX: number; let translatePointY: number; if (newZoomFactor < 1.2 && map.projectionType !== 'Eckert5') { if (mapTotalWidth === 0 || mapTotalHeight === 0 || mapTotalWidth === mapTotalHeight) { mapTotalWidth = availSize.width / 2; mapTotalHeight = availSize.height; } newZoomFactor = parseFloat(Math.min(availSize.width / mapTotalWidth, availSize.height / mapTotalHeight).toFixed(2)); newZoomFactor = newZoomFactor > 1.05 ? 1 : newZoomFactor; map.translatePoint = this.calculateInitalZoomTranslatePoint(newZoomFactor, mapTotalWidth, mapTotalHeight, availSize, minBounds, map); } else { const point: Point = map.translatePoint; translatePointX = point.x - (((availSize.width / scale) - (availSize.width / newZoomFactor)) / (availSize.width / position.x)); translatePointY = point.y - (((availSize.height / scale) - (availSize.height / newZoomFactor)) / (availSize.height / position.y)); const currentHeight: number = Math.abs(map.baseMapRectBounds['max']['y'] - map.baseMapRectBounds['min']['y']) * newZoomFactor; translatePointX = (currentHeight < map.mapAreaRect.height) ? (availSize.x + ((-(minBounds['x'])) + ((availSize.width / 2) - (mapTotalWidth / 2)))) : translatePointX; translatePointY = (currentHeight < map.mapAreaRect.height) ? (availSize.y + ((-(minBounds['y'])) + ((availSize.height / 2) - (mapTotalHeight / 2)))) : translatePointY; map.translatePoint = new Point(translatePointX, translatePointY); } map.scale = newZoomFactor; map.zoomTranslatePoint = map.translatePoint; if (this.triggerZoomEvent(prevTilePoint, prevLevel, type)) { map.translatePoint = map.previousPoint; map.scale = map.mapScaleValue = map.previousScale; } else { this.applyTransform(map, isMouseWheel); } } else if ((map.isTileMap) && (newZoomFactor >= minZoom && newZoomFactor <= maxZoom)) { this.getTileTranslatePosition(prevLevel, newZoomFactor, position, type); map.tileZoomLevel = newZoomFactor; map.zoomSettings.zoomFactor = newZoomFactor; map.scale = Math.pow(2, newZoomFactor - 1); if (type === 'ZoomOut' && map.zoomSettings.resetToInitial && map.applyZoomReset && newZoomFactor <= map.initialZoomLevel) { map.initialCheck = true; map.zoomPersistence = false; map.tileTranslatePoint.x = map.initialTileTranslate.x; map.tileTranslatePoint.y = map.initialTileTranslate.y; newZoomFactor = map.tileZoomLevel = map.mapScaleValue = map.initialZoomLevel; map.scale = Math.pow(2, newZoomFactor - 1); } map.mapScaleValue = isNaN(map.mapScaleValue) ? 1 : map.mapScaleValue; map.translatePoint.y = (map.tileTranslatePoint.y - (0.01 * map.mapScaleValue)) / map.scale; map.translatePoint.x = (map.tileTranslatePoint.x - (0.01 * map.mapScaleValue)) / map.scale; if (this.triggerZoomEvent(prevTilePoint, prevLevel, type)) { map.translatePoint = map.tileTranslatePoint = new Point(0, 0); map.scale = map.previousScale; map.tileZoomLevel = prevLevel; map.zoomSettings.zoomFactor = map.previousScale; } else { if (document.querySelector('.GroupElement')) { (document.querySelector('.GroupElement') as HTMLElement).style.display = 'none'; } if (document.getElementById(this.maps.element.id + '_LayerIndex_1')) { document.getElementById(this.maps.element.id + '_LayerIndex_1').style.display = 'none'; } this.markerLineAnimation(map); map.mapLayerPanel.generateTiles(newZoomFactor, map.tileTranslatePoint, type + 'wheel', null, position); const animationDuration: number = this.maps.layersCollection[0].animationDuration === 0 && animationMode === 'Enable' ? 1000 : this.maps.layersCollection[0].animationDuration; setTimeout(() => { // if (type === 'ZoomOut') { // element1.removeChild(element1.children[element1.childElementCount - 1]); // if (element1.childElementCount) { // element1.removeChild(element1.children[element1.childElementCount - 1]); // } else { // element1 = element1; // } // } this.applyTransform(this.maps, isMouseWheel); if (document.getElementById(this.maps.element.id + '_LayerIndex_1')) { document.getElementById(this.maps.element.id + '_LayerIndex_1').style.display = 'block'; } }, animationDuration); } } this.triggerZoomComplete(map, prevLevel, type); } this.maps.zoomNotApplied = false; if (this.maps.isDevice) { this.removeToolbarOpacity(map.isTileMap ? Math.round(map.tileZoomLevel) : map.scale, map.element.id + '_Zooming_'); } } // eslint-disable-next-line @typescript-eslint/no-explicit-any private calculateInitalZoomTranslatePoint(newZoomFactor: number, mapTotalWidth: number, mapTotalHeight: number, availSize: Rect, minBounds: any, map: Maps): Point { mapTotalWidth *= newZoomFactor; mapTotalHeight *= newZoomFactor; const widthDiff: number = minBounds['x'] !== 0 && map.translateType === 'layers' ? map.availableSize.width - availSize.width : 0; const translatePointX: number = availSize.x + ((-(minBounds['x'])) + ((availSize.width / 2) - (mapTotalWidth / 2))) - widthDiff; const translatePointY: number = availSize.y + ((-(minBounds['y'])) + ((availSize.height / 2) - (mapTotalHeight / 2))); return new Point(translatePointX, translatePointY); } private triggerZoomEvent(prevTilePoint: Point, prevLevel: number, type: string): boolean { const map: Maps = this.maps; let zoomArgs: IMapZoomEventArgs; if (map.isTileMap) { map.mapScaleValue = isNullOrUndefined(map.mapScaleValue) ? 1 : map.mapScaleValue; map.translatePoint.y = (map.tileTranslatePoint.y - (0.01 * map.mapScaleValue)) / map.scale; map.translatePoint.x = (map.tileTranslatePoint.x - (0.01 * map.mapScaleValue)) / map.scale; } const minMaxLatitudeLongitude : IMinMaxLatitudeLongitude = this.maps.getMinMaxLatitudeLongitude(); if (!map.isTileMap) { zoomArgs = { cancel: false, name: 'zoom', type: type, maps: map, tileTranslatePoint: {}, translatePoint: { previous: map.previousPoint, current: map.translatePoint }, tileZoomLevel: {}, scale: { previous: map.previousScale, current: map.scale }, minLatitude: minMaxLatitudeLongitude.minLatitude, maxLatitude: minMaxLatitudeLongitude.maxLatitude, minLongitude: minMaxLatitudeLongitude.minLongitude, maxLongitude: minMaxLatitudeLongitude.maxLongitude }; } else { zoomArgs = { cancel: false, name: 'zoom', type: type, maps: map, tileTranslatePoint: { previous: prevTilePoint, current: map.tileTranslatePoint }, translatePoint: { previous: map.previousPoint, current: map.translatePoint }, tileZoomLevel: { previous: prevLevel, current: map.tileZoomLevel }, scale: { previous: map.previousScale, current: map.scale }, minLatitude: minMaxLatitudeLongitude.minLatitude, maxLatitude: minMaxLatitudeLongitude.maxLatitude, minLongitude: minMaxLatitudeLongitude.minLongitude, maxLongitude: minMaxLatitudeLongitude.maxLongitude }; } map.trigger('zoom', zoomArgs); return zoomArgs.cancel; } private getTileTranslatePosition(prevLevel: number, currentLevel: number, position: Point, type?: string): void { const map: Maps = this.maps; const tileDefaultSize: number = 256; const padding: number = type === 'ZoomOut' ? 10 : (type === 'Reset' && currentLevel > 1) ? 0 : 10; const bounds: Size = map.availableSize; const prevSize: number = Math.pow(2, prevLevel) * 256; const totalSize: number = Math.pow(2, currentLevel) * 256; const x: number = ((position.x - map.tileTranslatePoint.x) / prevSize) * 100; const y: number = ((position.y - map.tileTranslatePoint.y) / prevSize) * 100; map.tileTranslatePoint.x = (currentLevel === 1) ? (bounds.width / 2) - ((tileDefaultSize * 2) / 2) : position.x - ((x * totalSize) / 100); map.tileTranslatePoint.y = (currentLevel === 1) ? ((bounds.height / 2) - ((tileDefaultSize * 2) / 2) + (padding * 2)) : position.y - ((y * totalSize) / 100); } private getTileTranslate(currentLevel: number, type?: string): void { const map: Maps = this.maps; const padding: number = type === 'ZoomOut' ? 10 : (type === 'Reset' && currentLevel > 1) ? 0 : 10; const bounds: Size = map.availableSize; const totalSize: number = Math.pow(2, currentLevel) * 256; let x: number = (bounds.width / 2) - (totalSize / 2); let y: number = (bounds.height / 2) - (totalSize / 2); const position: Point = convertTileLatLongToPoint( new MapLocation(this.pinchStartLatLong['longitude'], this.pinchStartLatLong['latitude']), currentLevel, {x: x, y: y}, true ); x -= position.x - (bounds.width / 2); y = y - (position.y - (bounds.height / 2)) + padding; const scale: number = Math.pow(2, currentLevel - 1); map.tileTranslatePoint.x = x; map.tileTranslatePoint.y = y; map.translatePoint.x = (x - (0.01 * this.tileZoomLevel)) / scale; map.translatePoint.y = (y - (0.01 * this.tileZoomLevel)) / scale; } /** * @returns {void} * @private */ public performRectZooming(): void { this.isDragZoom = true; const map: Maps = this.maps; const size: Size = map.availableSize; map.previousProjection = map.projectionType; const prevLevel: number = map.tileZoomLevel; const prevTilePoint: Point = map.tileTranslatePoint; const zoomRect: Rect = this.zoomingRect; const maxZoom: number = map.zoomSettings.maxZoom; const minZoom: number = map.zoomSettings.minZoom; let isZoomCancelled: boolean; if (zoomRect.height > 0 && zoomRect.width > 0) { const x: number = this.zoomingRect.x + (this.zoomingRect.width / 2); const y: number = this.zoomingRect.y + (this.zoomingRect.height / 2); let zoomCalculationFactor: number; if (!map.isTileMap) { const scale: number = map.previousScale = map.scale; zoomCalculationFactor = scale + Math.round((((size.width / zoomRect.width) + (size.height / zoomRect.height)) / 2)); zoomCalculationFactor = zoomCalculationFactor < this.maps.zoomSettings.maxZoom ? zoomCalculationFactor : this.maps.zoomSettings.maxZoom; const translatePoint: Point = map.previousPoint = map.translatePoint; if (zoomCalculationFactor <= maxZoom) { const translatePointX: number = translatePoint.x - (((size.width / scale) - (size.width / zoomCalculationFactor)) / (size.width / x)); const translatePointY: number = translatePoint.y - (((size.height / scale) - (size.height / zoomCalculationFactor)) / (size.height / y)); map.translatePoint = new Point(translatePointX, translatePointY); } map.scale = zoomCalculationFactor < this.maps.zoomSettings.maxZoom ? zoomCalculationFactor : this.maps.zoomSettings.maxZoom; isZoomCancelled = this.triggerZoomEvent(prevTilePoint, prevLevel, ''); if (isZoomCancelled) { map.translatePoint = map.previousPoint; map.scale = map.previousScale; } map.zoomTranslatePoint = map.translatePoint; } else { zoomCalculationFactor = prevLevel + (Math.round(prevLevel + (((size.width / zoomRect.width) + (size.height / zoomRect.height)) / 2))); zoomCalculationFactor = (zoomCalculationFactor >= minZoom && zoomCalculationFactor <= maxZoom) ? zoomCalculationFactor : maxZoom; map.zoomSettings.zoomFactor = zoomCalculationFactor; this.getTileTranslatePosition(prevLevel, zoomCalculationFactor, { x: x, y: y }); map.tileZoomLevel = zoomCalculationFactor; map.translatePoint.x = (map.tileTranslatePoint.x - (0.5 * Math.pow(2, zoomCalculationFactor))) / (Math.pow(2, zoomCalculationFactor)); map.translatePoint.y = (map.tileTranslatePoint.y - (0.5 * Math.pow(2, zoomCalculationFactor))) / (Math.pow(2, zoomCalculationFactor)); map.scale = (Math.pow(2, zoomCalculationFactor)); isZoomCancelled = this.triggerZoomEvent(prevTilePoint, prevLevel, ''); if (isZoomCancelled) { map.translatePoint = map.tileTranslatePoint = new Point(0, 0); map.scale = map.tileZoomLevel = map.zoomSettings.zoomFactor = prevLevel; } else { map.mapLayerPanel.generateTiles(zoomCalculationFactor, map.tileTranslatePoint); } } if (!isZoomCancelled) { map.mapScaleValue = zoomCalculationFactor; this.applyTransform(map, false, true); this.maps.zoomNotApplied = false; this.zoomingRect = null; } } this.isZoomFinal = this.isZoomSelection && Math.round(map.scale) === this.maps.zoomSettings.maxZoom; this.triggerZoomComplete(map, prevLevel, ''); this.removeToolbarOpacity(map.scale, this.maps.element.id + '_Zooming_'); } private setInteraction(newInteraction: string): void { this.lastScale = 1; this.interaction = newInteraction; } private updateInteraction(): void { if (this.fingers === 2) { this.setInteraction('zoom'); } else { this.setInteraction(null); } } private tilePinchingProcess (scale: number): void { this.tileZoomLevel = Math.round(scale); this.getTileTranslate(this.tileZoomLevel); this.maps.mapLayerPanel.generateTiles(this.tileZoomLevel, this.maps.tileTranslatePoint, null, null, null, true); } /** * @param {PointerEvent} e - Specifies the vent in the map * @returns {void} * @private */ // eslint-disable-next-line @typescript-eslint/no-unused-vars public performPinchZooming(e: PointerEvent | TouchEvent): void { const map: Maps = this.maps; const prevLevel: number = map.tileZoomLevel; let zoomCalculationFactor: number = this.pinchFactor; let isZoomCancelled: boolean; const prevTilePoint: Point = map.tileTranslatePoint; this.maps.mergeCluster(); if (!map.isTileMap) { const availSize: Rect = map.mapAreaRect; map.isMarkerZoomCompleted = false; map.previousScale = map.scale; map.previousPoint = map.translatePoint; map.previousProjection = map.projectionType; const scale: number = calculateScale(<ITouches[]>this.touchStartList, <ITouches[]>this.touchMoveList); const touchCenter: Point = getTouchCenter(getTouches(<ITouches[]>this.touchMoveList, this.maps)); const newScale: number = scale / this.lastScale; this.lastScale = scale; this.pinchFactor *= newScale; this.pinchFactor = Math.min(this.maps.zoomSettings.maxZoom, Math.max(this.pinchFactor, this.maps.zoomSettings.minZoom)); // eslint-disable-next-line @typescript-eslint/no-explicit-any const minBounds: any = map.baseMapRectBounds['min'] as any; // eslint-disable-next-line @typescript-eslint/no-explicit-any const maxBounds: any = map.baseMapRectBounds['max'] as any; let mapTotalHeight: number = Math.abs(minBounds['y'] - maxBounds['y']); let mapTotalWidth: number = Math.abs(minBounds['x'] - maxBounds['x']); const translatePoint: Point = map.translatePoint; let translatePointX: number; let translatePointY: number; if (zoomCalculationFactor < 1.2 && map.projectionType !== 'Eckert5') { if (mapTotalWidth === 0 || mapTotalHeight === 0 || mapTotalWidth === mapTotalHeight) { mapTotalWidth = availSize.width / 2; mapTotalHeight = availSize.height; } zoomCalculationFactor = parseFloat(Math.min(availSize.width / mapTotalWidth, availSize.height / mapTotalHeight).toFixed(2)); zoomCalculationFactor = zoomCalculationFactor > 1.05 ? 1 : zoomCalculationFactor; map.translatePoint = this.calculateInitalZoomTranslatePoint(zoomCalculationFactor, mapTotalWidth, mapTotalHeight, availSize, minBounds, map); } else { const currentHeight: number = Math.abs(map.baseMapRectBounds['max']['y'] - map.baseMapRectBounds['min']['y']) * zoomCalculationFactor; translatePointX = translatePoint.x - (((availSize.width / map.scale) - (availSize.width / zoomCalculationFactor)) / (availSize.width / touchCenter.x)); translatePointY = translatePoint.y - (((availSize.height / map.scale) - (availSize.height / zoomCalculationFactor)) / (availSize.height / touchCenter.y)); translatePointX = (currentHeight < map.mapAreaRect.height) ? (availSize.x + ((-(minBounds['x'])) + ((availSize.width / 2) - (mapTotalWidth / 2)))) : translatePointX; translatePointY = (currentHeight < map.mapAreaRect.height) ? (availSize.y + ((-(minBounds['y'])) + ((availSize.height / 2) - (mapTotalHeight / 2)))) : translatePointY; map.translatePoint = new Point(translatePointX, translatePointY); } map.scale = zoomCalculationFactor; isZoomCancelled = this.triggerZoomEvent(prevTilePoint, prevLevel, ''); if (isZoomCancelled) { map.translatePoint = map.previousPoint; map.scale = map.previousScale; } } else { this.isPinchZooming = true; const touchCenter: Point = this.touchCenter; const touchOnePoint: MapLocation = this.getMousePosition(this.touchMoveList[0].pageX, this.touchMoveList[0].pageY); const touchTwoPoint: MapLocation = this.getMousePosition(this.touchMoveList[1].pageX, this.touchMoveList[1].pageY); const distance: number = Math.sqrt(Math.pow((touchOnePoint.x - touchTwoPoint.x), 2) + Math.pow((touchOnePoint.y - touchTwoPoint.y), 2)); const pinchScale: number = distance / this.startDistance; if (!isNullOrUndefined(this.pinchDistance)) { let pinchZoomFactor: number = Math.log2(pinchScale * (256 * Math.pow(2, prevLevel)) / 256); pinchZoomFactor = Math.min(map.zoomSettings.maxZoom, Math.max(map.zoomSettings.minZoom, pinchZoomFactor)); const scaleFactor: number = this.pinchDistance > distance ? (prevLevel * pinchScale) : pinchZoomFactor; let factor: number = pinchZoomFactor; let isZoomOut: boolean = false; if (this.pinchDistance > distance) { factor = (scaleFactor % 1); isZoomOut = true; } else if (this.pinchDistance < distance) { factor = (scaleFactor % 1) + 1; } const zoomFactor: number = Math.ceil(scaleFactor); if (zoomFactor > map.zoomSettings.minZoom && zoomFactor <= map.zoomSettings.maxZoom) { const element: HTMLElement = document.getElementById(map.element.id); if (element) { element.style.overflow = 'hidden'; } this.tileZoomLevel = zoomFactor; const transformOriginX: number = (touchCenter.x / (map.mapAreaRect.width - map.mapAreaRect.x)) * 100; const transformOriginY: number = (touchCenter.y / (map.mapAreaRect.height - map.mapAreaRect.y)) * 100; const tilesParent: HTMLElement = document.getElementById(map.element.id + '_tile_parent'); let copyTilesParent: HTMLElement = document.getElementById(map.element.id + '_tiles'); if (!copyTilesParent) { copyTilesParent = document.createElement('div'); copyTilesParent.id = map.element.id + '_tiles'; map.element.appendChild(copyTilesParent); this.copyStyles(tilesParent, copyTilesParent); copyTilesParent.style.zIndex = '0'; } copyTilesParent.style.visibility = 'hidden'; tilesParent.style.transformOrigin = `${transformOriginX}% ${transformOriginY}%`; tilesParent.style.transform = `scale(${factor})`; const svgElement: HTMLElement = document.getElementById(map.element.id + '_Tile_SVG_Parent'); svgElement.style.transformOrigin = `${transformOriginX}% ${transformOriginY}%`; svgElement.style.transform = `scale(${factor})`; if (!this.isCancellation && (0.2 >= scaleFactor % 1 && scaleFactor % 1 >= 0.1 && !isZoomOut) || (scaleFactor % 1 <= 0.9 && isZoomOut)) { let animateTile: HTMLElement = document.getElementById(map.element.id + '_animates_tiles'); if (!animateTile) { animateTile = document.createElement('div'); animateTile.id = map.element.id + '_animates_tiles'; animateTile.classList.add(this.tileZoomLevel.toString()); copyTilesParent.appendChild(animateTile); } if (animateTile.childElementCount === 0) { this.pinchZoomScale = isZoomOut ? Math.floor(scaleFactor) : Math.ceil(scaleFactor); this.tilePinchingProcess(this.pinchZoomScale); this.isCancellation = true; } } if (this.isCancellation && (scaleFactor % 1 >= 0.99 && !isZoomOut) || (scaleFactor % 1 <= 0.1 && isZoomOut)) { if (tilesParent.style.transformOrigin !== '' && this.isCancellation) { tilesParent.style.transformOrigin = ''; tilesParent.style.transform = ''; svgElement.style.transformOrigin = ''; svgElement.style.transform = ''; this.pinchTileZoomScale = isZoomOut ? Math.floor(scaleFactor) : Math.ceil(scaleFactor); this.getTileTranslate(this.pinchTileZoomScale); const targetElement: HTMLElement = document.getElementById(map.element.id + '_animated_tiles'); const sourceElement: HTMLElement = document.getElementById(map.element.id + '_animates_tiles'); while (targetElement.firstChild) { targetElement.removeChild(targetElement.firstChild); } Array.from(sourceElement.children).forEach((child: Element) => { targetElement.appendChild(child.cloneNode(true)); }); document.getElementById(map.element.id + '_animated_tiles')['className'] = this.pinchTileZoomScale.toFixed(0); if (sourceElement) { while (sourceElement.firstChild) { sourceElement.removeChild(sourceElement.firstChild); } } this.isCancellation = false; map.mapScaleValue = this.pinchTileZoomScale; map.scale = Math.pow(2, this.pinchTileZoomScale - 1); this.applyTransform(map); } } } } this.pinchDistance = distance; } if (!map.isTileMap) { map.mapScaleValue = zoomCalculationFactor; if (!isZoomCancelled) { this.applyTransform(map); } this.triggerZoomComplete(map, prevLevel, ''); } if (Browser.isDevice) { this.removeToolbarOpacity(map.isTileMap ? Math.round(map.tileZoomLevel) : map.scale, map.element.id + '_Zooming_'); } } private copyStyles(sourceElement: HTMLElement, targetElement: HTMLElement): void { const sourceStyles: CSSStyleDeclaration = window.getComputedStyle(sourceElement); Array.from(sourceStyles).forEach((style: string) => { targetElement.style[style as string] = sourceStyles.getPropertyValue(style); }); } private getTouchCenterPoint(): Point { const touchList: Point[] = []; for (let i: number = 0; i < this.touchMoveList.length; i++) { touchList.push(this.getMousePosition(this.touchMoveList[i as number].pageX, this.touchMoveList[i as number].pageY)); } return { x: (touchList[0].x + touchList[1].x) / 2, y: (touchList[0].y + touchList[1].y) / 2 }; } private triggerZoomComplete(map: Maps, prevLevel: number, type: string): void { if (map.zoomSettings.enable) { let zoomArgs: IMapZoomEventArgs; if (map.isTileMap) { map.mapScaleValue = isNullOrUndefined(map.mapScaleValue) ? 1 : map.mapScaleValue; map.translatePoint.y = (map.tileTranslatePoint.y - (0.01 * map.mapScaleValue)) / map.scale; map.translatePoint.x = (map.tileTranslatePoint.x - (0.01 * map.mapScaleValue)) / map.scale; } const minMaxLatitudeLongitude : IMinMaxLatitudeLongitude = this.maps.getMinMaxLatitudeLongitude(); if (!map.isTileMap) { zoomArgs = { cancel: false, name: 'zoomComplete', type: type, maps: map, tileTranslatePoint: {}, translatePoint: { previous: map.previousPoint, current: map.translatePoint }, tileZoomLevel: {}, scale: { previous: map.previousScale, current: map.scale }, minLatitude: minMaxLatitudeLongitude.minLatitude, maxLatitude: minMaxLatitudeLongitude.maxLatitude, minLongitude: minMaxLatitudeLongitude.minLongitude, maxLongitude: minMaxLatitudeLongitude.maxLongitude }; } else { zoomArgs = { cancel: false, name: 'zoomComplete', type: type, maps: map, tileTranslatePoint: { previous: map.tileTranslatePoint, current: map.tileTranslatePoint }, translatePoint: { previous: map.previousPoint, current: map.translatePoint }, tileZoomLevel: { previous: prevLevel, current: map.tileZoomLevel }, scale: { previous: map.previousScale, current: map.scale }, minLatitude: minMaxLatitudeLongitude.minLatitude, maxLatitude: minMaxLatitudeLongitude.maxLatitude, minLongitude: minMaxLatitudeLongitude.minLongitude, maxLongitude: minMaxLatitudeLongitude.maxLongitude }; } this.maps.trigger('zoomComplete', zoomArgs); } } /** * @returns {void} * @private */ public drawZoomRectangle(): void { const map: Maps = this.maps; const down: Point = this.mouseDownPoints; const move: Point = this.mouseMovePoints; // eslint-disable-next-line @typescript-eslint/no-explicit-any const border: any = { width: 1, color: this.maps.themeStyle.rectangleZoomBorderColor }; const width: number = Math.abs(move.x - down.x); const height: number = Math.abs(move.y - down.y); const x: number = ((move.x > down.x) ? down.x : down.x - width); const y: number = ((move.y > down.y) ? down.y : down.y - height); if ((x > map.mapAreaRect.x && x < (map.mapAreaRect.x + map.mapAreaRect.width)) && (y > map.mapAreaRect.y) && (y < map.mapAreaRect.y + map.mapAreaRect.height)) { this.zoomingRect = new Rect(x, y, width, height); const rectSVGObject: Element = map.renderer.createSvg({ id: map.element.id + '_Selection_Rect_Zooming', width: map.availableSize.width, height: map.availableSize.height, style: 'position: absolute;' }); (rectSVGObject as HTMLElement).style.position = 'absolute'; const rectOption: RectOption = new RectOption( map.element.id + '_ZoomRect', this.maps.themeStyle.rectangleZoomFillColor, border, this.maps.themeStyle.rectangleZoomFillOpacity, this.zoomingRect, 0, 0, '', '3' ); rectSVGObject.appendChild(map.renderer.drawRectangle(rectOption)); getElementByID(map.element.id + '_Secondary_Element').appendChild(rectSVGObject); } } /** * To animate the zooming process. * * @param {Element} element - Specifies the element * @param {boolean} animate - Specifies the boolean value * @param {number} x - Specifies the x value * @param {number} y - Specifies the y value * @param {number} scale - Specifies the scale value * @returns {void} */ private animateTransform(element: Element, animate: boolean, x: number, y: number, scale: number): void { const duration: number = this.currentLayer.animationDuration === 0 && animationMode === 'Enable' ? 1000 : this.currentLayer.animationDuration; if (!animate || duration === 0 || this.maps.isTileMap) { if (!(this.maps.isTileMap && element.id.indexOf('_Polygons_Group') > -1)) { element.setAttribute('transform', 'scale(' + (scale) + ') translate( ' + x + ' ' + y + ' )'); } return; } if (!this.maps.isTileMap) { zoomAnimate(element, 0, duration, new MapLocation(x, y), scale, this.maps.mapAreaRect, this.maps); } } /** * @param {Maps} maps - Specifies the Map control * @param {boolean} isMouseWheel - Indicates whether the zoom operation was triggered by the mouse wheel. * @param {boolean} animate - Specifies the animation is available or not * @param {boolean} isPanning - Specifies that it is panning or not * @returns {void} * @private */ public applyTransform(maps: Maps, isMouseWheel?: boolean, animate?: boolean, isPanning?: boolean): void { let layerIndex: number; this.templateCount = 0; let markerStyle: string; const scale: number = maps.scale; const x: number = maps.translatePoint.x; const y: number = maps.translatePoint.y; let currentLabelIndex: number = 0; maps.zoomShapeCollection = []; this.isPanningInProgress = isPanning || false; if (document.getElementById(maps.element.id + '_mapsTooltip')) { removeElement(maps.element.id + '_mapsTooltip'); } if (maps.isTileMap) { const element: HTMLElement = document.getElementById(maps.element.id + '_svg'); if (element) { for (let k: number = 0; k < maps.layers.length; k++) { const layerElement: Element = element.querySelector('#' + maps.element.id + '_LayerIndex_' + k); if (layerElement) { element.removeChild(layerElement); } } } } if (this.layerCollectionEle) { for (let i: number = 0; i < this.layerCollectionEle.childElementCount; i++) { const layerElement: Element = this.layerCollectionEle.childNodes[i as number] as Element; if (layerElement.tagName === 'g') { this.templateCount++; this.index = layerElement.id.indexOf('_LayerIndex_') > -1 && parseFloat(layerElement.id.split('_LayerIndex_')[1].split('_')[0]); this.currentLayer = <LayerSettings>maps.layersCollection[this.index]; const factor: number = maps.mapLayerPanel.calculateFactor(this.currentLayer); const elementCount: number = layerElement.childElementCount; const templateElement: Element = document.getElementById(maps.element.id + '_LayerIndex_' + this.index + '_Markers_Template_Group'); for (let j: number = 0; j < elementCount; j++) { let currentEle: Element = layerElement.childNodes[j as number] as Element; if (!(currentEle.id.indexOf('_Markers_Group') > -1) && (!(currentEle.id.indexOf('_bubble_Group') > -1)) && (!(currentEle.id.indexOf('_dataLableIndex_Group') > -1)) ) { if (maps.isTileMap && (currentEle.id.indexOf('_line_Group') > -1)) { currentEle.remove(); if (layerElement.children.length > 0 && layerElement.children[0]) { layerElement.insertBefore( maps.navigationLineModule.renderNavigation( this.currentLayer, this.isPinchZooming ? this.pinchZoomScale : maps.tileZoomLevel, this.index ), layerElement.children[1] ); } else { layerElement.appendChild(maps.navigationLineModule.renderNavigation(this.currentLayer, this.isPinchZooming ? this.pinchZoomScale : maps.tileZoomLevel, this.index)); } } else if (maps.isTileMap && (currentEle.id.indexOf('_Polygons_Group') > -1)){ if (this.currentLayer.polygonSettings.polygons.length > 0) { this.currentLayer.polygonSettings.polygons.map((polygonSettings: PolygonSettingModel, polygonIndex: number) => { const markerData: Coordinate[] = polygonSettings.points; const path: string = calculatePolygonPath(maps, this.isPinchZooming ? this.pinchZoomScale : maps.tileZoomLevel, this.currentLayer, markerData); const element: Element = document.getElementById(maps.element.id + '_LayerIndex_' + this.index + '_PolygonIndex_' + polygonIndex); if (!isNullOrUndefined(element)) { element.setAttribute('d', path); } }); document.getElementById(maps.element.id + '_LayerIndex_' + this.index + '_Polygons_Group').style.visibility = ''; } } else if (currentEle.id.indexOf('Legend') === -1) { changeBorderWidth(currentEle, this.index, scale, maps); maps.zoomTranslatePoint = maps.translatePoint; this.animateTransform(currentEle, animate, x, y, scale); } } else if (currentEle.id.indexOf('_Markers_Group') > -1) { if ((!this.isPanModeEnabled || !isPanning) && (!isNullOrUndefined(currentEle.childNodes[0]) || !isNullOrUndefined(templateElement.childNodes[0]))) { const processElement: Element = <Element>(!isNullOrUndefined(currentEle.childNodes[0]) ? currentEle.childNodes[0] : templateElement.childNodes[0]); this.markerTranslates(processElement, factor, x, y, scale, 'Marker', layerElement); } currentEle = layerElement.childNodes[j as number] as Element; if (!isNullOrUndefined(currentEle) && currentEle.id.indexOf('Markers') !== -1) { Array.prototype.forEach.call(currentEle.childNodes, (childNode: HTMLElement, k: number) => { this.markerTranslate(<Element>childNode, factor, x, y, scale, 'Marker', animate); const dataIndex : number = parseInt(childNode['id'].split('_dataIndex_')[1].split('_')[0], 10); const markerIndex : number = parseInt(childNode['id'].split('_MarkerIndex_')[1].split('_')[0], 10); if (this.currentLayer.markerSettings[markerIndex as number].initialMarkerSelection.length > 0) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const markerSelectionValues: any = this.currentLayer.markerSettings[markerIndex as number].dataSource[dataIndex as number]; for (let x: number = 0; x < this.currentLayer.markerSettings[markerIndex as number].initialMarkerSelection.length; x++) { if (this.currentLayer.markerSettings[markerIndex as number].initialMarkerSelection[x as number]['latitude'] === markerSelectionValues['latitude'] || this.currentLayer.markerSettings[markerIndex as number].initialMarkerSelection[x as number]['longitude'] === markerSelectionValues['longitude']) { maps.markerSelection(this.currentLayer.markerSettings[markerIndex as number].selectionSettings, maps, currentEle.children[k as number], this.currentLayer.markerSettings[markerIndex as number].dataSource[dataIndex as number] ); } } } if (((this.currentLayer.animationDuration > 0 || animationMode === 'Enable') || ((maps.layersCollection[0].animationDuration > 0 || animationMode === 'Enable') && this.currentLayer.type === 'SubLayer')) && !this.isPanModeEnabled) { if (!maps.isTileMap) { markerStyle = isMouseWheel ? markerStyle : 'visibility:hidden'; if (!isNullOrUndefined(markerStyle)) { (currentEle as HTMLElement).style.cssText = markerStyle; } } } }); if (this.isPanModeEnabled && maps.markerModule.sameMarkerData.length > 0) { clusterSeparate(maps.markerModule.sameMarkerData, maps, currentEle, true); } else if (maps.markerModule.sameMarkerData.length > 0) { maps.markerModule.sameMarkerData = []; if (document.getElementById(maps.element.id + '_mapsTooltip')) { removeElement(maps.element.id + '_mapsTooltip'); } } if (document.getElementById(maps.element.id + '_mapsTooltip') && maps.mapsTooltipModule.tooltipTargetID.indexOf('_MarkerIndex_') && !this.isPanModeEnabled) { const mapsTooltip: MapsTooltip = maps.mapsTooltipModule; const tooltipElement: Element = currentEle.querySelector('#' + mapsTooltip.tooltipTargetID); if (!isNullOrUndefined(tooltipElement)) { if (tooltipElement['style']['visibility'] === 'hidden') { removeElement(maps.element.id + '_mapsTooltip'); } else { let x: number = parseFloat(tooltipElement.getAttribute('transform').split('(')[1].split(')')[0].split(' ')[1]); let y: number = parseFloat(tooltipElement.getAttribute('transform').split('(')[1].split(')')[0].split(' ')[2]); if (maps.isTileMap) { x += +getElement(maps.element.id + '_tile_parent')['style']['left'].split('px')[0]; y += +getElement(maps.element.id + '_tile_parent')['style']['top'].split('px')[0]; } mapsTooltip.svgTooltip.locati