Skip to content

Commit

Permalink
Merge pull request #146 from MIERUNE/fix/input-scale-in-ui
Browse files Browse the repository at this point in the history
Fix/input scale in UI
  • Loading branch information
Kanahiro authored Sep 22, 2023
2 parents 409aa69 + c5212e0 commit bf33b16
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 68 deletions.
121 changes: 121 additions & 0 deletions scale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from qgis.core import (
QgsProject,
QgsRectangle,
QgsPoint,
QgsScaleCalculator,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
)
from qgis.utils import iface


def get_scale_from_canvas() -> float:
"""get scale from map canvas.
For web mercator projection (EPSG:3857) case,
calculate scale with map extent correction according to scale factor"""

if QgsProject.instance().crs().authid() == "EPSG:3857":
canvas = iface.mapCanvas()
# get map canvas center coordinates in geographic
transform = QgsCoordinateTransform(
canvas.mapSettings().destinationCrs(),
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsProject.instance(),
)
center_geographic = transform.transform(canvas.center())
center_point = QgsPoint(center_geographic.x(), center_geographic.y())

# calculate scale_factor from center_point
# https://en.wikipedia.org/wiki/Mercator_projection#Scale_factor
scale_factor_x = (
QgsProject.instance().crs().factors(center_point).parallelScale()
)
scale_factor_y = (
QgsProject.instance().crs().factors(center_point).meridionalScale()
)

# determine extension corrected with scale factor
extent = canvas.extent()
delta_x = (extent.width() * scale_factor_x) - extent.width()
delta_y = (extent.height() * scale_factor_y) - extent.height()
corrected_extent = QgsRectangle(
extent.xMinimum() - delta_x / 2,
extent.yMinimum() - delta_y / 2,
extent.xMaximum() + delta_x / 2,
extent.yMaximum() + delta_y / 2,
)

# calculate scale based on corrected map extent
scale_calculator = QgsScaleCalculator(
canvas.mapSettings().outputDpi(), canvas.mapUnits()
)
return scale_calculator.calculate(corrected_extent, canvas.size().width())
else:
return iface.mapCanvas().scale()


def set_map_extent_from(scale: float, crs: str):
"""Calculate map extent according to scale and crs
input: scale and crs
action: in case of webmercator update map canvas extent with correction
according to scale factor
for other cases update map canvas with zoom to scale action
"""
if crs == "EPSG:3857":
# calculate extent with scale factor correction

canvas = iface.mapCanvas()
canvas_width_px = canvas.width()
canvas_height_px = canvas.height()
canvas_dpi = canvas.mapSettings().outputDpi()

# Get the center point of the canvas extent
transform = QgsCoordinateTransform(
canvas.mapSettings().destinationCrs(),
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsProject.instance(),
)
center_geographic = transform.transform(canvas.center())
center_point = QgsPoint(center_geographic.x(), center_geographic.y())

# calculate scale_factor from center_point
# https://en.wikipedia.org/wiki/Mercator_projection#Scale_factor
scale_factor_x = (
QgsProject.instance().crs().factors(center_point).parallelScale()
)
scale_factor_y = (
QgsProject.instance().crs().factors(center_point).meridionalScale()
)

# Calculate map units per pixel
meter_per_inch = 0.0254 # 0.0254m in 1 inch
map_units_per_pixel = (meter_per_inch / canvas_dpi) * scale

# Calculate extent width and height in map units
extent_width_map_units = canvas_width_px * map_units_per_pixel / scale_factor_x
extent_height_map_units = (
canvas_height_px * map_units_per_pixel / scale_factor_y
)

# Calculate the corrected extent
canvas_center = canvas.extent().center()
corrected_extent = QgsRectangle(
canvas_center.x() - extent_width_map_units / 2,
canvas_center.y() - extent_height_map_units / 2,
canvas_center.x() + extent_width_map_units / 2,
canvas_center.y() + extent_height_map_units / 2,
)

# update map canvas
canvas.setExtent(corrected_extent)
canvas.refresh()

# Fix setExtent bug
# Re-set extent if canvas scale is set same as Webmercator scale
if round(iface.mapCanvas().scale()) == scale:
canvas.setExtent(corrected_extent)
canvas.refresh()

else:
# zoom to scale for other crs
iface.mapCanvas().zoomScale(scale)
40 changes: 37 additions & 3 deletions ui/main_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
from translator.vector.label import generate_label_vector
from ui.progress_dialog import ProgressDialog
from translator.thread import ProcessingThread
from utils import write_json, get_tempdir, get_scale
from utils import write_json, get_tempdir
from scale import get_scale_from_canvas, set_map_extent_from


class MainDialog(QDialog):
Expand Down Expand Up @@ -50,6 +51,12 @@ def init_ui(self):
]
)

# perform update_ui_scale when it's true
# become false when UI scale widget is edited by user
self.enable_update_ui_scale = True

