#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# File: tower_location.py
# Author: Maciej Kaniewski
# Copyright (c) Maciej Kaniewski 2024
# MIT License
from scipy.optimize import minimize
import numpy as np
import matplotlib.pyplot as plt
# Define the towns' coordinates
towns = np.array(
[
[17, 14], # Cincinnati
[10, 10], # Florence
[12, 16], # Covington
[12, 22], # Evendale
[13, 17], # Fairfax
[19, 19], # Milford
]
)
# Town names for labeling
town_names = ["Cincinnati", "Florence", "Covington", "Evendale", "Fairfax", "Milford"]
# Define the objective function: maximum Euclidean distance to the towns
def objective(x):
distances = np.sqrt(np.sum((towns - x) ** 2, axis=1))
return np.max(distances)
# Initial guess for the tower location (average of the given points)
x0 = np.mean(towns, axis=0)
# Perform the minimization (Sequential Least Squares Programming)
result = minimize(objective, x0, method="SLSQP")
tower_location = result.x
print(f"The optimal tower location is at {tower_location}")
# Function to draw a line between two points with a label for the distance
def draw_line_with_label(ax, point1, point2, label, color="gray"):
ax.plot([point1[0], point2[0]], [point1[1], point2[1]], color=color, linestyle="--")
midpoint = (point1 + point2) / 2
ax.text(midpoint[0], midpoint[1], label, color=color, fontsize=9, ha="center")
# Create a new plot
fig, ax = plt.subplots()
# Plot the towns
ax.scatter(towns[:, 0], towns[:, 1], color="blue", label="Towns")
for i, txt in enumerate(town_names):
ax.annotate(txt, (towns[i, 0], towns[i, 1]))
# Plot the optimal tower location
ax.scatter(
tower_location[0], tower_location[1], color="red", label="Optimal Tower Location"
)
ax.text(tower_location[0], tower_location[1], " Tower", horizontalalignment="left")
# Draw lines from the optimal tower location to each town with distances
for town, name in zip(towns, town_names):
distance = np.sqrt(np.sum((town - tower_location) ** 2))
label = f"{distance:.2f} mi"
draw_line_with_label(ax, tower_location, town, label)
# Additional plot settings
ax.set_title("Optimal Tower Location, Towns, and Distances")
ax.set_xlabel("West-East Distance (miles)")
ax.set_ylabel("South-North Distance (miles)")
ax.legend()
ax.grid(True)
plt.show()