168 lines
7.4 KiB
Python
168 lines
7.4 KiB
Python
import json
|
||
import tkinter as tk
|
||
import numpy as np
|
||
from shapely.geometry import Polygon, Point
|
||
import tripy
|
||
import math
|
||
|
||
# Загрузка координат из JSON файла
|
||
with open('regions.json', 'r') as file:
|
||
data = json.load(file)
|
||
|
||
# Извлечение координат
|
||
coordinates = data['features'][1]['geometry']['coordinates'][0]
|
||
|
||
# Функция для запроса количества точек у пользователя
|
||
def get_number_of_points():
|
||
num_points = int(input("Введите количество точек: "))
|
||
return num_points
|
||
|
||
# Функция для генерации точки внутри треугольника с учетом минимального расстояния
|
||
def generate_point_in_triangle_with_distance(triangle, existing_points, min_distance, polygon, max_attempts=100):
|
||
# Генерирует случайную точку внутри треугольника с использованием барицентрических координат.
|
||
for _ in range(max_attempts):
|
||
r1, r2 = np.random.random(2)
|
||
sqrt_r1 = np.sqrt(r1)
|
||
a, b, c = triangle
|
||
point = (
|
||
(1 - sqrt_r1) * np.array(a) +
|
||
sqrt_r1 * (1 - r2) * np.array(b) +
|
||
sqrt_r1 * r2 * np.array(c)
|
||
)
|
||
|
||
point = Point(point)
|
||
|
||
# Проверка, что точка находится на достаточном расстоянии от других точек и границ многоугольника
|
||
if all(point.distance(existing_point) >= min_distance for existing_point in existing_points) and \
|
||
point.distance(polygon.boundary) >= min_distance:
|
||
return point
|
||
|
||
return None
|
||
|
||
def distribute_points_on_polygon_with_Seidal(coords, num_points):
|
||
# Создание многоугольника с использованием библиотеки Shapely
|
||
polygon = Polygon(coords)
|
||
|
||
# Выполняем триангуляцию многоугольника с использованием библиотеки tripy
|
||
triangles = tripy.earclip(coords)
|
||
|
||
# Преобразуем вершины треугольников в формат списков кортежей
|
||
triangle_ver = [list(map(tuple, triangle)) for triangle in triangles]
|
||
|
||
# Вычисляем площадь каждого треугольника
|
||
triangle_areas = [Polygon(triangle).area for triangle in triangle_ver]
|
||
|
||
# Находим общую площадь всех треугольников
|
||
total_area = sum(triangle_areas)
|
||
|
||
# Определяем минимальное расстояние между точками
|
||
min_distance = math.sqrt(total_area / num_points) / 2
|
||
|
||
points = []
|
||
|
||
# Равномерно распределяем точки в каждом треугольнике
|
||
for i, triangle in enumerate(triangle_ver):
|
||
# Вычисляем количество точек для данного треугольника пропорционально его площади
|
||
num_points_in_triangle = int(num_points * (triangle_areas[i] / total_area))
|
||
|
||
# Генерируем точки внутри треугольника с учетом минимального расстояния
|
||
for _ in range(num_points_in_triangle):
|
||
point = generate_point_in_triangle_with_distance(triangle, points, min_distance, polygon)
|
||
if polygon.contains(point):
|
||
points.append(point)
|
||
|
||
# Если точек меньше, чем нужно, добавляем дополнительные точки, уменьшая минимальное расстояние
|
||
while len(points) < num_points:
|
||
min_distance *= 0.9
|
||
for i, triangle in enumerate(triangle_ver):
|
||
if len(points) >= num_points:
|
||
break
|
||
point = generate_point_in_triangle_with_distance(triangle, points, min_distance, polygon)
|
||
if point and polygon.contains(point):
|
||
points.append(point)
|
||
|
||
# Возвращаем список точек, которые оказались внутри многоугольника
|
||
return [(point.x, point.y) for point in points]
|
||
|
||
|
||
|
||
# Функция для создания графика многоугольника
|
||
def draw_polygon(coords, points=None):
|
||
# Создание основного окна
|
||
root = tk.Tk()
|
||
root.title("Polygon from JSON coordinates")
|
||
|
||
# Создание холста для рисования
|
||
canvas = tk.Canvas(root, width=500, height=500)
|
||
canvas.pack()
|
||
|
||
# Нормализация координат для отображения на холсте
|
||
x_coords = [point[0] for point in coords]
|
||
y_coords = [point[1] for point in coords]
|
||
|
||
min_x, max_x = min(x_coords), max(x_coords)
|
||
min_y, max_y = min(y_coords), max(y_coords)
|
||
|
||
scale_x = 480 / (max_x - min_x) if max_x - min_x != 0 else 1
|
||
scale_y = 480 / (max_y - min_y) if max_y - min_y != 0 else 1
|
||
|
||
normalized_coords = [
|
||
((point[0] - min_x) * scale_x + 10, 490 - (point[1] - min_y) * scale_y + 10)
|
||
for point in coords
|
||
]
|
||
|
||
# Добавление первой точки в конец для замыкания многоугольника
|
||
normalized_coords.append(normalized_coords[0])
|
||
|
||
# Преобразование координат в плоский список
|
||
flat_coords = [coord for point in normalized_coords for coord in point]
|
||
|
||
# Рисование многоугольника
|
||
canvas.create_polygon(flat_coords, outline='black', fill='', width=2)
|
||
|
||
# Рисование дополнительных точек
|
||
if points:
|
||
for point in points:
|
||
normalized_point = (
|
||
(point[0] - min_x) * scale_x + 10,
|
||
490 - (point[1] - min_y) * scale_y + 10
|
||
)
|
||
canvas.create_oval(
|
||
normalized_point[0], normalized_point[1],
|
||
normalized_point[0], normalized_point[1],
|
||
fill='red'
|
||
)
|
||
|
||
# Запуск основного цикла приложения
|
||
root.mainloop()
|
||
|
||
# Функция для создания JavaScript файла с данными
|
||
def create_js_file(points):
|
||
features = []
|
||
for i, (x, y) in enumerate(points):
|
||
feature = {
|
||
"type": "Feature",
|
||
"id": i,
|
||
"geometry": {
|
||
"type": "Point",
|
||
"coordinates": [x, y]
|
||
}
|
||
}
|
||
features.append(feature)
|
||
|
||
js_content = f"var data = `{json.dumps({'type': 'FeatureCollection', 'features': features}, ensure_ascii=False)}`;"
|
||
|
||
with open('data.js', 'w', encoding='utf-8') as js_file:
|
||
js_file.write(js_content)
|
||
|
||
# Получение количества точек от пользователя
|
||
num_points = get_number_of_points()
|
||
|
||
# Равномерное распределение точек по многоугольнику
|
||
points_on_polygon = distribute_points_on_polygon_with_Seidal(coordinates, num_points)
|
||
|
||
create_js_file(points_on_polygon)
|
||
# Отрисовка многоугольника с равномерно распределенными точками
|
||
draw_polygon(coordinates, points_on_polygon)
|
||
|