-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscale.py
121 lines (104 loc) · 4.45 KB
/
scale.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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)