Merge pull request #3673 from ahocevar/draw-regular-polygon

More control over ol.interaction.Draw, to allow e.g. square drawing
This commit is contained in:
Andreas Hocevar
2015-05-20 12:08:15 +02:00
9 changed files with 394 additions and 110 deletions
+4
View File
@@ -2,6 +2,10 @@
### v3.6.0 ### 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 #### `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. * 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.
+6 -1
View File
@@ -6,7 +6,10 @@ docs: >
Example of using the Draw interaction. Select a geometry type from the Example of using the Draw interaction. Select a geometry type from the
dropdown above to start drawing. To finish drawing, click the last dropdown above to start drawing. To finish drawing, click the last
point. To activate freehand drawing for lines and polygons, hold the `Shift` 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" tags: "draw, edit, freehand, vector"
--- ---
<div class="row-fluid"> <div class="row-fluid">
@@ -20,6 +23,8 @@ tags: "draw, edit, freehand, vector"
<option value="LineString">LineString</option> <option value="LineString">LineString</option>
<option value="Polygon">Polygon</option> <option value="Polygon">Polygon</option>
<option value="Circle">Circle</option> <option value="Circle">Circle</option>
<option value="Square">Square</option>
<option value="Box">Box</option>
</select> </select>
</form> </form>
</div> </div>
+23 -1
View File
@@ -1,5 +1,6 @@
goog.require('ol.Map'); goog.require('ol.Map');
goog.require('ol.View'); goog.require('ol.View');
goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Draw'); goog.require('ol.interaction.Draw');
goog.require('ol.layer.Tile'); goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector'); goog.require('ol.layer.Vector');
@@ -51,9 +52,30 @@ var draw; // global so we can remove it later
function addInteraction() { function addInteraction() {
var value = typeSelect.value; var value = typeSelect.value;
if (value !== 'None') { 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({ draw = new ol.interaction.Draw({
source: source, source: source,
type: /** @type {ol.geom.GeometryType} */ (value) type: /** @type {ol.geom.GeometryType} */ (value),
geometryFunction: geometryFunction,
maxPoints: maxPoints
}); });
map.addInteraction(draw); map.addInteraction(draw);
} }
+24 -5
View File
@@ -2357,8 +2357,10 @@ olx.interaction.DragZoomOptions.prototype.style;
* source: (ol.source.Vector|undefined), * source: (ol.source.Vector|undefined),
* snapTolerance: (number|undefined), * snapTolerance: (number|undefined),
* type: ol.geom.GeometryType, * type: ol.geom.GeometryType,
* minPointsPerRing: (number|undefined), * maxPoints: (number|undefined),
* minPoints: (number|undefined),
* style: (ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction|undefined), * style: (ol.style.Style|Array.<ol.style.Style>|ol.style.StyleFunction|undefined),
* geometryFunction: (ol.interaction.Draw.GeometryFunctionType|undefined),
* geometryName: (string|undefined), * geometryName: (string|undefined),
* condition: (ol.events.ConditionType|undefined), * condition: (ol.events.ConditionType|undefined),
* freehandCondition: (ol.events.ConditionType|undefined)}} * freehandCondition: (ol.events.ConditionType|undefined)}}
@@ -2393,7 +2395,7 @@ olx.interaction.DrawOptions.prototype.snapTolerance;
/** /**
* Drawing type ('Point', 'LineString', 'Polygon', 'MultiPoint', * Drawing type ('Point', 'LineString', 'Polygon', 'MultiPoint',
* 'MultiLineString', or 'MultiPolygon'). * 'MultiLineString', 'MultiPolygon' or 'Circle').
* @type {ol.geom.GeometryType} * @type {ol.geom.GeometryType}
* @api * @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. * The number of points that can be drawn before a polygon ring or line string
* Default is `3`. * is finished. The default is no restriction.
* @type {number|undefined} * @type {number|undefined}
* @api * @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; 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. * Geometry name to use for features created by the draw interaction.
* @type {string|undefined} * @type {string|undefined}
+51
View File
@@ -2,6 +2,7 @@ goog.provide('ol.geom.Polygon');
goog.require('goog.array'); goog.require('goog.array');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.math');
goog.require('ol.extent'); goog.require('ol.extent');
goog.require('ol.geom.GeometryLayout'); goog.require('ol.geom.GeometryLayout');
goog.require('ol.geom.GeometryType'); goog.require('ol.geom.GeometryType');
@@ -422,3 +423,53 @@ ol.geom.Polygon.fromExtent = function(extent) {
ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]); ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]);
return polygon; 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);
};
+13
View File
@@ -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 the first coordinate of the geometry.
* @return {ol.Coordinate} First coordinate. * @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 {ol.geom.GeometryLayout|undefined} layout Layout.
* @param {Array} coordinates Coordinates. * @param {Array} coordinates Coordinates.
+209 -103
View File
@@ -12,6 +12,7 @@ goog.require('ol.FeatureOverlay');
goog.require('ol.MapBrowserEvent'); goog.require('ol.MapBrowserEvent');
goog.require('ol.MapBrowserEvent.EventType'); goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.Object'); goog.require('ol.Object');
goog.require('ol.coordinate');
goog.require('ol.events.condition'); goog.require('ol.events.condition');
goog.require('ol.geom.Circle'); goog.require('ol.geom.Circle');
goog.require('ol.geom.GeometryType'); goog.require('ol.geom.GeometryType');
@@ -21,6 +22,7 @@ goog.require('ol.geom.MultiPoint');
goog.require('ol.geom.MultiPolygon'); goog.require('ol.geom.MultiPolygon');
goog.require('ol.geom.Point'); goog.require('ol.geom.Point');
goog.require('ol.geom.Polygon'); goog.require('ol.geom.Polygon');
goog.require('ol.geom.SimpleGeometry');
goog.require('ol.interaction.InteractionProperty'); goog.require('ol.interaction.InteractionProperty');
goog.require('ol.interaction.Pointer'); goog.require('ol.interaction.Pointer');
goog.require('ol.source.Vector'); goog.require('ol.source.Vector');
@@ -126,15 +128,6 @@ ol.interaction.Draw = function(options) {
this.snapTolerance_ = goog.isDef(options.snapTolerance) ? this.snapTolerance_ = goog.isDef(options.snapTolerance) ?
options.snapTolerance : 12; 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. * Geometry type.
* @type {ol.geom.GeometryType} * @type {ol.geom.GeometryType}
@@ -149,6 +142,77 @@ ol.interaction.Draw = function(options) {
*/ */
this.mode_ = ol.interaction.Draw.getMode_(this.type_); 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.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} 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.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} 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 * Finish coordinate for the feature (first point for polygons, last point for
* linestrings). * linestrings).
@@ -171,6 +235,13 @@ ol.interaction.Draw = function(options) {
*/ */
this.sketchPoint_ = null; this.sketchPoint_ = null;
/**
* Sketch coordinates. Used when drawing a line or polygon.
* @type {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>}
* @private
*/
this.sketchCoords_ = null;
/** /**
* Sketch line. Used when drawing polygon. * Sketch line. Used when drawing polygon.
* @type {ol.Feature} * @type {ol.Feature}
@@ -179,11 +250,11 @@ ol.interaction.Draw = function(options) {
this.sketchLine_ = null; this.sketchLine_ = null;
/** /**
* Sketch polygon. Used when drawing polygon. * Sketch line coordinates. Used when drawing a polygon or circle.
* @type {Array.<Array.<ol.Coordinate>>} * @type {Array.<ol.Coordinate>}
* @private * @private
*/ */
this.sketchPolygonCoords_ = null; this.sketchLineCoords_ = null;
/** /**
* Squared tolerance for handling up events. If the squared distance * 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) { ol.interaction.Draw.prototype.atFinish_ = function(event) {
var at = false; var at = false;
if (!goog.isNull(this.sketchFeature_)) { if (!goog.isNull(this.sketchFeature_)) {
var geometry = this.sketchFeature_.getGeometry();
var potentiallyDone = false; var potentiallyDone = false;
var potentiallyFinishCoordinates = [this.finishCoordinate_]; var potentiallyFinishCoordinates = [this.finishCoordinate_];
if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
goog.asserts.assertInstanceof(geometry, ol.geom.LineString, potentiallyDone = this.sketchCoords_.length > this.minPoints_;
'geometry should be an ol.geom.LineString');
potentiallyDone = geometry.getCoordinates().length > 2;
} else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
goog.asserts.assertInstanceof(geometry, ol.geom.Polygon, potentiallyDone = this.sketchCoords_[0].length >
'geometry should be an ol.geom.Polygon'); this.minPoints_;
potentiallyDone = geometry.getCoordinates()[0].length > potentiallyFinishCoordinates = [this.sketchCoords_[0][0],
this.minPointsPerRing_; this.sketchCoords_[0][this.sketchCoords_[0].length - 2]];
potentiallyFinishCoordinates = [this.sketchPolygonCoords_[0][0],
this.sketchPolygonCoords_[0][this.sketchPolygonCoords_[0].length - 2]];
} }
if (potentiallyDone) { if (potentiallyDone) {
var map = event.map; var map = event.map;
@@ -425,23 +491,22 @@ ol.interaction.Draw.prototype.createOrUpdateSketchPoint_ = function(event) {
ol.interaction.Draw.prototype.startDrawing_ = function(event) { ol.interaction.Draw.prototype.startDrawing_ = function(event) {
var start = event.coordinate; var start = event.coordinate;
this.finishCoordinate_ = start; this.finishCoordinate_ = start;
var geometry;
if (this.mode_ === ol.interaction.DrawMode.POINT) { 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 { } else {
if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { this.sketchCoords_ = [start.slice(), start.slice()];
geometry = new ol.geom.LineString([start.slice(), start.slice()]); if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
} else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { this.sketchLineCoords_ = this.sketchCoords_;
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()]));
} }
} }
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'); goog.asserts.assert(goog.isDef(geometry), 'geometry should be defined');
this.sketchFeature_ = new ol.Feature(); this.sketchFeature_ = new ol.Feature();
if (goog.isDef(this.geometryName_)) { if (goog.isDef(this.geometryName_)) {
@@ -462,60 +527,50 @@ ol.interaction.Draw.prototype.startDrawing_ = function(event) {
ol.interaction.Draw.prototype.modifyDrawing_ = function(event) { ol.interaction.Draw.prototype.modifyDrawing_ = function(event) {
var coordinate = event.coordinate; var coordinate = event.coordinate;
var geometry = this.sketchFeature_.getGeometry(); 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) { if (this.mode_ === ol.interaction.DrawMode.POINT) {
goog.asserts.assertInstanceof(geometry, ol.geom.Point, last = this.sketchCoords_;
'geometry should be an ol.geom.Point'); } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
last = geometry.getCoordinates(); coordinates = this.sketchCoords_[0];
last[0] = coordinate[0]; last = coordinates[coordinates.length - 1];
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();
}
if (this.atFinish_(event)) { if (this.atFinish_(event)) {
// snap to finish // snap to finish
coordinate = this.finishCoordinate_.slice(); 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(); var sketchPointGeom = this.sketchPoint_.getGeometry();
goog.asserts.assertInstanceof(sketchPointGeom, ol.geom.Point, goog.asserts.assertInstanceof(sketchPointGeom, ol.geom.Point,
'sketchPointGeom should be an ol.geom.Point'); 'sketchPointGeom should be an ol.geom.Point');
sketchPointGeom.setCoordinates(coordinate); sketchPointGeom.setCoordinates(coordinate);
last = coordinates[coordinates.length - 1]; }
last[0] = coordinate[0]; var sketchLineGeom;
last[1] = coordinate[1]; if (geometry instanceof ol.geom.Polygon &&
if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { this.mode_ !== ol.interaction.DrawMode.POLYGON) {
goog.asserts.assertInstanceof(geometry, ol.geom.LineString, if (goog.isNull(this.sketchLine_)) {
'geometry should be an ol.geom.LineString'); this.sketchLine_ = new ol.Feature(new ol.geom.LineString(null));
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 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_(); this.updateSketchFeatures_();
}; };
@@ -529,21 +584,29 @@ ol.interaction.Draw.prototype.modifyDrawing_ = function(event) {
ol.interaction.Draw.prototype.addToDrawing_ = function(event) { ol.interaction.Draw.prototype.addToDrawing_ = function(event) {
var coordinate = event.coordinate; var coordinate = event.coordinate;
var geometry = this.sketchFeature_.getGeometry(); var geometry = this.sketchFeature_.getGeometry();
goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry,
'geometry must be an ol.geom.SimpleGeometry');
var done;
var coordinates; var coordinates;
if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
this.finishCoordinate_ = coordinate.slice(); this.finishCoordinate_ = coordinate.slice();
goog.asserts.assertInstanceof(geometry, ol.geom.LineString, coordinates = this.sketchCoords_;
'geometry should be an ol.geom.LineString');
coordinates = geometry.getCoordinates();
coordinates.push(coordinate.slice()); coordinates.push(coordinate.slice());
geometry.setCoordinates(coordinates); done = coordinates.length > this.maxPoints_;
this.geometryFunction_(coordinates, geometry);
} else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) {
this.sketchPolygonCoords_[0].push(coordinate.slice()); coordinates = this.sketchCoords_[0];
goog.asserts.assertInstanceof(geometry, ol.geom.Polygon, coordinates.push(coordinate.slice());
'geometry should be an ol.geom.Polygon'); done = coordinates.length > this.maxPoints_;
geometry.setCoordinates(this.sketchPolygonCoords_); if (done) {
this.finishCoordinate_ = coordinates[0];
}
this.geometryFunction_(this.sketchCoords_, geometry);
} }
this.updateSketchFeatures_(); this.updateSketchFeatures_();
if (done) {
this.finishDrawing();
}
}; };
@@ -557,29 +620,21 @@ ol.interaction.Draw.prototype.finishDrawing = function() {
var sketchFeature = this.abortDrawing_(); var sketchFeature = this.abortDrawing_();
goog.asserts.assert(!goog.isNull(sketchFeature), goog.asserts.assert(!goog.isNull(sketchFeature),
'sketchFeature should not be null'); 'sketchFeature should not be null');
var coordinates; var coordinates = this.sketchCoords_;
var geometry = sketchFeature.getGeometry(); var geometry = sketchFeature.getGeometry();
if (this.mode_ === ol.interaction.DrawMode.POINT) { goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry,
goog.asserts.assertInstanceof(geometry, ol.geom.Point, 'geometry must be an ol.geom.SimpleGeometry');
'geometry should be an ol.geom.Point'); if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) {
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();
// remove the redundant last point // remove the redundant last point
coordinates.pop(); coordinates.pop();
geometry.setCoordinates(coordinates); this.geometryFunction_(coordinates, geometry);
} else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { } 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, // When we finish drawing a polygon on the last point,
// the last coordinate is duplicated as for LineString // the last coordinate is duplicated as for LineString
// we force the replacement by the first point // we force the replacement by the first point
this.sketchPolygonCoords_[0].pop(); coordinates[0].pop();
this.sketchPolygonCoords_[0].push(this.sketchPolygonCoords_[0][0]); coordinates[0].push(coordinates[0][0]);
geometry.setCoordinates(this.sketchPolygonCoords_); this.geometryFunction_(coordinates, geometry);
coordinates = geometry.getCoordinates();
} }
// cast multi-part geometries // 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.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} 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 * Get the drawing mode. The mode for mult-part geometries is the same as for
* their single-part cousins. * 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.<ol.Coordinate>|
* Array.<Array.<ol.Coordinate>>), ol.geom.SimpleGeometry=):
* ol.geom.SimpleGeometry}
* @api
*/
ol.interaction.Draw.GeometryFunctionType;
/** /**
* Draw mode. This collapses multi-part geometry types with their single-part * Draw mode. This collapses multi-part geometry types with their single-part
* cousins. * cousins.
+34
View File
@@ -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.extent');
goog.require('ol.geom.Circle');
goog.require('ol.geom.GeometryLayout'); goog.require('ol.geom.GeometryLayout');
goog.require('ol.geom.LinearRing'); goog.require('ol.geom.LinearRing');
goog.require('ol.geom.Polygon'); goog.require('ol.geom.Polygon');
@@ -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'); goog.require('goog.dispose');