长答案来了。(有关一些最终产品,请参见http://mapster.me/mapbox-gl-draw-rotate-mode和http://npmjs.com/package/mapbox-gl-draw-rotate-mode,https://github 。 com/mapstertech/mapbox-gl-draw-rotate-mode )
我一直在为自定义项目做类似的事情,而不是使用绘图库。我的项目涉及一些非常规则大小的对象,而不是非常复杂的多边形,所以解决方案对你来说可能太简单了,但它可能是正确的路径。我只有旋转和移动。
从地理上讲,做运动并不太难。这里有一些帮助您入门。一个基本的 JSBin 位于https://jsbin.com/yoropolewo/edit?html ,输出带有一些拖动功能(太累了,不能旋转)。
首先,注册必要的点击事件以产生拖动事件。您可以在特定的 Mapbox 层上监听 mousedown,然后在整个文档中监听 mousemove 和 mouseup。
要进行单独的形状旋转,您需要确保您指的是正确的特征。在此示例中,我假设源数据中只有一个特征,但这对于大多数用途而言可能过于简单,因此您必须进行推断。源数据是我们setData()
以后影响的内容。显然有很多方法可以做我在这里做的事情,但我想弄清楚。
var currentDragging = false;
var currentDraggingFeature = false;
var currentDraggingType = false;
var firstDragEvent = false;
map.on('mousedown','my-layer-id',function(e) {
currentDragging = 'my-source-id'; // this must correspond to the source-id of the layer
currentDraggingFeature = e.features[0]; // you may have to filter this to make sure it's the right feature
currentDraggingType = 'move'; // rotation or move
firstDragEvent = map.unproject([e.originalEvent.layerX,e.originalEvent.layerY]);
});
window.addEventListener('mousemove',dragEvent);
window.addEventListener('mouseup',mouseUpEvent);
然后,您将需要一个函数,它需要一个初始点、一个距离和一个旋转,然后将一个点返回给您。像这样:
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
Number.prototype.toDeg = function() {
return this * 180 / Math.PI;
}
function getPoint(point, brng, dist) {
dist = dist / 63.78137; // this number depends on how you calculate the distance
brng = brng.toRad();
var lat1 = point.lat.toRad(), lon1 = point.lng.toRad();
var lat2 = Math.asin(Math.sin(lat1) * Math.cos(dist) +
Math.cos(lat1) * Math.sin(dist) * Math.cos(brng));
var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(dist) *
Math.cos(lat1),
Math.cos(dist) - Math.sin(lat1) *
Math.sin(lat2));
if (isNaN(lat2) || isNaN(lon2)) return null;
return [lon2.toDeg(),lat2.toDeg()];
}
现在,关键是unproject
Mapbox GL JS 中的方法,因此您可以在鼠标上的 x/y 坐标和地图上的 lng/lat 之间移动。然后,使用该map.getSource().setData()
函数设置一个新的 geoJSON。
我在这里立即将 x/y 转换为坐标,但您可以在任何时候进行。类似于以下移动的东西:
function moveEvent(e) {
// In the case of move, you are just translating the points based on distance and angle of the drag
// Exactly how your translate your points here can depend on the shape
var geoPoint = map.unproject([e.layerX,e.layerY]);
var xDrag = firstDragEvent.lng - geoPoint.lng;
var yDrag = firstDragEvent.lat - geoPoint.lat;
var distanceDrag = Math.sqrt( xDrag*xDrag + yDrag*yDrag );
var angle = Math.atan2(xDrag, yDrag) * 180 / Math.PI;
// Once you have this information, you loop over the coordinate points you have and use a function to find a new point for each
var newFeature = JSON.parse(JSON.stringify(currentDraggingFeature));
if(newFeature.geometry.type==='Polygon') {
var newCoordinates = [];
newFeature.geometry.coordinates.forEach(function(coords) {
newCoordinates.push(getPoint(coords,distanceDrag,angle));
});
newFeature.geometry.coordinates = newCoordinates;
}
map.getSource(currentDragging).setData(newFeature);
}
旋转有点困难,因为您希望形状围绕中心点旋转,并且您需要知道每个点到该中心点的距离才能做到这一点。如果你有一个简单的正方形多边形,这个计算会很容易。如果没有,那么使用这样的东西会有所帮助(找到传单多边形的中心?):
var getCentroid2 = function (arr) {
var twoTimesSignedArea = 0;
var cxTimes6SignedArea = 0;
var cyTimes6SignedArea = 0;
var length = arr.length
var x = function (i) { return arr[i % length][0] };
var y = function (i) { return arr[i % length][1] };
for ( var i = 0; i < arr.length; i++) {
var twoSA = x(i)*y(i+1) - x(i+1)*y(i);
twoTimesSignedArea += twoSA;
cxTimes6SignedArea += (x(i) + x(i+1)) * twoSA;
cyTimes6SignedArea += (y(i) + y(i+1)) * twoSA;
}
var sixSignedArea = 3 * twoTimesSignedArea;
return [ cxTimes6SignedArea / sixSignedArea, cyTimes6SignedArea / sixSignedArea];
}
一旦你有能力知道多边形的中心,你就是黄金:
function rotateEvent(e) {
// In the case of rotate, we are keeping the same distance from the center but changing the angle
var findPolygonCenter = findCenter(currentDraggingFeature);
var geoPoint = map.unproject([e.layerX,e.layerY]);
var xDistanceFromCenter = findPolygonCenter.lng - geoPoint.lng;
var yDistanceFromCenter = findPolygonCenter.lat - geoPoint.lat;
var angle = Math.atan2(xDistanceFromCenter, yDistanceFromCenter) * 180 / Math.PI;
var newFeature = JSON.parse(JSON.stringify(currentDraggingFeature));
if(newFeature.geometry.type==='Polygon') {
var newCoordinates = [];
newFeature.geometry.coordinates.forEach(function(coords) {
var xDist = findPolygonCenter.lng - coords[0];
var yDist = findPolygonCenter.lat - coords[1];
var distanceFromCenter = Math.sqrt( xDist*xDist + yDist*yDist );
var rotationFromCenter = Math.atan2(xDist, yDist) * 180 / Math.PI;
newCoordinates.push(
getPoint(coords,distanceFromCenter,rotationFromCenter+angle)
);
});
newFeature.geometry.coordinates = newCoordinates;
}
}
当然,在整个过程中,请确保您的坐标从函数中正确传递和返回。其中一些代码可能包含不正确的数组级别。使用 lat/lng 对象与 geoJSON 数组相比,很容易遇到错误。
我希望解释简短但足够清楚,并且您从逻辑上理解我们正在做什么来重新定位这些观点。这是重点,确切的代码是细节。
也许我应该只做一个模块或分叉 GL Draw...