Как вращать / преобразовывать функции 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...