diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md
index 7279cac26c..624e1a55f2 100644
--- a/changelog/upgrade-notes.md
+++ b/changelog/upgrade-notes.md
@@ -2,6 +2,10 @@
### v3.6.0
+#### `ol.interaction.Draw` changes
+
+* The `minPointsPerRing` config option has been renamed to `minPoints`. It is now also available for linestring drawing, not only for polygons.
+
#### `ol.tilegrid` changes
* The `ol.tilegrid.XYZ` constructor has been replaced by a static `ol.tilegrid.createXYZ()` function. The `ol.tilegrid.createXYZ()` function takes the same arguments as the previous `ol.tilegrid.XYZ` constructor, but returns an `ol.tilegrid.TileGrid` instance.
diff --git a/examples/draw-features.html b/examples/draw-features.html
index e9ec02e794..9c0c883333 100644
--- a/examples/draw-features.html
+++ b/examples/draw-features.html
@@ -7,7 +7,9 @@ docs: >
dropdown above to start drawing. To finish drawing, click the last
point. To activate freehand drawing for lines and polygons, hold the `Shift`
key. Square drawing is achieved by using Circle mode with a `geometryFunction`
- that creates a 4-sided regular polygon instead of a circle.
+ that creates a 4-sided regular polygon instead of a circle. Box drawing uses a
+ custom `geometryFunction` that takes start and end point of a line with 2
+ points and creates a rectangular box.
tags: "draw, edit, freehand, vector"
---
@@ -22,6 +24,7 @@ tags: "draw, edit, freehand, vector"
Polygon
Circle
Square
+ Box
diff --git a/examples/draw-features.js b/examples/draw-features.js
index 70ef9935ac..b7cb4cec5f 100644
--- a/examples/draw-features.js
+++ b/examples/draw-features.js
@@ -1,5 +1,6 @@
goog.require('ol.Map');
goog.require('ol.View');
+goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Draw');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
@@ -51,16 +52,30 @@ var draw; // global so we can remove it later
function addInteraction() {
var value = typeSelect.value;
if (value !== 'None') {
- var geometryFunction;
+ var geometryFunction, maxPoints;
if (value === 'Square') {
value = 'Circle';
- geometryFunction =
- ol.interaction.Draw.createRegularPolygon(4, Math.PI / 4);
+ geometryFunction = ol.interaction.Draw.createRegularPolygon(4);
+ } else if (value === 'Box') {
+ value = 'LineString';
+ maxPoints = 2;
+ geometryFunction = function(coordinates, geometry) {
+ if (!geometry) {
+ geometry = new ol.geom.Polygon(null);
+ }
+ var start = coordinates[0];
+ var end = coordinates[1];
+ geometry.setCoordinates([
+ [start, [start[0], end[1]], end, [end[0], start[1]], start]
+ ]);
+ return geometry;
+ };
}
draw = new ol.interaction.Draw({
source: source,
type: /** @type {ol.geom.GeometryType} */ (value),
- geometryFunction: geometryFunction
+ geometryFunction: geometryFunction,
+ maxPoints: maxPoints
});
map.addInteraction(draw);
}
diff --git a/externs/olx.js b/externs/olx.js
index 1711191910..d6b21a87c4 100644
--- a/externs/olx.js
+++ b/externs/olx.js
@@ -2357,7 +2357,8 @@ olx.interaction.DragZoomOptions.prototype.style;
* source: (ol.source.Vector|undefined),
* snapTolerance: (number|undefined),
* type: ol.geom.GeometryType,
- * minPointsPerRing: (number|undefined),
+ * maxPoints: (number|undefined),
+ * minPoints: (number|undefined),
* style: (ol.style.Style|Array.|ol.style.StyleFunction|undefined),
* geometryFunction: (ol.interaction.Draw.GeometryFunctionType|undefined),
* geometryName: (string|undefined),
@@ -2394,7 +2395,7 @@ olx.interaction.DrawOptions.prototype.snapTolerance;
/**
* Drawing type ('Point', 'LineString', 'Polygon', 'MultiPoint',
- * 'MultiLineString', or 'MultiPolygon').
+ * 'MultiLineString', 'MultiPolygon' or 'Circle').
* @type {ol.geom.GeometryType}
* @api
*/
@@ -2402,12 +2403,21 @@ olx.interaction.DrawOptions.prototype.type;
/**
- * The number of points that must be drawn before a polygon ring can be finished.
- * Default is `3`.
+ * The number of points that can be drawn before a polygon ring or line string
+ * is finished. The default is no restriction.
* @type {number|undefined}
* @api
*/
-olx.interaction.DrawOptions.prototype.minPointsPerRing;
+olx.interaction.DrawOptions.prototype.maxPoints;
+
+
+/**
+ * The number of points that must be drawn before a polygon ring or line string
+ * can be finished. Default is `3` for polygon rings and `2` for line strings.
+ * @type {number|undefined}
+ * @api
+ */
+olx.interaction.DrawOptions.prototype.minPoints;
/**
diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js
index 4d113ed4e3..676f160dcd 100644
--- a/src/ol/geom/polygon.js
+++ b/src/ol/geom/polygon.js
@@ -432,6 +432,7 @@ ol.geom.Polygon.fromExtent = function(extent) {
* @param {number=} opt_angle Start angle for the first vertex of the polygon in
* radians. Default is 0.
* @return {ol.geom.Polygon} Polygon geometry.
+ * @api
*/
ol.geom.Polygon.fromCircle = function(circle, opt_sides, opt_angle) {
var sides = goog.isDef(opt_sides) ? opt_sides : 32;
diff --git a/src/ol/interaction/drawinteraction.js b/src/ol/interaction/drawinteraction.js
index 2142494328..cf3388a6d8 100644
--- a/src/ol/interaction/drawinteraction.js
+++ b/src/ol/interaction/drawinteraction.js
@@ -128,15 +128,6 @@ ol.interaction.Draw = function(options) {
this.snapTolerance_ = goog.isDef(options.snapTolerance) ?
options.snapTolerance : 12;
- /**
- * The number of points that must be drawn before a polygon ring can be
- * finished. The default is 3.
- * @type {number}
- * @private
- */
- this.minPointsPerRing_ = goog.isDef(options.minPointsPerRing) ?
- options.minPointsPerRing : 3;
-
/**
* Geometry type.
* @type {ol.geom.GeometryType}
@@ -151,6 +142,26 @@ ol.interaction.Draw = function(options) {
*/
this.mode_ = ol.interaction.Draw.getMode_(this.type_);
+ /**
+ * The number of points that must be drawn before a polygon ring or line
+ * string can be finished. The default is 3 for polygon rings and 2 for
+ * line strings.
+ * @type {number}
+ * @private
+ */
+ this.minPoints_ = goog.isDef(options.minPoints) ?
+ options.minPoints :
+ (this.mode_ === ol.interaction.DrawMode.POLYGON ? 3 : 2);
+
+ /**
+ * The number of points that can be drawn before a polygon ring or line string
+ * is finished. The default is no restriction.
+ * @type {number}
+ * @private
+ */
+ this.maxPoints_ = goog.isDef(options.maxPoints) ?
+ options.maxPoints : Infinity;
+
var geometryFunction = options.geometryFunction;
if (!goog.isDef(geometryFunction)) {
if (this.type_ === ol.geom.GeometryType.CIRCLE) {
@@ -224,6 +235,13 @@ ol.interaction.Draw = function(options) {
*/
this.sketchPoint_ = null;
+ /**
+ * Sketch coordinates. Used when drawing a line or polygon.
+ * @type {ol.Coordinate|Array.|Array.>}
+ * @private
+ */
+ this.sketchCoords_ = null;
+
/**
* Sketch line. Used when drawing polygon.
* @type {ol.Feature}
@@ -232,11 +250,11 @@ ol.interaction.Draw = function(options) {
this.sketchLine_ = null;
/**
- * Sketch polygon. Used when drawing polygon.
- * @type {Array.>}
+ * Sketch line coordinates. Used when drawing a polygon or circle.
+ * @type {Array.}
* @private
*/
- this.sketchPolygonCoords_ = null;
+ this.sketchLineCoords_ = null;
/**
* Squared tolerance for handling up events. If the squared distance
@@ -415,20 +433,15 @@ ol.interaction.Draw.prototype.handlePointerMove_ = function(event) {
ol.interaction.Draw.prototype.atFinish_ = function(event) {
var at = false;
if (!goog.isNull(this.sketchFeature_)) {
- var geometry = this.sketchFeature_.getGeometry();
var potentiallyDone = false;
var potentiallyFinishCoordinates = [this.finishCoordinate_];
if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
- goog.asserts.assertInstanceof(geometry, ol.geom.LineString,
- 'geometry should be an ol.geom.LineString');
- potentiallyDone = geometry.getCoordinates().length > 2;
+ potentiallyDone = this.sketchCoords_.length > this.minPoints_;
} else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
- goog.asserts.assertInstanceof(geometry, ol.geom.Polygon,
- 'geometry should be an ol.geom.Polygon');
- potentiallyDone = geometry.getCoordinates()[0].length >
- this.minPointsPerRing_;
- potentiallyFinishCoordinates = [this.sketchPolygonCoords_[0][0],
- this.sketchPolygonCoords_[0][this.sketchPolygonCoords_[0].length - 2]];
+ potentiallyDone = this.sketchCoords_[0].length >
+ this.minPoints_;
+ potentiallyFinishCoordinates = [this.sketchCoords_[0][0],
+ this.sketchCoords_[0][this.sketchCoords_[0].length - 2]];
}
if (potentiallyDone) {
var map = event.map;
@@ -478,23 +491,22 @@ ol.interaction.Draw.prototype.createOrUpdateSketchPoint_ = function(event) {
ol.interaction.Draw.prototype.startDrawing_ = function(event) {
var start = event.coordinate;
this.finishCoordinate_ = start;
- var geometry;
if (this.mode_ === ol.interaction.DrawMode.POINT) {
- geometry = this.geometryFunction_(start.slice());
+ this.sketchCoords_ = start.slice();
+ } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
+ this.sketchCoords_ = [[start.slice(), start.slice()]];
+ this.sketchLineCoords_ = this.sketchCoords_[0];
} else {
- if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
- geometry = this.geometryFunction_([start.slice(), start.slice()]);
- } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
- this.sketchLine_ = new ol.Feature(new ol.geom.LineString([start.slice(),
- start.slice()]));
- this.sketchPolygonCoords_ = [[start.slice(), start.slice()]];
- geometry = this.geometryFunction_(this.sketchPolygonCoords_);
- } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
- var sketchCoords = [start.slice(), start.slice()];
- this.sketchLine_ = new ol.Feature(new ol.geom.LineString(sketchCoords));
- geometry = this.geometryFunction_(sketchCoords);
+ this.sketchCoords_ = [start.slice(), start.slice()];
+ if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
+ this.sketchLineCoords_ = this.sketchCoords_;
}
}
+ if (!goog.isNull(this.sketchLineCoords_)) {
+ this.sketchLine_ = new ol.Feature(
+ new ol.geom.LineString(this.sketchLineCoords_));
+ }
+ var geometry = this.geometryFunction_(this.sketchCoords_);
goog.asserts.assert(goog.isDef(geometry), 'geometry should be defined');
this.sketchFeature_ = new ol.Feature();
if (goog.isDef(this.geometryName_)) {
@@ -517,50 +529,48 @@ ol.interaction.Draw.prototype.modifyDrawing_ = function(event) {
var geometry = this.sketchFeature_.getGeometry();
goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry,
'geometry should be ol.geom.SimpleGeometry or subclass');
- var coordinates, last, sketchLineGeom;
+ var coordinates, last;
if (this.mode_ === ol.interaction.DrawMode.POINT) {
- last = geometry.getCoordinates();
- last[0] = coordinate[0];
- last[1] = coordinate[1];
- this.geometryFunction_(last, geometry);
- } else {
- if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
- coordinates = geometry.getCoordinates();
- } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
- coordinates = this.sketchPolygonCoords_[0];
- } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
- coordinates = coordinate;
- }
+ last = this.sketchCoords_;
+ } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
+ coordinates = this.sketchCoords_[0];
+ last = coordinates[coordinates.length - 1];
if (this.atFinish_(event)) {
// snap to finish
coordinate = this.finishCoordinate_.slice();
}
+ } else {
+ coordinates = this.sketchCoords_;
+ last = coordinates[coordinates.length - 1];
+ }
+ last[0] = coordinate[0];
+ last[1] = coordinate[1];
+ goog.asserts.assert(!goog.isNull(this.sketchCoords_),
+ 'sketchCoords_ must not be null');
+ this.geometryFunction_(this.sketchCoords_, geometry);
+ if (!goog.isNull(this.sketchPoint_)) {
var sketchPointGeom = this.sketchPoint_.getGeometry();
goog.asserts.assertInstanceof(sketchPointGeom, ol.geom.Point,
'sketchPointGeom should be an ol.geom.Point');
sketchPointGeom.setCoordinates(coordinate);
- last = coordinates[coordinates.length - 1];
- last[0] = coordinate[0];
- last[1] = coordinate[1];
- if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
- this.geometryFunction_(coordinates, geometry);
- } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
- sketchLineGeom = this.sketchLine_.getGeometry();
- goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString,
- 'sketchLineGeom should be an ol.geom.LineString');
- sketchLineGeom.setCoordinates(coordinates);
- goog.asserts.assert(!goog.isNull(this.sketchPolygonCoords_),
- 'sketchPolygonCoords_ must not be null');
- this.geometryFunction_(this.sketchPolygonCoords_, geometry);
- } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
- sketchLineGeom = this.sketchLine_.getGeometry();
- goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString,
- 'sketchLineGeom should be an ol.geom.LineString');
- var center = sketchLineGeom.getFlatCoordinates().slice(0, 2);
- coordinates = [center, coordinate];
- sketchLineGeom.setCoordinates(coordinates);
- this.geometryFunction_(coordinates, geometry);
+ }
+ var sketchLineGeom;
+ if (geometry instanceof ol.geom.Polygon &&
+ this.mode_ !== ol.interaction.DrawMode.POLYGON) {
+ if (goog.isNull(this.sketchLine_)) {
+ this.sketchLine_ = new ol.Feature(new ol.geom.LineString(null));
}
+ var ring = geometry.getLinearRing(0);
+ sketchLineGeom = this.sketchLine_.getGeometry();
+ goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString,
+ 'sketchLineGeom must be an ol.geom.LineString');
+ sketchLineGeom.setFlatCoordinates(
+ ring.getLayout(), ring.getFlatCoordinates());
+ } else if (!goog.isNull(this.sketchLineCoords_)) {
+ sketchLineGeom = this.sketchLine_.getGeometry();
+ goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString,
+ 'sketchLineGeom must be an ol.geom.LineString');
+ sketchLineGeom.setCoordinates(this.sketchLineCoords_);
}
this.updateSketchFeatures_();
};
@@ -574,21 +584,29 @@ ol.interaction.Draw.prototype.modifyDrawing_ = function(event) {
ol.interaction.Draw.prototype.addToDrawing_ = function(event) {
var coordinate = event.coordinate;
var geometry = this.sketchFeature_.getGeometry();
+ goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry,
+ 'geometry must be an ol.geom.SimpleGeometry');
+ var done;
var coordinates;
if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
this.finishCoordinate_ = coordinate.slice();
- goog.asserts.assertInstanceof(geometry, ol.geom.LineString,
- 'geometry should be an ol.geom.LineString');
- coordinates = geometry.getCoordinates();
+ coordinates = this.sketchCoords_;
coordinates.push(coordinate.slice());
+ done = coordinates.length > this.maxPoints_;
this.geometryFunction_(coordinates, geometry);
} else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
- this.sketchPolygonCoords_[0].push(coordinate.slice());
- goog.asserts.assertInstanceof(geometry, ol.geom.Polygon,
- 'geometry should be an ol.geom.Polygon');
- this.geometryFunction_(this.sketchPolygonCoords_, geometry);
+ coordinates = this.sketchCoords_[0];
+ coordinates.push(coordinate.slice());
+ done = coordinates.length > this.maxPoints_;
+ if (done) {
+ this.finishCoordinate_ = coordinates[0];
+ }
+ this.geometryFunction_(this.sketchCoords_, geometry);
}
this.updateSketchFeatures_();
+ if (done) {
+ this.finishDrawing();
+ }
};
@@ -602,29 +620,21 @@ ol.interaction.Draw.prototype.finishDrawing = function() {
var sketchFeature = this.abortDrawing_();
goog.asserts.assert(!goog.isNull(sketchFeature),
'sketchFeature should not be null');
- var coordinates;
+ var coordinates = this.sketchCoords_;
var geometry = sketchFeature.getGeometry();
- if (this.mode_ === ol.interaction.DrawMode.POINT) {
- goog.asserts.assertInstanceof(geometry, ol.geom.Point,
- 'geometry should be an ol.geom.Point');
- coordinates = geometry.getCoordinates();
- } else if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
- goog.asserts.assertInstanceof(geometry, ol.geom.LineString,
- 'geometry should be an ol.geom.LineString');
- coordinates = geometry.getCoordinates();
+ goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry,
+ 'geometry must be an ol.geom.SimpleGeometry');
+ if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
// remove the redundant last point
coordinates.pop();
this.geometryFunction_(coordinates, geometry);
} else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
- goog.asserts.assertInstanceof(geometry, ol.geom.Polygon,
- 'geometry should be an ol.geom.Polygon');
// When we finish drawing a polygon on the last point,
// the last coordinate is duplicated as for LineString
// we force the replacement by the first point
- this.sketchPolygonCoords_[0].pop();
- this.sketchPolygonCoords_[0].push(this.sketchPolygonCoords_[0][0]);
- this.geometryFunction_(this.sketchPolygonCoords_, geometry);
- coordinates = geometry.getCoordinates();
+ coordinates[0].pop();
+ coordinates[0].push(coordinates[0][0]);
+ this.geometryFunction_(coordinates, geometry);
}
// cast multi-part geometries
@@ -712,14 +722,13 @@ ol.interaction.Draw.prototype.updateState_ = function() {
* @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.
- * Default is 0.
+ * Default is the angle defined by the heading from the center of the
+ * regular polygon to the current pointer position.
* @return {ol.interaction.Draw.GeometryFunctionType} Function that draws a
* polygon.
* @api
*/
ol.interaction.Draw.createRegularPolygon = function(opt_sides, opt_angle) {
- var sides = goog.isDef(opt_sides) ? opt_sides : 32;
- var angle = goog.isDef(opt_angle) ? opt_angle : 0;
return (
/**
* @param {ol.Coordinate|Array.|Array.>} coordinates
@@ -728,12 +737,15 @@ ol.interaction.Draw.createRegularPolygon = function(opt_sides, opt_angle) {
*/
function(coordinates, opt_geometry) {
var center = coordinates[0];
+ var end = coordinates[1];
var radius = Math.sqrt(
- ol.coordinate.squaredDistance(coordinates[0], coordinates[1]));
+ ol.coordinate.squaredDistance(center, end));
var geometry = goog.isDef(opt_geometry) ? opt_geometry :
- ol.geom.Polygon.fromCircle(new ol.geom.Circle(center), sides);
+ ol.geom.Polygon.fromCircle(new ol.geom.Circle(center), opt_sides);
goog.asserts.assertInstanceof(geometry, ol.geom.Polygon,
'geometry must be a polygon');
+ var angle = goog.isDef(opt_angle) ? opt_angle :
+ Math.atan((end[1] - center[1]) / (end[0] - center[0]));
ol.geom.Polygon.makeRegular(geometry, center, radius, angle);
return geometry;
}