Hello Lennart,
Please check below the script that allows to define the bounding box (region) by XY coordinates of its opposite corners and using min/max altitude value.
The center, size and rotation of the bounding box is defined by region.center vector, region.size vector and region.rot matrix, but note that they are defined in the internal coordinate system and need to be transformed to/form reference coordinate system used in the project.
Hope you can figure out main principles for chunk.region definition, but if any comments are required, please do not hesitate to ask.
import Metashape
from PySide2 import QtCore, QtGui, QtWidgets
def cross(a, b):
"""
vector product of two vectors
"""
result = Metashape.Vector([a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y *b.x])
return result.normalized()
class define_box(QtWidgets.QDialog):
"""
main dialog
"""
def __init__ (self, parent):
QtWidgets.QDialog.__init__(self, parent)
self.setWindowTitle("Resize Bounding Box")
global doc
doc = Metashape.app.document
crs = Metashape.CoordinateSystem('LOCAL_CS["Local Coordinates (m)",LOCAL_DATUM["Local Datum",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]]]')
if len(doc.chunks):
crs = doc.chunk.crs
chunk = doc.chunk
center = chunk.region.center
size = chunk.region.size
rotate = chunk.region.rot
T = chunk.transform.matrix
corners = [T.mulp(center + rotate * Metashape.Vector([size[0] * ((i & 1) - 0.5), 0.5 * size[1] * ((i & 2) - 1), 0.25 * size[2] * ((i & 4) - 2)])) for i in range(8)]
if chunk.crs:
corners = [chunk.crs.project(x) for x in corners]
corners = [corners[3], corners[1], corners[2], corners[-1]]
else:
corners = [Metashape.Vector([0, 1, 0])] * 4
#labels and #text edits
i = 0
for label in ["X1", "Y1", "X2", "Y2"]:
attr = "txt" + label
setattr(self, attr, QtWidgets.QLabel())
box = getattr(self, attr)
box.setText(label[:-1] + ":")
attr = "edt" + label
setattr(self, attr, QtWidgets.QLineEdit())
box = getattr(self, attr)
box.setText("{:.5f}".format(corners[i // 2][i % 2])) #box.setText("0")
i += 1
if (i % 2):
attr = "Corner_" + str(i // 2 + 1)
setattr(self, attr, QtWidgets.QLabel())
box = getattr(self, attr)
box.setText(attr.replace("_"," ") + ":")
self.txtAltitude = QtWidgets.QLabel()
self.txtAltitude.setText("Altitude:")
self.txtAMin = QtWidgets.QLabel()
self.txtAMin.setText("Min:")
self.txtAMax = QtWidgets.QLabel()
self.txtAMax.setText("Max:")
self.edtAMin = QtWidgets.QLineEdit()
self.edtAMax = QtWidgets.QLineEdit()
self.edtAMin.setText("{:.2f}".format(corners[0].z))
self.edtAMax.setText("{:.2f}".format(corners[-1].z))
self.txtCrs = QtWidgets.QLabel()
self.txtCrs.setText("CS:")
self.edtCrs = QtWidgets.QLabel()
self.edtCrs.setDisabled(True)
self.edtCrs.setText(crs.name + " (" + crs.authority + ")")
#buttons
self.btnP1 = QtWidgets.QPushButton("Apply")
self.btnQuit = QtWidgets.QPushButton("Close")
#creating layout
layout = QtWidgets.QGridLayout()
layout.addWidget(self.txtCrs, 0, 0)
layout.addWidget(self.edtCrs, 0, 2, 1, 3)
layout.addWidget(self.Corner_1, 1, 0)
layout.addWidget(self.Corner_2, 2, 0)
layout.addWidget(self.txtX1, 1, 1)
layout.addWidget(self.txtY1, 1, 3)
layout.addWidget(self.txtX2, 2, 1)
layout.addWidget(self.txtY2, 2, 3)
layout.addWidget(self.edtX1, 1, 2)
layout.addWidget(self.edtY1, 1, 4)
layout.addWidget(self.edtX2, 2, 2)
layout.addWidget(self.edtY2, 2, 4)
layout.addWidget(self.txtAltitude, 4, 0)
layout.addWidget(self.txtAMin, 4, 1)
layout.addWidget(self.txtAMax, 4, 3)
layout.addWidget(self.edtAMin, 4, 2)
layout.addWidget(self.edtAMax, 4, 4)
layout.addWidget(self.btnP1, 5, 1, 5, 2)
layout.addWidget(self.btnQuit, 5, 3, 5, 4)
self.setLayout(layout)
proc_set_bbox = lambda : self.set_bbox()
QtCore.QObject.connect(self.btnP1, QtCore.SIGNAL("clicked()"), proc_set_bbox)
QtCore.QObject.connect(self.btnQuit, QtCore.SIGNAL("clicked()"), self, QtCore.SLOT("reject()"))
self.exec()
def set_bbox(self):
global doc
doc = Metashape.app.document
if not len(doc.chunks):
message = "No chunks! Script aborted"
print(message)
Metashape.app.messageBox(message)
return False
try:
altitude = float(self.edtAMin.text()), float(self.edtAMax.text())
except ValueError:
message = "Invalid altitude values. Can't apply to bounding box."
print(message)
Metashape.app.messageBox(message)
return False
if altitude[0] > altitude[1]:
altitude[0], altitude[1] = altitude[1], altitude[0]
print("Min and max altitude values switched")
self.corners = list()
avg_alt = (altitude[0] + altitude[1]) / 2.
try:
self.corners.append(Metashape.Vector([float(self.edtX1.text()), float(self.edtY1.text()), avg_alt]))
self.corners.append(Metashape.Vector([float(self.edtX2.text()), float(self.edtY1.text()), avg_alt]))
self.corners.append(Metashape.Vector([float(self.edtX2.text()), float(self.edtY2.text()), avg_alt]))
self.corners.append(Metashape.Vector([float(self.edtX1.text()), float(self.edtY2.text()), avg_alt]))
except ValueError:
message = "Invalid corner coordinates values. Can't apply to bounding box."
print(message)
Metashape.app.messageBox(message)
return False
print("Script started...")
chunk = Metashape.app.document.chunk
crs = chunk.crs
T = chunk.transform.matrix
s = chunk.transform.scale
new_region = Metashape.Region()
self.corners = [T.inv().mulp(crs.unproject(x)) for x in list(self.corners)]
new_center = (self.corners[0] + self.corners[1]) / 2.
new_region.center = new_center
side1 = self.corners[0] - self.corners[1]
side2 = self.corners[0] - self.corners[-1]
side1g = T.mulp(self.corners[0]) - T.mulp(self.corners[1])
side2g = T.mulp(self.corners[0]) - T.mulp(self.corners[-1])
side3g = altitude[1] - altitude[0]
new_size = Metashape.Vector([side2g.norm()/s, side1g.norm()/s, side3g/s])
new_region.size = new_size
horizontal = side2
vertical = side1
normal = cross(vertical, horizontal)
horizontal = -cross(vertical, normal)
vertical = vertical.normalized()
R = Metashape.Matrix ([horizontal, vertical, -normal])
new_region.rot = R.t()
chunk.region = new_region
message = "Script finished. Bounding box adjusted."
print(message)
Metashape.app.messageBox(message)
return True
def main_bbox():
app = QtWidgets.QApplication.instance()
parent = app.activeWindow()
dlg = define_box(parent)
Metashape.app.addMenuItem("Custom menu/Set bounding box by corners", main_bbox)