我正在构建一个工具,最终将利用Google Maps API v3在固定“网格”系统上固定边长(例如10米)的正方形构建的地图上构建一个区域(例如,纵坐标从地球的0,0点开始每隔0.0001个纬度单位间隔开。
我编写了代码,用户可以点击地图上的某个区域,代码会绘制一个轮廓并填充找到它的方块。用户可以单击该方块的其他相邻位置以构建越来越大的“块状”多边形,并可以单击各个方块以删除它们。我已经在HTML5 canvas / JavaScript以及Google Maps API中测试了所有这些。
现在我想编写删除任何内部边/顶点的代码,以便它只绘制此多边形的最外边界,以便它实际上绘制为一个大的多边形,而不是一个广场。可以这样想:即使我们知道像澳大利亚,美国等国家,由许多州组成,当我们在全国范围内划定边界时,我们通常不会对所有州的边界感兴趣,可以删除那些两者之间的线条,只绘制外边界。这与我想要完成的事情相同,只是使用正方形网格而不是复杂的多边形。
我目前的代码在这里:
https://jsfiddle.net/wyxcvmdf/14/
HTML:
<canvas id="myCanvas" width="500" height="250" style="border:1px solid #000000;"></canvas>
<!--etc.-->
JavaScript的:
// don't forget to set load type in jsfiddle to no wrap in <body>
// define the global variable and some helper variables to make the code shorter
var gv = {};
gv.o = function(id) {
return document.getElementById(id)
};
gv.i = 'innerHTML';
// etc.
关于我的代码的几个解释性说明:
•每个正方形的“原点”是该正方形左下角的顶点。没有特别的原因。
•HTML5画布如何绘制轮廓的“绘图方向”是从原点逆时针方向。再一次,没有特别的理由。
•您无法“点击”添加方块,因为它只是一个概念验证,因此您可以通过在相关文本输入框中输入x和y坐标来添加方块
证明我的代码所需的用例/测试是:
方形添加到多边形,有1个重复顶点(工作)
在所有情况下,方形添加到多边形中,包含2个和3个重复顶点:相邻边,非相邻边,非连续顶点(当前仅适用于第一种情况)
方形在所有情况下都添加到多边形中,有4个重复的顶点:插入一个洞,插入一部分洞,连接多个多边形(目前只适用于第一种情况)
在上述情况下,从多边形中移除了一个带有1个重复顶点的方块(尚未开发,但应该有效地与加法代码“反向”)
在上述情况下,从多边形中移除了方形,其中有2个和3个重复顶点(尚未开发,但应该有效地与加法代码“反向”)
在上述情况下,从具有4个重复顶点的多边形中删除了正方形(尚未开发,但应该有效地与添加代码“反向”)
在具有多个内边界的多边形外部方形添加/移除,即孔(尚未开发,可能很棘手)
在具有多个内边框的多边形内部方形添加/移除,即孔(尚未开发,可能很棘手)
注1:我使用“square”,“edge”等代替“polygons”等,只是为了简化解释。
注2:我对类似的问题和可能的解决方案进行过相当多的研究,但还没有真正找到满足我需求的东西。我所做的研究是:
旅行推销员问题。然而,这不是关于优化路径 - 它是关于确保路径是“可绘制的”并因此朝向一个方向。只要生成的形状看起来像用户期望的那样,重叠顶点就完全没了。
凸壳算法。不太适用,因为船体可能是凸的,凹的,甚至是非连续的!此外,我认为通过简化网格系统,我已经消除了许多散射顶点的问题,你需要确定它们与中心点的距离,使用三角函数等。
凹陷船体解决方案。这更接近于解决我的问题,但我所看到的是,有许多商业工具插件(例如ArcGIS)可以做到这一点,但是没有涵盖我所有用例的通用代码(不论编程语言)。
基于平铺的游戏。您会认为任何需要在图块周围绘制边界的基于图块的游戏(例如,实时战略游戏中的玩家区域)都会解决这个问题问题,但不是我能看到的。
答案 0 :(得分:1)
你说“画”而不是计算外部顶点,所以......
你可以使用剪辑加合成来“挖空”你的方块。
假设您已确定这些方块位于所需边界内(部分或完全位于内部):
var aInside=[ {x:60,y:60},{x:80,y:60},{x:40,y:60},{x:60,y:40},{x:60,y:80} ];
所需边界内的正方形图示。
然后,要仅绘制正方形的边界,您可以:
context.rect
将进一步的绘图限制在抚摸的地方:context.clip
导致所有新绘图擦除现有像素:context.globalCompositeOperation = 'destination-out'
使用纯色填充整个画布:context.fillRect(0,0,canvas.width,canvas.height)
。
诀窍:抚摸一个矩形实际上画了一个内部和一半的笔画。在矩形的一半外面,所以步骤#4将擦除矩形集的内部,但(重要的是!)将留下一半的外部笔划。
所以你最终得到了这个:
以下是示例代码和演示:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var aInside=[ {x:60,y:60},{x:80,y:60},{x:40,y:60},{x:60,y:40},{x:60,y:80} ];
// stroke all inside squares
ctx.save();
ctx.beginPath();
for(var i=0;i<aInside.length;i++){
var s=aInside[i];
ctx.rect(s.x,s.y,20,20);
}
ctx.stroke();
// clip to cause all new drawing to be inside the stroked squares
ctx.clip();
// set compositing to use new drawings to "erase" existing drawings
ctx.globalCompositeOperation='destination-out';
// Fill (===erase!) the entire canvas
// Clipping causes only the clipping area to be erased
// so the inside of the rects set is "hollowed out"
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.restore();
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=150 height=150></canvas>
算法注释:如果您想要一组幸存的顶点而不是绘图,则可以修改Marching Squares Algorithm以仅返回拐点。那些拐点是你的外边界的顶点。
答案 1 :(得分:1)
此方法仅处理绘图/外观 - 它不会生成任何新多边形。但它允许您使用多边形的集合(任何形状,这里是矩形)并在视觉上合并它们以产生合并的轮廓。我将此答案基于我的earlier answers之一,但修改并采用以适应此处的方案:
destination-out
并居中于顶部有几个步骤,但它的工作速度非常快。
几点说明:
var ctx = c.getContext("2d"),
rw = 50, rh = 50, // some demo size
rectangles = []; // rectangle collection
function render(ctx) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillStyle = "#a00";
ctx.globalCompositeOperation = "source-over"; // draw using standard mode3
// we will draw the same rects on top of each other eight times
// this will extrude the edge so we can in the next step punch a
// hole in the drawing and leave only the extrusion -
// offset array (x,y) pairs
var i, d = 2, // d = number of pixels to offset
offsets = [-d, -d, 0, -d, d, -d, d, 0, d, d, 0, d, -d, d, -d, 0];
for(i = 0; i < offsets.length; i += 2) {
ctx.setTransform(1,0,0,1, offsets[i], offsets[i+1]);
drawRects()
}
// punch hole in the center
ctx.setTransform(1,0,0,1,0,0); // reset transformatons
ctx.globalCompositeOperation = "destination-out"; // "erase" mode
drawRects(); // draw a final time, wo/ extrusion
function drawRects() {
ctx.beginPath();
rectangles.forEach(function(r) {
ctx.rect(r.x, r.y, r.w, r.h)
}); // loop through collection and draw
ctx.fill()
}
}
// demo stuff --
c.onclick = function(e) {
var r = this.getBoundingClientRect(), // for demo, get mouse position
x = e.clientX - r.left,
y = e.clientY - r.top;
// add rectangle to list
rectangles.push({ // generate a rect. from center
x: x - rw*0.5,
y: y - rh*0.5,
w: rw,
h: rh
});
render(ctx); // the key process
};
&#13;
canvas {border:1px solid #000}
&#13;
Click on the canvas below to place rectangles:<br>
<canvas width=600 height=600 id=c></canvas>
&#13;