File

projects/angular-cesium/src/lib/angular-cesium-widgets/services/entity-editors/points-editor/points-editor.service.ts

Description

Service for creating editable point

  • You must provide PointsEditorService yourself. PolygonsEditorService works together with <points-editor> component. Therefor you need to create <points-editor> for each PointsEditorService, And of course somewhere under <ac-map>/
  • create for starting a creation of the shape over the map. Returns a extension of PointEditorObservable.
  • edit for editing shape over the map starting from a given positions. Returns an extension of PointEditorObservable.
  • To stop editing call dsipose() from the PointEditorObservable you get back from create() \ edit().

Labels over editted shapes Angular Cesium allows you to draw labels over a shape that is being edited with one of the editors. To add label drawing logic to your editor use the function setLabelsRenderFn() that is defined on the PointEditorObservable that is returned from calling create() \ edit() of one of the editor services. setLabelsRenderFn() - receives a callback that is called every time the shape is redrawn (except when the shape is being dragged). The callback is called with the last shape state and with an array of the current labels. The callback should return type LabelProps[]. You can also use updateLabels() to pass an array of labels of type LabelProps[] to be drawn.

usage:

 *  // Start creating point
 *  const editing$ = pointEditorService.create();
 *  this.editing$.subscribe(editResult => {
 *                console.log(editResult.positions);
 *        });
 *
 *  // Or edit point from existing point cartesian3 positions
 *  const editing$ = this.pointEditor.edit(initialPos);
 *
 *

Index

Properties
Methods

Methods

create
create(options, eventPriority: number)
Parameters :
Name Type Optional Default value
options No DEFAULT_POINT_OPTIONS
eventPriority number No 100
Private createEditorObservable
createEditorObservable(observableToExtend: any, id: string, finishCreation?: (position?: Cartesian3) => void)
Parameters :
Name Type Optional
observableToExtend any No
id string No
finishCreation function Yes
edit
edit(position: Cartesian3, options, priority: number)
Parameters :
Name Type Optional Default value
position Cartesian3 No
options No DEFAULT_POINT_OPTIONS
priority number No 100
Private editPoint
editPoint(id: string, position: Cartesian3, priority: number, editSubject: Subject, options: PointEditOptions, editObservable?: PointEditorObservable)
Parameters :
Name Type Optional
id string No
position Cartesian3 No
priority number No
editSubject Subject<PointEditUpdate> No
options PointEditOptions No
editObservable PointEditorObservable Yes
Returns : any
Private getPoint
getPoint(id: string)
Parameters :
Name Type Optional
id string No
Returns : any
Private getPosition
getPosition(id: string)
Parameters :
Name Type Optional
id string No
Returns : any
init
init(mapEventsManager: MapEventsManagerService, coordinateConverter: CoordinateConverter, cameraService: CameraService, pointManager: PointsManagerService, cesiumViewer: CesiumService)
Parameters :
Name Type Optional
mapEventsManager MapEventsManagerService No
coordinateConverter CoordinateConverter No
cameraService CameraService No
pointManager PointsManagerService No
cesiumViewer CesiumService No
Returns : void
onUpdate
onUpdate()
Private screenToPosition
screenToPosition(cartesian2)
Parameters :
Name Optional
cartesian2 No
Returns : any
Private setOptions
setOptions(options: PointEditOptions)
Parameters :
Name Type Optional
options PointEditOptions No
Returns : any
Private switchToEditMode
switchToEditMode(id, clientEditSubject, position: Cartesian3, eventPriority, pointOptions, editorObservable, finishedCreate: boolean)
Parameters :
Name Type Optional
id No
clientEditSubject No
position Cartesian3 No
eventPriority No
pointOptions No
editorObservable No
finishedCreate boolean No
Returns : boolean

Properties

