需求是要将一个面用线将其分割成两块或多块的面。这个面包括多路径面的极端情况。
在ArcGIS For Android提供的API中 GeometryEngine工具类中未有Segment这个方法,在官网查阅中发现在最新的100.0.0版本中是有这个方法的,但是项目中使用的是10.2.8的版本,Eris在这两个版本变化很大,要换SDK十分麻烦,要更换和地图有关的所有地方,所以只能自己动手去写这个分割逻辑。
private void previousSegmentation(Geometry gonGeo, Geometry lineGeo) {
// 这个面的集合用来做遍历
List gonGraphics = new ArrayList();
// 取线的最后一个点 判断是否穿过面
MultiPath multiPath = (MultiPath) lineGeo;
MultiPath multiPathGon = (MultiPath) gonGeo;
boolean isIsland = false;
Point pointLast = multiPath.getPoint(multiPath.getPointCount() - 1);
Point pointFirst = multiPath.getPoint(0);
// 判断分割首要条件为线与面相交,并且线的第一个点与最后一个点不和面相交
if (!GeometryEngine.intersects(gonGeo, pointFirst, mMapView.getSpatialReference())
&& !GeometryEngine.intersects(gonGeo, pointLast, mMapView.getSpatialReference())
&& GeometryEngine.intersects(gonGeo, lineGeo, mMapView.getSpatialReference())
) {
// 遍历多路径 生成面集合
SegmentIterator segmentIterator = multiPathGon.querySegmentIterator();
while (segmentIterator.nextPath()) {
Polygon polygonPath = new Polygon();
while (segmentIterator.hasNextSegment()) {
polygonPath.addSegment(segmentIterator.nextSegment(), false);
}
polygonPath.removePoint(polygonPath.getPointCount() - 1);
gonGraphics.add(polygonPath);
}
if (gonGraphics.size() > 1) {
// 对自身集合遍历 判断是否是环岛面
for (int i = 0; i < gonGraphics.size(); i++) {
for (int j = 0; j < gonGraphics.size(); j++) {
if (i != j && GeometryEngine.contains(gonGraphics.get(i),
gonGraphics.get(j), mMapView.getSpatialReference())) {
isIsland = true;
break;
}
}
}
}
if (isIsland) { // 如果是环岛面
List> doubleGeo = new ArrayList>();
// 第一步 区分大面和小面
// 对面集合进行按面积大小的冒泡排序
for (int k = 0; k < gonGraphics.size(); k++) {
for (int z = 0; z < gonGraphics.size() - 1 - k; z++) {
double index = Math.abs(gonGraphics.get(z).calculateArea2D());
double index1 = Math.abs(gonGraphics.get(z + 1).calculateArea2D());
if (index < index1) {
gonGraphics.add(z, gonGraphics.get(z + 1));
gonGraphics.remove(z + 2);
}
}
}
// 对面进行分组
for (int i = 0; i < gonGraphics.size(); i++) {
List singleGeo = new ArrayList();
if (gonGraphics.get(i).calculateArea2D() > 0) {// 环岛小面的面积是负数
for (int j = 0; j < gonGraphics.size(); j++) {
if (i != j && GeometryEngine.contains(gonGraphics.get(i),
gonGraphics.get(j), mMapView.getSpatialReference())) {
if (singleGeo.isEmpty()) {// 如果暂时没有面 则是未放入大面
singleGeo.add(gonGraphics.get(i));// 取到大面
}
singleGeo.add(gonGraphics.get(j));// 取到被包含的小面
}
}
if (singleGeo.isEmpty()) {// 是空的情况 就是单一的普通的面
singleGeo.add(gonGraphics.get(i));
}// 不是空的情况 就是有环岛的面
doubleGeo.add(singleGeo);
}
}
// 存放结果的集合
List result = new ArrayList();
for (int a = 0; a < doubleGeo.size(); a++) {
List smallGeo = new ArrayList();
List geometries = doubleGeo.get(a);
Geometry bigGeo = new Polygon();
for (int i = 0; i < geometries.size(); i++) {
if (i == 0) {
bigGeo = geometries.get(i);// 取到大面
} else {
smallGeo.add(geometries.get(i));// 加入小面
}
}
// 第二步 大面作分割
List bigGeos = new ArrayList();
bigGeos.add(bigGeo);
bigGeos = segmentation(bigGeo, lineGeo, bigGeos);
// 第三步 小面作分割
List> smallData = new ArrayList>();
for (int i = 0; i < smallGeo.size(); i++) {
List smallGeos = new ArrayList();// 存储一个小面分割后的面集合
smallGeos.add(smallGeo.get(i));// 加入当前需要被分割的小面
smallGeos = segmentation(smallGeo.get(i), lineGeo, smallGeos);// 获取分割后的结果
smallData.add(smallGeos);
}
// 第四步 大面和小面的集合作差
for (int i = 0; i < bigGeos.size(); i++) {
Geometry big = bigGeos.get(i);
for (int j = 0; j < smallData.size(); j++) { // 循环小面的二维集合
for (int k = 0; k < smallData.get(j).size(); k++) {// 遍历小面的一个集合的面
big = GeometryEngine.difference(big, smallData.get(j).get(k), mMapView.getSpatialReference());
}
}
// 一个大面遍历完所有小面后 加入结果集合
if (((MultiPath) big).getPointCount() != 0) {// 判断是否是空图形
result.add(big);
}
}
}
segmentationOver(result);
} else {
List segmentation = segmentation(gonGeo, lineGeo, gonGraphics);
// 分割操作完成后
segmentationOver(segmentation);
}
} else {
ToastUtil.showShort(this, "需要画一条穿过面的线");
editGraphicsLayer.removeGraphic(graphicsSelected.get(1).getUid());
highGraphicsLayer.removeAll();
graphicsSelected.clear();
allDisSelector();
}
}
/**
* 分割操作
*
* @param gonGeo 传入需要分割的面
* @param lineGeo 传入画出的线
* @param gonGraphics 存有需要分割的面的集合 用于返回分割完后的面集
*/
private List segmentation(Geometry gonGeo, Geometry lineGeo, List gonGraphics) {
// 第一次相交操作 取得相交之后的线段
Geometry intersect = GeometryEngine.intersect(gonGeo, lineGeo, mMapView.getSpatialReference());
MultiPath intersectMulti = (MultiPath) intersect;
// 线的路径
for (int i = 0; i < intersectMulti.getPathCount(); i++) {
int pathStart = intersectMulti.getPathStart(i);
int pathEnd = intersectMulti.getPathEnd(i);
Polyline polyline = new Polyline();
// 完成一个路径的遍历
for (int j = pathStart; j < pathEnd - 1; j++) {
Line line = new Line();
line.setStart(intersectMulti.getPoint(j));
line.setEnd(intersectMulti.getPoint(j + 1));
polyline.addSegment(line, false);
}
// 拿路径去和面集合遍历
List indexList = new ArrayList();
List segmentationList = new ArrayList();
for (int j = 0; j < gonGraphics.size(); j++) {
if (GeometryEngine.intersects(gonGraphics.get(j), polyline, mMapView.getSpatialReference())) {
Geometry intersectLine = GeometryEngine.intersect(gonGraphics.get(j), polyline, mMapView.getSpatialReference());
// 分割
if (((MultiPath) intersectLine).getPointCount() >= 2) {
segmentationList.addAll(CalculateUtil.segmentation(gonGraphics.get(j), intersectLine, mMapView));
// 这个面被处理过后 就记录下标
indexList.add(j);
}
}
}
// 加入本次处理后的两个面
gonGraphics.addAll(segmentationList);
// 处理过的面的下标 去掉
for (int j = 0; j < indexList.size(); j++) {
gonGraphics.remove(indexList.get(indexList.size() - j - 1).intValue());
}
}
return gonGraphics;
}
最主要的方法:
public static List segmentation(Geometry gonGeo, Geometry intersect,MapView mapView) {
// 获得路径
MultiPath gonMulti = (MultiPath) gonGeo;
MultiPath lineMulti = (MultiPath) intersect;
Polygon polygonFirst = new Polygon();
Polygon polygonSecond = new Polygon();
int start = 0;
int end = 0;
// 线的顺序和面的顺序是否反向
boolean isReverse = false;
// 记录交点所在的下标
int[] index = new int[2];
int count = 0;
// 用交点去遍历面的线 取得 各个交点所在的线的下标
for (int j = 0; j < lineMulti.getPointCount(); j++) {
Point point = lineMulti.getPoint(j);
for (int i = 0; i < gonMulti.getPointCount(); i++) {
Point pointFirst = gonMulti.getPoint(i);
Point pointSecond;
if (i + 1 == gonMulti.getPointCount()) {
pointSecond = gonMulti.getPoint(0);
} else {
pointSecond = gonMulti.getPoint(i + 1);
}
Line lineInGon = new Line();
lineInGon.setStart(pointFirst);
lineInGon.setEnd(pointSecond);
// 面的线
Polyline polyline = new Polyline();
polyline.addSegment(lineInGon, false);
if (GeometryEngine.intersects(point, polyline, mapView.getSpatialReference())) {
if(count<2){
index[count] = i;
count++;
}
}
}
}
// 取到交点下标后 排序
if (index[0] < index[1]) {
start = index[0];
end = index[1];
isReverse = false;
} else if (index[0] > index[1]) {
start = index[1];
end = index[0];
isReverse = true;
} else if (index[0] == index[1]) {
start = index[0];
end = index[1];
}
for (int i = 0; i < gonMulti.getPointCount(); i++) {
if (start == end) { // 交点在同一个线上
if (i != start) {
Line line = new Line();
line.setStart(gonMulti.getPoint(i));
if (i + 1 == gonMulti.getPointCount()) {
line.setEnd(gonMulti.getPoint(0));
} else {
line.setEnd(gonMulti.getPoint(i + 1));
}
polygonFirst.addSegment(line, false);
}
if (i == start) {
double distance1 = GeometryEngine.distance(gonMulti.getPoint(i), lineMulti.getPoint(0), mapView.getSpatialReference());
double distance2 = GeometryEngine.distance(gonMulti.getPoint(i), lineMulti.getPoint(lineMulti.getPointCount() - 1), mapView.getSpatialReference());
// 判断是否反向
isReverse = distance1 > distance2;
// 第一个面的点
Line line = new Line();
if (isReverse) {
line.setStart(gonMulti.getPoint(i));
line.setEnd(lineMulti.getPoint(lineMulti.getPointCount() - 1));
} else {
line.setStart(gonMulti.getPoint(i));
line.setEnd(lineMulti.getPoint(0));
}
polygonFirst.addSegment(line, false);
// 加入面中的线段
if (isReverse) {
for (int k = 0; k < lineMulti.getPointCount() - 1; k++) {
Line lineFirst = new Line();
lineFirst.setStart(lineMulti.getPoint(lineMulti.getPointCount() - k - 1));
lineFirst.setEnd(lineMulti.getPoint(lineMulti.getPointCount() - k - 2));
polygonFirst.addSegment(lineFirst, false);
}
} else {
for (int k = 0; k < lineMulti.getPointCount() - 1; k++) {
Line lineFirst = new Line();
lineFirst.setStart(lineMulti.getPoint(k));
lineFirst.setEnd(lineMulti.getPoint(k + 1));
polygonFirst.addSegment(lineFirst, false);
}
}
// 加入穿过之后的线
Line lineAfter = new Line();
if (i + 1 == gonMulti.getPointCount()) {
if (isReverse) {
lineAfter.setStart(lineMulti.getPoint(0));
lineAfter.setEnd(gonMulti.getPoint(0));
} else {
lineAfter.setStart(lineMulti.getPoint(lineMulti.getPointCount() - 1));
lineAfter.setEnd(gonMulti.getPoint(0));
}
} else {
if (isReverse) {
lineAfter.setStart(lineMulti.getPoint(0));
lineAfter.setEnd(gonMulti.getPoint(i + 1));
} else {
lineAfter.setStart(lineMulti.getPoint(lineMulti.getPointCount() - 1));
lineAfter.setEnd(gonMulti.getPoint(i + 1));
}
}
polygonFirst.addSegment(lineAfter, false);
// 第二个面的闭合
Line lineSecond = new Line();
lineSecond.setStart(lineMulti.getPoint(lineMulti.getPointCount() - 1));
lineSecond.setEnd(lineMulti.getPoint(0));
polygonSecond.addSegment(lineSecond, false);
for (int k = 0; k < lineMulti.getPointCount() - 1; k++) {
Line line1 = new Line();
line1.setStart(lineMulti.getPoint(k));
line1.setEnd(lineMulti.getPoint(k + 1));
polygonSecond.addSegment(line1, false);
}
}
} else { // 交点不在同一个线上
if (i < start) { // 小于第一个交点的点连接至第一个交点
Line line = new Line();
line.setStart(gonMulti.getPoint(i));
line.setEnd(gonMulti.getPoint(i + 1));
polygonFirst.addSegment(line, false);
}
if (i == start) { // 等于则连接第一个交点 这个要加三个路径
Line line = new Line();
if (isReverse) {
line.setStart(gonMulti.getPoint(i));
line.setEnd(lineMulti.getPoint(lineMulti.getPointCount() - 1));
} else {
line.setStart(gonMulti.getPoint(i));
line.setEnd(lineMulti.getPoint(0));
}
polygonFirst.addSegment(line, false);
// 加入面中的线段
if (isReverse) {
for (int k = 0; k < lineMulti.getPointCount() - 1; k++) {
Line lineFirst = new Line();
lineFirst.setStart(lineMulti.getPoint(lineMulti.getPointCount() - k - 1));
lineFirst.setEnd(lineMulti.getPoint(lineMulti.getPointCount() - k - 2));
polygonFirst.addSegment(lineFirst, false);
}
} else {
for (int k = 0; k < lineMulti.getPointCount() - 1; k++) {
Line lineFirst = new Line();
lineFirst.setStart(lineMulti.getPoint(k));
lineFirst.setEnd(lineMulti.getPoint(k + 1));
polygonFirst.addSegment(lineFirst, false);
}
}
// 第二个面加入
Line lineSecond = new Line();
if (isReverse) {
lineSecond.setStart(lineMulti.getPoint(lineMulti.getPointCount() - 1));
lineSecond.setEnd(gonMulti.getPoint(i + 1));
} else {
lineSecond.setStart(lineMulti.getPoint(0));
lineSecond.setEnd(gonMulti.getPoint(i + 1));
}
polygonSecond.addSegment(lineSecond, false);
}
if (i > start && i < end) { // 在两个交点之间的时候
Line line = new Line();
line.setStart(gonMulti.getPoint(i));
line.setEnd(gonMulti.getPoint(i + 1));
polygonSecond.addSegment(line, false);
}
if (i == end) {
Line line = new Line();
line.setStart(gonMulti.getPoint(i));
if (isReverse) {
line.setEnd(lineMulti.getPoint(0));
} else {
line.setEnd(lineMulti.getPoint(lineMulti.getPointCount() - 1));
}
polygonSecond.addSegment(line, false);
// 反向加入面中的线
if (isReverse) {
for (int k = 0; k < lineMulti.getPointCount() - 1; k++) {
Line lineSecond = new Line();
lineSecond.setStart(lineMulti.getPoint(k));
lineSecond.setEnd(lineMulti.getPoint(k + 1));
polygonSecond.addSegment(lineSecond, false);
}
} else {
for (int k = 0; k < lineMulti.getPointCount() - 1; k++) {
Line lineSecond = new Line();
lineSecond.setStart(lineMulti.getPoint(lineMulti.getPointCount() - k - 1));
lineSecond.setEnd(lineMulti.getPoint(lineMulti.getPointCount() - k - 2));
polygonSecond.addSegment(lineSecond, false);
}
}
// 第一个面的线
Line lineFirst = new Line();
if (isReverse) {
lineFirst.setStart(lineMulti.getPoint(0));
} else {
lineFirst.setStart(lineMulti.getPoint(lineMulti.getPointCount() - 1));
}
if (i + 1 == gonMulti.getPointCount()) {
lineFirst.setEnd(gonMulti.getPoint(0));
} else {
lineFirst.setEnd(gonMulti.getPoint(i + 1));
}
polygonFirst.addSegment(lineFirst, false);
}
if (i > end) { // 这里完成第一个面的路径
Line line = new Line();
line.setStart(gonMulti.getPoint(i));
if (i + 1 == gonMulti.getPointCount()) { // 超出下标则到0点去
line.setEnd(gonMulti.getPoint(0));
} else {
line.setEnd(gonMulti.getPoint(i + 1));
}
polygonFirst.addSegment(line, false);
}
}
}
polygonFirst.removePoint(polygonFirst.getPointCount() - 1);
polygonSecond.removePoint(polygonSecond.getPointCount() - 1);
List gonLists = new ArrayList();
gonLists.add(polygonFirst);
gonLists.add(polygonSecond);
return gonLists;
}
这个是分割完的处理工作
private void segmentationOver(List gonGraphics) { editGraphicsLayer.removeGraphic(graphicsSelected.get(0).getUid());
editGraphicsLayer.removeGraphic(graphicsSelected.get(1).getUid());
highGraphicsLayer.removeAll();
graphicsSelected.clear();
allDisSelector();
}
editGraphicsLayer 是当前编辑的图层对象.
highGraphicsLayer 是用来高亮的图层对象.
allDisSelector 是我用来取消要素高亮的方法.
难点主要是在于点集是有顺序的,需要判断线切入面的交点顺序和原来面的点集顺序是是否是相同的,
这个判断在segmentation()这个方法中完成。