@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
text/typescript
/* 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