fix view point on map
This commit is contained in:
parent
6925e8e323
commit
aa7fb18626
@ -26,14 +26,17 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
Image = None # type: ignore
|
Image = None # type: ignore
|
||||||
ImageDraw = None # type: ignore # Define as None if import fails
|
ImageDraw = None # type: ignore # Define as None if import fails
|
||||||
ImageType = None # type: ignore
|
# MODIFIED: Added ImageType definition for type hinting even if PIL is missing.
|
||||||
|
# WHY: Allows static analysis tools to understand the intended type even if the library isn't installed.
|
||||||
|
# HOW: Defined ImageType = Any inside the except block.
|
||||||
|
ImageType = Any # type: ignore # Define ImageType as Any if PIL is not available
|
||||||
# This logger might not be configured yet if this is the first import in the process
|
# This logger might not be configured yet if this is the first import in the process
|
||||||
# So, direct print or rely on higher-level logger configuration.
|
# So, direct print or rely on higher-level logger configuration.
|
||||||
print("ERROR: GeoMapViewer - Pillow (PIL) library not found. Image operations will fail.")
|
print("ERROR: GeoMapViewer - Pillow (PIL) library not found. Image operations will fail.")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import cv2 # OpenCV for drawing operations
|
import cv2 # OpenCV for windowing and drawing
|
||||||
import numpy as np
|
import numpy as np
|
||||||
CV2_NUMPY_LIBS_AVAILABLE = True
|
CV2_NUMPY_LIBS_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -107,7 +110,7 @@ class GeoElevationMapViewer:
|
|||||||
initial_display_scale: float = 1.0 # Scale factor for the map image
|
initial_display_scale: float = 1.0 # Scale factor for the map image
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Initializes the GeoElevationMapViewer.
|
Initializes the GeoElevationMapViewer.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
elevation_manager_instance: Instance of ElevationManager for fetching elevations.
|
elevation_manager_instance: Instance of ElevationManager for fetching elevations.
|
||||||
@ -160,7 +163,7 @@ class GeoElevationMapViewer:
|
|||||||
self._current_stitched_map_pil: Optional[ImageType] = None
|
self._current_stitched_map_pil: Optional[ImageType] = None
|
||||||
self._current_map_geo_bounds_deg: Optional[Tuple[float, float, float, float]] = None
|
self._current_map_geo_bounds_deg: Optional[Tuple[float, float, float, float]] = None
|
||||||
self._current_map_render_zoom: Optional[int] = None
|
self._current_map_render_zoom: Optional[int] = None
|
||||||
self._current_stitched_map_pixel_shape: Optional[Tuple[int, int]] = None # H, W
|
self._current_stitched_map_pixel_shape: Optional[Tuple[int, int]] = (0, 0) # H, W
|
||||||
|
|
||||||
self._last_user_click_pixel_coords_on_displayed_image: Optional[Tuple[int, int]] = None
|
self._last_user_click_pixel_coords_on_displayed_image: Optional[Tuple[int, int]] = None
|
||||||
# MODIFIED: Added attribute to store the DEM tile bbox if a map view was initiated for a point with DEM data.
|
# MODIFIED: Added attribute to store the DEM tile bbox if a map view was initiated for a point with DEM data.
|
||||||
@ -214,6 +217,7 @@ class GeoElevationMapViewer:
|
|||||||
logger.critical(f"Failed to initialize map components: {e_init_map_comp}", exc_info=True)
|
logger.critical(f"Failed to initialize map components: {e_init_map_comp}", exc_info=True)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def display_map_for_point(
|
def display_map_for_point(
|
||||||
self,
|
self,
|
||||||
center_latitude: float,
|
center_latitude: float,
|
||||||
@ -225,10 +229,10 @@ class GeoElevationMapViewer:
|
|||||||
draws a marker at the point, and sends initial info back to the GUI.
|
draws a marker at the point, and sends initial info back to the GUI.
|
||||||
Applies the current display scale. The zoom level is calculated to fit the DEM tile.
|
Applies the current display scale. The zoom level is calculated to fit the DEM tile.
|
||||||
"""
|
"""
|
||||||
if not self.map_tile_fetch_manager or not self.map_display_window_controller or not self.elevation_manager:
|
if not self.map_tile_fetch_manager or not self.map_display_window_controller or not self.elevation_manager or not self.map_service_provider: # Added check for map_service_provider
|
||||||
logger.error("Map or Elevation components not ready for display_map_for_point.")
|
logger.error("Map or Elevation components not ready for display_map_for_point.")
|
||||||
# MODIFIED: If components aren't ready, send error info back to GUI queue.
|
# MODIFIED: Send error info to GUI queue if components aren't ready.
|
||||||
# WHY: The GUI needs to know the map view failed.
|
# WHY: GUI should update even if map isn't displayed.
|
||||||
# HOW: Put an error message into the queue.
|
# HOW: Put an error message into the queue.
|
||||||
error_payload = {"type": "map_info_update", "latitude": center_latitude, "longitude": center_longitude,
|
error_payload = {"type": "map_info_update", "latitude": center_latitude, "longitude": center_longitude,
|
||||||
"elevation_str": "Map Error", "map_area_size_str": "Error: Components N/A"}
|
"elevation_str": "Map Error", "map_area_size_str": "Error: Components N/A"}
|
||||||
@ -252,6 +256,10 @@ class GeoElevationMapViewer:
|
|||||||
# HOW: Set _dem_tile_geo_bbox_for_current_map to None.
|
# HOW: Set _dem_tile_geo_bbox_for_current_map to None.
|
||||||
self._dem_tile_geo_bbox_for_current_map = None
|
self._dem_tile_geo_bbox_for_current_map = None
|
||||||
|
|
||||||
|
# MODIFIED: Initialize map_tile_xy_ranges to None before the try block.
|
||||||
|
# WHY: To ensure the variable is defined even if an exception occurs before its assignment.
|
||||||
|
# HOW: Added the initialization here.
|
||||||
|
map_tile_xy_ranges = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# MODIFIED: 1. Get DEM tile info and its geographic bounds.
|
# MODIFIED: 1. Get DEM tile info and its geographic bounds.
|
||||||
@ -303,36 +311,52 @@ class GeoElevationMapViewer:
|
|||||||
# MODIFIED: 3. Calculate the appropriate zoom level to fit the map_fetch_geo_bbox into the target pixel size.
|
# MODIFIED: 3. Calculate the appropriate zoom level to fit the map_fetch_geo_bbox into the target pixel size.
|
||||||
# WHY: To prevent creating excessively large map images like 28160x40192 px.
|
# WHY: To prevent creating excessively large map images like 28160x40192 px.
|
||||||
# HOW: Calculate geographic height of map_fetch_geo_bbox and use calculate_zoom_level_for_geographic_size.
|
# HOW: Calculate geographic height of map_fetch_geo_bbox and use calculate_zoom_level_for_geographic_size.
|
||||||
map_bbox_size_km = calculate_geographic_bbox_size_km(map_fetch_geo_bbox)
|
|
||||||
calculated_zoom = None
|
calculated_zoom = None
|
||||||
if map_bbox_size_km:
|
zoom_calculation_successful = False
|
||||||
_, map_bbox_height_km = map_bbox_size_km
|
map_area_size_km = None # Added variable to store size for logging
|
||||||
map_bbox_height_meters = map_bbox_height_km * 1000.0
|
|
||||||
# Use the center latitude of the fetch box for zoom calculation accuracy
|
# MODIFIED: Check PyProj availability before calculating size.
|
||||||
center_lat_fetch_bbox = (map_fetch_geo_bbox[1] + map_fetch_geo_bbox[3]) / 2.0
|
# WHY: calculate_geographic_bbox_size_km requires PyProj.
|
||||||
|
# HOW: Added check.
|
||||||
|
if PYPROJ_AVAILABLE: # type: ignore
|
||||||
|
map_area_size_km = calculate_geographic_bbox_size_km(map_fetch_geo_bbox)
|
||||||
|
if map_area_size_km:
|
||||||
|
width_km, height_km = map_area_size_km
|
||||||
|
map_bbox_height_meters = height_km * 1000.0
|
||||||
|
# Use the center latitude of the fetch box for zoom calculation accuracy
|
||||||
|
center_lat_fetch_bbox = (map_fetch_geo_bbox[1] + map_fetch_geo_bbox[3]) / 2.0
|
||||||
|
|
||||||
|
calculated_zoom = calculate_zoom_level_for_geographic_size(
|
||||||
|
center_lat_fetch_bbox,
|
||||||
|
map_bbox_height_meters,
|
||||||
|
TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW, # Target pixel height
|
||||||
|
self.map_service_provider.tile_size # Tile size from the map service
|
||||||
|
)
|
||||||
|
if calculated_zoom is not None:
|
||||||
|
logger.info(f"Calculated zoom level {calculated_zoom} to fit BBox height ({map_bbox_height_meters:.2f}m) into {TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW}px.")
|
||||||
|
zoom_calculation_successful = True
|
||||||
|
else:
|
||||||
|
logger.warning("Could not calculate appropriate zoom level. Falling back to default zoom.")
|
||||||
|
|
||||||
calculated_zoom = calculate_zoom_level_for_geographic_size(
|
|
||||||
center_lat_fetch_bbox,
|
|
||||||
map_bbox_height_meters,
|
|
||||||
TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW, # Target pixel height
|
|
||||||
self.map_service_provider.tile_size # Tile size from the map service
|
|
||||||
)
|
|
||||||
if calculated_zoom is not None:
|
|
||||||
logger.info(f"Calculated zoom level {calculated_zoom} to fit BBox height ({map_bbox_height_meters:.2f}m) into {TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW}px.")
|
|
||||||
else:
|
else:
|
||||||
logger.warning("Could not calculate appropriate zoom level. Falling back to default zoom.")
|
logger.warning("Could not calculate geographic size of fetch BBox. Falling back to default zoom.")
|
||||||
|
else:
|
||||||
|
logger.warning("PyProj not available. Cannot calculate geographic size for zoom calculation. Falling back to default zoom.")
|
||||||
|
|
||||||
|
|
||||||
# MODIFIED: 4. Use the calculated zoom level for tile ranges and stitching, falling back to effective_zoom if calculation failed.
|
# MODIFIED: Determine the final zoom level to use.
|
||||||
# WHY: This is the core change to control the stitched image pixel size.
|
# WHY: Use the calculated zoom if successful, otherwise use the default zoom as a fallback.
|
||||||
# HOW: Replace `effective_zoom` with `calculated_zoom` (or `effective_zoom` if `calculated_zoom` is None) in the calls below.
|
# HOW: Check zoom_calculation_successful.
|
||||||
zoom_to_use = calculated_zoom if calculated_zoom is not None else effective_zoom
|
zoom_to_use = calculated_zoom if zoom_calculation_successful else DEFAULT_MAP_DISPLAY_ZOOM_LEVEL
|
||||||
logger.debug(f"Using zoom level {zoom_to_use} for tile fetching and stitching.")
|
logger.debug(f"Using zoom level {zoom_to_use} for tile fetching and stitching.")
|
||||||
|
|
||||||
|
|
||||||
|
# map_tile_xy_ranges assignment is here - line 346 originally
|
||||||
map_tile_xy_ranges = get_tile_ranges_for_bbox(map_fetch_geo_bbox, zoom_to_use)
|
map_tile_xy_ranges = get_tile_ranges_for_bbox(map_fetch_geo_bbox, zoom_to_use)
|
||||||
|
|
||||||
|
|
||||||
if not map_tile_xy_ranges:
|
if not map_tile_xy_ranges:
|
||||||
# This might happen if the BBox is valid but so small it doesn't intersect any tiles at this zoom
|
# This might happen if the BBox is very small or outside standard tile limits, mercantile.tiles might be empty.
|
||||||
logger.warning(f"No map tile ranges found for fetch BBox {map_fetch_geo_bbox} at zoom {zoom_to_use}. Showing placeholder.")
|
logger.warning(f"No map tile ranges found for fetch BBox {map_fetch_geo_bbox} at zoom {zoom_to_use}. Showing placeholder.")
|
||||||
self.map_display_window_controller.show_map(None)
|
self.map_display_window_controller.show_map(None)
|
||||||
# MODIFIED: Send initial info to GUI even if map fails, with error status.
|
# MODIFIED: Send initial info to GUI even if map fails, with error status.
|
||||||
@ -341,6 +365,7 @@ class GeoElevationMapViewer:
|
|||||||
self._send_initial_point_info_to_gui(center_latitude, center_longitude, "Map Tiles N/A", "Map Tiles N/A")
|
self._send_initial_point_info_to_gui(center_latitude, center_longitude, "Map Tiles N/A", "Map Tiles N/A")
|
||||||
return # Exit after showing placeholder/sending error
|
return # Exit after showing placeholder/sending error
|
||||||
|
|
||||||
|
|
||||||
# MODIFIED: Pass the chosen zoom_to_use to stitch_map_image.
|
# MODIFIED: Pass the chosen zoom_to_use to stitch_map_image.
|
||||||
stitched_pil = self.map_tile_fetch_manager.stitch_map_image(
|
stitched_pil = self.map_tile_fetch_manager.stitch_map_image(
|
||||||
zoom_to_use, map_tile_xy_ranges[0], map_tile_xy_ranges[1]
|
zoom_to_use, map_tile_xy_ranges[0], map_tile_xy_ranges[1]
|
||||||
@ -357,15 +382,15 @@ class GeoElevationMapViewer:
|
|||||||
|
|
||||||
self._current_stitched_map_pil = stitched_pil
|
self._current_stitched_map_pil = stitched_pil
|
||||||
# MODIFIED: Store the *actual* geographic bounds covered by the stitched tiles.
|
# MODIFIED: Store the *actual* geographic bounds covered by the stitched tiles.
|
||||||
# WHY: This is needed for pixel-to-geo conversions and calculating the displayed area size.
|
# WHY: Needed for pixel-to-geo conversions and calculating the displayed area size.
|
||||||
# HOW: Get bounds from map_tile_fetch_manager after stitching.
|
# HOW: Get bounds from map_tile_fetch_manager after stitching.
|
||||||
# MODIFIED: Pass the zoom level *actually used* for stitching to get_bounds_for_tile_range.
|
# MODIFIED: Pass the zoom level *actually used* for stitching (zoom_to_use) to get_bounds_for_tile_range.
|
||||||
# WHY: The bounds calculated must correspond to the tiles that were actually stitched.
|
# WHY: The bounds calculated must correspond to the tiles that were actually stitched.
|
||||||
# HOW: Replaced `effective_zoom` with `zoom_to_use`.
|
# HOW: Replaced `effective_zoom` with `zoom_to_use`.
|
||||||
self._current_map_geo_bounds_deg = self.map_tile_fetch_manager._get_bounds_for_tile_range(
|
self._current_map_geo_bounds_deg = self.map_tile_fetch_manager._get_bounds_for_tile_range(
|
||||||
zoom_to_use, map_tile_xy_ranges
|
zoom_to_use, map_tile_xy_ranges
|
||||||
)
|
)
|
||||||
# MODIFIED: Store the zoom level *actually used* for stitching.
|
# MODIFIED: Store the zoom level *actually used* for stitching (zoom_to_use).
|
||||||
# WHY: Consistency in context.
|
# WHY: Consistency in context.
|
||||||
# HOW: Assigned `zoom_to_use` to _current_map_render_zoom.
|
# HOW: Assigned `zoom_to_use` to _current_map_render_zoom.
|
||||||
self._current_map_render_zoom = zoom_to_use
|
self._current_map_render_zoom = zoom_to_use
|
||||||
@ -405,10 +430,21 @@ class GeoElevationMapViewer:
|
|||||||
# Calculate and send map area size
|
# Calculate and send map area size
|
||||||
map_area_size_str = "N/A"
|
map_area_size_str = "N/A"
|
||||||
if self._current_map_geo_bounds_deg:
|
if self._current_map_geo_bounds_deg:
|
||||||
size_km = calculate_geographic_bbox_size_km(self._current_map_geo_bounds_deg)
|
# MODIFIED: Check PyProj availability before calculating size.
|
||||||
if size_km:
|
# WHY: calculate_geographic_bbox_size_km requires PyProj.
|
||||||
width_km, height_km = size_km
|
# HOW: Added check.
|
||||||
map_area_size_str = f"{width_km:.2f} km W x {height_km:.2f} km H"
|
if PYPROJ_AVAILABLE: # type: ignore
|
||||||
|
size_km = calculate_geographic_bbox_size_km(self._current_map_geo_bounds_deg)
|
||||||
|
if size_km:
|
||||||
|
width_km, height_km = size_km
|
||||||
|
map_area_size_str = f"{width_km:.2f} km W x {height_km:.2f} km H"
|
||||||
|
else:
|
||||||
|
map_area_size_str = "Size Calc Failed"
|
||||||
|
logger.warning("calculate_geographic_bbox_size_km returned None for current map bounds.")
|
||||||
|
else:
|
||||||
|
map_area_size_str = "PyProj N/A (Size Unknown)"
|
||||||
|
logger.warning("PyProj not available, cannot calculate map area size.")
|
||||||
|
|
||||||
|
|
||||||
self._send_initial_point_info_to_gui(
|
self._send_initial_point_info_to_gui(
|
||||||
center_latitude, center_longitude, initial_elev_str, map_area_size_str
|
center_latitude, center_longitude, initial_elev_str, map_area_size_str
|
||||||
@ -434,10 +470,13 @@ class GeoElevationMapViewer:
|
|||||||
def display_map_for_area(
|
def display_map_for_area(
|
||||||
self,
|
self,
|
||||||
area_geo_bbox: Tuple[float, float, float, float], # west, south, east, north
|
area_geo_bbox: Tuple[float, float, float, float], # west, south, east, north
|
||||||
target_map_zoom: Optional[int] = None
|
target_map_zoom: Optional[int] = None # This parameter is now effectively ignored for area view sizing
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Displays a map for a geographic area, applying the current display scale."""
|
"""
|
||||||
if not self.map_tile_fetch_manager or not self.map_display_window_controller:
|
Displays a map for a geographic area, applying the current display scale.
|
||||||
|
Calculates the zoom level dynamically to fit the requested area into a target pixel size.
|
||||||
|
"""
|
||||||
|
if not self.map_tile_fetch_manager or not self.map_display_window_controller or not self.map_service_provider: # Added check for map_service_provider
|
||||||
logger.error("Map components not ready for display_map_for_area.")
|
logger.error("Map components not ready for display_map_for_area.")
|
||||||
# MODIFIED: Send error info to GUI queue if components aren't ready.
|
# MODIFIED: Send error info to GUI queue if components aren't ready.
|
||||||
# WHY: GUI should update even if map isn't displayed.
|
# WHY: GUI should update even if map isn't displayed.
|
||||||
@ -449,30 +488,83 @@ class GeoElevationMapViewer:
|
|||||||
if self.map_display_window_controller: self.map_display_window_controller.show_map(None) # Show placeholder
|
if self.map_display_window_controller: self.map_display_window_controller.show_map(None) # Show placeholder
|
||||||
return
|
return
|
||||||
|
|
||||||
# MODIFIED: Default zoom for area view can still be the global default map display zoom.
|
# MODIFIED: Remove the effective_zoom calculation that defaulted to DEFAULT_MAP_DISPLAY_ZOOM_LEVEL.
|
||||||
# WHY: For area view, the user requested a specific bounding box, not necessarily tied to a DEM tile size.
|
# WHY: The goal is to calculate the zoom dynamically based on the area size, not use a fixed default.
|
||||||
# A fixed default zoom might be acceptable, or we could calculate zoom based on area bbox size too (future).
|
# effective_zoom = target_map_zoom if target_map_zoom is not None else DEFAULT_MAP_DISPLAY_ZOOM_LEVEL
|
||||||
# HOW: Keep effective_zoom logic using DEFAULT_MAP_DISPLAY_ZOOM_LEVEL.
|
# logger.info(
|
||||||
effective_zoom = target_map_zoom if target_map_zoom is not None else DEFAULT_MAP_DISPLAY_ZOOM_LEVEL
|
# f"Requesting map display for area: BBox {area_geo_bbox}, "
|
||||||
|
# f"Zoom: {effective_zoom}, CurrentDisplayScale: {self.current_display_scale_factor:.2f}"
|
||||||
|
# )
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Requesting map display for area: BBox {area_geo_bbox}, "
|
f"Requesting map display for area: BBox {area_geo_bbox}, "
|
||||||
f"Zoom: {effective_zoom}, CurrentDisplayScale: {self.current_display_scale_factor:.2f}"
|
f"Target Pixel Size: {TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW}x{TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW}, "
|
||||||
|
f"CurrentDisplayScale: {self.current_display_scale_factor:.2f}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# MODIFIED: Clear the stored DEM tile bbox as this is an area view.
|
# MODIFIED: Clear the stored DEM tile bbox as this is an area view.
|
||||||
# WHY: The DEM boundary is specific to the initial point view.
|
# WHY: The DEM boundary is specific to the initial point view.
|
||||||
# HOW: Set _dem_tile_geo_bbox_for_current_map to None.
|
# HOW: Set _dem_tile_geo_bbox_for_current_map to None.
|
||||||
self._dem_tile_geo_bbox_for_current_map = None
|
self._dem_tile_geo_bbox_for_current_map = None
|
||||||
|
|
||||||
|
calculated_zoom: Optional[int] = None
|
||||||
|
zoom_calculation_successful = False
|
||||||
|
map_area_size_km: Optional[Tuple[float, float]] = None
|
||||||
|
|
||||||
|
# MODIFIED: Initialize map_tile_xy_ranges to None before the try block.
|
||||||
|
# WHY: To ensure the variable is defined even if an exception occurs before its assignment.
|
||||||
|
# HOW: Added the initialization here.
|
||||||
|
map_tile_xy_ranges = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# MODIFIED: Use the provided area_geo_bbox directly for tile range calculation.
|
# MODIFIED: Calculate the geographic size of the requested area bounding box.
|
||||||
# WHY: For area view, we want to show the requested area, not necessarily tied to a single DEM tile.
|
# WHY: Needed to determine the appropriate zoom level to fit this area into a target pixel size.
|
||||||
# HOW: Passed area_geo_bbox to get_tile_ranges_for_bbox.
|
# HOW: Call map_utils.calculate_geographic_bbox_size_km.
|
||||||
# MODIFIED: Pass the effective_zoom (default 15 or user-provided) to get_tile_ranges_for_bbox.
|
# MODIFIED: Check PyProj availability before calculating size.
|
||||||
# WHY: For area view, we use the specified or default zoom.
|
# WHY: calculate_geographic_bbox_size_km requires PyProj.
|
||||||
# HOW: Replaced `zoom_to_use` with `effective_zoom`.
|
# HOW: Added check.
|
||||||
map_tile_xy_ranges = get_tile_ranges_for_bbox(area_geo_bbox, effective_zoom)
|
if PYPROJ_AVAILABLE: # type: ignore
|
||||||
|
map_area_size_km = calculate_geographic_bbox_size_km(area_geo_bbox)
|
||||||
|
if map_area_size_km:
|
||||||
|
width_km, height_km = map_area_size_km
|
||||||
|
logger.debug(f"Calculated geographic size of requested area: {width_km:.2f}km W x {height_km:.2f}km H")
|
||||||
|
|
||||||
|
# MODIFIED: Calculate the appropriate zoom level to fit the area into the target pixel size.
|
||||||
|
# WHY: To prevent creating excessively large map images for large geographic areas.
|
||||||
|
# HOW: Use calculate_zoom_level_for_geographic_size based on the area's height.
|
||||||
|
map_bbox_height_meters = height_km * 1000.0
|
||||||
|
# Use the center latitude of the requested area BBox for zoom calculation accuracy
|
||||||
|
center_lat_area_bbox = (area_geo_bbox[1] + area_geo_bbox[3]) / 2.0
|
||||||
|
|
||||||
|
calculated_zoom = calculate_zoom_level_for_geographic_size(
|
||||||
|
center_lat_area_bbox,
|
||||||
|
map_bbox_height_meters,
|
||||||
|
TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW, # Target pixel height (reuse constant from point view)
|
||||||
|
self.map_service_provider.tile_size # Tile size from the map service
|
||||||
|
)
|
||||||
|
if calculated_zoom is not None:
|
||||||
|
logger.info(f"Calculated zoom level {calculated_zoom} to fit Area BBox height ({map_bbox_height_meters:.2f}m) into {TARGET_MAP_PIXEL_DIMENSION_FOR_POINT_VIEW}px.")
|
||||||
|
zoom_calculation_successful = True
|
||||||
|
else:
|
||||||
|
logger.warning("Could not calculate appropriate zoom level for area. Falling back to default zoom.")
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.warning("Could not calculate geographic size of requested area BBox. Falling back to default zoom.")
|
||||||
|
else:
|
||||||
|
logger.warning("PyProj not available. Cannot calculate geographic size for zoom calculation. Falling back to default zoom.")
|
||||||
|
|
||||||
|
|
||||||
|
# MODIFIED: Determine the final zoom level to use.
|
||||||
|
# WHY: Use the calculated zoom if successful, otherwise use the default zoom as a fallback.
|
||||||
|
# HOW: Check zoom_calculation_successful.
|
||||||
|
zoom_to_use = calculated_zoom if zoom_calculation_successful else DEFAULT_MAP_DISPLAY_ZOOM_LEVEL
|
||||||
|
logger.debug(f"Using zoom level {zoom_to_use} for tile fetching and stitching for area.")
|
||||||
|
|
||||||
|
# map_tile_xy_ranges assignment is here - corresponds to line 346 in point view
|
||||||
|
map_tile_xy_ranges = get_tile_ranges_for_bbox(area_geo_bbox, zoom_to_use)
|
||||||
|
|
||||||
if not map_tile_xy_ranges:
|
if not map_tile_xy_ranges:
|
||||||
logger.warning(f"No map tile ranges found for area BBox {area_geo_bbox} at zoom {effective_zoom}. Showing placeholder.")
|
logger.warning(f"No map tile ranges found for area BBox {area_geo_bbox} at zoom {zoom_to_use}. Showing placeholder.")
|
||||||
self.map_display_window_controller.show_map(None)
|
self.map_display_window_controller.show_map(None)
|
||||||
# MODIFIED: Send error info to GUI queue even if map fails.
|
# MODIFIED: Send error info to GUI queue even if map fails.
|
||||||
# WHY: GUI should update even if map isn't displayed.
|
# WHY: GUI should update even if map isn't displayed.
|
||||||
@ -481,9 +573,9 @@ class GeoElevationMapViewer:
|
|||||||
return # Exit after showing placeholder/sending error
|
return # Exit after showing placeholder/sending error
|
||||||
|
|
||||||
|
|
||||||
# MODIFIED: Pass the effective_zoom to stitch_map_image.
|
# MODIFIED: Pass the chosen zoom_to_use to stitch_map_image.
|
||||||
stitched_pil = self.map_tile_fetch_manager.stitch_map_image(
|
stitched_pil = self.map_tile_fetch_manager.stitch_map_image(
|
||||||
effective_zoom, map_tile_xy_ranges[0], map_tile_xy_ranges[1]
|
zoom_to_use, map_tile_xy_ranges[0], map_tile_xy_ranges[1]
|
||||||
)
|
)
|
||||||
if not stitched_pil:
|
if not stitched_pil:
|
||||||
logger.error("Failed to stitch map image for area display.")
|
logger.error("Failed to stitch map image for area display.")
|
||||||
@ -498,16 +590,16 @@ class GeoElevationMapViewer:
|
|||||||
# MODIFIED: Store the *actual* geographic bounds covered by the stitched tiles for the area view.
|
# MODIFIED: Store the *actual* geographic bounds covered by the stitched tiles for the area view.
|
||||||
# WHY: Needed for pixel-to-geo conversions and calculating the displayed area size.
|
# WHY: Needed for pixel-to-geo conversions and calculating the displayed area size.
|
||||||
# HOW: Get bounds from map_tile_fetch_manager after stitching.
|
# HOW: Get bounds from map_tile_fetch_manager after stitching.
|
||||||
# MODIFIED: Pass the zoom level *actually used* for stitching (effective_zoom) to get_bounds_for_tile_range.
|
# MODIFIED: Pass the zoom level *actually used* for stitching (zoom_to_use) to get_bounds_for_tile_range.
|
||||||
# WHY: The bounds calculated must correspond to the tiles that were actually stitched.
|
# WHY: The bounds calculated must correspond to the tiles that were actually stitched.
|
||||||
# HOW: Replaced `zoom_to_use` with `effective_zoom`.
|
# HOW: Replaced `effective_zoom` with `zoom_to_use`.
|
||||||
self._current_map_geo_bounds_deg = self.map_tile_fetch_manager._get_bounds_for_tile_range(
|
self._current_map_geo_bounds_deg = self.map_tile_fetch_manager._get_bounds_for_tile_range(
|
||||||
effective_zoom, map_tile_xy_ranges
|
zoom_to_use, map_tile_xy_ranges
|
||||||
)
|
)
|
||||||
# MODIFIED: Store the zoom level *actually used* for stitching (effective_zoom).
|
# MODIFIED: Store the zoom level *actually used* for stitching (zoom_to_use).
|
||||||
# WHY: Consistency in context.
|
# WHY: Consistency in context.
|
||||||
# HOW: Assigned `effective_zoom` to _current_map_render_zoom.
|
# HOW: Assigned `zoom_to_use` to _current_map_render_zoom.
|
||||||
self._current_map_render_zoom = effective_zoom
|
self._current_map_render_zoom = zoom_to_use
|
||||||
self._current_stitched_map_pixel_shape = (stitched_pil.height, stitched_pil.width)
|
self._current_stitched_map_pixel_shape = (stitched_pil.height, stitched_pil.width)
|
||||||
|
|
||||||
# MODIFIED: Draw the *requested* area bounding box on the map image.
|
# MODIFIED: Draw the *requested* area bounding box on the map image.
|
||||||
@ -524,10 +616,22 @@ class GeoElevationMapViewer:
|
|||||||
# HOW: Calculate size and send message (using N/A for point info in this case).
|
# HOW: Calculate size and send message (using N/A for point info in this case).
|
||||||
map_area_size_str = "N/A"
|
map_area_size_str = "N/A"
|
||||||
if self._current_map_geo_bounds_deg:
|
if self._current_map_geo_bounds_deg:
|
||||||
size_km = calculate_geographic_bbox_size_km(self._current_map_geo_bounds_deg)
|
# MODIFIED: Check PyProj availability before calculating size.
|
||||||
if size_km:
|
# WHY: calculate_geographic_bbox_size_km requires PyProj.
|
||||||
width_km, height_km = size_km
|
# HOW: Added check.
|
||||||
map_area_size_str = f"{width_km:.2f} km W x {height_km:.2f} km H"
|
if PYPROJ_AVAILABLE: # type: ignore
|
||||||
|
# Calculate size of the *stitched* area's bounds (which might be slightly larger than requested)
|
||||||
|
size_km = calculate_geographic_bbox_size_km(self._current_map_geo_bounds_deg)
|
||||||
|
if size_km:
|
||||||
|
width_km, height_km = size_km
|
||||||
|
map_area_size_str = f"{width_km:.2f} km W x {height_km:.2f} km H"
|
||||||
|
else:
|
||||||
|
map_area_size_str = "Size Calc Failed"
|
||||||
|
logger.warning("calculate_geographic_bbox_size_km returned None for current map bounds.")
|
||||||
|
else:
|
||||||
|
map_area_size_str = "PyProj N/A (Size Unknown)"
|
||||||
|
logger.warning("PyProj not available, cannot calculate map area size.")
|
||||||
|
|
||||||
|
|
||||||
# Send info for area view (point info is N/A)
|
# Send info for area view (point info is N/A)
|
||||||
self._send_initial_point_info_to_gui(None, None, "N/A (Area View)", map_area_size_str)
|
self._send_initial_point_info_to_gui(None, None, "N/A (Area View)", map_area_size_str)
|
||||||
@ -640,8 +744,8 @@ class GeoElevationMapViewer:
|
|||||||
|
|
||||||
# Relative position of the target geo point within the *unscaled* map's Mercator extent
|
# Relative position of the target geo point within the *unscaled* map's Mercator extent
|
||||||
# Need to handle potential division by zero if map width/height is zero (e.g., invalid bounds)
|
# Need to handle potential division by zero if map width/height is zero (e.g., invalid bounds)
|
||||||
relative_merc_x_in_map = (target_merc_x - map_ul_merc_x) / total_map_width_merc
|
relative_merc_x_in_map = (target_merc_x - map_ul_merc_x) / total_map_width_merc if total_map_width_merc > 0 else 0.0
|
||||||
relative_merc_y_in_map = (map_ul_merc_y - target_merc_y) / total_map_height_merc
|
relative_merc_y_in_map = (map_ul_merc_y - target_merc_y) / total_map_height_merc if total_map_height_merc > 0 else 0.0
|
||||||
|
|
||||||
|
|
||||||
# Convert these relative positions to pixel coordinates on the *unscaled* image
|
# Convert these relative positions to pixel coordinates on the *unscaled* image
|
||||||
@ -735,8 +839,8 @@ class GeoElevationMapViewer:
|
|||||||
target_merc_x, target_merc_y = local_mercantile.xy(lon, lat) # type: ignore
|
target_merc_x, target_merc_y = local_mercantile.xy(lon, lat) # type: ignore
|
||||||
|
|
||||||
# Handle relative position calculation, ensuring bounds are respected
|
# Handle relative position calculation, ensuring bounds are respected
|
||||||
relative_merc_x_in_map = (target_merc_x - map_ul_merc_x) / total_map_width_merc
|
relative_merc_x_in_map = (target_merc_x - map_ul_merc_x) / total_map_width_merc if total_map_width_merc > 0 else 0.0
|
||||||
relative_merc_y_in_map = (map_ul_merc_y - target_merc_y) / total_map_height_merc
|
relative_merc_y_in_map = (map_ul_merc_y - target_merc_y) / total_map_height_merc if total_map_height_merc > 0 else 0.0
|
||||||
|
|
||||||
pixel_x_on_unscaled = int(round(relative_merc_x_in_map * unscaled_width))
|
pixel_x_on_unscaled = int(round(relative_merc_x_in_map * unscaled_width))
|
||||||
pixel_y_on_unscaled = int(round(relative_merc_y_in_map * unscaled_height))
|
pixel_y_on_unscaled = int(round(relative_merc_y_in_map * unscaled_height))
|
||||||
@ -888,7 +992,7 @@ class GeoElevationMapViewer:
|
|||||||
logger.debug(
|
logger.debug(
|
||||||
f"Map mouse click (on scaled img) received at pixel ({pixel_x_on_displayed_img}, {pixel_y_on_displayed_img})"
|
f"Map mouse click (on scaled img) received at pixel ({pixel_x_on_displayed_img}, {pixel_y_on_displayed_img})"
|
||||||
)
|
)
|
||||||
# Store the pixel coordinates of the click on the *displayed* (scaled) image.
|
# Store the pixel coordinates of the click on the *displayed* (scalata) image.
|
||||||
self._last_user_click_pixel_coords_on_displayed_image = (pixel_x_on_displayed_img, pixel_y_on_displayed_img)
|
self._last_user_click_pixel_coords_on_displayed_image = (pixel_x_on_displayed_img, pixel_y_on_displayed_img)
|
||||||
|
|
||||||
# MODIFIED: Check if map context is ready before proceeding with conversion and elevation fetch.
|
# MODIFIED: Check if map context is ready before proceeding with conversion and elevation fetch.
|
||||||
@ -1039,7 +1143,7 @@ class GeoElevationMapViewer:
|
|||||||
self._current_stitched_map_pil = None
|
self._current_stitched_map_pil = None
|
||||||
self._current_map_geo_bounds_deg = None
|
self._current_map_geo_bounds_deg = None
|
||||||
self._current_map_render_zoom = None
|
self._current_map_render_zoom = None
|
||||||
self._current_stitched_map_pixel_shape = None
|
self._current_stitched_map_pixel_shape = (0, 0) # Reset to default tuple
|
||||||
self._last_user_click_pixel_coords_on_displayed_image = None
|
self._last_user_click_pixel_coords_on_displayed_image = None
|
||||||
self._dem_tile_geo_bbox_for_current_map = None
|
self._dem_tile_geo_bbox_for_current_map = None
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user