diff --git a/examples/draw-features.html b/examples/draw-features.html index 038a644a5a..613a82a911 100644 --- a/examples/draw-features.html +++ b/examples/draw-features.html @@ -5,11 +5,8 @@ shortdesc: Example of using the ol.interaction.Draw interaction. 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. 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. + point. To activate freehand drawing for lines, polygons, and circles, hold + the `Shift` key. tags: "draw, edit, freehand, vector" ---
@@ -20,8 +17,6 @@ tags: "draw, edit, freehand, vector" - - diff --git a/examples/draw-features.js b/examples/draw-features.js index e3a57c4d41..a7c77aeb07 100644 --- a/examples/draw-features.js +++ b/examples/draw-features.js @@ -1,15 +1,10 @@ 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'); goog.require('ol.source.OSM'); goog.require('ol.source.Vector'); -goog.require('ol.style.Circle'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style'); var raster = new ol.layer.Tile({ source: new ol.source.OSM() @@ -18,22 +13,7 @@ var raster = new ol.layer.Tile({ var source = new ol.source.Vector({wrapX: false}); var vector = new ol.layer.Vector({ - source: source, - style: new ol.style.Style({ - fill: new ol.style.Fill({ - color: 'rgba(255, 255, 255, 0.2)' - }), - stroke: new ol.style.Stroke({ - color: '#ffcc33', - width: 2 - }), - image: new ol.style.Circle({ - radius: 7, - fill: new ol.style.Fill({ - color: '#ffcc33' - }) - }) - }) + source: source }); var map = new ol.Map({ @@ -51,30 +31,9 @@ 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), - geometryFunction: geometryFunction, - maxPoints: maxPoints + type: /** @type {ol.geom.GeometryType} */ (typeSelect.value) }); map.addInteraction(draw); } diff --git a/examples/draw-freehand.html b/examples/draw-freehand.html new file mode 100644 index 0000000000..76f830b287 --- /dev/null +++ b/examples/draw-freehand.html @@ -0,0 +1,22 @@ +--- +layout: example.html +title: Freehand Drawing +shortdesc: Example using the ol.interaction.Draw interaction in freehand mode. +docs: > + This example demonstrates the `ol.interaction.Draw` in freehand mode. During + freehand drawing, points are added while dragging. Set `freehand: true` to + enable freehand mode. Note that freehand mode can be conditionally enabled + by using the `freehandCondition` option. For example to toggle freehand mode + with the `Shift` key, use `freehandCondition: ol.events.condition.shiftKeyOnly`. +tags: "draw, edit, freehand, vector" +--- +
+
+ + +
diff --git a/examples/draw-freehand.js b/examples/draw-freehand.js new file mode 100644 index 0000000000..01ef780b00 --- /dev/null +++ b/examples/draw-freehand.js @@ -0,0 +1,52 @@ +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.interaction.Draw'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.OSM'); +goog.require('ol.source.Vector'); + +var raster = new ol.layer.Tile({ + source: new ol.source.OSM() +}); + +var source = new ol.source.Vector({wrapX: false}); + +var vector = new ol.layer.Vector({ + source: source +}); + +var map = new ol.Map({ + layers: [raster, vector], + target: 'map', + view: new ol.View({ + center: [-11000000, 4600000], + zoom: 4 + }) +}); + +var typeSelect = document.getElementById('type'); + +var draw; // global so we can remove it later +function addInteraction() { + var value = typeSelect.value; + if (value !== 'None') { + draw = new ol.interaction.Draw({ + source: source, + type: /** @type {ol.geom.GeometryType} */ (typeSelect.value), + freehand: true + }); + map.addInteraction(draw); + } +} + + +/** + * Handle change event. + */ +typeSelect.onchange = function() { + map.removeInteraction(draw); + addInteraction(); +}; + +addInteraction(); diff --git a/examples/draw-shapes.html b/examples/draw-shapes.html new file mode 100644 index 0000000000..19501e090c --- /dev/null +++ b/examples/draw-shapes.html @@ -0,0 +1,25 @@ +--- +layout: example.html +title: Draw Shapes +shortdesc: Using the ol.interaction.Draw to create regular shapes +docs: > + This demonstrates the use of the `geometryFunction` option for the + `ol.interaction.Draw`. Select a shape type from the dropdown above to start + drawing. To activate freehand drawing, hold the `Shift` key. Square drawing is + 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. +tags: "draw, edit, freehand, vector" +--- +
+
+ + +
diff --git a/examples/draw-shapes.js b/examples/draw-shapes.js new file mode 100644 index 0000000000..b305802d6f --- /dev/null +++ b/examples/draw-shapes.js @@ -0,0 +1,86 @@ +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'); +goog.require('ol.source.OSM'); +goog.require('ol.source.Vector'); + +var raster = new ol.layer.Tile({ + source: new ol.source.OSM() +}); + +var source = new ol.source.Vector({wrapX: false}); + +var vector = new ol.layer.Vector({ + source: source +}); + +var map = new ol.Map({ + layers: [raster, vector], + target: 'map', + view: new ol.View({ + center: [-11000000, 4600000], + zoom: 4 + }) +}); + +var typeSelect = document.getElementById('type'); + +var draw; // global so we can remove it later +function addInteraction() { + var value = typeSelect.value; + if (value !== 'None') { + var geometryFunction; + if (value === 'Square') { + value = 'Circle'; + geometryFunction = ol.interaction.Draw.createRegularPolygon(4); + } else if (value === 'Box') { + value = 'Circle'; + geometryFunction = ol.interaction.Draw.createBox(); + } else if (value === 'Star') { + value = 'Circle'; + geometryFunction = function(coordinates, geometry) { + if (!geometry) { + geometry = new ol.geom.Polygon(null); + } + var center = coordinates[0]; + var last = coordinates[1]; + var dx = center[0] - last[0]; + var dy = center[1] - last[1]; + var radius = Math.sqrt(dx * dx + dy * dy); + var rotation = Math.atan2(dy, dx); + var newCoordinates = []; + var numPoints = 12; + for (var i = 0; i < numPoints; ++i) { + var angle = rotation + i * 2 * Math.PI / numPoints; + var fraction = i % 2 === 0 ? 1 : 0.5; + var offsetX = radius * fraction * Math.cos(angle); + var offsetY = radius * fraction * Math.sin(angle); + newCoordinates.push([center[0] + offsetX, center[1] + offsetY]); + } + newCoordinates.push(newCoordinates[0].slice()); + geometry.setCoordinates([newCoordinates]); + return geometry; + }; + } + draw = new ol.interaction.Draw({ + source: source, + type: /** @type {ol.geom.GeometryType} */ (value), + geometryFunction: geometryFunction + }); + map.addInteraction(draw); + } +} + + +/** + * Handle change event. + */ +typeSelect.onchange = function() { + map.removeInteraction(draw); + addInteraction(); +}; + +addInteraction(); diff --git a/src/ol/interaction/draw.js b/src/ol/interaction/draw.js index c0f05454c4..970e157e91 100644 --- a/src/ol/interaction/draw.js +++ b/src/ol/interaction/draw.js @@ -2,6 +2,7 @@ goog.provide('ol.interaction.Draw'); goog.require('ol'); goog.require('ol.events'); +goog.require('ol.extent'); goog.require('ol.events.Event'); goog.require('ol.Feature'); goog.require('ol.MapBrowserEvent.EventType'); @@ -731,7 +732,7 @@ ol.interaction.Draw.prototype.updateState_ = function() { /** - * Create a `geometryFunction` for `mode: 'Circle'` that will create a regular + * 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 * `ol.geom.Circle` geometry. * @param {number=} opt_sides Number of sides of the regular polygon. Default is @@ -766,6 +767,36 @@ ol.interaction.Draw.createRegularPolygon = function(opt_sides, opt_angle) { }; +/** + * Create a `geometryFunction` that will create a box-shaped polygon (aligned + * with the coordinate system axes). Use this with the draw interaction and + * `type: 'Circle'` to return a box instead of a circle geometry. + * @return {ol.DrawGeometryFunctionType} Function that draws a box-shaped polygon. + * @api + */ +ol.interaction.Draw.createBox = function() { + return ( + /** + * @param {ol.Coordinate|Array.|Array.>} coordinates + * @param {ol.geom.SimpleGeometry=} opt_geometry + * @return {ol.geom.SimpleGeometry} + */ + function(coordinates, opt_geometry) { + var extent = ol.extent.boundingExtent(coordinates); + var geometry = opt_geometry || new ol.geom.Polygon(null); + geometry.setCoordinates([[ + ol.extent.getBottomLeft(extent), + ol.extent.getBottomRight(extent), + ol.extent.getTopRight(extent), + ol.extent.getTopLeft(extent), + ol.extent.getBottomLeft(extent) + ]]); + return geometry; + } + ); +}; + + /** * Get the drawing mode. The mode for mult-part geometries is the same as for * their single-part cousins. diff --git a/test/spec/ol/interaction/draw.test.js b/test/spec/ol/interaction/draw.test.js index 78ef2b996c..603a59dde7 100644 --- a/test/spec/ol/interaction/draw.test.js +++ b/test/spec/ol/interaction/draw.test.js @@ -897,6 +897,35 @@ describe('ol.interaction.Draw', function() { }); }); + describe('ol.interaction.Draw.createBox', function() { + it('creates a box-shaped polygon in Circle mode', function() { + var draw = new ol.interaction.Draw({ + source: source, + type: 'Circle', + geometryFunction: ol.interaction.Draw.createBox() + }); + 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]).to.have.length(5); + expect(geometry.getArea()).to.equal(400); + expect(geometry.getExtent()).to.eql([0, -20, 20, 0]); + }); + }); + describe('extend an existing feature', function() { var draw; var feature;