import math from io import BytesIO from time import sleep import os import cv2 import numpy as np from PIL import Image from selenium.webdriver.common.actions.wheel_input import ScrollOrigin from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains def generateURL(lat: float, lon: float, zoom: int): return f"https://yandex.ru/maps/43/kazan/?l=sat&ll={lat}%2C{lon}&z={zoom}" class YandexMap: initial_zoom: int initial_lat: float initial_lon: float def __init__(self, initial_lat=49.103814, initial_lon=55.794258, initial_zoom=10): self.initial_lat = initial_lat self.initial_lon = initial_lon self.initial_zoom = initial_zoom options = webdriver.ChromeOptions() # options.add_experimental_option("detach", True) self.driver = webdriver.Chrome(options) self.driver.get(generateURL(initial_lat, initial_lon, initial_zoom)) self.driver.maximize_window() sleep(2) action = ActionChains(self.driver) # Закрытие левой панели action.click(self.driver.find_element(By.CLASS_NAME, 'sidebar-toggle-button')) action.perform() self.driver.execute_script("arguments[0].remove();", self.driver.find_element(By.XPATH, "//div[@data-chunk='header']")) self.driver.execute_script("arguments[0].remove();", self.driver.find_element(By.XPATH, "//div[@class='sidebar-toggle-button _collapsed _name_home']")) self.driver.execute_script("arguments[0].remove();", self.driver.find_element(By.XPATH, "//nav[@class='map-controls']")) self.driver.execute_script("arguments[0].remove();", self.driver.find_element(By.XPATH, "//footer")) sleep(0.2) def save_photo(self, filename: str) -> bytes: return self.driver.save_screenshot(filename) def destroy(self): self.driver.close() def get_size(self) -> tuple[int, int]: html = self.driver.find_element(By.TAG_NAME, 'html') return (html.size['width'], html.size['height']) def scroll(self, x: float, y: float, count: int = 1, inner_zoom: bool = True): html = self.driver.find_element(By.TAG_NAME, 'html') x_offset = (x - 0.5) * html.size['width'] y_offset = (y - 0.5) * html.size['height'] action = ActionChains(self.driver) for i in range(count-1): action.scroll_from_origin(ScrollOrigin(html, int(x_offset), int(y_offset)), 0, -100 if inner_zoom else 100) action.perform() if i != count - 1: sleep(0.25) def make_as_center(self, x: float, y: float): html = self.driver.find_element(By.TAG_NAME, 'html') action = ActionChains(self.driver) action.move_to_element_with_offset(html, 0, 0) action.click_and_hold() dx = (x - 0.5) * self.get_size()[0] dy = (0.5 - y) * self.get_size()[1] print(dx, dy) action.move_by_offset(-dx, dy) action.release() action.perform() def make_screenshot(self, x: float, y: float, width: float, height: float) -> cv2.typing.MatLike: # Сохраняем скриншот self.save_photo("temp.png") # Загружаем изображение image = cv2.imread("temp.png") if image is None: raise ValueError("Не удалось загрузить изображение temp.png") # Получаем размеры исходного изображения img_height, img_width = image.shape[:2] # Преобразуем относительные координаты в абсолютные пиксели center_x = int(x * img_width) center_y = int(y * img_height) crop_width = int(width * img_width) crop_height = int(height * img_height) # Вычисляем координаты прямоугольника для кадрирования x1 = max(0, center_x - crop_width // 2) y1 = max(0, center_y - crop_height // 2) x2 = min(img_width, center_x + crop_width // 2) y2 = min(img_height, center_y + crop_height // 2) # Проверяем, что прямоугольник имеет положительные размеры if x2 <= x1 or y2 <= y1: raise ValueError("Некорректные размеры для кадрирования") # Кадрируем изображение cropped_image = image[y1:y2, x1:x2] # Если нужно вернуть изображение как результат функции: return cropped_image