Use Mode.LINE_STRING for Circle geometries
Make predefined geometry functions compatible with removeLastPoint Add undo to example Make geometry function for Star compatible with use of removeLastPoint Additional createRegularPolygon tests
This commit is contained in:
@@ -9,18 +9,19 @@ docs: >
|
||||
achieved by using `type: 'Circle'` type with a `geometryFunction` that creates
|
||||
a 4-sided regular polygon instead of a circle. Box drawing uses `type: 'Circle'`
|
||||
with a `geometryFunction` that creates a box-shaped polygon instead of a
|
||||
circle. Star drawing uses a custom geometry function that coverts a circle
|
||||
into a start using the center and radius provided by the draw interaction.
|
||||
circle. Star drawing uses a custom geometry function that converts a circle
|
||||
into a star using the center and radius provided by the draw interaction.
|
||||
tags: "draw, edit, freehand, vector"
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<form class="form-inline">
|
||||
<label for="type">Shape type </label>
|
||||
<select id="type">
|
||||
<label for="type">Shape type: </label>
|
||||
<select class="form-control mr-2 mb-2 mt-2" id="type">
|
||||
<option value="Circle">Circle</option>
|
||||
<option value="Square">Square</option>
|
||||
<option value="Box">Box</option>
|
||||
<option value="Star">Star</option>
|
||||
<option value="None">None</option>
|
||||
</select>
|
||||
<input class="form-control mr-2 mb-2 mt-2" type="button" value="Undo" id="undo">
|
||||
</form>
|
||||
|
||||
@@ -43,8 +43,9 @@ function addInteraction() {
|
||||
} else if (value === 'Star') {
|
||||
value = 'Circle';
|
||||
geometryFunction = function (coordinates, geometry) {
|
||||
if (coordinates.length) {
|
||||
const center = coordinates[0];
|
||||
const last = coordinates[1];
|
||||
const last = coordinates[coordinates.length - 1];
|
||||
const dx = center[0] - last[0];
|
||||
const dy = center[1] - last[1];
|
||||
const radius = Math.sqrt(dx * dx + dy * dy);
|
||||
@@ -65,6 +66,7 @@ function addInteraction() {
|
||||
geometry.setCoordinates([newCoordinates]);
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
};
|
||||
}
|
||||
draw = new Draw({
|
||||
@@ -84,4 +86,8 @@ typeSelect.onchange = function () {
|
||||
addInteraction();
|
||||
};
|
||||
|
||||
document.getElementById('undo').addEventListener('click', function () {
|
||||
draw.removeLastPoint();
|
||||
});
|
||||
|
||||
addInteraction();
|
||||
|
||||
@@ -123,7 +123,6 @@ const Mode = {
|
||||
POINT: 'Point',
|
||||
LINE_STRING: 'LineString',
|
||||
POLYGON: 'Polygon',
|
||||
CIRCLE: 'Circle',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -290,7 +289,12 @@ class Draw extends PointerInteraction {
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.maxPoints_ = options.maxPoints ? options.maxPoints : Infinity;
|
||||
this.maxPoints_ =
|
||||
this.type_ === GeometryType.CIRCLE
|
||||
? 2
|
||||
: options.maxPoints
|
||||
? options.maxPoints
|
||||
: Infinity;
|
||||
|
||||
/**
|
||||
* A function to decide if a potential finish coordinate is permissible
|
||||
@@ -311,13 +315,17 @@ class Draw extends PointerInteraction {
|
||||
* @return {import("../geom/SimpleGeometry.js").default} A geometry.
|
||||
*/
|
||||
geometryFunction = function (coordinates, geometry, projection) {
|
||||
if (coordinates.length) {
|
||||
const circle = geometry
|
||||
? /** @type {Circle} */ (geometry)
|
||||
: new Circle([NaN, NaN]);
|
||||
const center = fromUserCoordinate(coordinates[0], projection);
|
||||
const squaredLength = squaredCoordinateDistance(
|
||||
center,
|
||||
fromUserCoordinate(coordinates[1], projection)
|
||||
fromUserCoordinate(
|
||||
coordinates[coordinates.length - 1],
|
||||
projection
|
||||
)
|
||||
);
|
||||
circle.setCenterAndRadius(center, Math.sqrt(squaredLength));
|
||||
const userProjection = getUserProjection();
|
||||
@@ -325,6 +333,7 @@ class Draw extends PointerInteraction {
|
||||
circle.transform(projection, userProjection);
|
||||
}
|
||||
return circle;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
let Constructor;
|
||||
@@ -551,7 +560,7 @@ class Draw extends PointerInteraction {
|
||||
event.preventDefault();
|
||||
}
|
||||
} else if (
|
||||
event.originalEvent.pointerType == 'mouse' ||
|
||||
event.originalEvent.pointerType === 'mouse' ||
|
||||
(event.type === MapBrowserEventType.POINTERDRAG &&
|
||||
this.downTimeout_ === undefined)
|
||||
) {
|
||||
@@ -618,15 +627,13 @@ class Draw extends PointerInteraction {
|
||||
|
||||
this.handlePointerMove_(event);
|
||||
|
||||
const circleMode = this.mode_ === Mode.CIRCLE;
|
||||
|
||||
if (this.shouldHandle_) {
|
||||
if (!this.finishCoordinate_) {
|
||||
this.startDrawing_(event);
|
||||
if (this.mode_ === Mode.POINT) {
|
||||
this.finishDrawing();
|
||||
}
|
||||
} else if (this.freehand_ || circleMode) {
|
||||
} else if (this.freehand_) {
|
||||
this.finishDrawing();
|
||||
} else if (this.atFinish_(event)) {
|
||||
if (this.finishCondition_(event)) {
|
||||
@@ -808,7 +815,7 @@ class Draw extends PointerInteraction {
|
||||
/** @type {LineString} */
|
||||
let sketchLineGeom;
|
||||
if (
|
||||
geometry.getType() == GeometryType.POLYGON &&
|
||||
geometry.getType() === GeometryType.POLYGON &&
|
||||
this.mode_ !== Mode.POLYGON
|
||||
) {
|
||||
if (!this.sketchLine_) {
|
||||
@@ -905,11 +912,23 @@ class Draw extends PointerInteraction {
|
||||
}
|
||||
}
|
||||
this.geometryFunction_(coordinates, geometry, projection);
|
||||
|
||||
if (geometry.getType() === GeometryType.POLYGON && this.sketchLine_) {
|
||||
sketchLineGeom = this.sketchLine_.getGeometry();
|
||||
if (sketchLineGeom) {
|
||||
const ring = geometry.getLinearRing(0);
|
||||
sketchLineGeom.setFlatCoordinates(
|
||||
ring.getLayout(),
|
||||
ring.getFlatCoordinates()
|
||||
);
|
||||
sketchLineGeom.changed();
|
||||
}
|
||||
}
|
||||
} else if (this.mode_ === Mode.POLYGON) {
|
||||
coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0];
|
||||
coordinates.splice(-2, 1);
|
||||
sketchLineGeom = this.sketchLine_.getGeometry();
|
||||
if (this.pointerType_ !== 'mouse') {
|
||||
if (coordinates.length >= 2 && this.pointerType_ !== 'mouse') {
|
||||
const finishCoordinate = coordinates[coordinates.length - 2].slice();
|
||||
coordinates.pop();
|
||||
coordinates.push(finishCoordinate);
|
||||
@@ -940,7 +959,7 @@ class Draw extends PointerInteraction {
|
||||
let coordinates = this.sketchCoords_;
|
||||
const geometry = sketchFeature.getGeometry();
|
||||
const projection = this.getMap().getView().getProjection();
|
||||
if (this.mode_ === Mode.LINE_STRING) {
|
||||
if (this.mode_ === Mode.LINE_STRING && this.type_ !== GeometryType.CIRCLE) {
|
||||
// remove the redundant last point
|
||||
coordinates.pop();
|
||||
this.geometryFunction_(coordinates, geometry, projection);
|
||||
@@ -1109,43 +1128,47 @@ function getDefaultStyleFunction() {
|
||||
|
||||
/**
|
||||
* Create a `geometryFunction` for `type: 'Circle'` that will create a regular
|
||||
* polygon with a user specified number of sides and start angle instead of an
|
||||
* polygon with a user specified number of sides and start angle instead of a
|
||||
* `import("../geom/Circle.js").Circle` geometry.
|
||||
* @param {number=} opt_sides Number of sides of the regular polygon. Default is
|
||||
* 32.
|
||||
* @param {number=} opt_angle Angle of the first point in radians. 0 means East.
|
||||
* @param {number=} opt_sides Number of sides of the regular polygon.
|
||||
* Default is 32.
|
||||
* @param {number=} opt_angle Angle of the first point in counter-clockwise
|
||||
* radians. 0 means East.
|
||||
* Default is the angle defined by the heading from the center of the
|
||||
* regular polygon to the current pointer position.
|
||||
* @return {GeometryFunction} Function that draws a
|
||||
* polygon.
|
||||
* @return {GeometryFunction} Function that draws a polygon.
|
||||
* @api
|
||||
*/
|
||||
export function createRegularPolygon(opt_sides, opt_angle) {
|
||||
return function (coordinates, opt_geometry, projection) {
|
||||
if (coordinates.length) {
|
||||
const center = fromUserCoordinate(
|
||||
/** @type {LineCoordType} */ (coordinates)[0],
|
||||
projection
|
||||
);
|
||||
const end = fromUserCoordinate(
|
||||
/** @type {LineCoordType} */ (coordinates)[1],
|
||||
/** @type {LineCoordType} */ (coordinates)[coordinates.length - 1],
|
||||
projection
|
||||
);
|
||||
const radius = Math.sqrt(squaredCoordinateDistance(center, end));
|
||||
const geometry = opt_geometry
|
||||
? /** @type {Polygon} */ (opt_geometry)
|
||||
: fromCircle(new Circle(center), opt_sides);
|
||||
|
||||
let angle = opt_angle;
|
||||
if (!opt_angle) {
|
||||
if (!opt_angle && opt_angle !== 0) {
|
||||
const x = end[0] - center[0];
|
||||
const y = end[1] - center[1];
|
||||
angle = Math.atan(y / x) - (x < 0 ? Math.PI : 0);
|
||||
angle = Math.atan2(y, x);
|
||||
}
|
||||
makeRegular(geometry, center, radius, angle);
|
||||
|
||||
const userProjection = getUserProjection();
|
||||
if (userProjection) {
|
||||
geometry.transform(projection, userProjection);
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1158,8 +1181,12 @@ export function createRegularPolygon(opt_sides, opt_angle) {
|
||||
*/
|
||||
export function createBox() {
|
||||
return function (coordinates, opt_geometry, projection) {
|
||||
if (coordinates.length) {
|
||||
const extent = boundingExtent(
|
||||
/** @type {LineCoordType} */ (coordinates).map(function (coordinate) {
|
||||
/** @type {LineCoordType} */ ([
|
||||
coordinates[0],
|
||||
coordinates[coordinates.length - 1],
|
||||
]).map(function (coordinate) {
|
||||
return fromUserCoordinate(coordinate, projection);
|
||||
})
|
||||
);
|
||||
@@ -1183,6 +1210,7 @@ export function createBox() {
|
||||
geometry.transform(projection, userProjection);
|
||||
}
|
||||
return geometry;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1198,7 +1226,8 @@ function getMode(type) {
|
||||
mode = Mode.POINT;
|
||||
} else if (
|
||||
type === GeometryType.LINE_STRING ||
|
||||
type === GeometryType.MULTI_LINE_STRING
|
||||
type === GeometryType.MULTI_LINE_STRING ||
|
||||
type === GeometryType.CIRCLE
|
||||
) {
|
||||
mode = Mode.LINE_STRING;
|
||||
} else if (
|
||||
@@ -1206,8 +1235,6 @@ function getMode(type) {
|
||||
type === GeometryType.MULTI_POLYGON
|
||||
) {
|
||||
mode = Mode.POLYGON;
|
||||
} else if (type === GeometryType.CIRCLE) {
|
||||
mode = Mode.CIRCLE;
|
||||
}
|
||||
return /** @type {!Mode} */ (mode);
|
||||
}
|
||||
|
||||
@@ -1346,6 +1346,33 @@ describe('ol.interaction.Draw', function () {
|
||||
|
||||
describe('createRegularPolygon', function () {
|
||||
it('creates a regular polygon in Circle mode', function () {
|
||||
const draw = new Draw({
|
||||
source: source,
|
||||
type: 'Circle',
|
||||
geometryFunction: createRegularPolygon(4),
|
||||
});
|
||||
map.addInteraction(draw);
|
||||
|
||||
// first point
|
||||
simulateEvent('pointermove', 0, 0);
|
||||
simulateEvent('pointerdown', 0, 0);
|
||||
simulateEvent('pointerup', 0, 0);
|
||||
|
||||
// finish on second point
|
||||
simulateEvent('pointermove', 20, 20);
|
||||
simulateEvent('pointerdown', 20, 20);
|
||||
simulateEvent('pointerup', 20, 20);
|
||||
|
||||
const features = source.getFeatures();
|
||||
const geometry = features[0].getGeometry();
|
||||
expect(geometry).to.be.a(Polygon);
|
||||
const coordinates = geometry.getCoordinates();
|
||||
expect(coordinates[0].length).to.eql(5);
|
||||
expect(coordinates[0][0][0]).to.roughlyEqual(20, 1e-9);
|
||||
expect(coordinates[0][0][1]).to.roughlyEqual(-20, 1e-9);
|
||||
});
|
||||
|
||||
it('creates a regular polygon at specified angle', function () {
|
||||
const draw = new Draw({
|
||||
source: source,
|
||||
type: 'Circle',
|
||||
@@ -1372,6 +1399,33 @@ describe('ol.interaction.Draw', function () {
|
||||
expect(coordinates[0][0][1]).to.roughlyEqual(20, 1e-9);
|
||||
});
|
||||
|
||||
it('creates a regular polygon at specified 0 angle', function () {
|
||||
const draw = new Draw({
|
||||
source: source,
|
||||
type: 'Circle',
|
||||
geometryFunction: createRegularPolygon(4, 0),
|
||||
});
|
||||
map.addInteraction(draw);
|
||||
|
||||
// first point
|
||||
simulateEvent('pointermove', 0, 0);
|
||||
simulateEvent('pointerdown', 0, 0);
|
||||
simulateEvent('pointerup', 0, 0);
|
||||
|
||||
// finish on second point
|
||||
simulateEvent('pointermove', 20, 20);
|
||||
simulateEvent('pointerdown', 20, 20);
|
||||
simulateEvent('pointerup', 20, 20);
|
||||
|
||||
const features = source.getFeatures();
|
||||
const geometry = features[0].getGeometry();
|
||||
expect(geometry).to.be.a(Polygon);
|
||||
const coordinates = geometry.getCoordinates();
|
||||
expect(coordinates[0].length).to.eql(5);
|
||||
expect(coordinates[0][0][0]).to.roughlyEqual(28.2842712474619, 1e-9);
|
||||
expect(coordinates[0][0][1]).to.roughlyEqual(0, 1e-9);
|
||||
});
|
||||
|
||||
it('creates a regular polygon in Circle mode in a user projection', function () {
|
||||
const userProjection = 'EPSG:3857';
|
||||
setUserProjection(userProjection);
|
||||
|
||||
Reference in New Issue
Block a user