969 lines
29 KiB
Python
969 lines
29 KiB
Python
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
data = {
|
|
"type": "FeatureCollection",
|
|
"name": "hu-1986",
|
|
"bbox": [
|
|
19.242326,
|
|
47.577571,
|
|
19.256609,
|
|
47.588474
|
|
],
|
|
"features": [
|
|
{
|
|
"type": "Feature",
|
|
"properties": {
|
|
"id": "hu-1986",
|
|
"Location": "Budapest",
|
|
"Name": "Hungaroring",
|
|
"opened": 1986,
|
|
"firstgp": 1986,
|
|
"length": 4381,
|
|
"altitude": 239
|
|
},
|
|
"bbox": [
|
|
19.242326,
|
|
47.577571,
|
|
19.256609,
|
|
47.588474
|
|
],
|
|
"geometry": {
|
|
"type": "LineString",
|
|
"coordinates": [
|
|
[
|
|
19.245888,
|
|
47.58026
|
|
],
|
|
[
|
|
19.243226,
|
|
47.581696
|
|
],
|
|
[
|
|
19.242439,
|
|
47.582111
|
|
],
|
|
[
|
|
19.242368,
|
|
47.582191
|
|
],
|
|
[
|
|
19.242326,
|
|
47.582271
|
|
],
|
|
[
|
|
19.242338,
|
|
47.582361
|
|
],
|
|
[
|
|
19.242391,
|
|
47.582431
|
|
],
|
|
[
|
|
19.242468,
|
|
47.582478
|
|
],
|
|
[
|
|
19.242569,
|
|
47.582511
|
|
],
|
|
[
|
|
19.242717,
|
|
47.582526
|
|
],
|
|
[
|
|
19.242936,
|
|
47.582526
|
|
],
|
|
[
|
|
19.243546,
|
|
47.582516
|
|
],
|
|
[
|
|
19.243954,
|
|
47.582469
|
|
],
|
|
[
|
|
19.244327,
|
|
47.582394
|
|
],
|
|
[
|
|
19.244718,
|
|
47.58229
|
|
],
|
|
[
|
|
19.245014,
|
|
47.582177
|
|
],
|
|
[
|
|
19.245238,
|
|
47.582073
|
|
],
|
|
[
|
|
19.247411,
|
|
47.580913
|
|
],
|
|
[
|
|
19.24757,
|
|
47.580857
|
|
],
|
|
[
|
|
19.247724,
|
|
47.580833
|
|
],
|
|
[
|
|
19.247878,
|
|
47.580833
|
|
],
|
|
[
|
|
19.248003,
|
|
47.580862
|
|
],
|
|
[
|
|
19.248139,
|
|
47.580895
|
|
],
|
|
[
|
|
19.248245,
|
|
47.580951
|
|
],
|
|
[
|
|
19.248334,
|
|
47.581017
|
|
],
|
|
[
|
|
19.248393,
|
|
47.581078
|
|
],
|
|
[
|
|
19.248446,
|
|
47.581163
|
|
],
|
|
[
|
|
19.248464,
|
|
47.581281
|
|
],
|
|
[
|
|
19.248458,
|
|
47.581347
|
|
],
|
|
[
|
|
19.248411,
|
|
47.581437
|
|
],
|
|
[
|
|
19.24837,
|
|
47.581526
|
|
],
|
|
[
|
|
19.247748,
|
|
47.582238
|
|
],
|
|
[
|
|
19.247683,
|
|
47.58237
|
|
],
|
|
[
|
|
19.247671,
|
|
47.582474
|
|
],
|
|
[
|
|
19.247707,
|
|
47.582563
|
|
],
|
|
[
|
|
19.247754,
|
|
47.582676
|
|
],
|
|
[
|
|
19.247813,
|
|
47.58278
|
|
],
|
|
[
|
|
19.2485,
|
|
47.583704
|
|
],
|
|
[
|
|
19.249719,
|
|
47.585368
|
|
],
|
|
[
|
|
19.249938,
|
|
47.585632
|
|
],
|
|
[
|
|
19.250193,
|
|
47.585863
|
|
],
|
|
[
|
|
19.250512,
|
|
47.586113
|
|
],
|
|
[
|
|
19.250577,
|
|
47.586183
|
|
],
|
|
[
|
|
19.250625,
|
|
47.586254
|
|
],
|
|
[
|
|
19.250636,
|
|
47.586334
|
|
],
|
|
[
|
|
19.250601,
|
|
47.586424
|
|
],
|
|
[
|
|
19.250045,
|
|
47.587838
|
|
],
|
|
[
|
|
19.250033,
|
|
47.587946
|
|
],
|
|
[
|
|
19.25005,
|
|
47.58805
|
|
],
|
|
[
|
|
19.250092,
|
|
47.588154
|
|
],
|
|
[
|
|
19.250157,
|
|
47.588253
|
|
],
|
|
[
|
|
19.250275,
|
|
47.588333
|
|
],
|
|
[
|
|
19.250417,
|
|
47.588404
|
|
],
|
|
[
|
|
19.250565,
|
|
47.588455
|
|
],
|
|
[
|
|
19.250713,
|
|
47.588474
|
|
],
|
|
[
|
|
19.250897,
|
|
47.58847
|
|
],
|
|
[
|
|
19.251074,
|
|
47.588441
|
|
],
|
|
[
|
|
19.251288,
|
|
47.58838
|
|
],
|
|
[
|
|
19.25153,
|
|
47.588276
|
|
],
|
|
[
|
|
19.251702,
|
|
47.588196
|
|
],
|
|
[
|
|
19.251903,
|
|
47.588097
|
|
],
|
|
[
|
|
19.252116,
|
|
47.587984
|
|
],
|
|
[
|
|
19.252383,
|
|
47.587796
|
|
],
|
|
[
|
|
19.253519,
|
|
47.586966
|
|
],
|
|
[
|
|
19.253572,
|
|
47.586886
|
|
],
|
|
[
|
|
19.25359,
|
|
47.586824
|
|
],
|
|
[
|
|
19.253584,
|
|
47.586758
|
|
],
|
|
[
|
|
19.253525,
|
|
47.586702
|
|
],
|
|
[
|
|
19.25343,
|
|
47.586641
|
|
],
|
|
[
|
|
19.253353,
|
|
47.586589
|
|
],
|
|
[
|
|
19.253324,
|
|
47.586542
|
|
],
|
|
[
|
|
19.25333,
|
|
47.586457
|
|
],
|
|
[
|
|
19.253359,
|
|
47.586363
|
|
],
|
|
[
|
|
19.253773,
|
|
47.585311
|
|
],
|
|
[
|
|
19.253862,
|
|
47.58517
|
|
],
|
|
[
|
|
19.253927,
|
|
47.585113
|
|
],
|
|
[
|
|
19.253992,
|
|
47.585062
|
|
],
|
|
[
|
|
19.254081,
|
|
47.585014
|
|
],
|
|
[
|
|
19.254188,
|
|
47.584991
|
|
],
|
|
[
|
|
19.254389,
|
|
47.584963
|
|
],
|
|
[
|
|
19.255247,
|
|
47.584878
|
|
],
|
|
[
|
|
19.255425,
|
|
47.584831
|
|
],
|
|
[
|
|
19.255537,
|
|
47.584779
|
|
],
|
|
[
|
|
19.255632,
|
|
47.584727
|
|
],
|
|
[
|
|
19.255715,
|
|
47.584637
|
|
],
|
|
[
|
|
19.255744,
|
|
47.584557
|
|
],
|
|
[
|
|
19.25578,
|
|
47.584463
|
|
],
|
|
[
|
|
19.25578,
|
|
47.584359
|
|
],
|
|
[
|
|
19.25575,
|
|
47.584246
|
|
],
|
|
[
|
|
19.255537,
|
|
47.583233
|
|
],
|
|
[
|
|
19.255531,
|
|
47.583053
|
|
],
|
|
[
|
|
19.255549,
|
|
47.58294
|
|
],
|
|
[
|
|
19.255596,
|
|
47.582823
|
|
],
|
|
[
|
|
19.255662,
|
|
47.582709
|
|
],
|
|
[
|
|
19.256502,
|
|
47.581804
|
|
],
|
|
[
|
|
19.256585,
|
|
47.581668
|
|
],
|
|
[
|
|
19.256603,
|
|
47.581573
|
|
],
|
|
[
|
|
19.256609,
|
|
47.581484
|
|
],
|
|
[
|
|
19.256591,
|
|
47.58139
|
|
],
|
|
[
|
|
19.256561,
|
|
47.581295
|
|
],
|
|
[
|
|
19.256502,
|
|
47.58121
|
|
],
|
|
[
|
|
19.256407,
|
|
47.581126
|
|
],
|
|
[
|
|
19.254963,
|
|
47.580032
|
|
],
|
|
[
|
|
19.253679,
|
|
47.579028
|
|
],
|
|
[
|
|
19.253241,
|
|
47.578698
|
|
],
|
|
[
|
|
19.253134,
|
|
47.578656
|
|
],
|
|
[
|
|
19.253034,
|
|
47.578646
|
|
],
|
|
[
|
|
19.252939,
|
|
47.578656
|
|
],
|
|
[
|
|
19.252856,
|
|
47.578689
|
|
],
|
|
[
|
|
19.252773,
|
|
47.57874
|
|
],
|
|
[
|
|
19.251897,
|
|
47.57941
|
|
],
|
|
[
|
|
19.251755,
|
|
47.579504
|
|
],
|
|
[
|
|
19.251086,
|
|
47.579872
|
|
],
|
|
[
|
|
19.250968,
|
|
47.579914
|
|
],
|
|
[
|
|
19.25085,
|
|
47.579947
|
|
],
|
|
[
|
|
19.250707,
|
|
47.579961
|
|
],
|
|
[
|
|
19.250583,
|
|
47.579952
|
|
],
|
|
[
|
|
19.250417,
|
|
47.5799
|
|
],
|
|
[
|
|
19.250317,
|
|
47.579825
|
|
],
|
|
[
|
|
19.250246,
|
|
47.579744
|
|
],
|
|
[
|
|
19.250204,
|
|
47.579674
|
|
],
|
|
[
|
|
19.250193,
|
|
47.579579
|
|
],
|
|
[
|
|
19.25021,
|
|
47.579485
|
|
],
|
|
[
|
|
19.250269,
|
|
47.579396
|
|
],
|
|
[
|
|
19.250346,
|
|
47.579325
|
|
],
|
|
[
|
|
19.251666,
|
|
47.578608
|
|
],
|
|
[
|
|
19.251909,
|
|
47.578458
|
|
],
|
|
[
|
|
19.25198,
|
|
47.578377
|
|
],
|
|
[
|
|
19.252033,
|
|
47.578264
|
|
],
|
|
[
|
|
19.252057,
|
|
47.578132
|
|
],
|
|
[
|
|
19.252039,
|
|
47.57801
|
|
],
|
|
[
|
|
19.25198,
|
|
47.577906
|
|
],
|
|
[
|
|
19.251909,
|
|
47.577807
|
|
],
|
|
[
|
|
19.251797,
|
|
47.577722
|
|
],
|
|
[
|
|
19.251613,
|
|
47.577642
|
|
],
|
|
[
|
|
19.251424,
|
|
47.57759
|
|
],
|
|
[
|
|
19.251217,
|
|
47.577571
|
|
],
|
|
[
|
|
19.250998,
|
|
47.57759
|
|
],
|
|
[
|
|
19.250802,
|
|
47.577642
|
|
],
|
|
[
|
|
19.250619,
|
|
47.577727
|
|
],
|
|
[
|
|
19.245888,
|
|
47.58026
|
|
]
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
def rotate(xy, *, angle):
|
|
"""Rotate coordinates by the given angle."""
|
|
rot_mat = np.array([[np.cos(angle), np.sin(angle)],
|
|
[-np.sin(angle), np.cos(angle)]])
|
|
return np.matmul(xy, rot_mat)
|
|
|
|
def calculate_aspect_ratio_rotation(coordinates, preferred_ratio=1.618): # Golden ratio by default
|
|
"""Find rotation that gives closest match to desired aspect ratio."""
|
|
best_rotation = 0
|
|
best_ratio_diff = float('inf')
|
|
|
|
for angle in range(0, 180, 5): # Check every 5 degrees
|
|
rad_angle = np.radians(angle)
|
|
rotated = rotate(coordinates, angle=rad_angle)
|
|
|
|
# Calculate bounding box
|
|
x_coords = [p[0] for p in rotated]
|
|
y_coords = [p[1] for p in rotated]
|
|
width = max(x_coords) - min(x_coords)
|
|
height = max(y_coords) - min(y_coords)
|
|
|
|
current_ratio = width / height
|
|
ratio_diff = abs(current_ratio - preferred_ratio)
|
|
|
|
if ratio_diff < best_ratio_diff:
|
|
best_ratio_diff = ratio_diff
|
|
best_rotation = angle
|
|
|
|
return best_rotation
|
|
|
|
def calculate_minimal_area_rotation(coordinates):
|
|
"""Find rotation that minimizes the bounding box area."""
|
|
best_rotation = 0
|
|
min_area = float('inf')
|
|
|
|
for angle in range(0, 180, 5):
|
|
rad_angle = np.radians(angle)
|
|
rotated = rotate(coordinates, angle=rad_angle)
|
|
|
|
x_coords = [p[0] for p in rotated]
|
|
y_coords = [p[1] for p in rotated]
|
|
width = max(x_coords) - min(x_coords)
|
|
height = max(y_coords) - min(y_coords)
|
|
|
|
area = width * height
|
|
if area < min_area:
|
|
min_area = area
|
|
best_rotation = angle
|
|
|
|
return best_rotation
|
|
|
|
def calculate_pca_rotation(coordinates):
|
|
"""Use PCA to align the track with its principal axes."""
|
|
from sklearn.decomposition import PCA
|
|
|
|
# Convert coordinates to numpy array if not already
|
|
coords_array = np.array(coordinates)
|
|
|
|
# Fit PCA
|
|
pca = PCA(n_components=2)
|
|
pca.fit(coords_array)
|
|
|
|
# Calculate rotation angle from first principal component
|
|
first_component = pca.components_[0]
|
|
angle = np.arctan2(first_component[1], first_component[0])
|
|
return np.degrees(angle)
|
|
|
|
def calculate_start_straight_rotation(coordinates, straight_length=10):
|
|
"""Align the track so the start/finish straight is vertical/horizontal."""
|
|
# Assuming first points are from start/finish straight
|
|
start_points = coordinates[:straight_length]
|
|
|
|
# Calculate direction vector of the straight
|
|
dx = start_points[-1][0] - start_points[0][0]
|
|
dy = start_points[-1][1] - start_points[0][1]
|
|
|
|
# Calculate angle to horizontal
|
|
angle = np.degrees(np.arctan2(dy, dx))
|
|
|
|
# Return rotation needed to align with horizontal (0°) or vertical (90°)
|
|
horizontal_rotation = -angle
|
|
vertical_rotation = 90 - angle
|
|
|
|
# Return whichever requires less rotation
|
|
return horizontal_rotation if abs(horizontal_rotation) < abs(vertical_rotation) else vertical_rotation
|
|
|
|
def find_longest_straight(coordinates, window_size=5):
|
|
"""Find the longest approximately straight section of the track, including wrap-around."""
|
|
max_distance = 0
|
|
best_start_idx = 0
|
|
n = len(coordinates)
|
|
|
|
# Helper function to check straightness
|
|
def is_straight(points, start, end, length):
|
|
direction = (end - start) / length
|
|
distances = []
|
|
for point in points:
|
|
projection = start + np.dot(point - start, direction) * direction
|
|
distance = np.linalg.norm(point - projection)
|
|
distances.append(distance)
|
|
return max(distances) < length * 0.05 # 5% tolerance
|
|
|
|
# Check all possible segments, including wrap-around
|
|
for i in range(n):
|
|
# Get window_size points, handling wrap-around
|
|
segment = []
|
|
for j in range(window_size):
|
|
idx = (i + j) % n
|
|
segment.append(np.array(coordinates[idx]))
|
|
|
|
start = np.array(segment[0])
|
|
end = np.array(segment[-1])
|
|
length = np.linalg.norm(end - start)
|
|
|
|
if length > max_distance:
|
|
# Check if all points are roughly on the line
|
|
if is_straight(segment, start, end, length):
|
|
max_distance = length
|
|
best_start_idx = i
|
|
|
|
# Return indices that might wrap around
|
|
end_idx = (best_start_idx + window_size) % len(coordinates)
|
|
return best_start_idx, end_idx
|
|
|
|
def calculate_longest_straight_rotation(coordinates):
|
|
"""Find the rotation that places the longest straight section horizontally at the bottom."""
|
|
# First find the longest straight section
|
|
start_idx, end_idx = find_longest_straight(coordinates)
|
|
|
|
best_angle = 0
|
|
min_y_diff = float('inf')
|
|
coordinates_array = np.array(coordinates)
|
|
|
|
# Create a figure for visualization
|
|
plt.figure(figsize=(15, 5))
|
|
|
|
# Try angles in smaller increments for more precision
|
|
for angle in np.linspace(0, 2*np.pi, 72): # 5-degree increments
|
|
# Rotate the entire track
|
|
rotated_track = rotate(coordinates_array, angle=angle)
|
|
|
|
# Get y-coordinates of the straight section
|
|
straight_y1 = rotated_track[start_idx][1]
|
|
straight_y2 = rotated_track[end_idx][1]
|
|
y_diff = abs(straight_y1 - straight_y2)
|
|
|
|
# If this is the best rotation so far, show it
|
|
if y_diff < min_y_diff:
|
|
min_y_diff = y_diff
|
|
best_angle = angle
|
|
|
|
# Clear previous plots
|
|
plt.clf()
|
|
|
|
# Create three subplots
|
|
plt.subplot(131)
|
|
plt.title('Original Track')
|
|
plt.plot(coordinates_array[:, 0], coordinates_array[:, 1], 'k-')
|
|
plt.plot([coordinates_array[start_idx][0], coordinates_array[end_idx][0]],
|
|
[coordinates_array[start_idx][1], coordinates_array[end_idx][1]], 'r-', linewidth=2)
|
|
plt.axis('equal')
|
|
|
|
plt.subplot(132)
|
|
plt.title(f'Current Rotation ({angle:.1f} rad)')
|
|
plt.plot(rotated_track[:, 0], rotated_track[:, 1], 'k-')
|
|
plt.plot([rotated_track[start_idx][0], rotated_track[end_idx][0]],
|
|
[rotated_track[start_idx][1], rotated_track[end_idx][1]], 'r-', linewidth=2)
|
|
plt.axis('equal')
|
|
|
|
# Show y-difference
|
|
plt.text(0.5, -0.1, f'Y-diff: {y_diff:.2f}',
|
|
horizontalalignment='center', transform=plt.gca().transAxes)
|
|
|
|
plt.subplot(133)
|
|
plt.title('Y-coordinates of Straight')
|
|
plt.plot([0, 1], [straight_y1, straight_y2], 'b-')
|
|
plt.axhline(y=0, color='k', linestyle='--')
|
|
plt.ylim(min(straight_y1, straight_y2) - 1, max(straight_y1, straight_y2) + 1)
|
|
|
|
plt.tight_layout()
|
|
plt.pause(0.1) # Show the plot for a moment
|
|
|
|
# Now rotate all coordinates with best angle
|
|
rotated_coords = rotate(coordinates_array, angle=best_angle)
|
|
|
|
# Check if we need to flip 180 degrees
|
|
straight_y = np.mean([rotated_coords[start_idx][1], rotated_coords[end_idx][1]])
|
|
track_center_y = np.mean(rotated_coords[:, 1])
|
|
|
|
if straight_y > track_center_y:
|
|
best_angle += np.pi
|
|
rotated_coords = rotate(coordinates_array, angle=best_angle)
|
|
|
|
# Show final result
|
|
plt.clf()
|
|
plt.title('Final Result')
|
|
plt.plot(rotated_coords[:, 0], rotated_coords[:, 1], 'k-')
|
|
plt.plot([rotated_coords[start_idx][0], rotated_coords[end_idx][0]],
|
|
[rotated_coords[start_idx][1], rotated_coords[end_idx][1]], 'r-', linewidth=2)
|
|
plt.axis('equal')
|
|
plt.show()
|
|
|
|
return best_angle
|
|
|
|
def evaluate_rotation_strategies(coordinates):
|
|
"""Compare different rotation strategies and score them."""
|
|
strategies = {
|
|
'aspect_ratio': calculate_aspect_ratio_rotation,
|
|
'minimal_area': calculate_minimal_area_rotation,
|
|
'pca': calculate_pca_rotation,
|
|
'start_straight': calculate_start_straight_rotation,
|
|
'longest_straight': calculate_longest_straight_rotation,
|
|
}
|
|
|
|
results = {}
|
|
|
|
for name, strategy in strategies.items():
|
|
angle = strategy(coordinates)
|
|
rotated = rotate(coordinates, angle=np.radians(angle))
|
|
|
|
# Calculate metrics
|
|
x_coords = [p[0] for p in rotated]
|
|
y_coords = [p[1] for p in rotated]
|
|
width = max(x_coords) - min(x_coords)
|
|
height = max(y_coords) - min(y_coords)
|
|
|
|
results[name] = {
|
|
'rotation_angle': angle,
|
|
'aspect_ratio': width / height,
|
|
'area': width * height,
|
|
'width': width,
|
|
'height': height
|
|
}
|
|
|
|
return results
|
|
|
|
def plot_rotation_outcomes(coordinates):
|
|
# Set up the figure
|
|
fig = plt.figure(figsize=(15, 12))
|
|
grid = plt.GridSpec(3, 2, figure=fig)
|
|
|
|
axes = [
|
|
fig.add_subplot(grid[0, 0]),
|
|
fig.add_subplot(grid[0, 1]),
|
|
fig.add_subplot(grid[1, 0]),
|
|
fig.add_subplot(grid[1, 1]),
|
|
fig.add_subplot(grid[2, :])
|
|
]
|
|
|
|
# Get results from all strategies
|
|
results = evaluate_rotation_strategies(coordinates)
|
|
longest_straight_angle = calculate_longest_straight_rotation(coordinates)
|
|
rotated = rotate(coordinates, angle=np.radians(longest_straight_angle))
|
|
x_coords = [p[0] for p in rotated]
|
|
y_coords = [p[1] for p in rotated]
|
|
width = max(x_coords) - min(x_coords)
|
|
height = max(y_coords) - min(y_coords)
|
|
|
|
results['longest_straight'] = {
|
|
'rotation_angle': longest_straight_angle,
|
|
'aspect_ratio': width / height,
|
|
'area': width * height,
|
|
'width': width,
|
|
'height': height
|
|
}
|
|
|
|
# Find the longest straight section once
|
|
start_idx, end_idx = find_longest_straight(coordinates)
|
|
|
|
# Create the straight section handling wrap-around
|
|
straight_section = []
|
|
i = start_idx
|
|
while i != end_idx:
|
|
straight_section.append(coordinates[i])
|
|
i = (i + 1) % len(coordinates)
|
|
straight_section.append(coordinates[end_idx])
|
|
|
|
for (name, metrics), ax in zip(results.items(), axes):
|
|
# Rotate coordinates using the calculated angle
|
|
rotated = rotate(coordinates, angle=np.radians(metrics['rotation_angle']))
|
|
rotated_straight = rotate(straight_section, angle=np.radians(metrics['rotation_angle']))
|
|
|
|
# Plot the main track in blue
|
|
x_coords = [p[0] for p in rotated]
|
|
y_coords = [p[1] for p in rotated]
|
|
ax.plot(x_coords, y_coords, 'b-', linewidth=2, label='Track')
|
|
|
|
# Plot the longest straight section in red
|
|
straight_x = [p[0] for p in rotated_straight]
|
|
straight_y = [p[1] for p in rotated_straight]
|
|
ax.plot(straight_x, straight_y, 'r-', linewidth=3, alpha=0.8, label='Longest straight')
|
|
|
|
ax.set_aspect('equal')
|
|
|
|
# Add title with metrics
|
|
title = f"{name}\nRotation: {metrics['rotation_angle']:.1f}°\n"
|
|
title += f"Aspect Ratio: {metrics['aspect_ratio']:.2f}\n"
|
|
title += f"Area: {metrics['area']:.0f}"
|
|
ax.set_title(title)
|
|
|
|
# Add bounding box
|
|
min_x, max_x = min(x_coords), max(x_coords)
|
|
min_y, max_y = min(y_coords), max(y_coords)
|
|
bbox = plt.Rectangle((min_x, min_y),
|
|
max_x - min_x,
|
|
max_y - min_y,
|
|
fill=False,
|
|
color='red',
|
|
linestyle='--')
|
|
ax.add_patch(bbox)
|
|
|
|
# Add legend
|
|
ax.legend(loc='upper right')
|
|
|
|
plt.tight_layout()
|
|
plt.show()
|
|
|
|
def get_best_rotation(coordinates, preferences):
|
|
"""
|
|
Get best rotation based on user preferences.
|
|
|
|
preferences: dict with weights for different factors:
|
|
{
|
|
'aspect_ratio_weight': 0.3,
|
|
'area_weight': 0.2,
|
|
'start_straight_weight': 0.3,
|
|
'preferred_orientation': 'landscape', # or 'portrait'
|
|
'preferred_ratio': 1.618 # desired aspect ratio
|
|
}
|
|
"""
|
|
results = evaluate_rotation_strategies(coordinates)
|
|
scores = {}
|
|
|
|
for strategy, metrics in results.items():
|
|
score = 0
|
|
|
|
# Aspect ratio scoring
|
|
if preferences['preferred_orientation'] == 'landscape':
|
|
score += (metrics['aspect_ratio'] > 1) * preferences['aspect_ratio_weight']
|
|
else:
|
|
score += (metrics['aspect_ratio'] < 1) * preferences['aspect_ratio_weight']
|
|
|
|
# Area efficiency scoring
|
|
min_area = min(r['area'] for r in results.values())
|
|
score += (min_area / metrics['area']) * preferences['area_weight']
|
|
|
|
# Start straight alignment scoring
|
|
if strategy == 'start_straight':
|
|
score += preferences['start_straight_weight']
|
|
|
|
scores[strategy] = score
|
|
|
|
best_strategy = max(scores.items(), key=lambda x: x[1])[0]
|
|
return results[best_strategy]['rotation_angle']
|
|
|
|
coordinates = data.get("features", [])[0].get("geometry", {}).get("coordinates", [])
|
|
plot_rotation_outcomes(coordinates)
|
|
|
|
# preferences = {
|
|
# 'aspect_ratio_weight': 0.3,
|
|
# 'area_weight': 0.2,
|
|
# 'start_straight_weight': 0.3,
|
|
# 'preferred_orientation': 'landscape',
|
|
# 'preferred_ratio': 1.618
|
|
# }
|
|
# optimal_rotation = get_best_rotation(coordinates, preferences)
|