openlayers / ol2

OpenLayers v2 - deprecated!
Other
1.48k stars 771 forks source link

Add split method for OpenLayers.Geometry.Point #159

Open drnextgis opened 12 years ago

drnextgis commented 12 years ago

It would be useful to have such method. Now only Linestring and Multilinestring have this method.

probins commented 12 years ago

why would you want to split a point?

drnextgis commented 12 years ago

I would like to have tool for split another geometry by point not point itself.

ahocevar commented 12 years ago

Then Geometry.Point will not need a split method, but the split method in Geometry.LineString needs to be modified to work with a Point geometry as target.

drnextgis commented 12 years ago

I'm not quite clear about split method of Geometry.LineString. This method uses linestring for split a target geometry. But I've told about reverse operation - split linestring by point geometry. As I can see for this purpose method splitWith is used: return geometry.split(this, options); So I think for splitting linestring by point - point itself needs has split method.

probins commented 12 years ago

What is it you're trying to achieve? With the split control, you draw a line on the map, and where that line crosses one or more linestrings, those linestrings are split at the point of intersection. The software works out where that intersection is in terms of coordinates. If you're entering a point, how would you know whether it is even on the linestring (with the exception of the vertices)? The segments of linestrings are simply straight lines drawn between the vertices at whatever scale/projected plane you're using. Are you wanting to split a linestring at one of its vertices, or are you thinking that the line would be changed to go to the point you enter?

drnextgis commented 12 years ago

Sorry if I confused you. I would like to split linestring by drawing point. Now native method for split line by point lacks and I need to emulate point by line. See my example: http://gis-lab.info/share/DR/sandbox/split.html Snapping can be used for putting point exact within linestring.

probins commented 12 years ago

ok, I see what you mean. But the existing control works, doesn't it? I just went to your url above, where there is one linestring in map.layers[2].features. I moved your little rectangle to around halfway along the line, and clicked. There were then two linestring features in map.layers[2], split where I had clicked. Is that not what you want?

drnextgis commented 12 years ago

Yes, but in fact in this example I split linestring by another linestring. When I put point with (x,y) coordinates - invisible linestring is created in background with the following coordinates: strart point: (x-epsilon, y-epsilon), end point: (x+epsilon, y+epsilon), where epsilon is small constant (0.0001 in my example). It looks like hack, do you agree? In some case it may happen that the invisible line will be parallel to the line of splitted layer and my approach will not work.

probins commented 12 years ago

ah, ok, I see what you're doing. Yes, that is quite devious :-)

And, yes, I think you're right. If you want to do this and use the existing split control and logic, you would need Point.split() which linestring.splitWith() would call, and which would take a linestring geometry and split it at its own coordinates. Alternatively, as Point.split() sounds illogical to me, change splitWith() so that if geometry is a Point, it would call linestring.splitWithPoint() or such like.

I'll leave others to decide whether that would be a useful addition to the library.

juanluisrp commented 12 years ago

Of course,it would be very useful.

oburakevych commented 11 years ago

+1

aleksandrmelnyk commented 11 years ago

it would be useful in situations when line needs to split by point programmatically, not from control etc.

aleksandrmelnyk commented 11 years ago

Something like:

OpenLayers.Geometry.LineString.prototype.splitByVertex = function(vertex) { var verts = this.getVertices(), i, l, p, points = {seg1:[], seg2: []}, lines = [], over = false;

for (i = 0, l = verts.length; i < l; i++) {
    if (!over && verts[i].toString() == vertex.toString()) {
        points.seg1.push(verts[i]);
        over = true;
    } 

    if (over) {
        points.seg2.push(verts[i]);
    } else {
        points.seg1.push(verts[i]);
    }

}

    for (p in points) {

            lines.push(new OpenLayers.Geometry.LineString(points[p]));
    }

    return lines

}

uhef commented 10 years ago

Below is an implementation that splits a given OpenLayers.Geometry.LineString by given OpenLayers.Geometry.Point. Line string is split from the point on the line string that is closest to the given point. One can enhance it to ignore points that lie too far from the line string.

Algorithm uses higher-order collection functions from Lo-Dash (like reduce and sortBy) but naturally the concept can be applied in contexts where such library is not available.

Function returns two arrays of OpenLayers.Geometry.Point - objects which define the two halfs of the split. One can use these to construct two OpenLayers.Geometry.LineString - objects.

  var splitLineStringByPoint = function(lineString, point) {
    var segments = _.reduce(lineString.getVertices(), function(acc, vertex, index, vertices) {
      if (index > 0) {
        var previousVertex = vertices[index - 1];
        var segmentGeometry = new OpenLayers.Geometry.LineString([previousVertex, vertex]);
        var distanceObject = segmentGeometry.distanceTo(point, { details: true });
        var segment = {
          distance: distanceObject.distance,
          splitPoint: {
            x: distanceObject.x0,
            y: distanceObject.y0
          },
          index: index - 1
        };
        return acc.concat([segment]);
      } else {
        return acc;
      }
    }, []);
    var splitSegment = _.head(_.sortBy(segments, 'distance'));
    var split = _.reduce(lineString.getVertices(), function(acc, vertex, index) {
      if (acc.firstSplit) {
        acc.firstSplitVertices.push(vertex);
        if (index === splitSegment.index) {
          acc.firstSplitVertices.push(new OpenLayers.Geometry.Point(splitSegment.splitPoint.x, splitSegment.splitPoint.y));
          acc.secondSplitVertices.push(new OpenLayers.Geometry.Point(splitSegment.splitPoint.x, splitSegment.splitPoint.y));
          acc.firstSplit = false;
        }
      } else {
        acc.secondSplitVertices.push(vertex);
      }
      return acc;
    }, {
      firstSplit: true,
      firstSplitVertices: [],
      secondSplitVertices: []
    });
    return [split.firstSplitVertices, split.secondSplitVertices];
  };

Also available as a gist: https://gist.github.com/uhef/d3d29052e41fba914579