【问题标题】:How to rotate/transform mapbox-gl-draw features?如何旋转/转换 mapbox-gl-draw 功能?
【发布时间】:2018-02-13 16:54:41
【问题描述】:

我正在使用mapbox-gl-draw 向我的地图添加可移动功能。除了可移动功能之外,我还需要 rotate/transform -ability 功能来实现类似于 Leaflet.Path.Transform 的功能。

目前,我唯一的选择是创建一个 custom mode

例如类似:

map.on('load', function() {
  Draw.changeMode('transform');
});

我无法将我的地图及其功能转换为 mapbox-gl-leaflet 以实施 Leaflet.Path.Transform,因为无法选择失去旋转/方位/俯仰支持。

【问题讨论】:

  • 嘿肖恩!您是出于绘图目的使用绘图还是仅允许地图上的要素移动?如果您实际上没有绘制特征,那么做一个我可以分享的自定义解决方案可能会更容易。
  • 嘿维克多!我使用 draw 只是为了允许 mapbox-gl-draw 功能在地图画布上移动。无论如何都愿意解决这个问题......
  • Victor,我应该更具体一点,因为我需要能够移动和旋转添加到地图中的预定义几何特征。
  • 我需要一点时间来为你写一些帮助。即将推出:)

标签: mapbox-gl-js mapbox-gl mapbox-gl-draw


【解决方案1】:

长答案传入。 (一些最终产品请参见http://mapster.me/mapbox-gl-draw-rotate-modehttp://npmjs.com/package/mapbox-gl-draw-rotate-modehttps://github.com/mapstertech/mapbox-gl-draw-rotate-mode

我一直在为自定义项目做类似的事情,而不是使用绘图库。我的项目涉及一些非常规则大小的对象,而不是非常复杂的多边形,所以解决方案对你来说可能太简单了,但它可能是正确的路径。我只是旋转和移动。

在地理上进行运动并不太难。这里有一些帮助您入门。 https://jsbin.com/yoropolewo/edit?html,output 上有一个基本的 JSBin,带有一些拖动功能(太累了,不能旋转)。

首先,注册必要的点击事件以产生拖动事件。您可以在特定的 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()];
}

现在,关键是 Mapbox GL JS 中的 unproject 方法,因此您可以在鼠标上的 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);
}

旋转有点困难,因为您希望形状围绕中心点旋转,并且您需要知道每个点到该中心点的距离才能做到这一点。如果你有一个简单的正方形多边形,这个计算会很容易。如果没有,那么使用这样的东西会有所帮助(Finding the center of Leaflet polygon?):

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 数组相比,很容易遇到错误。

我希望解释简短但足够清晰,并且您从逻辑上理解我们为重新定位这些要点所做的工作。这是重点,确切的代码是细节。

也许我应该只做一个模块或 fork GL Draw...

【讨论】:

  • 谢谢维克多。从概念上讲,我明白你在这里做什么:这都是关于多边形节点方向的。我确实认为这是 mapbox-gl-drawmapbox-gl-js 中的一个模块。 mapbox-gl-draw 已经实现了 move 功能,但这里添加的是 rotate 功能。我不确定什么用例需要不使用mapbox-gl-draw,只需要mapbox-gl-js?如果那里有争论,mapbox 可能想添加到mapbox-gl-js?我确实认为自定义多边形是必要的。我很乐意支持。
  • 嗨,Shawn,是的,上面也解释了旋转。您是否需要 JSBin 让它为您工作,而不是尝试使用上面的代码?另外,不确定您的意思是:是否延长平局。我不确定它应该是它自己的模块还是 GL Draw 上的一个分支。旋转似乎有点随机抽奖?就像我要分叉一样,我应该只创建一个 GL Transform 库(也可以缩放)?
  • 除非已经编写了 JSBin,否则无需编写。我将尝试直接在mapbox-gl-js 中实现这个逻辑。就像我在问题中所说的那样,mapbox-gl-drawcustom modes,而我最初的想法是在mapbox-gl-draw 中添加旋转模式。通过mapbox-gl-drawmapbox-gl-js 添加的多边形和线的旋转功能对我来说完全有意义。我可以想到许多在土地利用/地理规划应用中需要这样做的用例。
  • 酷。我将研究一个 GL Transform 库,它可以进行旋转、缩放和移动。由于 Leaflet Draw 和 Leaflet Transform 是分开的,因此保持分开可能是有意义的。如果您仍然感兴趣,我会在收到时更新您。如果您在轮换方面遇到问题,也请告诉我,我会用 JSBin 处理它。
  • 决定改用自定义模式路线。更少的工作:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-26
  • 2020-07-11
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-02
相关资源
最近更新 更多