Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 69 additions & 14 deletions app/inpututils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,34 +321,84 @@ void InputUtils::setExtentToGeom( const QgsGeometry &geom, InputMapSettings *map
mapSettings->setExtent( currentExtent );
}

QPointF InputUtils::relevantGeometryCenterToScreenCoordinates( const QgsGeometry &geom, InputMapSettings *mapSettings )
QPointF InputUtils::whereToPanWhenIdentifying( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset, const QPointF &identifyLocation )
{
QPointF screenPoint;
QgsPoint target;
if ( !mapSettings || geom.isNull() || !geom.constGet() )
return screenPoint;
QgsRectangle effectiveExtent( mapSettings->visibleExtent() );

// canvas size in logical pixels; bottomOffset is in logical pixels too and
// screenToCoordinate() expects logical pixel coordinates
const double canvasWidth = mapSettings->outputSize().width() / mapSettings->devicePixelRatio();
const double canvasHeight = mapSettings->outputSize().height() / mapSettings->devicePixelRatio();
const QPointF bottomPoint( canvasWidth / 2.0, canvasHeight - bottomOffset );

const QgsRectangle currentExtent = mapSettings->mapSettings().visibleExtent();
const QgsPoint bottomPointMap = mapSettings->screenToCoordinate( bottomPoint );

// Cut the geometry to current extent
const QgsGeometry currentExtentAsGeom = QgsGeometry::fromRect( currentExtent );
const QgsGeometry intersectedGeom = geom.intersection( currentExtentAsGeom );
effectiveExtent.setYMinimum( bottomPointMap.y() );

if ( !intersectedGeom.isEmpty() )
// Now we calculate a safeEffectiveExtent, slightly smaller, so that we don't allow geometries too close to the screen borders
constexpr double EXTENT_BUFFER_SCALE = 0.82;
const QgsRectangle safeEffectiveExtent( effectiveExtent.scaled( EXTENT_BUFFER_SCALE ) );

QPointF screenPoint;
QgsPoint target;
if ( safeEffectiveExtent.contains( geom.boundingBox() ) )
{
target = QgsPoint( intersectedGeom.boundingBox().center() );
// If the whole geometry is visible, don't move map
return { std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() };
}
else
else if ( effectiveExtent.width() >= geom.boundingBox().width() &&
effectiveExtent.height() >= geom.boundingBox().height() )
{
// The geometry is outside the current viewed extent
setExtentToGeom( geom, mapSettings );
// if the whole geometry would fit without changing scale, center it
target = QgsPoint( geom.boundingBox().center() );
}
else
{
// the geometry is big, let's pan to the point the user clicked on the map
target = QgsPoint( identifyLocation );
}

screenPoint = mapSettings->coordinateToScreen( target );
screenPoint.ry() += bottomOffset / 2;

return screenPoint;
}

QgsRectangle InputUtils::drawerCompensatedExtent( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset )
{
const QgsRectangle bbox = geom.boundingBox();
QgsRectangle currentExtent = mapSettings->mapSettings().visibleExtent();

// canvas size in logical pixels; the bottom bottomOffset of the canvas is covered by another
// component (e.g. preview drawer), so we center the geometry in the remaining visible part
const double canvasWidth = mapSettings->outputSize().width() / mapSettings->devicePixelRatio();
const double canvasHeight = mapSettings->outputSize().height() / mapSettings->devicePixelRatio();
const double visibleHeight = std::max( canvasHeight - bottomOffset, 1.0 );

if ( bbox.isEmpty() ) // Deal with an empty bouding box e.g : a point
{
const QgsPointXY center( bbox.center().x(), bbox.center().y() - bottomOffset / 2.0 * mapSettings->mapUnitsPerPoint() );
const QgsVector offset = currentExtent.center() - center;
currentExtent -= offset;
}
else
{
QgsRectangle paddedBbox = bbox;

// Add a offset to encompass handles etc..
// This number is based on what feel confortable for the user
constexpr double SCALE_FACTOR = 1.18;
paddedBbox.scale( SCALE_FACTOR );

const double mapUnitsPerPoint = std::max( paddedBbox.width() / canvasWidth, paddedBbox.height() / visibleHeight );
const double cx = bbox.center().x();
const double cy = bbox.center().y() - bottomOffset / 2.0 * mapUnitsPerPoint;
currentExtent = QgsRectangle( cx - canvasWidth * mapUnitsPerPoint / 2, cy - canvasHeight * mapUnitsPerPoint / 2,
cx + canvasWidth * mapUnitsPerPoint / 2, cy + canvasHeight * mapUnitsPerPoint / 2 );
}
return currentExtent;
}

