Загрузить файлы в «/»
This commit is contained in:
parent
117c81b557
commit
47554eadad
167
RavResp.py
Normal file
167
RavResp.py
Normal file
@ -0,0 +1,167 @@
|
||||
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)
|
||||
|
40934
regions.json
Normal file
40934
regions.json
Normal file
File diff suppressed because it is too large
Load Diff
144
tripy.py
Normal file
144
tripy.py
Normal file
@ -0,0 +1,144 @@
|
||||
import math
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
Point = namedtuple('Point', ['x', 'y'])
|
||||
|
||||
EPSILON = math.sqrt(sys.float_info.epsilon)
|
||||
|
||||
|
||||
def earclip(polygon):
|
||||
"""
|
||||
Simple earclipping algorithm for a given polygon p.
|
||||
polygon is expected to be an array of 2-tuples of the cartesian points of the polygon
|
||||
|
||||
For a polygon with n points it will return n-2 triangles.
|
||||
The triangles are returned as an array of 3-tuples where each item in the tuple is a 2-tuple of the cartesian point.
|
||||
|
||||
e.g
|
||||
>>> polygon = [(0,1), (-1, 0), (0, -1), (1, 0)]
|
||||
>>> triangles = tripy.earclip(polygon)
|
||||
>>> triangles
|
||||
[((1, 0), (0, 1), (-1, 0)), ((1, 0), (-1, 0), (0, -1))]
|
||||
|
||||
Implementation Reference:
|
||||
- https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
|
||||
"""
|
||||
ear_vertex = []
|
||||
triangles = []
|
||||
|
||||
polygon = [Point(*point) for point in polygon]
|
||||
|
||||
if _is_clockwise(polygon):
|
||||
polygon.reverse()
|
||||
|
||||
point_count = len(polygon)
|
||||
for i in range(point_count):
|
||||
prev_index = i - 1
|
||||
prev_point = polygon[prev_index]
|
||||
point = polygon[i]
|
||||
next_index = (i + 1) % point_count
|
||||
next_point = polygon[next_index]
|
||||
|
||||
if _is_ear(prev_point, point, next_point, polygon):
|
||||
ear_vertex.append(point)
|
||||
|
||||
while ear_vertex and point_count >= 3:
|
||||
ear = ear_vertex.pop(0)
|
||||
i = polygon.index(ear)
|
||||
prev_index = i - 1
|
||||
prev_point = polygon[prev_index]
|
||||
next_index = (i + 1) % point_count
|
||||
next_point = polygon[next_index]
|
||||
|
||||
polygon.remove(ear)
|
||||
point_count -= 1
|
||||
triangles.append(((prev_point.x, prev_point.y), (ear.x, ear.y), (next_point.x, next_point.y)))
|
||||
if point_count > 3:
|
||||
prev_prev_point = polygon[prev_index - 1]
|
||||
next_next_index = (i + 1) % point_count
|
||||
next_next_point = polygon[next_next_index]
|
||||
|
||||
groups = [
|
||||
(prev_prev_point, prev_point, next_point, polygon),
|
||||
(prev_point, next_point, next_next_point, polygon),
|
||||
]
|
||||
for group in groups:
|
||||
p = group[1]
|
||||
if _is_ear(*group):
|
||||
if p not in ear_vertex:
|
||||
ear_vertex.append(p)
|
||||
elif p in ear_vertex:
|
||||
ear_vertex.remove(p)
|
||||
return triangles
|
||||
|
||||
|
||||
def _is_clockwise(polygon):
|
||||
s = 0
|
||||
polygon_count = len(polygon)
|
||||
for i in range(polygon_count):
|
||||
point = polygon[i]
|
||||
point2 = polygon[(i + 1) % polygon_count]
|
||||
s += (point2.x - point.x) * (point2.y + point.y)
|
||||
return s > 0
|
||||
|
||||
|
||||
def _is_convex(prev, point, next):
|
||||
return _triangle_sum(prev.x, prev.y, point.x, point.y, next.x, next.y) < 0
|
||||
|
||||
|
||||
def _is_ear(p1, p2, p3, polygon):
|
||||
ear = _contains_no_points(p1, p2, p3, polygon) and \
|
||||
_is_convex(p1, p2, p3) and \
|
||||
_triangle_area(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y) > 0
|
||||
return ear
|
||||
|
||||
|
||||
def _contains_no_points(p1, p2, p3, polygon):
|
||||
for pn in polygon:
|
||||
if pn in (p1, p2, p3):
|
||||
continue
|
||||
elif _is_point_inside(pn, p1, p2, p3):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _is_point_inside(p, a, b, c):
|
||||
area = _triangle_area(a.x, a.y, b.x, b.y, c.x, c.y)
|
||||
area1 = _triangle_area(p.x, p.y, b.x, b.y, c.x, c.y)
|
||||
area2 = _triangle_area(p.x, p.y, a.x, a.y, c.x, c.y)
|
||||
area3 = _triangle_area(p.x, p.y, a.x, a.y, b.x, b.y)
|
||||
areadiff = abs(area - sum([area1, area2, area3])) < EPSILON
|
||||
return areadiff
|
||||
|
||||
|
||||
def _triangle_area(x1, y1, x2, y2, x3, y3):
|
||||
return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0)
|
||||
|
||||
|
||||
def _triangle_sum(x1, y1, x2, y2, x3, y3):
|
||||
return x1 * (y3 - y2) + x2 * (y1 - y3) + x3 * (y2 - y1)
|
||||
|
||||
|
||||
def calculate_total_area(triangles):
|
||||
result = []
|
||||
for triangle in triangles:
|
||||
sides = []
|
||||
for i in range(3):
|
||||
next_index = (i + 1) % 3
|
||||
pt = triangle[i]
|
||||
pt2 = triangle[next_index]
|
||||
# Distance between two points
|
||||
side = math.sqrt(math.pow(pt2[0] - pt[0], 2) + math.pow(pt2[1] - pt[1], 2))
|
||||
sides.append(side)
|
||||
# Heron's numerically stable forumla for area of a triangle:
|
||||
# https://en.wikipedia.org/wiki/Heron%27s_formula
|
||||
# However, for line-like triangles of zero area this formula can produce an infinitesimally negative value
|
||||
# as an input to sqrt() due to the cumulative arithmetic errors inherent to floating point calculations:
|
||||
# https://people.eecs.berkeley.edu/~wkahan/Triangle.pdf
|
||||
# For this purpose, abs() is used as a reasonable guard against this condition.
|
||||
c, b, a = sorted(sides)
|
||||
area = .25 * math.sqrt(abs((a + (b + c)) * (c - (a - b)) * (c + (a - b)) * (a + (b - c))))
|
||||
result.append((area, a, b, c))
|
||||
triangle_area = sum(tri[0] for tri in result)
|
||||
return triangle_area
|
BIN
Пример1.png
Normal file
BIN
Пример1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
Пример2.png
Normal file
BIN
Пример2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
Loading…
Reference in New Issue
Block a user