Two polygons SDO_OVERLAPS, but their intersection is a LINESTRING - oracle-spatial

A have a problem similar to Why does st_intersection return non-polygons, but this time in Oracle. SDO_OVERLAPS is true for two polygons, and SDO_GEOM.RELATE with the mask 'determine' says OVERLAPBDYINTERSECT. However, SDO_GEOM.SDO_INTERSECTION returns a LINESTRING. I do understand, that SDO_GEOM.SDO_INTERSECTION returns a LINESTRING, since inspecting the geometries I would think, that the geometries share just a border. So what I do not understand is, why do SDO_OVERLAPS and SDO_GEOM.RELATE report, that these polygons overlap?
My polygons are in SRID 25832 as given below.
MULTIPOLYGON (((698985.94 6167518.54, 698978.2 6167472.42, 698978.74 6167454.55, 698977.43 6167449.98, 698976.25 6167445.86, 698970.71 6167437.43, 698963.2 6167429.88, 698960.01 6167426.67, 698958.43 6167424.83, 698937.66 6167400.72, 698925.43 6167386.53, 698901.71 6167353.91, 698876.51 6167313.75, 698864.62 6167296.17, 698847.52 6167262.63, 698836.1 6167235.78, 698834.09 6167231.54, 698826.83 6167216.93, 698824.31 6167204.35, 698822.09 6167204.79, 698808.74 6167178.12, 698806.88 6167169.49, 698798.62 6167161.68, 698782.01 6167136.17, 698770.26 6167115.94, 698754.86 6167084.58, 698724.06 6167009.8, 698721.4 6166987.42, 698692.78 6166929.5, 698648.77 6166845.25, 698636.84 6166822.99, 698629.96 6166814.59, 698619.09 6166797.03, 698609.01 6166784.5, 698582.8 6166742.11, 698565.32 6166714.01, 698550.41 6166689.13, 698529.58 6166655.42, 698527.57 6166652.09, 698512.71 6166627.51, 698502.06 6166609.94, 698498.12 6166603.44, 698496.02 6166599.54, 698474.11 6166558.43, 698464.19 6166532.15, 698452.26 6166513.57, 698447.31 6166499.65, 698437.18 6166479.95, 698402.34 6166402.32, 698382.89 6166357.8, 698375.94 6166341.89, 698367.26 6166320.78, 698366.14 6166317.49, 698334.96 6166246.45, 698336.52 6166244.08, 698329.46 6166226.47, 698321.69 6166184.66, 698311.16 6166131.38, 698307.04 6166112.13, 698262.27 6166121.6, 698219.7 6166132.92, 698214.96 6166132.79, 698142.06 6166240.41, 697976.28 6166329.01, 697862.56 6166426.6, 697675.81 6166308.88, 697303.27 6166612.66, 697101.35 6166523.15, 696776.36 6166716.02, 696747.15 6167047.53, 696553.17 6167180.09, 696527.43 6167279.09, 696453.09 6167430.1, 696345.66 6167515.84, 696321.87 6167633.86, 696322.4 6167638.1, 696432.67 6167967.62, 696385.59 6168080.19, 696337.13 6168099.34, 696094.03 6168225.88, 696086.71 6168295.21, 696085.15 6168314.16, 696085.43 6168370.63, 696089.78 6168433.68, 696104.64 6168520.24, 696098.03 6168583.63, 696059.2 6168685.38, 696073.22 6168764.87, 696088.29 6168826.75, 696034.77 6168985.87, 696019.17 6169042.41, 696012.68 6169074.01, 696010.52 6169088.54, 696004.15 6169144.98, 696002.99 6169183.19, 696001.11 6169187.42, 696001.09 6169187.61, 696002.51 6169215.68, 696007.49 6169260.16, 696023.51 6169331.44, 696043.72 6169390.49, 696049.35 6169401.04, 696052.37 6169510.44, 696056.9 6169545.27, 696060.89 6169562.31, 696106.23 6169711.22, 696111.9 6169715.05, 696123.25 6169721.92, 696215.1 6169769.11, 696229.15 6169783.47, 696222.32 6169822.4, 696236.31 6170319.56, 696311.79 6170052.04, 696469.64 6170097.76, 696551.94 6170099.86, 696794.59 6170471.48, 696773.92 6169987.3, 696841.34 6170010.16, 696841.57 6170011.66, 697203.92 6170383.95, 697146.89 6170429.24, 697138.09 6170527.67, 697143.64 6170544.57, 697205.27 6170655.09, 697267.85 6170660.89, 697406.83 6170589.4, 697408.22 6170588.45, 697459.48 6170608.72, 697462.17 6170618.31, 697530.28 6170771.27, 697596.32 6170793.69, 697649.75 6170787.81, 697782.33 6170585.87, 697879.05 6170599.47, 697887.14 6170674.72, 697921.38 6170770.16, 697893.45 6170873.79, 697959.64 6171001.48, 697989.84 6171028.99, 697999.66 6171033.08, 698088.67 6170902.76, 698236.63 6170869.46, 698243.46 6170864.86, 698289.0 6170811.92, 698116.13 6170448.1, 698200.82 6170258.79, 698179.34 6170071.72, 698247.03 6169996.46, 698291.83 6169985.75, 698432.68 6169915.71, 698680.58 6169922.66, 698721.72 6169867.81, 698721.42 6169860.31, 698720.69 6169854.74, 698720.33 6169851.99, 698740.43 6169854.23, 698742.24 6169854.41, 698743.81 6169854.6, 698791.12 6169859.88, 698826.28 6169863.78, 698868.54 6169868.54, 698875.93 6169869.36, 699008.5 6169884.26, 699045.27 6169887.82, 699068.48 6169889.39, 699088.31 6169890.73, 699093.68 6169891.09, 699094.71 6169891.16, 699123.65 6169893.11, 699125.29 6169893.22, 699126.99 6169893.17, 699181.52 6169897.02, 699204.3 6169899.47, 699255.56 6169905.6, 699289.67 6169909.67, 699324.6 6169914.16, 699335.29 6169915.56, 699335.7 6169905.1, 699336.02 6169897.09, 699336.11 6169895.01, 699336.3 6169890.27, 699334.28 6169874.83, 699334.14 6169874.14, 699329.23 6169849.64, 699329.59 6169822.75, 699329.79 6169807.12, 699329.85 6169803.23, 699329.88 6169801.25, 699329.94 6169796.23, 699329.98 6169793.76, 699330.0 6169792.03, 699330.12 6169787.99, 699330.68 6169769.02, 699331.99 6169725.05, 699336.61 6169692.44, 699336.32 6169687.55, 699335.37 6169683.86, 699335.11 6169682.86, 699332.42 6169678.61, 699328.48 6169672.67, 699320.16 6169660.11, 699317.72 6169655.56, 699316.59 6169652.64, 699314.99 6169646.83, 699314.62 6169644.71, 699314.19 6169642.3, 699313.71 6169636.74, 699313.49 6169634.26, 699309.86 6169598.24, 699305.74 6169522.7, 699304.3 6169499.73, 699301.41 6169447.28, 699296.86 6169363.61, 699289.64 6169260.48, 699284.83 6169191.68, 699284.35 6169173.03, 699284.4 6169144.27, 699281.69 6169120.86, 699279.33 6169101.53, 699278.29 6169086.94, 699279.31 6169070.6, 699279.14 6169060.18, 699277.71 6169039.74, 699276.91 6169021.39, 699275.3 6169007.58, 699275.83 6168990.67, 699276.67 6168979.56, 699280.21 6168964.98, 699281.24 6168953.75, 699274.9 6168897.39, 699272.26 6168864.96, 699271.52 6168858.07, 699268.72 6168832.32, 699266.37 6168823.64, 699250.35 6168676.57, 699234.82 6168518.09, 699226.63 6168480.66, 699224.93 6168472.45, 699220.45 6168450.73, 699220.28 6168449.92, 699219.9 6168448.06, 699219.39 6168445.55, 699215.25 6168425.39, 699213.82 6168413.17, 699212.55 6168412.58, 699208.28 6168391.57, 699203.94 6168377.51, 699199.7 6168360.09, 699194.23 6168333.97, 699194.0 6168332.84, 699193.54 6168330.66, 699192.07 6168323.67, 699190.56 6168315.78, 699188.33 6168305.18, 699187.6 6168304.06, 699187.42 6168303.1, 699186.1 6168292.65, 699185.84 6168291.13, 699185.25 6168287.68, 699183.01 6168276.3, 699182.66 6168274.49, 699181.55 6168274.95, 699175.09 6168229.9, 699163.01 6168232.46, 699158.27 6168206.8, 699159.11 6168196.53, 699161.97 6168186.54, 699167.6 6168175.56, 699169.79 6168168.02, 699169.37 6168166.76, 699163.53 6168149.28, 699158.17 6168133.49, 699153.34 6168121.34, 699151.33 6168115.3, 699139.83 6168087.47, 699123.85 6168033.17, 699101.07 6167955.76, 699096.63 6167939.43, 699074.87 6167859.57, 699065.9 6167826.42, 699059.93 6167804.75, 699058.44 6167799.3, 699050.59 6167770.45, 699043.96 6167728.54, 699031.13 6167674.83, 699030.41 6167671.02, 699025.31 6167644.04, 699023.61 6167636.66, 699019.52 6167621.72, 699015.08 6167609.24, 699011.76 6167599.93, 699002.58 6167580.05, 698998.71 6167568.98, 698992.52 6167540.98, 698987.17 6167524.74, 698985.94 6167518.54)))
MULTIPOLYGON (((695804.47 6166608.43, 695737.37 6166980.94, 695667.03 6167012.02, 695477.06 6167082.19, 695387.32 6167013.26, 695329.91 6166957.31, 695302.89 6166906.19, 695298.16 6166845.2, 695344.14 6166774.84, 695435.94 6166712.73, 695439.25 6166711.41, 695804.47 6166608.43)), ((698214.96 6166132.79, 698142.06 6166240.41, 697976.28 6166329.01, 697862.56 6166426.6, 697675.81 6166308.88, 697303.27 6166612.66, 697101.35 6166523.15, 696776.36 6166716.02, 696399.06 6166351.69, 696246.01 6166357.98, 695936.74 6166566.48, 695865.41 6166582.86, 695806.11 6166605.23, 695769.44 6166571.02, 695614.94 6166399.72, 695601.96 6166388.53, 695173.07 6166138.52, 695148.45 6166125.48, 695002.23 6166165.04, 694625.56 6165748.37, 694611.16 6165610.52, 694612.69 6165604.06, 694695.6 6165479.55, 694595.06 6165098.14, 694636.06 6164905.24, 694691.39 6164696.75, 694790.68 6164611.75, 695048.64 6164570.2, 695445.27 6164901.75, 695568.87 6164904.53, 696052.95 6165108.51, 696070.37 6165162.19, 696247.21 6165374.81, 696339.18 6165415.3, 696520.44 6165404.71, 696570.74 6165413.38, 696730.68 6165488.0, 696850.75 6165588.44, 696892.21 6165635.71, 697083.46 6165682.7, 697097.91 6165684.09, 697103.35 6165686.31, 697128.44 6165683.68, 697198.23 6165629.82, 697278.74 6165592.65, 697334.45 6165575.89, 697374.71 6165571.04, 697376.89 6165571.04, 697452.75 6165554.08, 697492.980000001 6165548.73, 697603.76 6165545.74, 697704.69 6165553.29, 697756.65 6165513.45, 697974.9 6165304.92, 697985.06 6165334.31, 697995.55 6165364.35, 698000.0 6165376.89, 698003.42 6165386.31, 698015.1 6165419.24, 698026.78 6165453.05, 698037.81 6165483.72, 698048.71 6165515.14, 698059.25 6165544.52, 698069.22 6165573.08, 698080.33 6165604.75, 698091.71 6165636.24, 698096.940000001 6165651.0, 698097.58 6165653.73, 698098.0 6165655.84, 698103.25 6165669.46, 698103.28 6165683.2, 698106.66 6165700.72, 698112.980000001 6165736.2, 698122.51 6165787.84, 698135.68 6165861.72, 698145.59 6165919.19, 698144.34 6165928.99, 698158.31 6166000.1, 698166.17 6166043.16, 698176.18 6166088.55, 698186.1 6166139.35, 698186.13 6166139.52, 698214.96 6166132.79)))