double InputUtils::convertCoordinateString( const QString &rationalValue )
{
QStringList values = rationalValue.split( "," );
Expand Down Expand Up @@ -2146,6 +2196,11 @@ QString InputUtils::getUniqueString( const QString &newString, const QStringList
return uniqueString;
}

QgsRectangle InputUtils::extentFromMinMax( double xMin, double yMin, double xMax, double yMax )
{
return QgsRectangle( xMin, yMin, xMax, yMax );
}

bool InputUtils::rescaleImage( const QString &path, QgsProject *activeProject )
{
int quality = activeProject->readNumEntry( QStringLiteral( "Mergin" ), QStringLiteral( "PhotoQuality" ), 0 );
Expand Down
25 changes: 22 additions & 3 deletions app/inpututils.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,29 @@ class InputUtils: public QObject
Q_INVOKABLE void setExtentToGeom( const QgsGeometry &geom, InputMapSettings *mapSettings );

/**
* Returns the center point of the \a geom currently displayed on screen
* Returns the screen point where the map should jump to when identifying features.
* If the map should not pan, a QPointF with NaNs is returned.
*
* Fall back to \see setExtentToGeom in case we don't find any geometry relevant
* \param geom The geometry identified
* \param mapSettings Current map settings
* \param bottomOffset Pixel size (height) of drawer that may be covering the map canvas, eg preview panel
* \param identifyLocation The map coords of the location the user clicked for identification
*
* Nota Bene: Assume geometry and map canvas CRS are the same
*/
Q_INVOKABLE QPointF relevantGeometryCenterToScreenCoordinates( const QgsGeometry &geom, InputMapSettings *mapSettings );
Q_INVOKABLE static QPointF whereToPanWhenIdentifying( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset, const QPointF &identifyLocation );

/**
* Returns the extent that the map needs to zoom to so that the \a geom fits (with a safe buffer) the visible part
* of the map canvas when the canvas lower part is covered by a drawer of \a bottomOffset pixels high.
*
* \param geom The geometry identified
* \param mapSettings Current map settings
* \param bottomOffset Pixel size (height) of drawer that may be covering the map canvas, eg preview panel
*
* Nota Bene: Assume geometry and map canvas CRS are the same
*/
Q_INVOKABLE static QgsRectangle drawerCompensatedExtent( const QgsGeometry &geom, InputMapSettings *mapSettings, double bottomOffset );

// utility functions to extract information from map settings
// (in theory this data should be directly available from .MapTransform
Expand Down Expand Up @@ -681,6 +697,9 @@ class InputUtils: public QObject
*/
static QString getUniqueString( const QString &newString, const QStringList &existingStrings );

//! Trivial constructor for QgsRectangle since the actual constructor is not invokable
Q_INVOKABLE static QgsRectangle extentFromMinMax( double xMin, double yMin, double xMax, double yMax );

public slots:
void onQgsLogMessageReceived( const QString &message, const QString &tag, Qgis::MessageLevel level );

Expand Down
19 changes: 5 additions & 14 deletions app/qml/form/MMFormController.qml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Item {
property bool layerIsReadOnly: featureLayerPair?.layer?.readOnly ?? false
property bool layerIsSpatial: featureLayerPair ? __inputUtils.isSpatialLayer( featureLayerPair.layer ) : false

property real drawerHeight: drawer.height
property real previewPanelHeight: previewPanel.implicitHeight

signal closed()
signal saveRequested()
Expand All @@ -46,7 +46,7 @@ Item {
signal createLinkedFeature( var targetLayer, var parentPair )
signal multiSelectFeature( var feature )
signal stakeoutFeature( var feature )
signal previewPanelChanged( var panelHeight )
signal previewPanelChanged()

function openDrawer() {
root.panelState = "form"
Expand Down Expand Up @@ -142,11 +142,6 @@ Item {
edge: Qt.BottomEdge
closePolicy: Popup.CloseOnEscape // prevents the drawer closing while moving canvas

onOpened: {
if ( panelState === "preview" )
previewPanelChanged( previewPanel.implicitHeight )
}

onClosed: {
if ( statesManager.state !== "hidden" )
statesManager.state = "closed"
Expand Down Expand Up @@ -179,8 +174,9 @@ Item {

onCloseClicked: drawer.close()

onImplicitHeightChanged: {
previewPanelChanged( previewPanel.implicitHeight )
onDoneLoading: {
if ( root.panelState === "preview" )
previewPanelChanged()
}
}

Expand Down Expand Up @@ -243,9 +239,4 @@ Item {
}
}
}

onFeatureLayerPairChanged: {
if ( panelState === "preview" )
previewPanelChanged( previewPanel.implicitHeight )
}
}
10 changes: 5 additions & 5 deletions app/qml/form/MMFormStackController.qml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ Item {
return root.height // form is the highest always
}
else if ( form.panelState === "preview" ) {
if ( form.drawerHeight > maxHeight ) {
maxHeight = form.drawerHeight
if ( form.previewPanelHeight > maxHeight ) {
maxHeight = form.previewPanelHeight
}
}
}
Expand All @@ -54,7 +54,7 @@ Item {
signal createLinkedFeatureRequested( var targetLayer, var parentPair )
signal multiSelectFeature( var feature )
signal stakeoutFeature( var feature )
signal previewPanelChanged( var panelHeight )
signal previewPanelChanged()

function openForm( pair, formState, panelState ) {
if ( formsStack.depth === 0 )
Expand Down Expand Up @@ -308,8 +308,8 @@ Item {
formsStack.syncWhenFormCloses = true
}

onPreviewPanelChanged: function( panelHeight ) {
root.previewPanelChanged( panelHeight )
onPreviewPanelChanged: {
root.previewPanelChanged()
}

onEditGeometry: function( pair ) {
Expand Down
19 changes: 19 additions & 0 deletions app/qml/form/MMPreviewDrawer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Item {
signal selectMoreClicked( var feature )
signal stakeoutClicked( var feature )
signal closeClicked()
// this signal fires once after content-layout has settled when the feature changes
signal doneLoading()

MouseArea {
anchors.fill: parent
Expand Down Expand Up @@ -271,6 +273,23 @@ Item {
}
}

Timer {
id: heightDebounceTimer
interval: 100
onTriggered: root.doneLoading()
}

onImplicitHeightChanged: {
heightDebounceTimer.restart()
}

Connections {
target: controller
function onFeatureLayerPairChanged() {
heightDebounceTimer.start()
}
}

QtObject {
id: internal

Expand Down
22 changes: 11 additions & 11 deletions app/qml/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,16 @@ ApplicationWindow {
projDialog.open()
}

function identifyFeature( pair ) {
let hasNullGeometry = pair.feature.geometry.isNull
function identifyFeature( pair, point = Qt.point(NaN, NaN) ) {
map.identifyLocation = point

if ( hasNullGeometry ) {
let skipPreview = __inputUtils.isEmptyGeometry( pair.feature.geometry )
if ( skipPreview ) {
formsStackManager.openForm( pair, "readOnly", "form" )
}
else if ( pair.valid ) {
map.highlightPair( pair )
formsStackManager.openForm( pair, "readOnly", "preview")
formsStackManager.openForm( pair, "readOnly", "preview" )
}
}

Expand Down Expand Up @@ -180,8 +181,8 @@ ApplicationWindow {
return 0
}

onFeatureIdentified: function( pair ) {
formsStackManager.openForm( pair, "readOnly", "preview" );
onFeatureIdentified: function( pair, point ) {
window.identifyFeature( pair, point )
}

onFeaturesIdentified: function( pairs ) {
Expand All @@ -190,6 +191,7 @@ ApplicationWindow {
}

onNothingIdentified: {
map.hideHighlight()
formsStackManager.closeDrawer()
}

Expand Down Expand Up @@ -860,8 +862,8 @@ ApplicationWindow {
closeDrawer()
}

onPreviewPanelChanged: function( panelHeight ) {
map.jumpToHighlighted( panelHeight - mapToolbar.height )
onPreviewPanelChanged: {
map.jumpToHighlighted()
}
}

Expand Down Expand Up @@ -957,10 +959,8 @@ ApplicationWindow {
secondaryText: model.LayerName
leftContent: MMIcon { source: model.LayerIcon }
onClicked: {
let pair = model.FeaturePair
featurePairSelection.close()
map.highlightPair( pair )
formsStackManager.openForm( pair, "readOnly", "preview" );
window.identifyFeature( model.FeaturePair );
}
}

Expand Down
Loading
Loading