From 9b12cac5c6db72932056079e0c588c87e2ba2858 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 16 Oct 2016 15:49:13 -0600 Subject: [PATCH 1/5] Convenience function for drawing box-shaped polygons --- src/ol/interaction/draw.js | 31 +++++++++++++++++++++++++++ test/spec/ol/interaction/draw.test.js | 29 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/ol/interaction/draw.js b/src/ol/interaction/draw.js index c0f05454c4..6b8a8b3012 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'); @@ -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; From 1e6c4472b2ab3d3ccaf925b41fd9ea8127ced7b9 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 16 Oct 2016 15:49:43 -0600 Subject: [PATCH 2/5] Use the createBox() geometry function --- examples/draw-features.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/examples/draw-features.js b/examples/draw-features.js index e3a57c4d41..d045c2955f 100644 --- a/examples/draw-features.js +++ b/examples/draw-features.js @@ -1,6 +1,5 @@ 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,30 +50,18 @@ var draw; // global so we can remove it later function addInteraction() { var value = typeSelect.value; if (value !== 'None') { - var geometryFunction, maxPoints; + var geometryFunction; 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; - }; + value = 'Circle'; + geometryFunction = ol.interaction.Draw.createBox(); } draw = new ol.interaction.Draw({ source: source, type: /** @type {ol.geom.GeometryType} */ (value), - geometryFunction: geometryFunction, - maxPoints: maxPoints + geometryFunction: geometryFunction }); map.addInteraction(draw); } From ec1ad893384d3bf673d1e672affe485fd0513f9c Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 16 Oct 2016 15:50:07 -0600 Subject: [PATCH 3/5] Correct docs for createRegularPolygon() function --- src/ol/interaction/draw.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/interaction/draw.js b/src/ol/interaction/draw.js index 6b8a8b3012..970e157e91 100644 --- a/src/ol/interaction/draw.js +++ b/src/ol/interaction/draw.js @@ -732,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 From ac805e804ba3207cda6f57bb1b207a533ed5c537 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 16 Oct 2016 16:38:00 -0600 Subject: [PATCH 4/5] Divide the draw features example into two examples --- examples/draw-features.html | 9 +--- examples/draw-features.js | 32 +------------- examples/draw-shapes.html | 25 +++++++++++ examples/draw-shapes.js | 86 +++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 37 deletions(-) create mode 100644 examples/draw-shapes.html create mode 100644 examples/draw-shapes.js 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 d045c2955f..a7c77aeb07 100644 --- a/examples/draw-features.js +++ b/examples/draw-features.js @@ -5,10 +5,6 @@ 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() @@ -17,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({ @@ -50,18 +31,9 @@ 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(); - } draw = new ol.interaction.Draw({ source: source, - type: /** @type {ol.geom.GeometryType} */ (value), - geometryFunction: geometryFunction + type: /** @type {ol.geom.GeometryType} */ (typeSelect.value) }); map.addInteraction(draw); } 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(); From 5644fe3e2f1c3b26462db2051c39c1b42f8561eb Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 16 Oct 2016 16:45:42 -0600 Subject: [PATCH 5/5] Add example demonstrating freehand drawing --- examples/draw-freehand.html | 22 ++++++++++++++++ examples/draw-freehand.js | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 examples/draw-freehand.html create mode 100644 examples/draw-freehand.js 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();