The most probable cause for such behavior is poor use of the tolerance parameter. According to your ordinates (excluding e.g. 697492.980000001 - a known artifact), the proper tolerance is 0.005.
If, as a rough example, your metadata are at 0.00005 and a node of polygon A is inside polygon B by 0.0001 SDO_OVERLAPS would be TRUE. SDO_GEOM.RELATE(a.geom,'determine',b.geom,TOL) would output OBERLAPBDYINTERSECT for TOL < 0.00005, but the output would change to TOUCH for TOL >= 0.00005
Accordingly, SDO_GEOM.SDO_INTERSECTION with an improperly high tolerance would produce a (multi)line, whereas, in the proper tolerance, a (sliver)polygon should be produced.

The problem seems to be related to one of the polygons having wrong orientation. Appearantly you cannot trust the Oracle spatial operators or functions, when a geometry is registered with a "wrong" orientation. Here a three simple examples showing that:
select sdo_geom.relate(sdo_geometry('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', 25832), 'determine', sdo_geometry('POLYGON((0 1, 1 1, 1 2, 0 2, 0 1))', 25832), 0.05)
from dual
; -- correct orientation: TOUCH
select sdo_geom.relate(sdo_geometry('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))', 25832), 'determine', sdo_geometry('POLYGON((0 1, 0 2, 1 2, 1 1, 0 1))', 25832), 0.05)
from dual
; -- both wrong orientation, south-north: COVERS
select sdo_geom.relate(sdo_geometry('POLYGON((0 1, 0 2, 1 2, 1 1, 0 1))', 25832), 'determine', sdo_geometry('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))', 25832), 0.05)
from dual
; -- both wrong orientation, north-south: OVERLAPBDYINTERSECT
I write "wrong" in quotation marks, since in my opinion strictly speaking there is no such thing as a wrong orientation. You could consistently assert, that the interior of a polygon is on the left side of the boundary when travelling in the direction given by the sequence of points. Thus a simple polygon in "wrong" orientation would mean a punched hole in the world. But Oracle seems not to implement the same orientation logic in the RELATE functionality and the INTERSECTION functionality.

