File

projects/angular-cesium/src/lib/angular-cesium-widgets/services/zoom-to-rectangle.service.ts

Index

Properties
Methods

Constructor

constructor(mapsManager: MapsManagerService, cameraService: CameraService, cesiumService: CesiumService)
Parameters :
Name Type Optional
mapsManager MapsManagerService No
cameraService CameraService No
cesiumService CesiumService No

Methods

activate
activate(options: literal type, mapId?: string)
Parameters :
Name Type Optional Default value
options literal type No {}
mapId string Yes
Returns : void
disable
disable(mapId?: string)
Parameters :
Name Type Optional
mapId string Yes
Returns : void
init
init(cesiumService: CesiumService, cameraService: CameraService)
Parameters :
Name Type Optional
cesiumService CesiumService No
cameraService CameraService No
Returns : void
Private zoomCameraToRectangle
zoomCameraToRectangle(cameraService: CameraService, positions: literal type, animationDuration, options)
Parameters :
Name Type Optional
cameraService CameraService No
positions literal type No
animationDuration No
options No
Returns : boolean

Properties

Private cameraService
cameraService: CameraService
Type : CameraService
Private cesiumService
cesiumService: CesiumService
Type : CesiumService
Private defaultOptions
defaultOptions: object
Type : object
Default value : { animationDurationInSeconds: 0.5, resetKeyCode: 27, borderStyle: '2px solid rgba(0,0,0,0.5)', backgroundColor: 'rgba(0,0,0,0.2)', autoDisableOnZoom: true, threshold: 9, keepRotation: true, mouseButton: MouseButtons.LEFT, }
Private mapsZoomElements
mapsZoomElements:
Default value : new Map<string, ZoomData>()
import { Injectable, Optional } from '@angular/core';
import { MapsManagerService } from '../../angular-cesium/services/maps-manager/maps-manager.service';
import { CameraService } from '../../angular-cesium/services/camera/camera.service';
import { CesiumService } from '../../angular-cesium/services/cesium/cesium.service';
import { AcMapComponent } from '../../angular-cesium/components/ac-map/ac-map.component';

interface ZoomData {
  container: HTMLElement;
  borderElement?: HTMLElement;
  resetOnEscapePressFunc?: EventListenerOrEventListenerObject;
}

/**
 * The Service is as a "zoom to rectangle" tool
 *
 * example:
 * ```
 * constructor(
 *   private cameraService: CameraService,
 *   private cesiumService: CesiumService,
 *   private zoomToRectangleService: ZoomToRectangleService,
 * ) {
 *   this.zoomToRectangleService.init(cesiumService, cameraService);
 * }
 * ...
 * this.zoomToRectangleService.activate({onComplete: () => this.zoomToRectangleService.disable()});
 * ```
 *
 * `init()` - initialize the service with CameraService and CesiumService.
 * If no mapId is provided to activate() - must be called before calling `activate()`.
 *
 * `disable()` - disables the tool.
 *
 * `activate()` -
 * @param options
 * {
 *  onStart - optional - a callback that will be called when the user start drawing the rectangle
 *  onComplete - optional - a callback that will be called when the tool zoom in
 *  autoDisableOnZoom - optional - determines if the tool should auto disable after zoom - default: true
 *  animationDurationInSeconds - optional - zoom animation duration in seconds - default: 0.5
 *  borderStyle - optional - the style of the rectangle element border - default: '3px dashed #FFFFFF'
 *  backgroundColor - optional - the background color of the rectangle element - default: 'transparent'
 *  resetKeyCode - optional - the key code of the key that is used to reset the drawing of the rectangle - default: 27 (ESC key)
 *  threshold - optional - the minimum area of the screen rectangle (in pixels) that is required to perform zoom - default: 9
 *  keepRotation - optional - whether or not to keep the rotation when zooming in - default: true
 *  mouseButton - optional - sets the mouse button for drawing the rectangle - default: left mouse button (0)
 * }
 * @param mapId - optional - the mapId of the map that the tool will be used in.
 *
 */

export enum MouseButtons {
  LEFT = 0,
  MIDDLE = 1,
  RIGHT = 2,
}

@Injectable()
export class ZoomToRectangleService {
  constructor(
    private mapsManager: MapsManagerService,
    @Optional() cameraService: CameraService,
    @Optional() cesiumService: CesiumService,
  ) {}

  private cameraService: CameraService;
  private cesiumService: CesiumService;

  private mapsZoomElements = new Map<string, ZoomData>();
  private defaultOptions = {
    animationDurationInSeconds: 0.5,
    resetKeyCode: 27,
    borderStyle: '2px solid rgba(0,0,0,0.5)',
    backgroundColor: 'rgba(0,0,0,0.2)',
    autoDisableOnZoom: true,
    threshold: 9,
    keepRotation: true,
    mouseButton: MouseButtons.LEFT,
  };

  init(cesiumService: CesiumService, cameraService: CameraService) {
    this.cameraService = cameraService;
    this.cesiumService = cesiumService;
  }

