Adding interior ring digitizing for polygons. Thanks jachym for the initial work. r=ahocevar (closes #1894)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@10828 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
@@ -20,6 +20,20 @@
|
||||
*/
|
||||
OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
|
||||
|
||||
/**
|
||||
* APIProperty: holeModifier
|
||||
* {String} Key modifier to trigger hole digitizing. Acceptable values are
|
||||
* "altKey", "shiftKey", or "ctrlKey". If not set, no hole digitizing
|
||||
* will take place. Default is null.
|
||||
*/
|
||||
holeModifier: null,
|
||||
|
||||
/**
|
||||
* Property: drawingHole
|
||||
* {Boolean} Currently drawing an interior ring.
|
||||
*/
|
||||
drawingHole: false,
|
||||
|
||||
/**
|
||||
* Parameter: polygon
|
||||
* {<OpenLayers.Feature.Vector>}
|
||||
@@ -68,13 +82,150 @@ OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
|
||||
this.line = new OpenLayers.Feature.Vector(
|
||||
new OpenLayers.Geometry.LinearRing([this.point.geometry])
|
||||
);
|
||||
this.polygon = new OpenLayers.Feature.Vector(
|
||||
new OpenLayers.Geometry.Polygon([this.line.geometry])
|
||||
);
|
||||
|
||||
// check for hole digitizing
|
||||
var polygon;
|
||||
if (this.holeModifier && (this.evt[this.holeModifier])) {
|
||||
var geometry = this.point.geometry;
|
||||
var features = this.control.layer.features;
|
||||
var candidate;
|
||||
// look for intersections, last drawn gets priority
|
||||
for (var i=features.length-1; i>=0; --i) {
|
||||
candidate = features[i].geometry;
|
||||
if ((candidate instanceof OpenLayers.Geometry.Polygon ||
|
||||
candidate instanceof OpenLayers.Geometry.MultiPolygon) &&
|
||||
candidate.intersects(geometry)) {
|
||||
polygon = features[i];
|
||||
this.control.layer.removeFeatures([polygon], {silent: true});
|
||||
this.control.layer.events.registerPriority(
|
||||
"sketchcomplete", this, this.finalizeInteriorRing
|
||||
);
|
||||
this.control.layer.events.registerPriority(
|
||||
"sketchmodified", this, this.enforceTopology
|
||||
);
|
||||
polygon.geometry.addComponent(this.line.geometry);
|
||||
this.polygon = polygon;
|
||||
this.drawingHole = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!polygon) {
|
||||
this.polygon = new OpenLayers.Feature.Vector(
|
||||
new OpenLayers.Geometry.Polygon([this.line.geometry])
|
||||
);
|
||||
}
|
||||
|
||||
this.callback("create", [this.point.geometry, this.getSketch()]);
|
||||
this.point.geometry.clearBounds();
|
||||
this.layer.addFeatures([this.polygon, this.point], {silent: true});
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: enforceTopology
|
||||
* Simple topology enforcement for drawing interior rings. Ensures vertices
|
||||
* of interior rings are contained by exterior ring. Other topology
|
||||
* rules are enforced in <finalizeInteriorRing> to allow drawing of
|
||||
* rings that intersect only during the sketch (e.g. a "C" shaped ring
|
||||
* that nearly encloses another ring).
|
||||
*/
|
||||
enforceTopology: function(event) {
|
||||
var point = event.vertex;
|
||||
var components = this.line.geometry.components;
|
||||
// ensure that vertices of interior ring are contained by exterior ring
|
||||
if (!this.polygon.geometry.intersects(point)) {
|
||||
var last = components[components.length-3];
|
||||
point.x = last.x;
|
||||
point.y = last.y;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: finalizeInteriorRing
|
||||
* Enforces that new ring has some area and doesn't contain vertices of any
|
||||
* other rings.
|
||||
*/
|
||||
finalizeInteriorRing: function() {
|
||||
var ring = this.line.geometry;
|
||||
// ensure that ring has some area
|
||||
var modified = (ring.getArea() !== 0);
|
||||
if (modified) {
|
||||
// ensure that new ring doesn't intersect any other rings
|
||||
var rings = this.polygon.geometry.components;
|
||||
for (var i=rings.length-2; i>=0; --i) {
|
||||
if (ring.intersects(rings[i])) {
|
||||
modified = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (modified) {
|
||||
// ensure that new ring doesn't contain any other rings
|
||||
var target;
|
||||
outer: for (var i=rings.length-2; i>0; --i) {
|
||||
points = rings[i].components;
|
||||
for (var j=0, jj=points.length; j<jj; ++j) {
|
||||
if (ring.containsPoint(points[j])) {
|
||||
modified = false;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modified) {
|
||||
if (this.polygon.state !== OpenLayers.State.INSERT) {
|
||||
this.polygon.state = OpenLayers.State.UPDATE;
|
||||
}
|
||||
} else {
|
||||
this.polygon.geometry.removeComponent(ring);
|
||||
}
|
||||
this.restoreFeature();
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: cancel
|
||||
* Finish the geometry and call the "cancel" callback.
|
||||
*/
|
||||
cancel: function() {
|
||||
if (this.drawingHole) {
|
||||
this.polygon.geometry.removeComponent(this.line.geometry);
|
||||
this.restoreFeature(true);
|
||||
}
|
||||
return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: restoreFeature
|
||||
* Move the feature from the sketch layer to the target layer.
|
||||
*
|
||||
* Properties:
|
||||
* cancel - {Boolean} Cancel drawing. If falsey, the "sketchcomplete" event
|
||||
* will be fired.
|
||||
*/
|
||||
restoreFeature: function(cancel) {
|
||||
this.control.layer.events.unregister(
|
||||
"sketchcomplete", this, this.finalizeInteriorRing
|
||||
);
|
||||
this.control.layer.events.unregister(
|
||||
"sketchmodified", this, this.enforceTopology
|
||||
);
|
||||
this.layer.removeFeatures([this.polygon], {silent: true});
|
||||
this.control.layer.addFeatures([this.polygon], {silent: true});
|
||||
this.drawingHole = false;
|
||||
if (!cancel) {
|
||||
// Re-trigger "sketchcomplete" so other listeners can do their
|
||||
// business. While this is somewhat sloppy (if a listener is
|
||||
// registered with registerPriority - not common - between the start
|
||||
// and end of a single ring drawing - very uncommon - it will be
|
||||
// called twice).
|
||||
// TODO: In 3.0, collapse sketch handlers into geometry specific
|
||||
// drawing controls.
|
||||
this.control.layer.events.triggerEvent(
|
||||
"sketchcomplete", {feature : this.polygon}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: destroyFeature
|
||||
|
||||
Reference in New Issue
Block a user