Related

QGridLayout creates an incorrect layout in PyQt5

I am having an issue with QGridLayout using PyQt5. In my UI, I have two pyqtgraph PlotWidget(). One of them contains 5 different plots and the other one has only one. I want the PlotWidget() with 5 plots to be 5 times that of the other one. The following code configures everything for this plot size proportion:
grid_layout.addLayout(vlayout_signals, 0, 0, 2, 1)
grid_layout.addWidget(self.p1, 0, 1, 5, 1) # self.p1 has 5 plots in it.
grid_layout.addWidget(self.p2, 5, 1, 1, 1) # self.p2 has only 1 plot in it.
The problem is python does exactly the opposite. The widget with one plot becomes 5 times bigger. I don't know why. I wanted self.p1 to be at (row=0, col=1) and occupy 5 rows and 1 column, and self.p2 to be at (row=5, col=1) and only occupy 1 row and 1 column. For some reason, Python is not doing it (I'm using python 3.11).
Here is a minimal working example:
NFRAME = 200 * 6
import sys
import numpy as np
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QHBoxLayout, QPushButton, QWidget, QGridLayout, QComboBox, QSlider
from PyQt5.QtGui import QColor
import pyqtgraph as pg
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
grid_layout = QGridLayout()
vlayout = QVBoxLayout()
self.p1 = pg.PlotWidget()
self.p2 = pg.PlotWidget()
self.p1.plotItem.addLegend()
self.p2.plotItem.addLegend()
x = np.linspace(0, 2 * np.pi * 2, NFRAME)
y1 = np.sin(2 * np.pi * 1 * x)
y2 = 10 * np.sin(2 * np.pi * 2 * x) + 50
y3 = 20 * np.sin(2 * np.pi * 3 * x) + 200
y4 = 30 * np.sin(2 * np.pi * 4 * x) + 300
y5 = 40 * np.sin(2 * np.pi * 5 * x) + 400
self.ph1_y1 = self.p1.plot(x, y1, pen=pg.mkPen(QColor(255, 0, 0)), name="y1")
self.ph1_y2 = self.p1.plot(x, y2, pen=pg.mkPen(QColor(255, 255, 0)), name="y2")
self.ph1_y3 = self.p1.plot(x, y3, pen=pg.mkPen(QColor(0, 255, 255)), name="y3")
self.ph1_y4 = self.p1.plot(x, y4, pen=pg.mkPen(QColor(255, 0, 255)), name="y4")
self.ph1_y5 = self.p1.plot(x, y5, pen=pg.mkPen(QColor(102, 102, 255)), name="y5")
self.ph2_y1 = self.p2.plot(x, 0 * x + 6)
signal_select = QComboBox(self)
signal_select.addItem("Item1")
signal_select.addItem("Item2")
signal_offset = QSlider(Qt.Orientation.Vertical)
signal_gain = QSlider(Qt.Orientation.Vertical)
hlayout_sliders = QHBoxLayout()
hlayout_sliders.addWidget(signal_offset)
hlayout_sliders.addWidget(signal_gain)
hlayout_sliders_labels = QHBoxLayout()
signal_offset_label = QLabel()
signal_gain_label = QLabel()
signal_offset_label.setText("Offset")
signal_offset_label.setAlignment(Qt.AlignCenter)
signal_gain_label.setText("Gain")
signal_gain_label.setAlignment(Qt.AlignCenter)
hlayout_sliders_labels.addWidget(signal_offset_label)
hlayout_sliders_labels.addWidget(signal_gain_label)
arduino_signal_reset = QPushButton("Reset")
vlayout_signals = QVBoxLayout()
vlayout_signals.addWidget(signal_select)
vlayout_signals.addLayout(hlayout_sliders)
vlayout_signals.addLayout(hlayout_sliders_labels)
vlayout_signals.addWidget(arduino_signal_reset)
grid_layout.addLayout(vlayout_signals, 0, 0, 2, 1)
grid_layout.addWidget(self.p1, 0, 1, 5, 1)
grid_layout.addWidget(self.p2, 5, 1, 1, 1)
widget = QWidget()
widget.setLayout(grid_layout)
vlayout.addWidget(widget)
self.setLayout(grid_layout)
self.setCentralWidget(widget)
self.setWindowTitle('My softwaree')
if __name__ == '__main__':
app = QApplication(sys.argv)
windowExample = MainWindow()
windowExample.show()
sys.exit(app.exec_())
TL;DR
Don't use spans to set the proportions between widgets, instead use setRowStretch(), or, better, nested layouts.
Explanation
A common misconception about QGridLayout is that using "gaps" in rows and columns would result in placing them at different positions, and, similarly, using bigger row/column spans would result in a bigger size available for the widget.
That is not how the grid layout works, since its rows and columns have no specific size, nor they are directly considered for size ratios.
...At least, in theory.
What happens in your case is related to the complex way both QGridLayout and PlotWidget behave and interact.
When a layout-managed widget is shown for the first time (and whenever it's resized), it queries its layout, which in turn makes lots of internal computations for the items it manages, including considering their size hints, constraints and policies.
Note that by "items" I mean widgets, spacers and even inner layouts (see QLayoutItem).
QGridLayout is quite complex, and its behavior is not always intuitive, especially when dealing with complex widgets and layouts.
Items in a grid layout are placed in "cells", and when computing geometries for those cells (and the items they occupy) the layout engine does, more or less, the following:
query each layout item (widget, spacer or nested layout) about their size hints, constraints and policies;
correlate those items with the cells they occupy;
compute each item final geometry, which is based on the item x and y, and width and height of the last row/column they occupy;
add the last row/height coordinates to the width/height of the item, minus the initial x/y;
compute the internal required geometry of the item, considering the contents of the area occupied by the item;
set the geometry of the items, and eventually do everything again in case those items react to their resizing by updating their hints (which happens for "size reactive" widgets like QLabel or those that implement heightForWidth());
While this generally works, it has an important drawback: if the chosen span doesn't include other items in the same rows or columns, the resulting widths and heights are possibly "nullified" (meaning that their possible size is the minimum), especially whenever any widget has an Expanding size policy, which is the case of PlotWidget, which inherits from QAbstractScrollArea (since it's based on QGraphicsView), that by default always tries to expand its contents whenever there is space left from other widgets that don't expand theirselves.
Note that things are actually even more complex, as other widgets that by default have Expanding size policies might not give the same result (ie. QScrollArea). This answer is already quite extended, and I won't go too much into the darkness of Qt size management... The problem is mainly caused by a wrong usage of spans: if you want to know more, I suggest you to take your time to patiently study the Qt sources.
Take this basic example:
from PyQt5.QtWidgets import *
class Test(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet('''
QLabel {
border: 1px solid red;
}
''')
layout = QGridLayout(self)
w1 = QLabel('Row span=2')
w1.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
w2 = QLabel('Row span=1')
w2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
layout.addWidget(w1, 0, 0, 2, 1)
layout.addWidget(w2, 2, 0, 1, 1)
app = QApplication([])
example = Test()
example.show()
sys.exit(app.exec_())
Which will result conceptually like your case:
This behavior may seem invalid and certainly not perfect, but the point remains: as mentioned above, the purpose of spans is not to directly suggest a size for the item; using something in the wrong way often results in unexpected behavior.
This is one of the reasons for which I generally don't use (and often discourage) QGridLayout for complex UIs: most of the times that I decide for a QGridLayout, I end up with dismantling it and use nested layouts instead.
Solutions
If you want your widgets to have proportional sizes, then you have two choices. Either use QGridLayout properly (with item based rows/columns and proper stretch factors), or use nested layouts.
In your case, keeping a grid layout requires you to:
use two rows, with each plot widget taking just one row;
use setRowStretch() for both rows, with a value of 5 for the first, and 1 for the second;
use a row span of 2 for the left panel;
add a stretch to the bottom of the left panel layout, so that its contents are "pushed" to the top;
vlayout_signals.addStretch()
grid_layout.addLayout(vlayout_signals, 0, 0, 2, 1)
grid_layout.addWidget(self.p1, 0, 1)
grid_layout.addWidget(self.p2, 1, 1)
grid_layout.setRowStretch(0, 5)
grid_layout.setRowStretch(1, 1)
With a nested layout, instead:
use a HBoxLayout as main layout;
create a vertical layout for the plot widgets, and add them using the stretch argument;
add a stretch to the left panel as above;
main_layout = QHBoxLayout(self)
vlayout_signals.addStretch()
main_layout.addLayout(vlayout_signals)
plot_layout = QVBoxLayout()
main_layout.addLayout(plot_layout)
plot_layout.addWidget(self.p1, stretch=5)
plot_layout.addWidget(self.p2, stretch=1)
Note: all QLayouts have an optional QWidget argument that automatically installs the layout on the given widget, thus avoiding calling layout.setLayout(widget).
I would strongly suggest you this last solution, as it's more consistent with widgets that will probably have no size relations: the moment you need to add more plots, you don't have to care about the row span of the panel.
Interestingly enough, the only case for which a grid layout would have really made sense is the slider/label pair: instead of adding two unrelated horizontal layouts, you should have used a grid for them. You were "lucky" because those labels are relatively small and similar in widths, but the reality is that they are not properly aligned with their corresponding sliders. Try to add the following:
self.setStyleSheet('QSlider, QLabel {border: 1px solid red;}')
And that's the result:
If those label had very different lengths, their offset would have been very confusing.
Conclusions
While QGridLayout is certainly useful, it's important to consider that it can rarely be used and relied upon as "main layout" that directly manages items that are very different in their "visual" purpose; its row/column cells are just abstract references to item positions, not their resulting sizes, and spans must always be used only according to the other contents in the corresponding rows and columns.
Complex UIs almost always require nested layout managers that group their contents based on the hierarchy of their usage.
The rule of thumb is that a layout has to be considered for items that are grouped within the same hierarchy. You can certainly use QGridLayout as a main layout, but only as long as it manages items that can be considered siblings. For instance, you could consider a grid layout like this:
┌─────────────┬─────────────────────────┐
|┌───────────┐|┌───────────────────────┐|
|| combo ||| ||
||┌────┬────┐||| plot1 ||
||| | |||| ||
||| s1 | s2 |||└───────────────────────┘|
||| | |||┌───────────────────────┐|
||├────┼────┤||| ||
||| l1 | l2 |||| ||
||└─────────┘||| ||
|| btn ||| ||
|| ^ ||| plot2 ||
|| ║ ||| ||
|| stretch ||| ||
|| ║ ||| ||
|| v ||| ||
|└───────────┘|└───────────────────────┘|
├─────────────┴─────────────────────────┤
| something else |
└───────────────────────────────────────┘
Which would be written as follows:
main_layout = QGridLayout()
panel_layout = QVBoxLayout()
main_layout.addLayout(panel_layout)
panel_layout.addWidget(combo)
slider_layout = QGridLayout()
panel_layout.addLayout(slider_layout)
slider_layout.addWidget(s1)
slider_layout.addWidget(s2, 0, 1)
slider_layout.addWidget(l1, 1, 0)
slider_layout.addWidget(l2, 1, 0)
panel_layout.addWidget(btn)
panel_layout.addStretch()
plot_layout = QVBoxLayout()
main_layout.addLayout(plot_layout, 0, 1)
plot_layout.addWidget(plot1)
plot_layout.addWidget(plot2)
main_layout.addWidget(else, 1, 0, 1, 2)
I suggest you to take your time to experiment with Qt Designer, in order to get accustomed with layout managers and different widget types, policies or restraints.
You can try adjusting the stretch factors on the grid rows.
For example something like this might be what you are looking for.
grid_layout.addLayout(vlayout_signals, 0, 0, 2, 1)
grid_layout.addWidget(self.p1, 0, 1, 5, 1)
grid_layout.addWidget(self.p2, 5, 1, 1, 1)
for row in range(6):
grid_layout.setRowStretch(row, 1)
Note
The above solution will not work in all situations. For a better explanation see #musicmante's answer.

Halcon - get lowest pixel of a region along a line

I have a random region, and would need the coordinates in the world plane of the intersection between the region and the center of the image, with the highest y coordinate.
this is what I have so far:
fill_up(SelectedRegions, RegionFillUp)
get_image_size(Image, w,h)
gen_region_line(RegionLine,0,w/2,h,w/2)
disp_line (3600, 0, w/2, h, w/2)
intersection (RegionFillUp, RegionLine, RegionIntersection)
*EDIT
I've made some progress and have now all the intersections. But cannot figure out how to get the last entry of the array if the array is bigger than one...
gen_contour_region_xld (RegionFillUp, Contours, 'border')
get_image_size(Image, w,h)
gen_contour_polygon_xld (Line1,[0,h],[w/2,w/2])
intersection_line_contour_xld(Contours,0,w/2,h,w/2,rowcoords, columncoords, isOverlapping)
Solved it this way:
fill_up(SelectedRegions, RegionFillUp)
gen_contour_region_xld (RegionFillUp, Contours, 'border')
get_image_size(Image, w,h)
intersection_line_contour_xld(Contours,0,w/2,h,w/2,rowcoords, columncoords, isOverlapping)
disp_cross (3600, rowcoords, columncoords, 6, 0)
tuple_length (rowcoords,Length)
Position := 0
if (Length > 0)
Position := rowcoords[Length-1]
endif
image_points_to_world_plane (CamParam, Pose, Position, w/2, 1, X1, Y1)

Linear programming : how to turn a decision variable (binary) as 1 if values in an array exceeds a certain threshold

I have an array which holds a linear expression in terms of decision variable. Let's say decision variables take values such that the array = [1.7 , 0.3, 0]. Now what I want is the following :
1) If any of the values from the above array is > 0.5, then decision variables : y1 (binary) = 1, else 0.
so y1 should turn out to be [1, 0, 0]
2) If any of the values from the above array is > 0.5, then decision variables : y2 (real-valued) = the
value, else 0. Hence y2 = [1.7, 0, 0]
3) If any value in the array is > 0 and <= 0.5, then decision variables : y3 (binary) = 1, else 0. Hence
y3 = [0, 1, 0]
I know that big M formulation can help, but I am struggling to find a way.
Can somebody please help me with the formulation of above 3 points. I am working on pyomo and gurobi for programming the problem.

