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

305 lines (300 loc) 16.1 kB
import { Maps } from '../../index'; import { BubbleSettingsModel, ColorMapping, IBubbleRenderingEventArgs, bubbleRendering } from '../index'; import { IBubbleClickEventArgs, bubbleClick, LayerSettings, IBubbleMoveEventArgs, bubbleMouseMove } from '../index'; import { isNullOrUndefined, animationMode } from '@syncfusion/ej2-base'; import { CircleOption, MapLocation, findMidPointOfPolygon, Point, drawCircle, elementAnimate, getTranslate } from '../utils/helper'; import { RectOption, Rect, drawRectangle, checkPropertyPath, getZoomTranslate, getRatioOfBubble, maintainSelection, getValueFromObject } from '../utils/helper'; import { BorderModel } from '../model/base-model'; /** * Bubble module class */ export class Bubble { private maps: Maps; /** @private */ public bubbleCollection: object[]; /** * Bubble Id for current layer * * @private */ public id: string = ''; constructor(maps: Maps) { this.maps = maps; this.bubbleCollection = []; } /** * To render bubble * * @param {BubbleSettingsModel} bubbleSettings - Specifies the bubble data to be rendered * @param {object} shapeData - Specifies the data about the shape * @param {string} color - Specifies the color of the bubble * @param {number} range - Specifies the range of the bubble * @param {number} range.min - Specifies the minimum range of the bubble * @param {number} range.max - Specifies the maximum range of the bubble * @param {number} bubbleIndex - Specifies the index of the bubble * @param {number} dataIndex - Specifies the index of the data * @param {number} layerIndex - Specifies the index of the layer * @param {LayerSettings} layer - Specifies the layer data * @param {Element} group - Specifies the element group * @param {string} bubbleID - Specifies the ID of the bubble * @returns {void} * @private */ public renderBubble( bubbleSettings: BubbleSettingsModel, shapeData: object, color: string, range: { min: number, max: number }, bubbleIndex: number, dataIndex: number, layerIndex: number, layer: LayerSettings, group: Element, bubbleID? : string ): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any const layerData: any[] = layer.layerData; const colorValuePath: string = bubbleSettings.colorValuePath; const equalValue: string = (!isNullOrUndefined(colorValuePath)) ? ((colorValuePath.indexOf('.') > -1) ? (getValueFromObject(shapeData, bubbleSettings.colorValuePath)) : shapeData[colorValuePath as string]) : shapeData[colorValuePath as string]; const colorValue: number = (!isNullOrUndefined(colorValuePath)) ? ((colorValuePath.indexOf('.') > -1) ? Number(getValueFromObject(shapeData, bubbleSettings.colorValuePath)) : Number(shapeData[colorValuePath as string])) : Number(shapeData[colorValuePath as string]); const bubbleValue: number = (!isNullOrUndefined(bubbleSettings.valuePath)) ? ((bubbleSettings.valuePath.indexOf('.') > -1) ? Number(getValueFromObject(shapeData, bubbleSettings.valuePath)) : Number(shapeData[bubbleSettings.valuePath])) : Number(shapeData[bubbleSettings.valuePath]); let opacity: number; let bubbleColor: string; if (isNaN(bubbleValue) && isNaN(colorValue) && isNullOrUndefined(equalValue)) { return null; } let radius: number = getRatioOfBubble(bubbleSettings.minRadius, bubbleSettings.maxRadius, bubbleValue, range.min, range.max); const colorMapping: ColorMapping = new ColorMapping(this.maps); // eslint-disable-next-line @typescript-eslint/no-explicit-any const shapeColor: any = colorMapping.getColorByValue(bubbleSettings.colorMapping, colorValue, equalValue); // eslint-disable-next-line prefer-const bubbleColor = (Object.prototype.toString.call(shapeColor) === '[object Object]' && !isNullOrUndefined(shapeColor['fill'])) ? shapeColor['fill'] : color; // eslint-disable-next-line prefer-const opacity = (Object.prototype.toString.call(shapeColor) === '[object Object]' && !isNullOrUndefined(shapeColor['opacity'])) ? shapeColor['opacity'] : bubbleSettings.opacity; const shapePoints: [MapLocation[]] = [[]]; this.maps.translateType = 'bubble'; let midIndex: number = 0; let pointsLength: number = 0; let currentLength: number = 0; for (let i: number = 0, len: number = layerData.length; i < len; i++) { // eslint-disable-next-line @typescript-eslint/no-explicit-any let shape: any = layerData[i as number]; shape = shape['property']; const shapePath: string = checkPropertyPath(shapeData[layer.shapeDataPath], layer.shapePropertyPath, shape); const shapeDataLayerPathValue: string = !isNullOrUndefined(shapeData[layer.shapeDataPath]) && isNaN(shapeData[layer.shapeDataPath]) ? shapeData[layer.shapeDataPath].toLowerCase() : shapeData[layer.shapeDataPath]; const shapePathValue: string = !isNullOrUndefined(shape[shapePath as string]) && isNaN(shape[shapePath as string]) ? shape[shapePath as string].toLowerCase() : shape[shapePath as string]; if (shapeDataLayerPathValue === shapePathValue && (layerData[i as number].type !== 'LineString' && layerData[i as number].type !== 'MultiLineString' && layerData[i as number]['type'] !== 'Point' && layerData[i as number]['type'] !== 'MultiPoint')) { if (!layerData[i as number]['_isMultiPolygon']) { // eslint-disable-next-line @typescript-eslint/no-explicit-any shapePoints.push(this.getPoints(layerData[i as number] as any[], [])); currentLength = shapePoints[shapePoints.length - 1].length; if (pointsLength < currentLength) { pointsLength = currentLength; midIndex = shapePoints.length - 1; } } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any const layer: any[] = <any[]>layerData[i as number]; for (let j: number = 0; j < layer.length; j++) { // eslint-disable-next-line @typescript-eslint/no-explicit-any shapePoints.push(this.getPoints(layer[j as number] as any[], [])); currentLength = shapePoints[shapePoints.length - 1].length; if (pointsLength < currentLength) { pointsLength = currentLength; midIndex = shapePoints.length - 1; } } } } } const projectionType: string = this.maps.projectionType; let centerY: number; let eventArgs: IBubbleRenderingEventArgs; const bubbleBorder: BorderModel = { color: bubbleSettings.border.color, opacity: bubbleSettings.border.opacity, width: bubbleSettings.border.width }; // eslint-disable-next-line @typescript-eslint/no-explicit-any const center: any = findMidPointOfPolygon(shapePoints[midIndex as number], projectionType, layer.geometryType); if (bubbleSettings.visible) { if (!isNullOrUndefined(center)) { centerY = this.maps.projectionType === 'Mercator' ? center['y'] : (-center['y']); eventArgs = { cancel: false, name: bubbleRendering, border: bubbleBorder, cx: center['x'], cy: centerY, data: shapeData, fill: bubbleColor, maps: this.maps, radius: radius }; } else { const shapePointsLength: number = shapePoints.length - 1; if (shapePoints[shapePointsLength as number]['x'] && shapePoints[shapePointsLength as number]['y']) { eventArgs = { cancel: false, name: bubbleRendering, border: bubbleBorder, cx: shapePoints[shapePointsLength as number]['x'], cy: shapePoints[shapePointsLength as number]['y'], data: shapeData, fill: bubbleColor, maps: this.maps, radius: radius }; } else { return; } } // eslint-disable-next-line @typescript-eslint/no-unused-vars this.maps.trigger('bubbleRendering', eventArgs, (bubbleArgs: IBubbleRenderingEventArgs) => { if (eventArgs.cancel) { return; } let bubbleElement: Element; eventArgs.border.opacity = isNullOrUndefined(eventArgs.border.opacity) ? opacity : eventArgs.border.opacity; if (bubbleSettings.bubbleType === 'Circle') { const circle: CircleOption = new CircleOption( bubbleID, eventArgs.fill, eventArgs.border, opacity, 0, 0, eventArgs.radius, null ); bubbleElement = drawCircle(this.maps, circle, group); } else { const y: number = this.maps.projectionType === 'Mercator' ? (eventArgs.cy - radius) : (eventArgs.cy + radius); const rectangle: RectOption = new RectOption( bubbleID, eventArgs.fill, eventArgs.border, opacity, new Rect(0, 0, radius * 2, radius * 2), 2, 2 ); eventArgs.cx -= radius; eventArgs.cy = y; bubbleElement = drawRectangle(this.maps, rectangle, group); } maintainSelection(this.maps.selectedBubbleElementId, this.maps.bubbleSelectionClass, bubbleElement, 'BubbleselectionMapStyle'); this.bubbleCollection.push({ LayerIndex: layerIndex, BubbleIndex: bubbleIndex, DataIndex: dataIndex, element: bubbleElement, center: { x: eventArgs.cx, y: eventArgs.cy } }); // eslint-disable-next-line @typescript-eslint/no-explicit-any let translate: any; const animate: boolean = (layer.animationDuration !== 0 || animationMode === 'Enable') || isNullOrUndefined(this.maps.zoomModule); if (this.maps.zoomSettings.zoomFactor > 1 && !isNullOrUndefined(this.maps.zoomModule) && !this.maps.isTileMap) { translate = getZoomTranslate(this.maps, layer, animate); } else { translate = getTranslate(this.maps, layer, animate); } // eslint-disable-next-line @typescript-eslint/no-explicit-any const bubbleDataSource: any[] = bubbleSettings.dataSource as any[]; const scale: number = translate['scale']; const transPoint: Point = translate['location'] as Point; const position: MapLocation = new MapLocation( (this.maps.isTileMap ? ((eventArgs.cx + this.maps.translatePoint.x) * this.maps.tileZoomLevel) : ((eventArgs.cx + transPoint.x) * scale)), (this.maps.isTileMap ? ((eventArgs.cy + this.maps.translatePoint.y) * this.maps.tileZoomLevel) : ((eventArgs.cy + transPoint.y) * scale))); bubbleElement.setAttribute('transform', 'translate( ' + (position.x) + ' ' + (position.y) + ' )'); const bubble: string = (bubbleDataSource.length - 1) === dataIndex ? 'bubble' : null; if (bubbleSettings.bubbleType === 'Square') { position.x += radius; position.y += radius * (this.maps.projectionType === 'Mercator' ? 1 : -1); } else { radius = 0; } if (bubbleSettings.animationDuration > 0 || animationMode === 'Enable') { elementAnimate( bubbleElement, bubbleSettings.animationDelay, bubbleSettings.animationDuration, position, this.maps, bubble, radius ); } }); } } // eslint-disable-next-line @typescript-eslint/no-explicit-any private getPoints(shape: any[], points: MapLocation[]): MapLocation[] { if (isNullOrUndefined(shape.map)) { points = shape['point']; } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any shape.map((current: any) => { points.push(new Point(current['point']['x'], current['point']['y'])); }); } return points; } /** * To check and trigger bubble click event. * * @param {PointerEvent} e - Specifies the pointer event argument. * @returns {void} * @private */ public bubbleClick(e: PointerEvent): void { const target: string = (e.target as Element).id; if (target.indexOf('_LayerIndex_') === -1) { return; } // eslint-disable-next-line @typescript-eslint/no-explicit-any const data: any = this.getbubble(target); if (isNullOrUndefined(data)) { return; } const eventArgs: IBubbleClickEventArgs = { cancel: false, name: bubbleClick, data: data, maps: this.maps, target: target, x: e.clientX, y: e.clientY }; this.maps.trigger(bubbleClick, eventArgs); } /** * To get bubble from target id. * * @param {string} target - Specifies the target * @returns {object} - Returns the object */ // eslint-disable-next-line @typescript-eslint/no-explicit-any private getbubble(target: string): any { const id: string[] = target.split('_LayerIndex_'); const index: number = parseInt(id[1].split('_')[0], 10); // eslint-disable-next-line @typescript-eslint/no-explicit-any let data: any; if (target.indexOf('_BubbleIndex_') > -1) { const layer: LayerSettings = <LayerSettings>this.maps.layers[index as number]; const bubbleIndex: number = parseInt(id[1].split('_BubbleIndex_')[1], 10); const dataIndex: number = parseInt(id[1].split('_BubbleIndex_')[1].split('_dataIndex_')[1], 10); if (!isNaN(bubbleIndex as number)) { data = layer.bubbleSettings[bubbleIndex as number].dataSource[dataIndex as number]; return data; } } return null; } // eslint-disable-next-line valid-jsdoc /** * To check and trigger bubble move event. * * @param {PointerEvent} e - Specifies the pointer event argument. * @retruns {void} * @private */ public bubbleMove(e: PointerEvent): void { const target: string = (e.target as Element).id; if (target.indexOf('_LayerIndex_') === -1) { return; } // eslint-disable-next-line @typescript-eslint/no-explicit-any const data: any = this.getbubble(target); if (isNullOrUndefined(data)) { return; } const eventArgs: IBubbleMoveEventArgs = { cancel: false, name: bubbleMouseMove, data: data, maps: this.maps, target: target, x: e.clientX, y: e.clientY }; this.maps.trigger(bubbleMouseMove, eventArgs); } /** * Get module name. * * @returns {string} - Returns the module name. */ protected getModuleName(): string { return 'Bubble'; } /** * To destroy the bubble. * * @returns {void} * @private */ public destroy(): void { this.bubbleCollection = []; this.maps = null; } }