Skip to content

Commit

Permalink
Cleanup EditMode adapters (#316)
Browse files Browse the repository at this point in the history
This cleans up all the adapter functions that were in place to ease the refactor from the more stateful ModeHandlers to the more stateless EditModes interface.
  • Loading branch information
supersonicclay authored Jan 13, 2020
1 parent 9973a2d commit 1502cec
Show file tree
Hide file tree
Showing 37 changed files with 1,893 additions and 1,352 deletions.
2 changes: 1 addition & 1 deletion examples/advanced/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function getEditHandleColor(handle: {}) {
switch (getEditHandleTypeFromEitherLayer(handle)) {
case 'existing':
return [0xff, 0x80, 0x00, 0xff];
case 'snap':
case 'snap-source':
return [0xc0, 0x80, 0xf0, 0xff];
case 'intermediate':
default:
Expand Down
2 changes: 1 addition & 1 deletion modules/edit-modes/src/geojson-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export type BoundingBoxArray = [number, number, number, number];
export type FeatureOf<T: Geometry> = {
type: 'Feature',
geometry: T,
properties?: {},
properties?: { [key: string]: any },
id?: string | number,
bbox?: BoundingBoxArray
};
Expand Down
49 changes: 21 additions & 28 deletions modules/edit-modes/src/lib/composite-mode.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
// @flow

import type { FeatureCollection, Feature, Position } from '../geojson-types.js';
import type { FeatureCollection } from '../geojson-types.js';
import type {
ModeProps,
ClickEvent,
PointerMoveEvent,
StartDraggingEvent,
StopDraggingEvent
StopDraggingEvent,
GuideFeatureCollection
} from '../types.js';
import { BaseGeoJsonEditMode, type EditHandle } from './geojson-edit-mode.js';
import { BaseGeoJsonEditMode } from './geojson-edit-mode.js';

export class CompositeMode extends BaseGeoJsonEditMode {
handlers: Array<BaseGeoJsonEditMode>;
options: Object;
_modes: Array<BaseGeoJsonEditMode>;

constructor(handlers: Array<BaseGeoJsonEditMode>, options: Object = {}) {
constructor(modes: Array<BaseGeoJsonEditMode>) {
super();
this.handlers = handlers;
this.options = options;
this._modes = modes;
}

_coalesce<T>(callback: BaseGeoJsonEditMode => T, resultEval: ?(T) => boolean = null): T {
let result: T;

for (let i = 0; i < this.handlers.length; i++) {
result = callback(this.handlers[i]);
for (let i = 0; i < this._modes.length; i++) {
result = callback(this._modes[i]);
if (resultEval ? resultEval(result) : result) {
break;
}
Expand All @@ -49,25 +48,19 @@ export class CompositeMode extends BaseGeoJsonEditMode {
return this._coalesce(handler => handler.handleStopDragging(event, props));
}

getTentativeFeature(): ?Feature {
return this._coalesce(handler => handler.getTentativeFeature());
}

getEditHandlesAdapter(
picks: ?Array<Object>,
mapCoords: ?Position,
props: ModeProps<FeatureCollection>
): EditHandle[] {
// TODO: Combine the handles *BUT* make sure if none of the results have
// changed to return the same object so that "editHandles !== this.state.editHandles"
getGuides(props: ModeProps<FeatureCollection>): GuideFeatureCollection {
// TODO: Combine the guides *BUT* make sure if none of the results have
// changed to return the same object so that "guides !== this.state.guides"
// in editable-geojson-layer works.
return this._coalesce(
handler => handler.getEditHandlesAdapter(picks, mapCoords, props),
handles => Array.isArray(handles) && handles.length > 0
);
}

getCursorAdapter(props: ModeProps<FeatureCollection>): ?string {
return this._coalesce(handler => handler.getCursorAdapter(props));
const allGuides = [];
for (const mode of this._modes) {
allGuides.push(...mode.getGuides(props).features);
}

return {
type: 'FeatureCollection',
features: allGuides
};
}
}
143 changes: 71 additions & 72 deletions modules/edit-modes/src/lib/draw-90degree-polygon-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,29 @@ import destination from '@turf/destination';
import bearing from '@turf/bearing';
import lineIntersect from '@turf/line-intersect';
import turfDistance from '@turf/distance';
import { point, lineString } from '@turf/helpers';
import { generatePointsParallelToLinePoints } from '../utils';
import type { ClickEvent, PointerMoveEvent, ModeProps } from '../types.js';
import type { Polygon, Position, FeatureCollection } from '../geojson-types.js';
import { point, lineString as turfLineString } from '@turf/helpers';
import {
BaseGeoJsonEditMode,
generatePointsParallelToLinePoints,
getPickedEditHandle,
getEditHandlesForGeometry,
type GeoJsonEditAction,
type EditHandle
} from './geojson-edit-mode.js';
getEditHandlesForGeometry
} from '../utils';
import type { ClickEvent, PointerMoveEvent, ModeProps, GuideFeatureCollection } from '../types.js';
import type { Polygon, LineString, Position, FeatureCollection } from '../geojson-types.js';
import { BaseGeoJsonEditMode } from './geojson-edit-mode.js';

export class Draw90DegreePolygonMode extends BaseGeoJsonEditMode {
getEditHandlesAdapter(
picks: ?Array<Object>,
mapCoords: ?Position,
props: ModeProps<FeatureCollection>
): EditHandle[] {
let handles = super.getEditHandlesAdapter(picks, mapCoords, props);

const tentativeFeature = this.getTentativeFeature();
if (tentativeFeature) {
handles = handles.concat(getEditHandlesForGeometry(tentativeFeature.geometry, -1));
// Slice off the handles that are are next to the pointer
if (tentativeFeature && tentativeFeature.geometry.type === 'LineString') {
// Remove the last existing handle
handles = handles.slice(0, -1);
} else if (tentativeFeature && tentativeFeature.geometry.type === 'Polygon') {
// Remove the last existing handle
handles = handles.slice(0, -1);
}
}

return handles;
}
getGuides(props: ModeProps<FeatureCollection>): GuideFeatureCollection {
const guides: GuideFeatureCollection = {
type: 'FeatureCollection',
features: []
};

handlePointerMoveAdapter({
mapCoords
}: PointerMoveEvent): { editAction: ?GeoJsonEditAction, cancelMapPan: boolean } {
const clickSequence = this.getClickSequence();
const result = { editAction: null, cancelMapPan: false };

if (clickSequence.length === 0) {
// nothing to do yet
return result;
}

const tentativeFeature = this.getTentativeFeature();
if (tentativeFeature && tentativeFeature.geometry.type === 'Polygon') {
clickSequence[clickSequence.length - 1] =
tentativeFeature.geometry.coordinates[0][clickSequence.length - 1];
} else if (tentativeFeature && tentativeFeature.geometry.type === 'LineString') {
clickSequence[clickSequence.length - 1] =
tentativeFeature.geometry.coordinates[clickSequence.length - 1];
if (clickSequence.length === 0 || !props.lastPointerMoveEvent) {
return guides;
}
const { mapCoords } = props.lastPointerMoveEvent;

let p3;
if (clickSequence.length === 1) {
Expand All @@ -69,46 +37,80 @@ export class Draw90DegreePolygonMode extends BaseGeoJsonEditMode {
[p3] = generatePointsParallelToLinePoints(p1, p2, mapCoords);
}

let tentativeFeature;

if (clickSequence.length < 3) {
// Draw a LineString connecting all the clicked points with the hovered point
this._setTentativeFeature({
tentativeFeature = {
type: 'Feature',
properties: {
guideType: 'tentative'
},
geometry: {
type: 'LineString',
coordinates: [...clickSequence, p3]
}
});
};
} else {
// Draw a Polygon connecting all the clicked points with the hovered point
this._setTentativeFeature({
tentativeFeature = {
type: 'Feature',
properties: {
guideType: 'tentative'
},
geometry: {
type: 'Polygon',
coordinates: [[...clickSequence, p3, clickSequence[0]]]
}
});
};
}

return result;
guides.features.push(tentativeFeature);

guides.features = guides.features.concat(
getEditHandlesForGeometry(tentativeFeature.geometry, -1)
);

// Slice off the handles that are are next to the pointer
guides.features = guides.features.slice(0, -1);

return guides;
}

handleClickAdapter(event: ClickEvent, props: ModeProps<FeatureCollection>): ?GeoJsonEditAction {
super.handleClickAdapter(event, props);
handlePointerMove({ mapCoords }: PointerMoveEvent, props: ModeProps<FeatureCollection>) {
props.onUpdateCursor('cell');
}

handleClick(event: ClickEvent, props: ModeProps<FeatureCollection>) {
const { picks } = event;
const tentativeFeature = this.getTentativeFeature();
const tentativeFeature = this.getTentativeGuide(props);
this.addClickSequence(event);
const clickSequence = this.getClickSequence();

let editAction: ?GeoJsonEditAction = null;
const clickedEditHandle = getPickedEditHandle(picks);
if (!tentativeFeature) {
// nothing else to do
return;
}

if (tentativeFeature && tentativeFeature.geometry.type === 'Polygon') {
if (clickSequence.length === 3 && tentativeFeature.geometry.type === 'LineString') {
const lineString: LineString = tentativeFeature.geometry;

// Tweak the clicked position to be the snapped 90 degree point along the polygon
clickSequence[clickSequence.length - 1] =
lineString.coordinates[lineString.coordinates.length - 1];
} else if (clickSequence.length > 3 && tentativeFeature.geometry.type === 'Polygon') {
const polygon: Polygon = tentativeFeature.geometry;

// Tweak the clicked position to be the snapped 90 degree point along the polygon
clickSequence[clickSequence.length - 1] =
polygon.coordinates[0][polygon.coordinates[0].length - 2];

const clickedEditHandle = getPickedEditHandle(picks);

if (
clickedEditHandle &&
clickedEditHandle.featureIndex === -1 &&
(clickedEditHandle.positionIndexes[1] === 0 ||
clickedEditHandle.positionIndexes[1] === polygon.coordinates[0].length - 3)
(clickedEditHandle.properties.positionIndexes[1] === 0 ||
clickedEditHandle.properties.positionIndexes[1] === polygon.coordinates[0].length - 3)
) {
// They clicked the first or last point (or double-clicked), so complete the polygon
const polygonToAdd: Polygon = {
Expand All @@ -117,8 +119,11 @@ export class Draw90DegreePolygonMode extends BaseGeoJsonEditMode {
};

this.resetClickSequence();
this._setTentativeFeature(null);
editAction = this.getAddFeatureOrBooleanPolygonAction(polygonToAdd, props);

const editAction = this.getAddFeatureOrBooleanPolygonAction(polygonToAdd, props);
if (editAction) {
props.onEdit(editAction);
}
}
}

Expand All @@ -133,9 +138,7 @@ export class Draw90DegreePolygonMode extends BaseGeoJsonEditMode {
pointerDownMapCoords: null,
sourceEvent: null
};
this.handlePointerMoveAdapter(fakePointerMoveEvent);

return editAction;
this.handlePointerMove(fakePointerMoveEvent, props);
}

finalizedCoordinates(coords: Position[]) {
Expand Down Expand Up @@ -180,12 +183,12 @@ export class Draw90DegreePolygonMode extends BaseGeoJsonEditMode {
// Draw imaginary right angle lines for both first and last points in lineString
// If there is intersection point for any 2 lines, will be the 90 degree point.
[0, 1, 2].forEach(indexFirst => {
const line1 = lineString([
const line1 = turfLineString([
p1,
destination(p1, distance, angles.first[indexFirst]).geometry.coordinates
]);
[0, 1, 2].forEach(indexSecond => {
const line2 = lineString([
const line2 = turfLineString([
p3,
destination(p3, distance, angles.second[indexSecond]).geometry.coordinates
]);
Expand All @@ -199,8 +202,4 @@ export class Draw90DegreePolygonMode extends BaseGeoJsonEditMode {
}
return pt;
}

getCursorAdapter() {
return 'cell';
}
}
29 changes: 7 additions & 22 deletions modules/edit-modes/src/lib/draw-circle-by-diameter-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,24 @@

import circle from '@turf/circle';
import distance from '@turf/distance';
import type { PointerMoveEvent, ModeProps } from '../types.js';
import type { FeatureCollection } from '../geojson-types.js';
import { getIntermediatePosition, type GeoJsonEditAction } from './geojson-edit-mode.js';
import type { Position, Polygon, FeatureOf } from '../geojson-types.js';
import { getIntermediatePosition } from './geojson-edit-mode.js';
import { TwoClickPolygonMode } from './two-click-polygon-mode.js';

export class DrawCircleByDiameterMode extends TwoClickPolygonMode {
handlePointerMoveAdapter(
event: PointerMoveEvent,
props: ModeProps<FeatureCollection>
): { editAction: ?GeoJsonEditAction, cancelMapPan: boolean } {
const result = { editAction: null, cancelMapPan: false };
const clickSequence = this.getClickSequence();

if (clickSequence.length === 0) {
// nothing to do yet
return result;
}

const modeConfig = props.modeConfig || {};
getTwoClickPolygon(coord1: Position, coord2: Position, modeConfig: any): FeatureOf<Polygon> {
// Default turf value for circle is 64
const { steps = 64 } = modeConfig;
const { steps = 64 } = modeConfig || {};
const options = { steps };

if (steps < 4) {
console.warn(`Minimum steps to draw a circle is 4 `); // eslint-disable-line no-console,no-undef
options.steps = 4;
}

const firstClickedPoint = clickSequence[0];
const centerCoordinates = getIntermediatePosition(firstClickedPoint, event.mapCoords);
const radius = Math.max(distance(firstClickedPoint, centerCoordinates), 0.001);
this._setTentativeFeature(circle(centerCoordinates, radius, options));
const centerCoordinates = getIntermediatePosition(coord1, coord2);
const radius = Math.max(distance(coord1, centerCoordinates), 0.001);

return result;
return circle(centerCoordinates, radius, options);
}
}
Loading

0 comments on commit 1502cec

Please sign in to comment.