Private cameraService
cameraService: CameraService
Type : CameraService
Private cesiumScene
cesiumScene:
Private coordinateConverter
coordinateConverter: CoordinateConverter
Type : CoordinateConverter
Private mapEventsManager
mapEventsManager: MapEventsManagerService
Type : MapEventsManagerService
Private observablesMap
observablesMap:
Default value : new Map<string, DisposableObservable<any>[]>()
Private pointManager
pointManager: PointsManagerService
Type : PointsManagerService
Private updatePublisher
updatePublisher:
Default value : publish<PointEditUpdate>()(this.updateSubject)
Private updateSubject
updateSubject:
Default value : new Subject<PointEditUpdate>()
import { publish, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MapEventsManagerService } from '../../../../angular-cesium/services/map-events-mananger/map-events-manager';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { CesiumEvent } from '../../../../angular-cesium/services/map-events-mananger/consts/cesium-event.enum';
import { PickOptions } from '../../../../angular-cesium/services/map-events-mananger/consts/pickOptions.enum';
import { EditModes } from '../../../models/edit-mode.enum';
import { EditActions } from '../../../models/edit-actions.enum';
import { DisposableObservable } from '../../../../angular-cesium/services/map-events-mananger/disposable-observable';
import { CoordinateConverter } from '../../../../angular-cesium/services/coordinate-converter/coordinate-converter.service';
import { EditPoint } from '../../../models/edit-point';
import { CameraService } from '../../../../angular-cesium/services/camera/camera.service';
import { Cartesian3 } from '../../../../angular-cesium/models/cartesian3';
import { PointsManagerService } from './points-manager.service';
import { LabelProps } from '../../../models/label-props';
import { generateKey } from '../../utils';
import { CesiumService } from '../../../../angular-cesium';
import { PointEditOptions, PointProps } from '../../../models/point-edit-options';
import { PointEditUpdate } from '../../../models/point-edit-update';
import { PointEditorObservable } from '../../../models/point-editor-observable';

export const DEFAULT_POINT_OPTIONS: PointEditOptions = {
  addLastPointEvent: CesiumEvent.LEFT_CLICK,
  removePointEvent: CesiumEvent.RIGHT_CLICK,
  dragPointEvent: CesiumEvent.LEFT_CLICK_DRAG,
  allowDrag: true,
  pointProps: {
    color: Cesium.Color.WHITE.withAlpha(0.95),
    outlineColor: Cesium.Color.BLACK.withAlpha(0.5),
    outlineWidth: 1,
    pixelSize: 10,
    show: true,
    disableDepthTestDistance: Number.POSITIVE_INFINITY,
  },
};

/**
 * Service for creating editable point
 *
 *  * You must provide `PointsEditorService` yourself.
 * PolygonsEditorService works together with `<points-editor>` component. Therefor you need to create `<points-editor>`
 * for each `PointsEditorService`, And of course somewhere under `<ac-map>`/
 *
 * + `create` for starting a creation of the shape over the map. Returns a extension of `PointEditorObservable`.
 * + `edit` for editing shape over the map starting from a given positions. Returns an extension of `PointEditorObservable`.
 * + To stop editing call `dsipose()` from the `PointEditorObservable` you get back from `create()` \ `edit()`.
 *
 * **Labels over editted shapes**
 * Angular Cesium allows you to draw labels over a shape that is being edited with one of the editors.
 * To add label drawing logic to your editor use the function `setLabelsRenderFn()` that is defined on the
 * `PointEditorObservable` that is returned from calling `create()` \ `edit()` of one of the editor services.
 * `setLabelsRenderFn()` - receives a callback that is called every time the shape is redrawn
 * (except when the shape is being dragged). The callback is called with the last shape state and with an array of the current labels.
 * The callback should return type `LabelProps[]`.
 * You can also use `updateLabels()` to pass an array of labels of type `LabelProps[]` to be drawn.
 *
 * usage:
 * ```typescript
 *  // Start creating point
 *  const editing$ = pointEditorService.create();
 *  this.editing$.subscribe(editResult => {
 *				console.log(editResult.positions);
 *		});
 *
 *  // Or edit point from existing point cartesian3 positions
 *  const editing$ = this.pointEditor.edit(initialPos);
 *
 * ```
 */
@Injectable()
export class PointsEditorService {
  private mapEventsManager: MapEventsManagerService;
  private updateSubject = new Subject<PointEditUpdate>();
  private updatePublisher = publish<PointEditUpdate>()(this.updateSubject); // TODO maybe not needed
  private coordinateConverter: CoordinateConverter;
  private cameraService: CameraService;
  private pointManager: PointsManagerService;
  private observablesMap = new Map<string, DisposableObservable<any>[]>();
  private cesiumScene;

  init(mapEventsManager: MapEventsManagerService,
       coordinateConverter: CoordinateConverter,
       cameraService: CameraService,
       pointManager: PointsManagerService,
       cesiumViewer: CesiumService) {
    this.mapEventsManager = mapEventsManager;
    this.coordinateConverter = coordinateConverter;
    this.cameraService = cameraService;
    this.pointManager = pointManager;
    this.updatePublisher.connect();

    this.cesiumScene = cesiumViewer.getScene();
  }

  onUpdate(): Observable<PointEditUpdate> {
    return this.updatePublisher;
  }

