Adds an acquisition interval parameter and a distance threshold
parameter to the gps plugin in order to keep the cursor still when
the receiver is in static conditions.
Prevents crashes when PyQt4 modules are imported in QGIS 3.x.
This instantly segfaults QGIS. Throwing an exception makes it
easier to identify the cause as a faulty plugin, and shows
exactly where the bad import is located.
Adds a native k-means clustering algorithm.
Based on a port of PostGIS' ST_ClusterKMeans function, this
new algorithm adds a new cluster ID field to a set of input
features identify the feature's cluster based on the k-means
clustering approach. If non-point geometries are used as input,
the clustering is based off the centroid of the input geometries.
This will update camera's view center as the camera moves around.
Before the view center would be always at the zero elevation, which
means that with terrain further away from zero elevation tilting
and rotation of camera would feel weird due to the center point being
far away.
In order to update camera's view center we need to calculate intersection
of terrain with a 3D ray coming from the camera's position towards the center
of the viewport. This is done by going through the active terrain tiles
and checking whether their bounding box intersects the ray - if it does,
then we do an exact test of terrain tile's triangle mesh against the ray
to find the closest intersection point. When we have the intersection,
we update the view center to be at the terrain's surface.
Unfortunately raycasting in Qt3D is only available from 5.11 which has been
released only very recently. I have therefore ported some code from Qt3D
internals and added ray vs axis-aligned box from a different source
(Qt3D uses bounding spheres but they are not available in public API either)
bool(obj) in Python has the following semantics:
1. if the object has __bool__() method, return its value
2. if the object has __len__() method, return its value
3. return True
So for objects in QGIS API that implement __len__() method, we were getting
unexpected behavior - for example, "if layer: ..." would evaluate as False
in case the layer was empty, while the usual expectation is that any reference
to an object that is not None should evaluate to True.