95 lines
4.0 KiB
Python
95 lines
4.0 KiB
Python
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=49.103814, lon=55.794258, zoom=10):
|
||
return f"https://yandex.ru/maps/43/kazan/?l=sat&ll={lat}%2C{lon}&z={zoom}"
|
||
|
||
class YandexMap:
|
||
def __init__(self):
|
||
options = webdriver.ChromeOptions()
|
||
# options.add_experimental_option("detach", True)
|
||
self.driver = webdriver.Chrome(options)
|
||
self.driver.get(generateURL())
|
||
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_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 |