Javascript 如何在不检查画布上的每个点的情况下获取SVG路径字符串中包含的所有点?
是否有一种有效的方法可以找到路径中每个点的Javascript 如何在不检查画布上的每个点的情况下获取SVG路径字符串中包含的所有点?,javascript,svg,canvas,html5-canvas,Javascript,Svg,Canvas,Html5 Canvas,是否有一种有效的方法可以找到路径中每个点的[x,y]坐标,而无需使用上下文手动检查整个画布上的每个点。isPointInPath() 来自geojson的示例路径 <path d=" M 588, 173, L 588, 176, L 585, 216, L 585, 216, L 584, 223, L 580, 274, L 565, 273, L 565, 273, L 549, 271, L 539, 270, L 535, 269, L 513,
[x,y]
坐标,而无需使用上下文手动检查整个画布上的每个点。isPointInPath()
来自geojson的示例路径
<path d="
M 588, 173,
L 588, 176, L 585, 216, L 585, 216, L 584, 223,
L 580, 274, L 565, 273, L 565, 273, L 549, 271,
L 539, 270, L 535, 269, L 513, 267, L 503, 266,
L 486, 264, L 477, 262, L 468, 261, L 464, 260,
L 455, 259, L 449, 258, L 451, 249, L 452, 244,
L 453, 233, L 456, 218, L 456, 215, L 460, 192,
L 460, 191, L 462, 180, L 463, 174, L 463, 171,
L 464, 167, L 464, 161, L 465, 158, L 470, 159,
L 471, 159, L 477, 160, L 477, 160, L 480, 161,
L 482, 161, L 487, 161, L 499, 163, L 500, 163,
L 514, 165, L 534, 168, L 535, 168, L 544, 169,
L 555, 170, L 557, 170, L 565, 171, L 581, 173,
Z"
fill="transparent" stroke-width="1" stroke="black"></path>
例如,我现在正在使用geojson文件执行以下操作:
const points = [];
const pointWidth = 2;
for (let x = 0; x < canvasWidth; x += pointWidth) {
for (let y = 0; y < canvasHeight; y += pointWidth) {
for (const feature of countyGeoJson.features) {
const d = pathGenerator(feature);
const countyShape = new Path2D(d);
if (context.isPointInPath(countyShape, x, y)) {
points.push({ coords: [x, y], data: feature.properties });
break;
}
}
}
}
const points=[];
const pointWidth=2;
for(设x=0;x
基本上,我在画布网格中循环每个点(超过一百万个),然后嵌套另一个循环,查看每个特征以确定该点是否在特征的投影路径字符串中。这是非常低效的,我的浏览器无法处理它
有没有办法使用路径字符串本身来生成点?下面是我们在评论中讨论的完整实现
var图像=`
`;
内部功能(p,vs){
var内=假;
对于(变量i=0,j=vs.length-1;ip[1])!=(yj>p[1])和&(p[0]<(xj-xi)*(p[1]-yi)/(yj-yi)+xi);
如果(相交)内部=!内部;
}
返回内部;
}
函数绘图(){
ctx.drawImage(img,0,0);
常量匹配=image.matchAll(/L(…),(…),/g)
const poly=Array.from(匹配项,m=>[Number(m[1]),Number(m[2]))
设xs=poly.map(p=>p[0]);
设ys=poly.map(p=>p[1]);
让[xmin,xmax]=[Math.min(…xs),Math.max(…xs)];
让[ymin,ymax]=[Math.min(…ys),Math.max(…ys)];
ctx.globalAlpha=0.5
常数点宽度=4
对于(让x=xmin;x我认为您的算法非常好,在big-O复杂性方面,您几乎找不到性能更好的算法。
不过,您可以在代码中做一些改进:
1) 重要的是要知道画布与所有特征相比有多大。如果画布比所有特征都大,那么找到所有特征的边框(一个边框用于所有特征),然后使用该矩形而不是整个画布进行搜索。如果画布稍微大一点,这可能不会有任何改进
2) 您正在最内部的循环中执行一些工作(如调用路径生成器
),尽管这并不取决于外部循环的变量。如果这项工作很昂贵,最好事先进行预计算
3) 从您的代码中,由于您的break;
,我可以假设,如果您发现该点属于一个功能,那么它就不属于任何其他功能。如果这是真的,那么如果您能够以智能方式对功能进行排序,以便更快地从循环中中断,那么对这些功能进行排序可能是有益的。而不知道有关这些功能的任何信息对于你的特征,我将在这里做一个简单的假设:如果一个点属于某个特征,那么相邻的点很可能属于同一个特征
根据我的第2)点和第3)点,这是我建议的代码更新:
//预先计算最内层循环外部的形状(上面第2点)
//具有形状的功能(fws):
const fws=countyGeoJson.features.map(feature=>{
特写,
形状:新Path2D(路径生成器(功能))
});
常数点=[];
const pointWidth=2;
for(设x=0;x
要获得填充路径将产生的所有像素坐标,只需填充该路径并获得已绘制的像素即可。
由于我们不希望有太大的画布进行搜索,因此可以首先提取文档中的的BBox,调整画布大小,并从BBox反向转换其坐标。我们只需在将像素映射回坐标时重新添加转换:
const path_elem=document.querySelector('path');
const coords=getPathFillCoords(路径元素);
设str='';
coords.forEach(([x,y],i)=>{
str+=`;
} );
路径元素insertAdjacentHTML(“beforebeagin”,str);
函数getPathFillCoords(elem){
const bbox=path_elem.getBBox();
const def=path_elem.getAttribute('d');
常量路径=新路径2D(def);
const canvas=document.createElement('canvas');
const width=canvas.width=bbox.width;
const height=canvas.height=bbox.height;
const ctx=canvas.getContext('2d');
ctx.translate(-bbox.x,-bbox.y);
ctx.fill(路径);
常量img=ctx.getImageData(0,0,宽度,高度);
常量像素=新的UINT32阵列(img.data.buffer);
常数坐标=像素。减少((arr,px,ind)=>{
如果(px!==0xFF000000){return arr;}//不完全不透明
常数比=ind/宽度;
常数y=数学楼层(比率);
常数x=数学圆((比值-y)*宽度);
常数坐标=[x+bbox.x,y+bbox.y];
arr.push(协调);
返回arr;
}, [] );
返回坐标;
}
body{背景:白色}