F1 circuit layouts with year-by-year SVGs — manually traced track variations
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

969 wiersze
29KB

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. data = {
  4. "type": "FeatureCollection",
  5. "name": "hu-1986",
  6. "bbox": [
  7. 19.242326,
  8. 47.577571,
  9. 19.256609,
  10. 47.588474
  11. ],
  12. "features": [
  13. {
  14. "type": "Feature",
  15. "properties": {
  16. "id": "hu-1986",
  17. "Location": "Budapest",
  18. "Name": "Hungaroring",
  19. "opened": 1986,
  20. "firstgp": 1986,
  21. "length": 4381,
  22. "altitude": 239
  23. },
  24. "bbox": [
  25. 19.242326,
  26. 47.577571,
  27. 19.256609,
  28. 47.588474
  29. ],
  30. "geometry": {
  31. "type": "LineString",
  32. "coordinates": [
  33. [
  34. 19.245888,
  35. 47.58026
  36. ],
  37. [
  38. 19.243226,
  39. 47.581696
  40. ],
  41. [
  42. 19.242439,
  43. 47.582111
  44. ],
  45. [
  46. 19.242368,
  47. 47.582191
  48. ],
  49. [
  50. 19.242326,
  51. 47.582271
  52. ],
  53. [
  54. 19.242338,
  55. 47.582361
  56. ],
  57. [
  58. 19.242391,
  59. 47.582431
  60. ],
  61. [
  62. 19.242468,
  63. 47.582478
  64. ],
  65. [
  66. 19.242569,
  67. 47.582511
  68. ],
  69. [
  70. 19.242717,
  71. 47.582526
  72. ],
  73. [
  74. 19.242936,
  75. 47.582526
  76. ],
  77. [
  78. 19.243546,
  79. 47.582516
  80. ],
  81. [
  82. 19.243954,
  83. 47.582469
  84. ],
  85. [
  86. 19.244327,
  87. 47.582394
  88. ],
  89. [
  90. 19.244718,
  91. 47.58229
  92. ],
  93. [
  94. 19.245014,
  95. 47.582177
  96. ],
  97. [
  98. 19.245238,
  99. 47.582073
  100. ],
  101. [
  102. 19.247411,
  103. 47.580913
  104. ],
  105. [
  106. 19.24757,
  107. 47.580857
  108. ],
  109. [
  110. 19.247724,
  111. 47.580833
  112. ],
  113. [
  114. 19.247878,
  115. 47.580833
  116. ],
  117. [
  118. 19.248003,
  119. 47.580862
  120. ],
  121. [
  122. 19.248139,
  123. 47.580895
  124. ],
  125. [
  126. 19.248245,
  127. 47.580951
  128. ],
  129. [
  130. 19.248334,
  131. 47.581017
  132. ],
  133. [
  134. 19.248393,
  135. 47.581078
  136. ],
  137. [
  138. 19.248446,
  139. 47.581163
  140. ],
  141. [
  142. 19.248464,
  143. 47.581281
  144. ],
  145. [
  146. 19.248458,
  147. 47.581347
  148. ],
  149. [
  150. 19.248411,
  151. 47.581437
  152. ],
  153. [
  154. 19.24837,
  155. 47.581526
  156. ],
  157. [
  158. 19.247748,
  159. 47.582238
  160. ],
  161. [
  162. 19.247683,
  163. 47.58237
  164. ],
  165. [
  166. 19.247671,
  167. 47.582474
  168. ],
  169. [
  170. 19.247707,
  171. 47.582563
  172. ],
  173. [
  174. 19.247754,
  175. 47.582676
  176. ],
  177. [
  178. 19.247813,
  179. 47.58278
  180. ],
  181. [
  182. 19.2485,
  183. 47.583704
  184. ],
  185. [
  186. 19.249719,
  187. 47.585368
  188. ],
  189. [
  190. 19.249938,
  191. 47.585632
  192. ],
  193. [
  194. 19.250193,
  195. 47.585863
  196. ],
  197. [
  198. 19.250512,
  199. 47.586113
  200. ],
  201. [
  202. 19.250577,
  203. 47.586183
  204. ],
  205. [
  206. 19.250625,
  207. 47.586254
  208. ],
  209. [
  210. 19.250636,
  211. 47.586334
  212. ],
  213. [
  214. 19.250601,
  215. 47.586424
  216. ],
  217. [
  218. 19.250045,
  219. 47.587838
  220. ],
  221. [
  222. 19.250033,
  223. 47.587946
  224. ],
  225. [
  226. 19.25005,
  227. 47.58805
  228. ],
  229. [
  230. 19.250092,
  231. 47.588154
  232. ],
  233. [
  234. 19.250157,
  235. 47.588253
  236. ],
  237. [
  238. 19.250275,
  239. 47.588333
  240. ],
  241. [
  242. 19.250417,
  243. 47.588404
  244. ],
  245. [
  246. 19.250565,
  247. 47.588455
  248. ],
  249. [
  250. 19.250713,
  251. 47.588474
  252. ],
  253. [
  254. 19.250897,
  255. 47.58847
  256. ],
  257. [
  258. 19.251074,
  259. 47.588441
  260. ],
  261. [
  262. 19.251288,
  263. 47.58838
  264. ],
  265. [
  266. 19.25153,
  267. 47.588276
  268. ],
  269. [
  270. 19.251702,
  271. 47.588196
  272. ],
  273. [
  274. 19.251903,
  275. 47.588097
  276. ],
  277. [
  278. 19.252116,
  279. 47.587984
  280. ],
  281. [
  282. 19.252383,
  283. 47.587796
  284. ],
  285. [
  286. 19.253519,
  287. 47.586966
  288. ],
  289. [
  290. 19.253572,
  291. 47.586886
  292. ],
  293. [
  294. 19.25359,
  295. 47.586824
  296. ],
  297. [
  298. 19.253584,
  299. 47.586758
  300. ],
  301. [
  302. 19.253525,
  303. 47.586702
  304. ],
  305. [
  306. 19.25343,
  307. 47.586641
  308. ],
  309. [
  310. 19.253353,
  311. 47.586589
  312. ],
  313. [
  314. 19.253324,
  315. 47.586542
  316. ],
  317. [
  318. 19.25333,
  319. 47.586457
  320. ],
  321. [
  322. 19.253359,
  323. 47.586363
  324. ],
  325. [
  326. 19.253773,
  327. 47.585311
  328. ],
  329. [
  330. 19.253862,
  331. 47.58517
  332. ],
  333. [
  334. 19.253927,
  335. 47.585113
  336. ],
  337. [
  338. 19.253992,
  339. 47.585062
  340. ],
  341. [
  342. 19.254081,
  343. 47.585014
  344. ],
  345. [
  346. 19.254188,
  347. 47.584991
  348. ],
  349. [
  350. 19.254389,
  351. 47.584963
  352. ],
  353. [
  354. 19.255247,
  355. 47.584878
  356. ],
  357. [
  358. 19.255425,
  359. 47.584831
  360. ],
  361. [
  362. 19.255537,
  363. 47.584779
  364. ],
  365. [
  366. 19.255632,
  367. 47.584727
  368. ],
  369. [
  370. 19.255715,
  371. 47.584637
  372. ],
  373. [
  374. 19.255744,
  375. 47.584557
  376. ],
  377. [
  378. 19.25578,
  379. 47.584463
  380. ],
  381. [
  382. 19.25578,
  383. 47.584359
  384. ],
  385. [
  386. 19.25575,
  387. 47.584246
  388. ],
  389. [
  390. 19.255537,
  391. 47.583233
  392. ],
  393. [
  394. 19.255531,
  395. 47.583053
  396. ],
  397. [
  398. 19.255549,
  399. 47.58294
  400. ],
  401. [
  402. 19.255596,
  403. 47.582823
  404. ],
  405. [
  406. 19.255662,
  407. 47.582709
  408. ],
  409. [
  410. 19.256502,
  411. 47.581804
  412. ],
  413. [
  414. 19.256585,
  415. 47.581668
  416. ],
  417. [
  418. 19.256603,
  419. 47.581573
  420. ],
  421. [
  422. 19.256609,
  423. 47.581484
  424. ],
  425. [
  426. 19.256591,
  427. 47.58139
  428. ],
  429. [
  430. 19.256561,
  431. 47.581295
  432. ],
  433. [
  434. 19.256502,
  435. 47.58121
  436. ],
  437. [
  438. 19.256407,
  439. 47.581126
  440. ],
  441. [
  442. 19.254963,
  443. 47.580032
  444. ],
  445. [
  446. 19.253679,
  447. 47.579028
  448. ],
  449. [
  450. 19.253241,
  451. 47.578698
  452. ],
  453. [
  454. 19.253134,
  455. 47.578656
  456. ],
  457. [
  458. 19.253034,
  459. 47.578646
  460. ],
  461. [
  462. 19.252939,
  463. 47.578656
  464. ],
  465. [
  466. 19.252856,
  467. 47.578689
  468. ],
  469. [
  470. 19.252773,
  471. 47.57874
  472. ],
  473. [
  474. 19.251897,
  475. 47.57941
  476. ],
  477. [
  478. 19.251755,
  479. 47.579504
  480. ],
  481. [
  482. 19.251086,
  483. 47.579872
  484. ],
  485. [
  486. 19.250968,
  487. 47.579914
  488. ],
  489. [
  490. 19.25085,
  491. 47.579947
  492. ],
  493. [
  494. 19.250707,
  495. 47.579961
  496. ],
  497. [
  498. 19.250583,
  499. 47.579952
  500. ],
  501. [
  502. 19.250417,
  503. 47.5799
  504. ],
  505. [
  506. 19.250317,
  507. 47.579825
  508. ],
  509. [
  510. 19.250246,
  511. 47.579744
  512. ],
  513. [
  514. 19.250204,
  515. 47.579674
  516. ],
  517. [
  518. 19.250193,
  519. 47.579579
  520. ],
  521. [
  522. 19.25021,
  523. 47.579485
  524. ],
  525. [
  526. 19.250269,
  527. 47.579396
  528. ],
  529. [
  530. 19.250346,
  531. 47.579325
  532. ],
  533. [
  534. 19.251666,
  535. 47.578608
  536. ],
  537. [
  538. 19.251909,
  539. 47.578458
  540. ],
  541. [
  542. 19.25198,
  543. 47.578377
  544. ],
  545. [
  546. 19.252033,
  547. 47.578264
  548. ],
  549. [
  550. 19.252057,
  551. 47.578132
  552. ],
  553. [
  554. 19.252039,
  555. 47.57801
  556. ],
  557. [
  558. 19.25198,
  559. 47.577906
  560. ],
  561. [
  562. 19.251909,
  563. 47.577807
  564. ],
  565. [
  566. 19.251797,
  567. 47.577722
  568. ],
  569. [
  570. 19.251613,
  571. 47.577642
  572. ],
  573. [
  574. 19.251424,
  575. 47.57759
  576. ],
  577. [
  578. 19.251217,
  579. 47.577571
  580. ],
  581. [
  582. 19.250998,
  583. 47.57759
  584. ],
  585. [
  586. 19.250802,
  587. 47.577642
  588. ],
  589. [
  590. 19.250619,
  591. 47.577727
  592. ],
  593. [
  594. 19.245888,
  595. 47.58026
  596. ]
  597. ]
  598. }
  599. }
  600. ]
  601. }
  602. def rotate(xy, *, angle):
  603. """Rotate coordinates by the given angle."""
  604. rot_mat = np.array([[np.cos(angle), np.sin(angle)],
  605. [-np.sin(angle), np.cos(angle)]])
  606. return np.matmul(xy, rot_mat)
  607. def calculate_aspect_ratio_rotation(coordinates, preferred_ratio=1.618): # Golden ratio by default
  608. """Find rotation that gives closest match to desired aspect ratio."""
  609. best_rotation = 0
  610. best_ratio_diff = float('inf')
  611. for angle in range(0, 180, 5): # Check every 5 degrees
  612. rad_angle = np.radians(angle)
  613. rotated = rotate(coordinates, angle=rad_angle)
  614. # Calculate bounding box
  615. x_coords = [p[0] for p in rotated]
  616. y_coords = [p[1] for p in rotated]
  617. width = max(x_coords) - min(x_coords)
  618. height = max(y_coords) - min(y_coords)
  619. current_ratio = width / height
  620. ratio_diff = abs(current_ratio - preferred_ratio)
  621. if ratio_diff < best_ratio_diff:
  622. best_ratio_diff = ratio_diff
  623. best_rotation = angle
  624. return best_rotation
  625. def calculate_minimal_area_rotation(coordinates):
  626. """Find rotation that minimizes the bounding box area."""
  627. best_rotation = 0
  628. min_area = float('inf')
  629. for angle in range(0, 180, 5):
  630. rad_angle = np.radians(angle)
  631. rotated = rotate(coordinates, angle=rad_angle)
  632. x_coords = [p[0] for p in rotated]
  633. y_coords = [p[1] for p in rotated]
  634. width = max(x_coords) - min(x_coords)
  635. height = max(y_coords) - min(y_coords)
  636. area = width * height
  637. if area < min_area:
  638. min_area = area
  639. best_rotation = angle
  640. return best_rotation
  641. def calculate_pca_rotation(coordinates):
  642. """Use PCA to align the track with its principal axes."""
  643. from sklearn.decomposition import PCA
  644. # Convert coordinates to numpy array if not already
  645. coords_array = np.array(coordinates)
  646. # Fit PCA
  647. pca = PCA(n_components=2)
  648. pca.fit(coords_array)
  649. # Calculate rotation angle from first principal component
  650. first_component = pca.components_[0]
  651. angle = np.arctan2(first_component[1], first_component[0])
  652. return np.degrees(angle)
  653. def calculate_start_straight_rotation(coordinates, straight_length=10):
  654. """Align the track so the start/finish straight is vertical/horizontal."""
  655. # Assuming first points are from start/finish straight
  656. start_points = coordinates[:straight_length]
  657. # Calculate direction vector of the straight
  658. dx = start_points[-1][0] - start_points[0][0]
  659. dy = start_points[-1][1] - start_points[0][1]
  660. # Calculate angle to horizontal
  661. angle = np.degrees(np.arctan2(dy, dx))
  662. # Return rotation needed to align with horizontal (0°) or vertical (90°)
  663. horizontal_rotation = -angle
  664. vertical_rotation = 90 - angle
  665. # Return whichever requires less rotation
  666. return horizontal_rotation if abs(horizontal_rotation) < abs(vertical_rotation) else vertical_rotation
  667. def find_longest_straight(coordinates, window_size=5):
  668. """Find the longest approximately straight section of the track, including wrap-around."""
  669. max_distance = 0
  670. best_start_idx = 0
  671. n = len(coordinates)
  672. # Helper function to check straightness
  673. def is_straight(points, start, end, length):
  674. direction = (end - start) / length
  675. distances = []
  676. for point in points:
  677. projection = start + np.dot(point - start, direction) * direction
  678. distance = np.linalg.norm(point - projection)
  679. distances.append(distance)
  680. return max(distances) < length * 0.05 # 5% tolerance
  681. # Check all possible segments, including wrap-around
  682. for i in range(n):
  683. # Get window_size points, handling wrap-around
  684. segment = []
  685. for j in range(window_size):
  686. idx = (i + j) % n
  687. segment.append(np.array(coordinates[idx]))
  688. start = np.array(segment[0])
  689. end = np.array(segment[-1])
  690. length = np.linalg.norm(end - start)
  691. if length > max_distance:
  692. # Check if all points are roughly on the line
  693. if is_straight(segment, start, end, length):
  694. max_distance = length
  695. best_start_idx = i
  696. # Return indices that might wrap around
  697. end_idx = (best_start_idx + window_size) % len(coordinates)
  698. return best_start_idx, end_idx
  699. def calculate_longest_straight_rotation(coordinates):
  700. """Find the rotation that places the longest straight section horizontally at the bottom."""
  701. # First find the longest straight section
  702. start_idx, end_idx = find_longest_straight(coordinates)
  703. best_angle = 0
  704. min_y_diff = float('inf')
  705. coordinates_array = np.array(coordinates)
  706. # Create a figure for visualization
  707. plt.figure(figsize=(15, 5))
  708. # Try angles in smaller increments for more precision
  709. for angle in np.linspace(0, 2*np.pi, 72): # 5-degree increments
  710. # Rotate the entire track
  711. rotated_track = rotate(coordinates_array, angle=angle)
  712. # Get y-coordinates of the straight section
  713. straight_y1 = rotated_track[start_idx][1]
  714. straight_y2 = rotated_track[end_idx][1]
  715. y_diff = abs(straight_y1 - straight_y2)
  716. # If this is the best rotation so far, show it
  717. if y_diff < min_y_diff:
  718. min_y_diff = y_diff
  719. best_angle = angle
  720. # Clear previous plots
  721. plt.clf()
  722. # Create three subplots
  723. plt.subplot(131)
  724. plt.title('Original Track')
  725. plt.plot(coordinates_array[:, 0], coordinates_array[:, 1], 'k-')
  726. plt.plot([coordinates_array[start_idx][0], coordinates_array[end_idx][0]],
  727. [coordinates_array[start_idx][1], coordinates_array[end_idx][1]], 'r-', linewidth=2)
  728. plt.axis('equal')
  729. plt.subplot(132)
  730. plt.title(f'Current Rotation ({angle:.1f} rad)')
  731. plt.plot(rotated_track[:, 0], rotated_track[:, 1], 'k-')
  732. plt.plot([rotated_track[start_idx][0], rotated_track[end_idx][0]],
  733. [rotated_track[start_idx][1], rotated_track[end_idx][1]], 'r-', linewidth=2)
  734. plt.axis('equal')
  735. # Show y-difference
  736. plt.text(0.5, -0.1, f'Y-diff: {y_diff:.2f}',
  737. horizontalalignment='center', transform=plt.gca().transAxes)
  738. plt.subplot(133)
  739. plt.title('Y-coordinates of Straight')
  740. plt.plot([0, 1], [straight_y1, straight_y2], 'b-')
  741. plt.axhline(y=0, color='k', linestyle='--')
  742. plt.ylim(min(straight_y1, straight_y2) - 1, max(straight_y1, straight_y2) + 1)
  743. plt.tight_layout()
  744. plt.pause(0.1) # Show the plot for a moment
  745. # Now rotate all coordinates with best angle
  746. rotated_coords = rotate(coordinates_array, angle=best_angle)
  747. # Check if we need to flip 180 degrees
  748. straight_y = np.mean([rotated_coords[start_idx][1], rotated_coords[end_idx][1]])
  749. track_center_y = np.mean(rotated_coords[:, 1])
  750. if straight_y > track_center_y:
  751. best_angle += np.pi
  752. rotated_coords = rotate(coordinates_array, angle=best_angle)
  753. # Show final result
  754. plt.clf()
  755. plt.title('Final Result')
  756. plt.plot(rotated_coords[:, 0], rotated_coords[:, 1], 'k-')
  757. plt.plot([rotated_coords[start_idx][0], rotated_coords[end_idx][0]],
  758. [rotated_coords[start_idx][1], rotated_coords[end_idx][1]], 'r-', linewidth=2)
  759. plt.axis('equal')
  760. plt.show()
  761. return best_angle
  762. def evaluate_rotation_strategies(coordinates):
  763. """Compare different rotation strategies and score them."""
  764. strategies = {
  765. 'aspect_ratio': calculate_aspect_ratio_rotation,
  766. 'minimal_area': calculate_minimal_area_rotation,
  767. 'pca': calculate_pca_rotation,
  768. 'start_straight': calculate_start_straight_rotation,
  769. 'longest_straight': calculate_longest_straight_rotation,
  770. }
  771. results = {}
  772. for name, strategy in strategies.items():
  773. angle = strategy(coordinates)
  774. rotated = rotate(coordinates, angle=np.radians(angle))
  775. # Calculate metrics
  776. x_coords = [p[0] for p in rotated]
  777. y_coords = [p[1] for p in rotated]
  778. width = max(x_coords) - min(x_coords)
  779. height = max(y_coords) - min(y_coords)
  780. results[name] = {
  781. 'rotation_angle': angle,
  782. 'aspect_ratio': width / height,
  783. 'area': width * height,
  784. 'width': width,
  785. 'height': height
  786. }
  787. return results
  788. def plot_rotation_outcomes(coordinates):
  789. # Set up the figure
  790. fig = plt.figure(figsize=(15, 12))
  791. grid = plt.GridSpec(3, 2, figure=fig)
  792. axes = [
  793. fig.add_subplot(grid[0, 0]),
  794. fig.add_subplot(grid[0, 1]),
  795. fig.add_subplot(grid[1, 0]),
  796. fig.add_subplot(grid[1, 1]),
  797. fig.add_subplot(grid[2, :])
  798. ]
  799. # Get results from all strategies
  800. results = evaluate_rotation_strategies(coordinates)
  801. longest_straight_angle = calculate_longest_straight_rotation(coordinates)
  802. rotated = rotate(coordinates, angle=np.radians(longest_straight_angle))
  803. x_coords = [p[0] for p in rotated]
  804. y_coords = [p[1] for p in rotated]
  805. width = max(x_coords) - min(x_coords)
  806. height = max(y_coords) - min(y_coords)
  807. results['longest_straight'] = {
  808. 'rotation_angle': longest_straight_angle,
  809. 'aspect_ratio': width / height,
  810. 'area': width * height,
  811. 'width': width,
  812. 'height': height
  813. }
  814. # Find the longest straight section once
  815. start_idx, end_idx = find_longest_straight(coordinates)
  816. # Create the straight section handling wrap-around
  817. straight_section = []
  818. i = start_idx
  819. while i != end_idx:
  820. straight_section.append(coordinates[i])
  821. i = (i + 1) % len(coordinates)
  822. straight_section.append(coordinates[end_idx])
  823. for (name, metrics), ax in zip(results.items(), axes):
  824. # Rotate coordinates using the calculated angle
  825. rotated = rotate(coordinates, angle=np.radians(metrics['rotation_angle']))
  826. rotated_straight = rotate(straight_section, angle=np.radians(metrics['rotation_angle']))
  827. # Plot the main track in blue
  828. x_coords = [p[0] for p in rotated]
  829. y_coords = [p[1] for p in rotated]
  830. ax.plot(x_coords, y_coords, 'b-', linewidth=2, label='Track')
  831. # Plot the longest straight section in red
  832. straight_x = [p[0] for p in rotated_straight]
  833. straight_y = [p[1] for p in rotated_straight]
  834. ax.plot(straight_x, straight_y, 'r-', linewidth=3, alpha=0.8, label='Longest straight')
  835. ax.set_aspect('equal')
  836. # Add title with metrics
  837. title = f"{name}\nRotation: {metrics['rotation_angle']:.1f}°\n"
  838. title += f"Aspect Ratio: {metrics['aspect_ratio']:.2f}\n"
  839. title += f"Area: {metrics['area']:.0f}"
  840. ax.set_title(title)
  841. # Add bounding box
  842. min_x, max_x = min(x_coords), max(x_coords)
  843. min_y, max_y = min(y_coords), max(y_coords)
  844. bbox = plt.Rectangle((min_x, min_y),
  845. max_x - min_x,
  846. max_y - min_y,
  847. fill=False,
  848. color='red',
  849. linestyle='--')
  850. ax.add_patch(bbox)
  851. # Add legend
  852. ax.legend(loc='upper right')
  853. plt.tight_layout()
  854. plt.show()
  855. def get_best_rotation(coordinates, preferences):
  856. """
  857. Get best rotation based on user preferences.
  858. preferences: dict with weights for different factors:
  859. {
  860. 'aspect_ratio_weight': 0.3,
  861. 'area_weight': 0.2,
  862. 'start_straight_weight': 0.3,
  863. 'preferred_orientation': 'landscape', # or 'portrait'
  864. 'preferred_ratio': 1.618 # desired aspect ratio
  865. }
  866. """
  867. results = evaluate_rotation_strategies(coordinates)
  868. scores = {}
  869. for strategy, metrics in results.items():
  870. score = 0
  871. # Aspect ratio scoring
  872. if preferences['preferred_orientation'] == 'landscape':
  873. score += (metrics['aspect_ratio'] > 1) * preferences['aspect_ratio_weight']
  874. else:
  875. score += (metrics['aspect_ratio'] < 1) * preferences['aspect_ratio_weight']
  876. # Area efficiency scoring
  877. min_area = min(r['area'] for r in results.values())
  878. score += (min_area / metrics['area']) * preferences['area_weight']
  879. # Start straight alignment scoring
  880. if strategy == 'start_straight':
  881. score += preferences['start_straight_weight']
  882. scores[strategy] = score
  883. best_strategy = max(scores.items(), key=lambda x: x[1])[0]
  884. return results[best_strategy]['rotation_angle']
  885. coordinates = data.get("features", [])[0].get("geometry", {}).get("coordinates", [])
  886. plot_rotation_outcomes(coordinates)
  887. # preferences = {
  888. # 'aspect_ratio_weight': 0.3,
  889. # 'area_weight': 0.2,
  890. # 'start_straight_weight': 0.3,
  891. # 'preferred_orientation': 'landscape',
  892. # 'preferred_ratio': 1.618
  893. # }
  894. # optimal_rotation = get_best_rotation(coordinates, preferences)