Geometry Drawing Tools

Part of AngularCesiumWidgetsModule are useful geometry drawing tools:

Check out our blog post about geometry drawing tools.

Basic Usage

First Add AngularCesiumWidgetsModule to your app.module file.

@NgModule({
// ...
imports: [
AngularCesiumModule.forRoot(),
AngularCesiumWidgetsModule,
// ...
],
})
export class AppModule {}

Each editor service work with a with editor component. Therefor you need to create a editor-componentfor each editor service, And of course somewhere under <ac-map> . For the Polygons editor service we will do:

<ac-map>
<polygons-editor></polygons-editor> // Or somewhere else in the hierarchy
</ac-map>

All editors have a similar API, the following examples are relevant to all editors

Next we just need to provide the editor service, with the editor service you will create() and edit() your geometry. You can provide the service in the root module level or any where in your hierarchy, In our Example:

my-map.component.ts
my-map.component.html
my-map.component.ts
@Component({
...
providers: [PolygonEditorService],
})
export class MyMapComponent {
editing$: PolygonEditorObservable;
constructor(private polygonEditor: PolygonsEditorService) {}
startDraw() {
this.editing$ = this.polygonEditor.create();
// Or Edit from existing points
const initialPos = [
Cesium.Cartesian3.fromDegrees(20, 40),
Cesium.Cartesian3.fromDegrees(45, 40),
Cesium.Cartesian3.fromDegrees(30, 20)
];
this.editing$ = this.polygonEditor.edit(initialPos);
});
}
}
my-map.component.html
<ac-map>
<polygons-editor></polygons-editor>
</ac-map>

Check the full stackblitz example:

Create, Edit and Handle your editable geometry

Use create() to start creating your geometry. The default events are click for adding points and double click for finish creating the shape, these events are configurable:

startDraw() {
// Each editor has it own events to configure
const editing$ = this.polygonsEditor.create({
addPointEvent: CesiumEvent.RIGHT_CLICK, // Default: LEFT_CLICK
addLastPointEvent: CesiumEvent.RIGHT_CLICK, // Default: LEFT_DOUBLE_CLICK
addLastPointModifier: CesiumEventModifier.CTRL, // Default: none
})
}

Create mode and Edit mode

In create mode a geometry is created from scratch, for every click on the map a new point is added to your shape. After you finish creating your shape in will change to edit mode.

In edit mode you can update, delete and add your shape positions. You can do it by dragging the points to update their locations and deleting points by right click on the point. Adding new points can be done by dragging the "virtual points" created in the edit mode - which converts the virtual point the a "real" point, of course all is configurable:

this.editing$ = this.polygonsEditor.edit(initialPositions, {
pointProps: {
showVirtual: true // Default: true
}
});
polygon with "virtual" edit points

Controlling your edited geometry

All editors return a custom Observer object that allows you to subscribe to the shape actions and to manipulate your geometry from the code. All have dispose() method for removing the geometry.

this.editing$ = this.polygonsEditor.create();
// Stop editng and remove the geomerty
this.editing$.dispose();
// Subscribe to events updates
this.editing$.subscribe((editUpdate: PolygonEditUpdate) => {
if (editUpdate.editAction === EditActions.DRAG_POINT_FINISH) {
console.log(editUpdate.points); // point = position with id
console.log(editUpdate.positions); // or just position
console.log(editUpdate.updatedPosition); // added position
}
// Enable/disable geometry editing
this.editing$.enable();
this.editing$.disable();
// Get current postions
const cartesian3Positions = this.editing$.getCurrentPoints();
// Update your positions manually (in edit mode only)
this.editing$.setManually(newCartesian3s);
// Update the the first point (in edit mode only)
const polygonPoints = this.editing$.getCurrentPoints();
polygonPoints[0].setPosition(Cesium.Cartesian3.fromDegrees(20, 20));
const newUpdatedPoints = polygonPoints.map(p => ({position: p.getPosition(),pointProps: p.props,}));
this.editing$.setManually(newUpdatedPoints);
// Add new position manually (in edit mode only)
const polygonPositions = this.editing$.getCurrentPoints().map(p => p.getPosition());
polygonPositions.push( Cesium.Cartesian3.fromDegrees(30, 24));
this.editing$.setManually(polygonPositions); // accepts Cartesian3 or Point Obj

Geometry Customisation

All editors appearance is customisable, each editor has it own configuration to change the material, size, and behaviour. Each have it's own default configuration, but you can pass your own settings.

In the following example we create a transparent polygon with a dashed outline:

// create accepts PolygonEditOptions object
const editing$ = this.polygonsEditor.create({
pointProps: {
color: Cesium.Color.WHITE,
pixelSize: 10,
},
polygonProps: {
material: Cesium.Color.TRANSPARENT,
},
polylineProps: {
material: () => new Cesium.PolylineDashMaterialProperty()
}
});

Adding labels

The editors 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() .

  • setLabelsRenderFn(callback) - receives a callback that is called every time the shape is redrawn (except when the shape is being dragged).

    • callback: (update: T, labels: LabelProps[]) => LabelProps[]

    The callback is called with the last shape state and with an array of the current labels. Return type: LabelProps[].

  • You can also use updateLabels() to pass an array of labels of type LabelProps[] to be drawn.

this.editing$ = this.polygonsEditor.create();
this.editing$.setLabelsRenderFn((update: PolygonEditUpdate) => {
let counter = 0;
const newLabels: LabelProps[] = [];
update.positions.forEach(position => newLabels.push({
text: `Point ${counter++}`,
scale: 0.6,
eyeOffset: new Cesium.Cartesian3(10, 10, -1000),
fillColor: Cesium.Color.BLUE,
}));
return newLabels;
});

Drawing over 3dTiles and Terrain

You can draw over 3dTiles and terrain, the editor will clamp to the relevant 3D object. By default the the 3D drawing is false, so you must enable it:

// Enable clamp to 3dTiles
this.editing$ = this.polygonsEditor.create({
clampHeightTo3D: true
});
// Enbale clamp to terrain
this.editing$ = this.polygonsEditor.create({
clampHeightTo3D: true,
clampHeightTo3DOptions: {
clampToTerrain: true, // Default: false (if true will only clamp to terrain)
clampMostDetailed: true, // Fix height after finish editing
clampToHeightPickWidth: 1 // scene.clampToHeight() width , 3dTiles only
}
});

currently supported : PolylineEditorService, PolygonEditorService

Behind the scene the editor is using scene.pickPosition and scene.clampToGround for 3dTiles and camera.getPickRay() for terrain, to set the correct height of the shape. So the shape positions will accurate as much as the rendered model is.

Please notice, according to cesium issue, using scene.pick() simultaneously with pickPosition() can make the pickPosition() return wrong height values, so try to avoid it.