# set canvas scale when user input scale in ui
self.ui.scale_widget.scaleChanged.connect(self._zoom_canvas_from_scale)
# calculate export scale and show to ui
self._update_ui_scale()
# update export scale shown in ui when change map extent
Expand Down Expand Up @@ -143,7 +150,7 @@ def _run(self):
self.ui.mExtentGroupBox.outputExtent().xMaximum(),
self.ui.mExtentGroupBox.outputExtent().yMaximum(),
],
"scale": get_scale(),
"scale": get_scale_from_canvas(),
"layers": layers_processed_successfully, # layer_0,2,5..
"assets_path": "assets",
}
Expand Down Expand Up @@ -279,4 +286,31 @@ def _process_node_recursive(self, node, parent_node):
self._process_node_recursive(child, item)

def _update_ui_scale(self):
self.ui.label_scale_value.setText(str(get_scale()))
# do not update when enable_update_ui_scale is False
# in case of canvas is calculated from scale widget
if not self.enable_update_ui_scale:
return

# disable auto ui scale update
try:
self.ui.scale_widget.scaleChanged.disconnect()
except TypeError:
# when signal is not connected
pass

# update ui scale
self.ui.scale_widget.setScale(get_scale_from_canvas())
# reactivate auto ui scale update
self.ui.scale_widget.scaleChanged.connect(self._zoom_canvas_from_scale)

def _zoom_canvas_from_scale(self):
# disable temporary scale auto-calculation when extent changed
self.enable_update_ui_scale = False

# update canvas
set_map_extent_from(
scale=self.ui.scale_widget.scale(), crs=QgsProject.instance().crs().authid()
)

# reactive scale auto-calculation when extent changed
self.enable_update_ui_scale = True
27 changes: 13 additions & 14 deletions ui/main_dialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@
</property>
</widget>
</item>
<item>
<widget class="QgsExtentGroupBox" name="mExtentGroupBox"/>
</item>
<item>
<item>
<layout class="QHBoxLayout" name="export_scale">
<item>
<widget class="QLabel" name="label_scale">
Expand All @@ -55,22 +52,19 @@
</property>
</widget>
</item>
<item alignment="Qt::AlignRight">
<widget class="QLabel" name="label_scale_1">
<property name="text">
<string>1:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_scale_value">
<property name="text">
<string>1</string>
<widget class="QgsScaleWidget" name="scale_widget">
<property name="showCurrentScaleButton">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QgsExtentGroupBox" name="mExtentGroupBox"/>
</item>

<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
Expand Down Expand Up @@ -109,6 +103,11 @@
<extends>QWidget</extends>
<header>qgsfilewidget.h</header>
</customwidget>
<customwidget>
<class>QgsScaleWidget</class>
<extends>QWidget</extends>
<header>qgsscalewidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
Expand Down
51 changes: 0 additions & 51 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
from qgis.core import (
QgsRenderContext,
QgsUnitTypes,
QgsProject,
QgsRectangle,
QgsPoint,
QgsScaleCalculator,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
)
from qgis.utils import iface

Expand Down Expand Up @@ -61,48 +55,3 @@ def get_tempdir(output_dir: str) -> str:
os.mkdir(os.path.join(output_dir, temp_dir_path))

return os.path.join(output_dir, temp_dir_path)


def get_scale() -> float:
"""get scale from map canvas.
For web mercator projection (EPSG:3857) case,
calculate scale with map extent correction according to scale factor"""

if QgsProject.instance().crs().authid() == "EPSG:3857":
canvas = iface.mapCanvas()
# get map canvas center coordinates in geographic
transform = QgsCoordinateTransform(
canvas.mapSettings().destinationCrs(),
QgsCoordinateReferenceSystem("EPSG:4326"),
QgsProject.instance(),
)
center_geographic = transform.transform(canvas.center())
center_point = QgsPoint(center_geographic.x(), center_geographic.y())

# calculate scale_factor from center_point
# https://en.wikipedia.org/wiki/Mercator_projection#Scale_factor
scale_factor_x = (
QgsProject.instance().crs().factors(center_point).parallelScale()
)
scale_factor_y = (
QgsProject.instance().crs().factors(center_point).meridionalScale()
)

# determine extension corrected with scale factor
extent = canvas.extent()
delta_x = (extent.width() * scale_factor_x) - extent.width()
delta_y = (extent.height() * scale_factor_y) - extent.height()
corrected_extent = QgsRectangle(
extent.xMinimum() - delta_x / 2,
extent.yMinimum() - delta_y / 2,
extent.xMaximum() + delta_x / 2,
extent.yMaximum() + delta_y / 2,
)

# calculate scale based on corrected map extent
scale_calculator = QgsScaleCalculator(
canvas.mapSettings().outputDpi(), canvas.mapUnits()
)
return scale_calculator.calculate(corrected_extent, canvas.size().width())
else:
return iface.mapCanvas().scale()

0 comments on commit bf33b16

Please sign in to comment.