OpenGL: How to set z co-ordinate of vertex to positive quantity?

I'm just starting to learn opengl so I may not know all things. Using OpenTK with VB.net.
I have this code:
Dim pm As Matrix4 = Matrix4.CreatePerspectiveFieldOfView(1.57, 16 / 9, 0.1, 100)
GL.MatrixMode(MatrixMode.Projection)
GL.LoadMatrix(pm)
GL.Begin(PrimitiveType.Triangles)
GL.Vertex3(0, 0, -1) : GL.Color4(1.0!, 0!, 0!, 1.0!)
GL.Vertex3(1, 0, -1) : GL.Color4(0.0!, 1.0!, 0!, 1.0!)
GL.Vertex3(0, 1, -1) : GL.Color4(0.0!, 0.0!, 1.0!, 1.0!)
GL.End()
This correctly draws the triangle.
But when I change the z co-ordinate to +ve, they disappear. Like when GL.Vertex3(0, 0, +1), that vertex disappears. When all 3 vertices' z coords are +ve then nothing is visible.
I thought that this was because of some matrix stuff so I added a translation to the 'pm' matrix defined above:
Matrix4.CreateTranslation(0, 0, +1.0!, pm)
Still nothing happens.
But when all are -ve then all is good:
GL.Vertex3(0, 0, -10) : GL.Color4(1.0!, 0!, 0!, 1.0!)
GL.Vertex3(1, 0, -10) : GL.Color4(0.0!, 1.0!, 0!, 1.0!)
GL.Vertex3(0, 1, -10) : GL.Color4(0.0!, 0.0!, 1.0!, 1.0!)
Note: I've followed this guide. His code seems to have the same problem though no one addressed it. His code
Can someone explain this stuff to me? Thanks in advance.
In OpenGL one usually assumes that the camera is looking in -z direction (it's a convention, not a limit). Due to this, projection matrices are usually constructed in a way that they map the interval between [-nearPlane, -farPlane] to the visible depth region.
If you want to look in positive z, then you have to add a additional viewing matrix that rotates the camera by 180° around the y-axis (Or one that simply mirrors the z-axis but then the winding order changes).

How to use "SCNVector4Make()"?

I have a short question. I don't know which values I have to put in this function and I can't find any valuable examples on the internet.
This is my function:
I already set up a node and everything else.
node.rotation = SCNVector4Make(x,y,z,w);
What are the values for x, y, z, and w when I want to turn my object with an angle of 45 degrees?
The first value is for "x"
SCNVector4Make(1,0,0,0)
The second is "Y"
SCNVector4Make(0,1,0,0)
The third is "Z"
SCNVector4Make(0,0,1,0)
The fourth "W" is rotation in radians. To rotate your object on the "x" axis 45 degs. It will look like so...
SCNVector4Make(1,0,0,M_PI/4)
M_PI is equal to 180 degs.
from the SCNNode reference:
The four-component rotation vector specifies the direction of the rotation axis in the first three components and the angle of rotation (in radians) in the fourth.
In Swift 4.2 you can use the following values for 45 degrees rotation in SCNVector4Make(x, y, z, w):
X-axis:
node.rotation = SCNVector4Make(1, 0, 0, .pi/4)
Y-axis:
node.rotation = SCNVector4Make(0, 1, 0, .pi/4)
Z-axis:
node.rotation = SCNVector4Make(0, 0, 1, .pi/4)
Remember, w parameter must be in Radians,
so 3.14159 / 4 = 0.78539 radians
(or 180 / 4 = 45 degrees).