  private screenToPosition(cartesian2) {
    const cartesian3 = this.coordinateConverter.screenToCartesian3(cartesian2);

    // If cartesian3 is undefined then the point inst on the globe
    if (cartesian3) {
      const ray = this.cameraService.getCamera().getPickRay(cartesian2);
      return this.cesiumScene.globe.pick(ray, this.cesiumScene);
    }
    return cartesian3;
  }

  create(options = DEFAULT_POINT_OPTIONS, eventPriority = 100): PointEditorObservable {
    const id = generateKey();
    const pointOptions = this.setOptions(options);

    const clientEditSubject = new BehaviorSubject<PointEditUpdate>({
      id,
      editAction: null,
      editMode: EditModes.CREATE
    });
    let finishedCreate = false;

    this.updateSubject.next({
      id,
      editMode: EditModes.CREATE,
      editAction: EditActions.INIT,
      pointOptions: pointOptions,
    });

    const finishCreation = (position: Cartesian3) => {
      return this.switchToEditMode(
        id,
        clientEditSubject,
        position,
        eventPriority,
        pointOptions,
        editorObservable,
        true
      );
    };

    const mouseMoveRegistration = this.mapEventsManager.register({
      event: CesiumEvent.MOUSE_MOVE,
      pick: PickOptions.NO_PICK,
      priority: eventPriority,
      pickConfig: options.pickConfiguration,
    });
    const addLastPointRegistration = this.mapEventsManager.register({
      event: pointOptions.addLastPointEvent,
      modifier: pointOptions.addLastPointModifier,
      pick: PickOptions.NO_PICK,
      priority: eventPriority,
      pickConfig: options.pickConfiguration,
    });

    this.observablesMap.set(id, [mouseMoveRegistration, addLastPointRegistration]);
    const editorObservable = this.createEditorObservable(clientEditSubject, id, finishCreation);

    mouseMoveRegistration.subscribe(({ movement: { endPosition } }) => {
      const position = this.screenToPosition(endPosition);
      if (position) {

        this.updateSubject.next({
          id,
          position,
          editMode: EditModes.CREATE,
          updatedPosition: position,
          editAction: EditActions.MOUSE_MOVE,
        });
      }
    });
    addLastPointRegistration.subscribe(({ movement: { endPosition } }) => {
      const position = this.screenToPosition(endPosition);
      finishedCreate = finishCreation(position);
    });
    return editorObservable;
  }

  private switchToEditMode(id,
                           clientEditSubject,
                           position: Cartesian3,
                           eventPriority,
                           pointOptions,
                           editorObservable,
                           finishedCreate: boolean) {
    const update = {
      id,
      position: position,
      editMode: EditModes.CREATE_OR_EDIT,
      updatedPosition: position,
      editAction: EditActions.ADD_LAST_POINT,
    };
    this.updateSubject.next(update);
    clientEditSubject.next({
      ...update,
      position: position,
      point: this.getPoint(id),
    });

    const changeMode = {
      id,
      editMode: EditModes.CREATE,
      editAction: EditActions.CHANGE_TO_EDIT,
    };
    this.updateSubject.next(changeMode);
    clientEditSubject.next(changeMode);
    if (this.observablesMap.has(id)) {
      this.observablesMap.get(id).forEach(registration => registration.dispose());
    }
    this.observablesMap.delete(id);
    this.editPoint(id, position, eventPriority, clientEditSubject, pointOptions, editorObservable);
    finishedCreate = true;
    return finishedCreate;
  }

  edit(position: Cartesian3, options = DEFAULT_POINT_OPTIONS, priority = 100): PointEditorObservable {
    const id = generateKey();
    const pointOptions = this.setOptions(options);
    const editSubject = new BehaviorSubject<PointEditUpdate>({
      id,
      editAction: null,
      editMode: EditModes.EDIT
    });
    const update = {
      id,
      position: position,
      editMode: EditModes.EDIT,
      editAction: EditActions.INIT,
      pointOptions: pointOptions,
    };
    this.updateSubject.next(update);
    editSubject.next({
      ...update,
      position: position,
      point: this.getPoint(id),
    });
    return this.editPoint(
      id,
      position,
      priority,
      editSubject,
      pointOptions
    );
  }