  activate(
    options: {
      onStart?: (acMap?: AcMapComponent) => any;
      onComplete?: (acMap?: AcMapComponent) => any;
      mouseButton?: MouseButtons;
      autoDisableOnZoom?: boolean;
      animationDurationInSeconds?: number;
      threshold?: number;
      keepRotation?: boolean;
      borderStyle?: string;
      backgroundColor?: string;
      resetKeyCode?: number;
    } = {},
    mapId?: string,
  ) {
    if ((!this.cameraService || !this.cesiumService) && !mapId) {
      throw new Error(`The function must receive a mapId if the service wasn't initialized`);
    }
    const finalOptions = Object.assign({}, this.defaultOptions, options);
    let cameraService = this.cameraService;
    let mapContainer;
    let map;
    if (this.cesiumService) {
      mapContainer = this.cesiumService.getViewer().container;
      map = this.cesiumService.getMap();
    }
    if (mapId) {
      map = this.mapsManager.getMap(mapId);
      if (!map) {
        throw new Error(`Map not found with id: ${mapId}`);
      }
      cameraService = map.getCameraService();
      mapContainer = map.getCesiumViewer().container;
    }

    if (!cameraService || !mapContainer) {
      throw new Error(`The function must receive a mapId if the service wasn't initialized`);
    }
    this.disable(mapId);
    const container = document.createElement('div');
    mapContainer.style.position = 'relative';
    container.style.position = 'absolute';
    container.style.width = '100%';
    container.style.height = '100%';
    container.style.top = '0';
    container.style.left = '0';
    mapContainer.appendChild(container);
    const mapZoomData: ZoomData = { container };
    this.mapsZoomElements.set(mapId || this.cesiumService.getMap().getId(), mapZoomData);
    let mouse = {
      endX: 0,
      endY: 0,
      startX: 0,
      startY: 0,
    };
    let borderElement: HTMLElement | undefined;

    container.onmousedown = e => {
      if (e.button !== finalOptions.mouseButton) {
        return;
      }
      if (!borderElement) {
        if (options && options.onStart) {
          options.onStart(map);
        }

        const rect = (e.currentTarget as any).getBoundingClientRect();
        const offsetX = e.clientX - rect.left;
        const offsetY = e.clientY - rect.top;
        mouse.startX = offsetX;
        mouse.startY = offsetY;
        borderElement = document.createElement('div');
        borderElement.className = 'zoom-to-rectangle-border';
        borderElement.style.position = 'absolute';
        borderElement.style.border = finalOptions.borderStyle;
        borderElement.style.backgroundColor = finalOptions.backgroundColor;
        borderElement.style.left = mouse.startX + 'px';
        borderElement.style.top = mouse.startY + 'px';
        container.appendChild(borderElement);
        mapZoomData.borderElement = borderElement;
      }
    };

    container.onmouseup = e => {
      if (borderElement) {
        let zoomApplied;
        if (mouse && Math.abs(mouse.endX - mouse.startX) * Math.abs(mouse.endY - mouse.startY) > finalOptions.threshold) {
          zoomApplied = this.zoomCameraToRectangle(
            cameraService,
            mouse,
            finalOptions.animationDurationInSeconds,
            finalOptions,
          );
        }
        borderElement.remove();
        borderElement = undefined;
        mapZoomData.borderElement = undefined;
        mouse = {
          endX: 0,
          endY: 0,
          startX: 0,
          startY: 0,
        };
        if (!!finalOptions.onComplete) {
          finalOptions.onComplete(map);
        }
        if (finalOptions.autoDisableOnZoom && zoomApplied) {
          this.disable(mapId);
        }
      }
    };

    container.onmousemove = e => {
      if (borderElement) {
        const rect = (e.currentTarget as any).getBoundingClientRect();
        const offsetX = e.clientX - rect.left;
        const offsetY = e.clientY - rect.top;
        mouse.endX = offsetX;
        mouse.endY = offsetY;
        borderElement.style.width = Math.abs(mouse.endX - mouse.startX) + 'px';
        borderElement.style.height = Math.abs(mouse.endY - mouse.startY) + 'px';
        borderElement.style.left = Math.min(mouse.startX, mouse.endX) + 'px';
        borderElement.style.top = Math.min(mouse.startY, mouse.endY) + 'px';
      }
    };

    const resetOnEscapePress = e => {
      if (e.keyCode === finalOptions.resetKeyCode && borderElement) {
        borderElement.remove();
        borderElement = undefined;
        mapZoomData.borderElement = undefined;
        mouse = {
          endX: 0,
          endY: 0,
          startX: 0,
          startY: 0,
        };
      }
    };
    document.addEventListener('keydown', resetOnEscapePress);
    mapZoomData.resetOnEscapePressFunc = resetOnEscapePress;
  }

  disable(mapId?: string) {
    if (!this.cesiumService && !mapId) {
      throw new Error('If the service was not initialized with CesiumService, mapId must be provided');
    }
    const data = this.mapsZoomElements.get(mapId || this.cesiumService.getMap().getId());
    if (data) {
      data.container.remove();
      if (data.borderElement) {
        data.borderElement.remove();
      }
      if (data.resetOnEscapePressFunc) {
        document.removeEventListener('keydown', data.resetOnEscapePressFunc);
      }
    }
    this.mapsZoomElements.delete(mapId);
  }

  private zoomCameraToRectangle(
    cameraService: CameraService,
    positions: { endX: number; endY: number; startX: number; startY: number },
    animationDuration,
    options,
  ): boolean {
    const camera = cameraService.getCamera();
    const cartesian1 = camera.pickEllipsoid({ x: positions.startX, y: positions.startY });
    const cartesian2 = camera.pickEllipsoid({ x: positions.endX, y: positions.endY });
    if (!cartesian1 || !cartesian2) {
      return false;
    }
    const cartographic1 = Cesium.Cartographic.fromCartesian(cartesian1);
    const cartographic2 = Cesium.Cartographic.fromCartesian(cartesian2);
    cameraService.cameraFlyTo({
      destination: new Cesium.Rectangle(
        Math.min(cartographic1.longitude, cartographic2.longitude),
        Math.min(cartographic1.latitude, cartographic2.latitude),
        Math.max(cartographic1.longitude, cartographic2.longitude),
        Math.max(cartographic1.latitude, cartographic2.latitude),
      ),
      orientation: options.keepRotation ? { heading: camera.heading } : undefined,
      duration: animationDuration,
    });
    return true;
  }
}

result-matching ""

    No results matching ""