iClient for JavaScript求两线交点、线线打断、点打断线

2024-02-25 11:08

本文主要是介绍iClient for JavaScript求两线交点、线线打断、点打断线,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:MR.

完整代码[^footnote]在页面底部提供下载

一、求两线交点

###1. 思路
    线都是由数量不等的点连起来形成的。计算两条线的交点,我们需要一个基础算子,其中一个办法是判断两条线段(两点相连)是否相交、相交的话算出交点。有了这个算子,我们循环整条线就行了,比较好的办法是把整条线上,每相邻两点取到的线段,按x坐标排下序,再去循环执行算子。
###2. 计算线段交点算子
    两条线段求交点使用数学的办法就可以了,这里不再赘述。关于容限的处理可以在下面下载完整代码查看。

//参数seg1、seg2{x1,x2,y1,y2}(x1<x2);options{pOrl:Boolean,tolerance:number}
var pOrl = options && options.pOrl;//是否返回交点或交线
var tolerance = options && options.tolerance;//容限,低于它视为相交
var intersection = false;//返回值,不相交返回false,否则true或
//{SuperMap.Geometry.Point}或{SuperMap.Geometry.LineString}
var x11_21 = seg1.x1 - seg2.x1;
var y11_21 = seg1.y1 - seg2.y1;
var x12_11 = seg1.x2 - seg1.x1;
var y12_11 = seg1.y2 - seg1.y1;
var y22_21 = seg2.y2 - seg2.y1;
var x22_21 = seg2.x2 - seg2.x1;
var d = (y22_21 * x12_11) - (x22_21 * y12_11);
var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
if(d == 0) {// 平行if(n1 == 0 && n2 == 0) {// 重合if(!pOrl){intersection=true}else{//返回交线var spt=seg1.x1>seg2.x1?seg1:seg2;var ept=seg1.x2<seg2.x2?seg1:seg2;intersection = new SuperMap.Geometry.LineString([new SuperMap.Geometry.Point(spt.x1,spt.y1),new SuperMap.Geometry.Point(ept.x2,ept.y2)]);}}
} else {var along1 = n1 / d;var along2 = n2 / d;if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {// 相交if(!pOrl) {intersection = true;} else {// 计算交点var x = seg1.x1 + (along1 * x12_11);var y = seg1.y1 + (along1 * y12_11);intersection = new SuperMap.Geometry.Point(x, y);}}
}

###3. 线上相邻点组成的线段循环代入算子
    首先上述算子的线段要求x1 < x2,其次,我们还需要去循环判断两条线上的线段是否分别相交,我们需要先处理下线,使其线段按起点x坐标递增排序,方法如下(采用快速排序):

function getSortedSegments(line) {//line{SuperMap.Geometry.LineString}//快速排序// var quickSort = function(arr) {//   if (arr.length <= 1) { return arr; }//   var pivot = arr[0];//   var left = [];//   var right = [];//   for (var i = 1; i < arr.length; i++){//     if (arr[i] < pivot) {//       left.push(arr[i]);//     } else {//       right.push(arr[i]);//     }//   }//   return quickSort(left).concat([pivot], quickSort(right));// };//每相邻两点视为整体执行快速排序var arr=line.components;if(!arr.length){return [];}var pivot=[{x1: arr[0].x,y1: arr[0].y,x2: arr[1].x,y2: arr[1].y,}];if(arr.length==2){return pivot;}var left = {components:[]};var right = {components:[]};var i=!line.CLASS_NAME ? 2 : 1;var spt,ept;while(i<arr.length-1){spt=arr[i].x<arr[i+1].x?arr[i]:arr[i+1];ept=arr[i].x>arr[i+1].x?arr[i]:arr[i+1];if(arr[i].x<=pivot[0].x1){left.components.push({x:spt.x,y:spt.y});left.components.push({x:ept.x,y:ept.y});}else{right.components.push({x:spt.x,y:spt.y});right.components.push({x:ept.x,y:ept.y});}if(!line.CLASS_NAME){i+=2;}else{i++;}}return getSortedSegments(left).concat(pivot, getSortedSegments(right));
}

接下来,找出两条线的交点:

//计算两条线交点
function intersects(line1,line2){var intersect = false;//无交点返回false,否则[{SuperMap.Geometry.Point},{SuperMap.Geometry.LineString}]var type1 = line1.CLASS_NAME;var type2 = line2.CLASS_NAME;if(type1 === "SuperMap.Geometry.LineString" ||type1 === "SuperMap.Geometry.LinearRing" ||type1 === "SuperMap.Geometry.Point" || type2 === "SuperMap.Geometry.LineString" ||type2 === "SuperMap.Geometry.LinearRing" ||type2 === "SuperMap.Geometry.Point") {var segs1;var segs2;if(type1 === "SuperMap.Geometry.Point") {segs1 = [{x1: line1.x, y1: line1.y,x2: line1.x, y2: line1.y}];} else {segs1 = getSortedSegments(line1);}if(type2 === "SuperMap.Geometry.Point") {segs2 = [{x1: line2.x, y1: line2.y,x2: line2.x, y2: line2.y}];} else {segs2 = getSortedSegments(line2);}var seg1, seg1x1, seg1x2, seg1y1, seg1y2,seg2, seg2y1, seg2y2;// 判断俩线段位置for(var i=0, len=segs1.length; i<len; i++) {seg1 = segs1[i];seg1x1 = seg1.x1;seg1x2 = seg1.x2;seg1y1 = seg1.y1;seg1y2 = seg1.y2;for(var j=0, jlen=segs2.length; j<jlen; j++) {seg2 = segs2[j];if(seg2.x1 > seg1x2) {// seg1在seg2左边break;}if(seg2.x2 < seg1x1) {// seg1在eg2右边continue;}seg2y1 = seg2.y1;seg2y2 = seg2.y2;if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {// seg2在seg1上continue;}if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {// seg2在seg1下continue;}var result=intersectoperator(seg1, seg2,{pOrl:true});result.line=[line1.id,line2.id];//标记结果if(result) {if(!intersect){intersect={points:[],lines:[]};}if(result.CLASS_NAME==="SuperMap.Geometry.Point"){intersect.points.push(result);}else{intersect.lines.push(result);}}}}} else {intersect = line1.intersects(line2);//调用SuperMap.Geometry对象方法返回是否相交bool值}return intersect;
}

    如果有需要你可以基于上述获取两线交点/线的方法来对更多的线求交点/线,在得到结果时,给结果加上属性标记,标记它是哪两条线的交点/线,还需要判断是否多条线有共同的交点/线,有的话,增加标记属性。
    下面示例求出整个Vector图层上所有线之间的交点(未判断该图层上是否都是线):

//计算多条线交点
function getlinescrosspt(lines){//lines[SuperMap.Geometry.LineString]var result={points:[],lines:[]},tempresult;//result{points:[{SuperMap.Geometry.Point}],lines:[{SuperMap.Geometry.LineString}]}for(var i =0; i<lines.length-1;i++){for(int j=i+1;j<lines.length;j++){tempresult=intersects(lines[i],lines[j]);if(tempresult){//判断result是否存在相同结果,是则增加标记for(var m in tempresult.points){//点var ptflag=true;for(var n in result.points){if(result.points[n].toString()==tempresult.points[m].toString()){//增加标记for(var pt1 in tempresult.points[m].line){var attflag=true;for(var pt2 in result.points[n].line){if(tempresult.points[m].line[pt1]==result.points[n].line[pt2]){attflag=false;break;}}if(attflag){result.points[n].line.push(tempresult.points[m].line[pt1]);}}ptflag=false;break;}}if(ptflag){result.points.push(tempresult.points[m]);}}//线与点类似}}}return result;
}

    另外,结果是线段的话,需不需要把有共同点的线段连成一条线、结果线如何标记需要根据不同情况做不同调整,这里不做上述处理。

二、线线打断

###1.思路
    线线打断还是需要先求交点,上面的例子已经可以得到两线之间的交点并标记了属性,但是我们需要知道交点的精确位置,因此,我们需要把前面的几个方法修改一下,让其返回交点在各条线分别的位置,然后可以根据交点及所在位置拆分一条线为多条。
###2.实现
    首先,在getSortedSegments方法里标记每个点在原数组的位置:

function getSortedSegments(line) {//line{SuperMap.Geometry.LineString}
//省略...var pivot=[{x1: arr[0].x,y1: arr[0].y,pos1:arr[0].pos||[line.id,0],x2: arr[1].x,y2: arr[1].y,pos2:arr[1].pos||[line.id,1]}];
//省略...var spt,ept;while(i<arr.length-1){//标记点原始位置if(arr[i].x<=arr[i+1].x){spt={x:arr[i].x,y:arr[i].y};ept={x:arr[i+1].x,y:arr[i+1].y};if(line.CLASS_NAME){spt.pos=[line.id,i];ept.pos=[line.id,i+1];}else{spt.pos=arr[i].pos;ept.pos=arr[i+1].pos;}}else{spt={x:arr[i+1].x,y:arr[i+1].y};ept={x:arr[i].x,y:arr[i].y};if(line.CLASS_NAME){spt.pos=[line.id,i+1];ept.pos=[line.id,i];}else{spt.pos=arr[i+1].pos;ept.pos=arr[i].pos;}}//省略...

    其次,在计算交点/线时,返回结果增加交点/线在线中的位置:

//计算线段交点
function intersectoperator(seg1, seg2, options){
//略...if(intersection&&pOrl){//判断点位置var segs=[seg1, seg2];intersection.pos=[];var judgepoint=function(segs,intersection){for(var k in segs){if(segs[k].x1==intersection.x && segs[k].y1== intersection.y){if(segs[k].pos1[0]!==true){segs[k].pos1.unshift(true);}intersection.pos.push(segs[k].pos1);}else if(segs[k].x2==intersection.x && segs[k].y2== intersection.y){if(segs[k].pos2[0]!==true){segs[k].pos2.unshift(true);}intersection.pos.push(segs[k].pos2);}else{intersection.pos.push([false,segs[k].pos1[0],segs[k].pos1[1],segs[k].pos2[1]]);}}return intersection;}if(intersection.CLASS_NAME=="SuperMap.Geometry.Point"){intersection=judgepoint(segs,intersection);}else{if(intersection.components[0].toString()==intersection.components[1].toString()){//线的两点相等intersection=intersection.components[0];intersection.pos=[];intersection=judgepoint(segs,intersection);}else{intersection.components[0].pos=[];intersection.components[1].pos=[];intersection.components[0]=judgepoint(segs,intersection.components[0]);intersection.components[1]=judgepoint(segs,intersection.components[1]);intersection.pos.push(intersection.components[0].pos.concat(intersection.components[1].pos));}}}return intersection;//点或线pos:[boolen,lineid,pos1,pos2]
}

    然后,求多条线交点需要做与上面line属性相同操作,即,多条线共交点/线,增加位置标记。
    最后,拿到交点/线,根据其位置从原来的线里取出对应点数组,即可完成线线打断的操作,为了方便处理,交点最好按其标记的位置增序排序。在getlinescrosspt的基础上处理,每趟一条线与后面的线求交点完成后就进行打断线操作,这里偷个懒,直接调用getlinescrosspt方法得到所有交点/线,再根据这些点/线去打断线。

//这里先得到线两两之间的交点
var result=getlinescrosspt(lines);console.log(result,"linexline");
//交点和交线做同样处理(可以只取其中一个点)
for(var x in result.lines){result.points.push(result.lines[x].components[0]);result.points.push(result.lines[x].components[1]);
}var resultlines=[];
var getlinfeas=function(line,sortedpoints){//sortedpoints排序后的点结果if(!sortedpoints.length){//该线上无断点return [line];}var resultlines=[];var linecoms=line.geometry.clone().components;var lineid=line.attributes.IDvar lineflag=1;var startpos=0,endpos;var n;for(n in sortedpoints){//从断点拆分线var linefea;//getstyle(label),返回随机样式,参数为要显示的文本标签if(sortedpoints[n].pos[0]){//当前断点是线组成点里的点endpos=sortedpoints[n].pos[2];if(endpos==0)continue;linefea = new SuperMap.Feature.Vector( new SuperMap.Geometry.LineString(linecoms.slice(startpos+1,endpos+1)),{ID:lineid+"_"+lineflag},getstyle(lineid+"_"+lineflag));if(n>0 && !sortedpoints[n-1].pos[0]){//上一个断点也不在线组分里linefea.geometry.components.unshift(sortedpoints[n-1]);}else{linefea.geometry.components.unshift(linecoms[startpos]);}}else{endpos=Math.min(sortedpoints[n].pos[2],sortedpoints[n].pos[3]);linefea = new SuperMap.Feature.Vector( new SuperMap.Geometry.LineString(linecoms.slice(startpos+1,endpos+1)),{ID:lineid+"_"+lineflag},getstyle(lineid+"_"+lineflag));linefea.geometry.components.push(sortedpoints[n]);if(n>0 && !sortedpoints[n-1].pos[0]){//上一个断点也不在线组分里linefea.geometry.components.unshift(sortedpoints[n-1]);}else{linefea.geometry.components.unshift(linecoms[startpos]);}}resultlines.push(linefea);startpos=endpos;lineflag++;}n=parseInt(n);if(startpos!=linecoms.length-1){var linefea = new SuperMap.Feature.Vector( new SuperMap.Geometry.LineString(linecoms.slice(startpos,linecoms.length)),{ID:lineid+"_"+lineflag},getstyle(lineid+"_"+lineflag));if(!sortedpoints[n].pos[0]){//最后一个断点不是线组成点linefea.geometry.components[0]=sortedpoints[n];}resultlines.push(linefea);}return resultlines;//直接返回设置了样式的线要素而不是Geometry对象
};
var sortresultpt=function(result,lineid){//返回匹配的线id排序后的断点var sortedpoints=[];for(var j in result.points){//结果点var rstpt=result.points[j].clone();//Geometry克隆对象方法for(var k in rstpt.pos){//对比标记if(rstpt.pos[k][1]==lineid){//是此线交点//插入排序rstpt.pos=rstpt.pos[k];if(!sortedpoints.length){sortedpoints.push(rstpt);break;}var maxflag=true;for(var m in sortedpoints){if(rstpt.pos[2]<sortedpoints[m].pos[2]){sortedpoints.splice(m, 0, rstpt);maxflag=false;break;}}if(maxflag){sortedpoints.push(rstpt);break;}}}}return sortedpoints;
};for(var i in vectorLayer.features){//图层上的线var lineid=vectorLayer.features[i].attributes.ID;//线idvar sortedpoints=sortresultpt(result,lineid);//按线id及位置排序后的点resultlines=resultlines.concat( getlinfeas(vectorLayer.features[i],sortedpoints) );
}
//resultlines可直接添加到Vector图层上的线要素数组

三、点打断线

###1.思路
    还是需要求点与线交点,前面写的求线段之间交点的方法也兼容点和线,并且可以设置容限,直接使用就行。但其实,intersectoperator(计算线段交点)方法调用了另一个方法,distanceToSegment,该方法计算并返回点到线段距离及垂足(点到线的垂线与线的交点),具体实现不再详述,可以在页面底部下载代码查看。所以,点打断线建议可以以distanceToSegment为算子另写个方法,可以提高少许执行效率,这里不再详述,直接使用线线相交的方法。另,SuperMap.Control.DrawFeature可以设置捕捉(snap)对象,方便画点时把点画在线上。
###2.实现
    这里不再多说,请下载下面的代码查看。

四、拓展

    有了前面的铺垫,我们还可以做别的更多的事情,比如,线打断面面裁剪线等等。面对象和线对象类似,也是点串,所以上面说的操作都是基于带位置(数组位置)标记的交点/线。
    可以看到其中还有不成熟的地方以及算法进一步优化的空间,欢迎大家讨论交流、批评指正。(查看CSDN账号资料联系我们)。

这篇关于iClient for JavaScript求两线交点、线线打断、点打断线的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/745277

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定