  private editPoint(id: string,
                       position: Cartesian3,
                       priority: number,
                       editSubject: Subject<PointEditUpdate>,
                       options: PointEditOptions,
                       editObservable?: PointEditorObservable) {
    const pointDragRegistration = this.mapEventsManager.register({
      event: options.dragPointEvent,
      entityType: EditPoint,
      pick: PickOptions.PICK_FIRST,
      pickConfig: options.pickConfiguration,
      priority,
      pickFilter: entity => id === entity.editedEntityId,
    });

    const pointRemoveRegistration = this.mapEventsManager.register({
      event: options.removePointEvent,
      modifier: options.removePointModifier,
      entityType: EditPoint,
      pick: PickOptions.PICK_FIRST,
      pickConfig: options.pickConfiguration,
      priority,
      pickFilter: entity => id === entity.editedEntityId,
    });

    pointDragRegistration.pipe(
      tap(({ movement: { drop } }) => this.cameraService.enableInputs(drop)))
      .subscribe(({ movement: { endPosition, drop }, entities }) => {
        const updatedPosition = this.screenToPosition(endPosition);
        if (!updatedPosition) {
          return;
        }
        const update = {
          id,
          editMode: EditModes.EDIT,
          updatedPosition,
          editAction: drop ? EditActions.DRAG_POINT_FINISH : EditActions.DRAG_POINT,
        };
        this.updateSubject.next(update);
        editSubject.next({
          ...update,
          position: updatedPosition,
          point: this.getPoint(id),
        });

      });

    const observables = [pointDragRegistration, pointRemoveRegistration];
    this.observablesMap.set(id, observables);
    return this.createEditorObservable(editSubject, id);
  }

  private setOptions(options: PointEditOptions) {
    const defaultClone = JSON.parse(JSON.stringify(DEFAULT_POINT_OPTIONS));
    const pointOptions: PointEditOptions = Object.assign(defaultClone, options);
    pointOptions.pointProps = {...DEFAULT_POINT_OPTIONS.pointProps, ...options.pointProps};
    pointOptions.pointProps = {...DEFAULT_POINT_OPTIONS.pointProps, ...options.pointProps};
    return pointOptions;
  }


  private createEditorObservable(observableToExtend: any, id: string, finishCreation?: (position: Cartesian3) => boolean)
                                                                                                    : PointEditorObservable {
    observableToExtend.dispose = () => {
      const observables = this.observablesMap.get(id);
      if (observables) {
        observables.forEach(obs => obs.dispose());
      }
      this.observablesMap.delete(id);
      this.updateSubject.next({
        id,
        editMode: EditModes.CREATE_OR_EDIT,
        editAction: EditActions.DISPOSE,
      });
    };

    observableToExtend.enable = () => {
      this.updateSubject.next({
        id,
        position: this.getPosition(id),
        editMode: EditModes.EDIT,
        editAction: EditActions.ENABLE,
      });
    };

    observableToExtend.disable = () => {
      this.updateSubject.next({
        id,
        position: this.getPosition(id),
        editMode: EditModes.EDIT,
        editAction: EditActions.DISABLE,
      });
    };

    observableToExtend.setManually = (point: {
      position: Cartesian3,
      pointProp?: PointProps
    } | Cartesian3, pointProps?: PointProps) => {
      const newPoint = this.pointManager.get(id);
      newPoint.setManually(point, pointProps);
      this.updateSubject.next({
        id,
        editMode: EditModes.CREATE_OR_EDIT,
        editAction: EditActions.SET_MANUALLY,
      });
    };

    observableToExtend.setLabelsRenderFn = (callback: any) => {
      this.updateSubject.next({
        id,
        editMode: EditModes.CREATE_OR_EDIT,
        editAction: EditActions.SET_EDIT_LABELS_RENDER_CALLBACK,
        labelsRenderFn: callback,
      });
    };

    observableToExtend.updateLabels = (labels: LabelProps[]) => {
      this.updateSubject.next({
        id,
        editMode: EditModes.CREATE_OR_EDIT,
        editAction: EditActions.UPDATE_EDIT_LABELS,
        updateLabels: labels,
      });
    };

    observableToExtend.finishCreation = () => {
      if (!finishCreation) {
        throw new Error('Points editor error edit(): cannot call finishCreation() on edit');
      }

      return finishCreation(null);
    };

    observableToExtend.getCurrentPoint = () => this.getPoint(id);

    observableToExtend.getEditValue = () => observableToExtend.getValue();

    observableToExtend.getLabels = (): LabelProps[] => this.pointManager.get(id).labels;

    return observableToExtend as PointEditorObservable;
  }

  private getPosition(id: string) {
    const point = this.pointManager.get(id);
    return point.getPosition();
  }

  private getPoint(id: string) {
    const point = this.pointManager.get(id);
    if (point) {
      return point.getCurrentPoint();
    }
  }
}

result-matching ""

    No results matching ""