diff --git a/scale.py b/scale.py
new file mode 100644
index 0000000..b1f920f
--- /dev/null
+++ b/scale.py
@@ -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)
diff --git a/ui/main_dialog.py b/ui/main_dialog.py
index ac8fb3a..823588c 100644
--- a/ui/main_dialog.py
+++ b/ui/main_dialog.py
@@ -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):
@@ -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
@@ -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",
}
@@ -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
diff --git a/ui/main_dialog.ui b/ui/main_dialog.ui
index 51e7274..42bd288 100644
--- a/ui/main_dialog.ui
+++ b/ui/main_dialog.ui
@@ -43,10 +43,7 @@
- -
-
-
- -
+
-
-
@@ -55,22 +52,19 @@
- -
-
-
- 1:
-
-
-
-
-
-
- 1
+
+
+ true
+ -
+
+
+
-
-
@@ -109,6 +103,11 @@
QWidget
+
+ QgsScaleWidget
+ QWidget
+
+
diff --git a/utils.py b/utils.py
index ee9f7aa..d0d8f62 100644
--- a/utils.py
+++ b/utils.py
@@ -6,12 +6,6 @@
from qgis.core import (
QgsRenderContext,
QgsUnitTypes,
- QgsProject,
- QgsRectangle,
- QgsPoint,
- QgsScaleCalculator,
- QgsCoordinateReferenceSystem,
- QgsCoordinateTransform,
)
from qgis.utils import iface
@@ -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()