diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md
index da27391b41..d5acbc293a 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 a7c902d6d8..9c0c883333 100644
--- a/examples/draw-features.html
+++ b/examples/draw-features.html
@@ -6,7 +6,10 @@ docs: >
Example of using the Draw interaction. Select a geometry type from the
dropdown above to start drawing. To finish drawing, click the last
point. To activate freehand drawing for lines and polygons, hold the `Shift`
- key.
+ key. Square drawing is achieved by using Circle mode with a `geometryFunction`
+ 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"
---
@@ -20,6 +23,8 @@ tags: "draw, edit, freehand, vector"
LineString
Polygon
Circle
+ Square
+ Box
diff --git a/examples/draw-features.js b/examples/draw-features.js
index dcab9c70db..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,9 +52,30 @@ var draw; // global so we can remove it later
function addInteraction() {
var value = typeSelect.value;
if (value !== 'None') {
+ var geometryFunction, maxPoints;
+ if (value === 'Square') {
+ value = 'Circle';
+ 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)
+ type: /** @type {ol.geom.GeometryType} */ (value),
+ geometryFunction: geometryFunction,
+ maxPoints: maxPoints
});
map.addInteraction(draw);
}
diff --git a/externs/olx.js b/externs/olx.js
index 9f34c4723c..d6b21a87c4 100644
--- a/externs/olx.js
+++ b/externs/olx.js
@@ -2357,8 +2357,10 @@ 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),
* condition: (ol.events.ConditionType|undefined),
* freehandCondition: (ol.events.ConditionType|undefined)}}
@@ -2393,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
*/
@@ -2401,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;
/**
@@ -2417,6 +2428,14 @@ olx.interaction.DrawOptions.prototype.minPointsPerRing;
olx.interaction.DrawOptions.prototype.style;
+/**
+ * Function that is called when a geometry's coordinates are updated.
+ * @type {ol.interaction.Draw.GeometryFunctionType|undefined}
+ * @api
+ */
+olx.interaction.DrawOptions.prototype.geometryFunction;
+
+
/**
* Geometry name to use for features created by the draw interaction.
* @type {string|undefined}
diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js
index 4a8d3b6ac8..676f160dcd 100644
--- a/src/ol/geom/polygon.js
+++ b/src/ol/geom/polygon.js
@@ -2,6 +2,7 @@ goog.provide('ol.geom.Polygon');
goog.require('goog.array');
goog.require('goog.asserts');
+goog.require('goog.math');
goog.require('ol.extent');
goog.require('ol.geom.GeometryLayout');
goog.require('ol.geom.GeometryType');
@@ -422,3 +423,53 @@ ol.geom.Polygon.fromExtent = function(extent) {
ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
return polygon;
};
+
+
+/**
+ * Create a regular polygon from a circle.
+ * @param {ol.geom.Circle} circle Circle geometry.
+ * @param {number=} opt_sides Number of sides of the polygon. Default is 32.
+ * @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;
+ var stride = circle.getStride();
+ var layout = circle.getLayout();
+ var polygon = new ol.geom.Polygon(null, layout);
+ var flatCoordinates = goog.array.repeat(0, stride * (sides + 1));
+ var ends = [flatCoordinates.length];
+ polygon.setFlatCoordinates(layout, flatCoordinates, ends);
+ ol.geom.Polygon.makeRegular(
+ polygon, circle.getCenter(), circle.getRadius(), opt_angle);
+ return polygon;
+};
+
+
+/**
+ * Modify the coordinates of a polygon to make it a regular polygon.
+ * @param {ol.geom.Polygon} polygon Polygon geometry.
+ * @param {ol.Coordinate} center Center of the regular polygon.
+ * @param {number} radius Radius of the regular polygon.
+ * @param {number=} opt_angle Start angle for the first vertex of the polygon in
+ * radians. Default is 0.
+ */
+ol.geom.Polygon.makeRegular = function(polygon, center, radius, opt_angle) {
+ var flatCoordinates = polygon.getFlatCoordinates();
+ var layout = polygon.getLayout();
+ var stride = polygon.getStride();
+ var ends = polygon.getEnds();
+ goog.asserts.assert(ends.length === 1, 'only 1 ring is supported');
+ var sides = flatCoordinates.length / stride - 1;
+ var startAngle = goog.isDef(opt_angle) ? opt_angle : 0;
+ var angle, coord, offset;
+ for (var i = 0; i <= sides; ++i) {
+ offset = i * stride;
+ angle = startAngle + (goog.math.modulo(i, sides) * 2 * Math.PI / sides);
+ flatCoordinates[offset] = center[0] + (radius * Math.cos(angle));
+ flatCoordinates[offset + 1] = center[1] + (radius * Math.sin(angle));
+ }
+ polygon.setFlatCoordinates(layout, flatCoordinates, ends);
+};
diff --git a/src/ol/geom/simplegeometry.js b/src/ol/geom/simplegeometry.js
index c2991011bb..e99e5479b9 100644
--- a/src/ol/geom/simplegeometry.js
+++ b/src/ol/geom/simplegeometry.js
@@ -98,6 +98,12 @@ ol.geom.SimpleGeometry.prototype.computeExtent = function(extent) {
};
+/**
+ * @return {Array} Coordinates.
+ */
+ol.geom.SimpleGeometry.prototype.getCoordinates = goog.abstractMethod;
+
+
/**
* Return the first coordinate of the geometry.
* @return {ol.Coordinate} First coordinate.
@@ -209,6 +215,13 @@ ol.geom.SimpleGeometry.prototype.setFlatCoordinatesInternal =
};
+/**
+ * @param {Array} coordinates Coordinates.
+ * @param {ol.geom.GeometryLayout=} opt_layout Layout.
+ */
+ol.geom.SimpleGeometry.prototype.setCoordinates = goog.abstractMethod;
+
+
/**
* @param {ol.geom.GeometryLayout|undefined} layout Layout.
* @param {Array} coordinates Coordinates.
diff --git a/src/ol/interaction/drawinteraction.js b/src/ol/interaction/drawinteraction.js
index 8fdbf488ce..cf3388a6d8 100644
--- a/src/ol/interaction/drawinteraction.js
+++ b/src/ol/interaction/drawinteraction.js
@@ -12,6 +12,7 @@ goog.require('ol.FeatureOverlay');
goog.require('ol.MapBrowserEvent');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.Object');
+goog.require('ol.coordinate');
goog.require('ol.events.condition');
goog.require('ol.geom.Circle');
goog.require('ol.geom.GeometryType');
@@ -21,6 +22,7 @@ goog.require('ol.geom.MultiPoint');
goog.require('ol.geom.MultiPolygon');
goog.require('ol.geom.Point');
goog.require('ol.geom.Polygon');
+goog.require('ol.geom.SimpleGeometry');
goog.require('ol.interaction.InteractionProperty');
goog.require('ol.interaction.Pointer');
goog.require('ol.source.Vector');
@@ -126,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}
@@ -149,6 +142,77 @@ 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) {
+ /**
+ * @param {ol.Coordinate|Array.|Array.>} coordinates
+ * @param {ol.geom.SimpleGeometry=} opt_geometry
+ * @return {ol.geom.SimpleGeometry}
+ */
+ geometryFunction = function(coordinates, opt_geometry) {
+ var circle = goog.isDef(opt_geometry) ? opt_geometry :
+ new ol.geom.Circle([NaN, NaN]);
+ goog.asserts.assertInstanceof(circle, ol.geom.Circle,
+ 'geometry must be an ol.geom.Circle');
+ var squaredLength = ol.coordinate.squaredDistance(
+ coordinates[0], coordinates[1]);
+ circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength));
+ return circle;
+ };
+ } else {
+ var Constructor;
+ var mode = this.mode_;
+ if (mode === ol.interaction.DrawMode.POINT) {
+ Constructor = ol.geom.Point;
+ } else if (mode === ol.interaction.DrawMode.LINE_STRING) {
+ Constructor = ol.geom.LineString;
+ } else if (mode === ol.interaction.DrawMode.POLYGON) {
+ Constructor = ol.geom.Polygon;
+ }
+ /**
+ * @param {ol.Coordinate|Array.|Array.>} coordinates
+ * @param {ol.geom.SimpleGeometry=} opt_geometry
+ * @return {ol.geom.SimpleGeometry}
+ */
+ geometryFunction = function(coordinates, opt_geometry) {
+ var geometry = opt_geometry;
+ if (goog.isDef(geometry)) {
+ geometry.setCoordinates(coordinates);
+ } else {
+ geometry = new Constructor(coordinates);
+ }
+ return geometry;
+ };
+ }
+ }
+
+ /**
+ * @type {ol.interaction.Draw.GeometryFunctionType}
+ * @private
+ */
+ this.geometryFunction_ = geometryFunction;
+
/**
* Finish coordinate for the feature (first point for polygons, last point for
* linestrings).
@@ -171,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}
@@ -179,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
@@ -362,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;
@@ -425,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 = new ol.geom.Point(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 = new ol.geom.LineString([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 = new ol.geom.Polygon(this.sketchPolygonCoords_);
- } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
- geometry = new ol.geom.Circle(start.slice(), 0);
- this.sketchLine_ = new ol.Feature(new ol.geom.LineString([start.slice(),
- start.slice()]));
+ 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_)) {
@@ -462,60 +527,50 @@ ol.interaction.Draw.prototype.startDrawing_ = function(event) {
ol.interaction.Draw.prototype.modifyDrawing_ = function(event) {
var coordinate = event.coordinate;
var geometry = this.sketchFeature_.getGeometry();
- var coordinates, last, sketchLineGeom;
+ goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry,
+ 'geometry should be ol.geom.SimpleGeometry or subclass');
+ var coordinates, last;
if (this.mode_ === ol.interaction.DrawMode.POINT) {
- goog.asserts.assertInstanceof(geometry, ol.geom.Point,
- 'geometry should be an ol.geom.Point');
- last = geometry.getCoordinates();
- last[0] = coordinate[0];
- last[1] = coordinate[1];
- geometry.setCoordinates(last);
- } 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();
- } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
- goog.asserts.assertInstanceof(geometry, ol.geom.Polygon,
- 'geometry should be an ol.geom.Polygon');
- coordinates = this.sketchPolygonCoords_[0];
- } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
- goog.asserts.assertInstanceof(geometry, ol.geom.Circle,
- 'geometry should be an ol.geom.Circle');
- coordinates = geometry.getCenter();
- }
+ 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) {
- goog.asserts.assertInstanceof(geometry, ol.geom.LineString,
- 'geometry should be an ol.geom.LineString');
- geometry.setCoordinates(coordinates);
- } 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.assertInstanceof(geometry, ol.geom.Polygon,
- 'geometry should be an ol.geom.Polygon');
- geometry.setCoordinates(this.sketchPolygonCoords_);
- } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
- goog.asserts.assertInstanceof(geometry, ol.geom.Circle,
- 'geometry should be an ol.geom.Circle');
- sketchLineGeom = this.sketchLine_.getGeometry();
- goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString,
- 'sketchLineGeom should be an ol.geom.LineString');
- sketchLineGeom.setCoordinates([geometry.getCenter(), coordinate]);
- geometry.setRadius(sketchLineGeom.getLength());
+ }
+ 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_();
};
@@ -529,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());
- geometry.setCoordinates(coordinates);
+ 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');
- geometry.setCoordinates(this.sketchPolygonCoords_);
+ 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();
+ }
};
@@ -557,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();
- geometry.setCoordinates(coordinates);
+ 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]);
- geometry.setCoordinates(this.sketchPolygonCoords_);
- coordinates = geometry.getCoordinates();
+ coordinates[0].pop();
+ coordinates[0].push(coordinates[0][0]);
+ this.geometryFunction_(coordinates, geometry);
}
// cast multi-part geometries
@@ -660,6 +715,44 @@ ol.interaction.Draw.prototype.updateState_ = function() {
};
+/**
+ * Create a `geometryFunction` for `mode: 'Circle'` that will create a regular
+ * polygon with a user specified number of sides and start angle instead of an
+ * `ol.geom.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.
+ * 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) {
+ return (
+ /**
+ * @param {ol.Coordinate|Array.|Array.>} coordinates
+ * @param {ol.geom.SimpleGeometry=} opt_geometry
+ * @return {ol.geom.SimpleGeometry}
+ */
+ function(coordinates, opt_geometry) {
+ var center = coordinates[0];
+ var end = coordinates[1];
+ var radius = Math.sqrt(
+ ol.coordinate.squaredDistance(center, end));
+ var geometry = goog.isDef(opt_geometry) ? opt_geometry :
+ 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;
+ }
+ );
+};
+
+
/**
* Get the drawing mode. The mode for mult-part geometries is the same as for
* their single-part cousins.
@@ -686,6 +779,19 @@ ol.interaction.Draw.getMode_ = function(type) {
};
+/**
+ * Function that takes coordinates and an optional existing geometry as
+ * arguments, and returns a geometry. The optional existing geometry is the
+ * geometry that is returned when the function is called without a second
+ * argument.
+ * @typedef {function(!(ol.Coordinate|Array.|
+ * Array.>), ol.geom.SimpleGeometry=):
+ * ol.geom.SimpleGeometry}
+ * @api
+ */
+ol.interaction.Draw.GeometryFunctionType;
+
+
/**
* Draw mode. This collapses multi-part geometry types with their single-part
* cousins.
diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js
index 25e99faf1b..2b42111f91 100644
--- a/test/spec/ol/geom/polygon.test.js
+++ b/test/spec/ol/geom/polygon.test.js
@@ -436,10 +436,44 @@ describe('ol.geom.Polygon', function() {
});
});
+ describe('ol.geom.Polygon.fromCircle', function() {
+
+ it('creates a regular polygon', function() {
+ var circle = new ol.geom.Circle([0, 0, 0], 1, ol.geom.GeometryLayout.XYZ);
+ var polygon = ol.geom.Polygon.fromCircle(circle);
+ var coordinates = polygon.getLinearRing(0).getCoordinates();
+ expect(coordinates[0].length).to.eql(3);
+ expect(coordinates[0][2]).to.eql(0);
+ expect(coordinates[32]).to.eql(coordinates[0]);
+ // east
+ expect(coordinates[0][0]).to.roughlyEqual(1, 1e-9);
+ expect(coordinates[0][1]).to.roughlyEqual(0, 1e-9);
+ // south
+ expect(coordinates[8][0]).to.roughlyEqual(0, 1e-9);
+ expect(coordinates[8][1]).to.roughlyEqual(1, 1e-9);
+ // west
+ expect(coordinates[16][0]).to.roughlyEqual(-1, 1e-9);
+ expect(coordinates[16][1]).to.roughlyEqual(0, 1e-9);
+ // north
+ expect(coordinates[24][0]).to.roughlyEqual(0, 1e-9);
+ expect(coordinates[24][1]).to.roughlyEqual(-1, 1e-9);
+ });
+
+ it('creates a regular polygon with custom sides and angle', function() {
+ var circle = new ol.geom.Circle([0, 0], 1);
+ var polygon = ol.geom.Polygon.fromCircle(circle, 4, Math.PI / 2);
+ var coordinates = polygon.getLinearRing(0).getCoordinates();
+ expect(coordinates[4]).to.eql(coordinates[0]);
+ expect(coordinates[0][0]).to.roughlyEqual(0, 1e-9);
+ expect(coordinates[0][1]).to.roughlyEqual(1, 1e-9);
+ });
+ });
+
});
goog.require('ol.extent');
+goog.require('ol.geom.Circle');
goog.require('ol.geom.GeometryLayout');
goog.require('ol.geom.LinearRing');
goog.require('ol.geom.Polygon');
diff --git a/test/spec/ol/interaction/drawinteraction.test.js b/test/spec/ol/interaction/drawinteraction.test.js
index dda8d2dcdc..684449a289 100644
--- a/test/spec/ol/interaction/drawinteraction.test.js
+++ b/test/spec/ol/interaction/drawinteraction.test.js
@@ -708,6 +708,36 @@ describe('ol.interaction.Draw', function() {
});
});
+
+ describe('ol.interaction.Draw.createRegularPolygon', function() {
+ it('creates a regular polygon in Circle mode', function() {
+ var draw = new ol.interaction.Draw({
+ source: source,
+ type: ol.geom.GeometryType.CIRCLE,
+ geometryFunction:
+ ol.interaction.Draw.createRegularPolygon(4, Math.PI / 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);
+
+ var features = source.getFeatures();
+ var geometry = features[0].getGeometry();
+ expect(geometry).to.be.a(ol.geom.Polygon);
+ var 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);
+ });
+ });
});
goog.require('goog.dispose');