Skip to content

Commit

Permalink
Measurement tool for map equinor#882 (equinor#973)
Browse files Browse the repository at this point in the history
Provided Escape and Delete key functionaly to remove data points while drawing polyline and polygon

Co-authored-by: Shadab Khan <[email protected]>
Co-authored-by: Håvard Bjerke <[email protected]>
  • Loading branch information
3 people authored May 10, 2022
1 parent 4ce8fb4 commit 34f709a
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 3 deletions.
7 changes: 6 additions & 1 deletion react/src/lib/components/DeckGLMap/components/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
validateColorTables,
validateLayers,
} from "../../../inputSchema/schemaValidationUtil";
import { DrawingPickInfo } from "../layers/drawing/drawingLayer";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const colorTables = require("@emerson-eps/color-tables/dist/component/color-tables.json");
Expand Down Expand Up @@ -421,10 +422,14 @@ const Map: React.FC<MapProps> = ({
}
// @ts-expect-error: Fix type in WellsLayer
getTooltip={(
info: PickInfo<unknown> | WellsPickInfo
info: PickInfo<unknown>
): string | null | undefined => {
if ((info as WellsPickInfo)?.logName) {
return (info as WellsPickInfo)?.logName;
} else if (info.layer?.id === "drawing-layer") {
return (info as DrawingPickInfo).measurement?.toFixed(
2
);
} else {
const feat = info.object as Feature;
return feat?.properties?.["name"];
Expand Down
65 changes: 63 additions & 2 deletions react/src/lib/components/DeckGLMap/layers/drawing/drawingLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
EditAction,
Feature,
FeatureCollection,
GeoJsonEditMode,
ImmutableFeatureCollection,
ModeProps,
ModifyMode,
Expand All @@ -17,6 +18,7 @@ import { EditableGeoJsonLayer } from "@nebula.gl/layers";
import { CompositeLayer, PickInfo } from "deck.gl";
import { layersDefaultProps } from "../layersDefaultProps";
import { DeckGLLayerContext } from "../../components/Map";
import { area, length } from "../../utils/measurement";

// Custom drawing mode that deletes the selected GeoJson feature when releasing the Delete key.
class CustomModifyMode extends ModifyMode {
Expand All @@ -41,14 +43,50 @@ class CustomModifyMode extends ModifyMode {
}
}

function deleteEscapeKeyHandler(
drawMode: GeoJsonEditMode,
event: KeyboardEvent,
props: ModeProps<FeatureCollection>
) {
if (event.key === "Escape") drawMode.getClickSequence().pop();
else if (event.key === "Delete") drawMode.resetClickSequence();
else return;

// used to set state so layer can be rerendered
const updatedData = new ImmutableFeatureCollection(props.data).getObject();
if (updatedData) {
props.onEdit({
updatedData,
editType: "undoDrawing",
editContext: {
featureIndexes: props.selectedIndexes,
},
});
}
}

class CustomDrawLineStringMode extends DrawLineStringMode {
handleKeyUp(event: KeyboardEvent, props: ModeProps<FeatureCollection>) {
super.handleKeyUp(event, props);
deleteEscapeKeyHandler(this, event, props);
}
}

class CustomDrawPolygonMode extends DrawPolygonMode {
handleKeyUp(event: KeyboardEvent, props: ModeProps<FeatureCollection>) {
super.handleKeyUp(event, props);
deleteEscapeKeyHandler(this, event, props);
}
}

// Mapping of mode name to mode class
const MODE_MAP = {
view: ViewMode,
modify: CustomModifyMode,
transform: TransformMode,
drawPoint: DrawPointMode,
drawLineString: DrawLineStringMode,
drawPolygon: DrawPolygonMode,
drawLineString: CustomDrawLineStringMode,
drawPolygon: CustomDrawPolygonMode,
};

const UNSELECTED_LINE_COLOR: RGBAColor = [0x50, 0x50, 0x50, 0xcc];
Expand All @@ -59,6 +97,10 @@ export interface DrawingLayerProps<D> extends ExtendedLayerProps<D> {
selectedFeatureIndexes: number[];
}

export interface DrawingPickInfo extends PickInfo<unknown> {
measurement?: number;
}

// Composite layer that contains an EditableGeoJsonLayer from nebula.gl
// See https://nebula.gl/docs/api-reference/layers/editable-geojson-layer
export default class DrawingLayer extends CompositeLayer<
Expand Down Expand Up @@ -91,6 +133,24 @@ export default class DrawingLayer extends CompositeLayer<
return false;
}

// For now, use `any` for the picking types because this function should
// recieve PickInfo<FeatureCollection>, but it recieves PickInfo<Feature>.
//eslint-disable-next-line
getPickingInfo({ info }: { info: any }): any {
if (!info.object) return info as DrawingPickInfo;
const feature = info.object;
let measurement;
if (feature.geometry.type === "LineString") {
measurement = length(feature);
} else if (feature.geometry.type === "Polygon") {
measurement = area(feature);
} else return info as DrawingPickInfo;
return {
...info,
measurement: measurement,
} as DrawingPickInfo;
}

// Callback for various editing events. Most events will update this component
// through patches sent to the map parent. See patchLayerPropsin layerTools.ts.
_onEdit(editAction: EditAction<FeatureCollection>): void {
Expand Down Expand Up @@ -127,6 +187,7 @@ export default class DrawingLayer extends CompositeLayer<
});
break;
case "movePosition":
case "undoDrawing":
// Don't use setEditedData to avoid an expensive roundtrip,
// since this is done on every mouse move when editing.
this.setState({ data: editAction.updatedData });
Expand Down
57 changes: 57 additions & 0 deletions react/src/lib/components/DeckGLMap/utils/measurement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { FeatureOf, LineString, Polygon } from "@nebula.gl/edit-modes";
import { Position } from "deck.gl";

// eslint-disable-next-line @typescript-eslint/no-var-requires
const meta = require("@turf/meta");

export function length(geojson: FeatureOf<LineString>): number {
// Calculate distance from 2-vertex line segments
return meta.segmentReduce(
geojson,
function (previousValue: number, segment: FeatureOf<LineString>) {
const coords = segment.geometry.coordinates;
return previousValue + distance(coords[0], coords[1]);
},
0
);
}

/**
* Takes one or more features and returns their area in square meters.
*/
export function area(geojson: FeatureOf<Polygon>): number {
return meta.geomReduce(
geojson,
function (value: number, geom: Polygon) {
return value + calculateArea(geom);
},
0
);
}

// return distance between two points in XY plane
function distance(from: Position, to: Position): number {
const [x1, y1, z1] = from;
const [x2, y2, z2] = to;
let a = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
if (z1 && z2) a += Math.pow(z2 - z1, 2);
return Math.sqrt(a);
}

// Calculate Area
function calculateArea(geom: Polygon): number {
const coords = geom.coordinates[0];

let total = 0;
for (let i = 0, l = coords.length; i < l; i++) {
const addX = coords[i][0];
const addY = coords[i == coords.length - 1 ? 0 : i + 1][1];
const subX = coords[i == coords.length - 1 ? 0 : i + 1][0];
const subY = coords[i][1];

total += addX * addY * 0.5;
total -= subX * subY * 0.5;
}

return Math.abs(total);
}

0 comments on commit 34f709a

Please sign in to comment.