Как вращать / преобразовывать функции mapbox-gl-draw?

Я использую mapbox-gl-draw добавить подвижные объекты на мою карту. В дополнение к функциональности подвижности, мне нужны функциональные возможности поворота / преобразования для функций, сродни Leaflet.Path.Transform,

В настоящее время мой единственный вариант - создать собственный режим?

например что-то вроде:

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

Я не могу конвертировать свою карту и ее функции в mapbox-gl-leaflet для того, чтобы реализовать Leaflet.Path.Transform так как потеря вращения / опоры / опоры не возможна.

1 ответ

Длинный ответ входящий. (см. 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,output output с некоторыми функциями перетаскивания (слишком утомленными, чтобы поворачивать).

Сначала зарегистрируйте необходимые события щелчка, чтобы иметь событие перетаскивания. Вы можете прослушать определенные слои 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);
}

Поворот немного сложнее, потому что вы хотите, чтобы фигура вращалась вокруг центральной точки, и вам нужно знать расстояние каждой точки до этой центральной точки, чтобы сделать это. Если у вас есть простой квадратный многоугольник, этот расчет будет простым. Если нет, то было бы полезно использовать что-то вроде этого ( Найти центр многоугольника Leaflet?):

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...

Другие вопросы по тегам