318 lines
9.6 KiB
Python
318 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
IZV cast1 projektu 2025
|
|
|
|
Autor: Roman Nečas
|
|
Login: xnecasr00
|
|
|
|
Detailni zadani projektu je v samostatnem projektu e-learningu.
|
|
Nezapomente na to, ze python soubory maji dane formatovani.
|
|
|
|
Muzete pouzit libovolnou vestavenou knihovnu a knihovny predstavene
|
|
na prednasce
|
|
"""
|
|
|
|
from typing import Any, Dict, List
|
|
|
|
import numpy as np
|
|
import requests
|
|
from bs4 import BeautifulSoup
|
|
from matplotlib.collections import LineCollection
|
|
from numpy.typing import NDArray
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
def wave_inference_bad(
|
|
x: NDArray[Any], y: NDArray[Any], sources: NDArray[Any], wavelength: float
|
|
) -> NDArray[Any]:
|
|
"""
|
|
Referencni implementace, ktera je pomala a nevyuziva numpy efektivne;
|
|
nezasahujte do ni!
|
|
"""
|
|
k = 2 * np.pi / wavelength
|
|
|
|
Z = np.zeros(x.shape + y.shape)
|
|
for sx, sy in sources:
|
|
for i in range(x.shape[0]):
|
|
for j in range(y.shape[0]):
|
|
R = np.sqrt((x[i] - sx) ** 2 + (y[j] - sy) ** 2)
|
|
Z[j, i] += np.cos(k * R) / (1 + R)
|
|
return Z
|
|
|
|
|
|
def wave_inference(
|
|
x: NDArray[Any], y: NDArray[Any], sources: NDArray[Any], wavelength: float
|
|
) -> NDArray[Any]:
|
|
"""
|
|
Efektivny vypocet interferencie vln pomocou NumPy vektorizacie.
|
|
|
|
Vypocita amplitudu vlny Z pre 2D suradnicove polia x a y z viacerych
|
|
zdrojov vln. Pouziva optimalizovany pristup s predalokovanym polom
|
|
a iteraciou cez zdroje, co je rychlejsie pre maly pocet zdrojov.
|
|
|
|
Args:
|
|
x: 1D pole x suradnic
|
|
y: 1D pole y suradnic
|
|
sources: 2D pole tvaru (n, 2) s poziciami zdrojov [sx, sy]
|
|
wavelength: vlnova dlzka vlny
|
|
|
|
Returns:
|
|
2D pole tvaru (len(y), len(x)) obsahujuce amplitudy vln
|
|
"""
|
|
k = 2 * np.pi / wavelength
|
|
|
|
# Vytvorenie meshgrid pre x a y suradnice
|
|
X, Y = np.meshgrid(x, y)
|
|
|
|
# Predalokacia vysledneho pola (rychlejsie a menej pamate)
|
|
Z = np.zeros_like(X)
|
|
|
|
# Iteracia cez zdroje - pre maly pocet zdrojov rychlejsie
|
|
# nez vytvarat 3D tensor (nizsie rezijne naklady)
|
|
for sx, sy in sources:
|
|
# Vypocet vzdialenosti od zdroja
|
|
# np.hypot je rychlejsi a presnejsi nez sqrt(dx**2 + dy**2)
|
|
R = np.hypot(X - sx, Y - sy)
|
|
# Pripocitanie prispevku amplitudy z tohto zdroja
|
|
Z += np.cos(k * R) / (1 + R)
|
|
|
|
return Z
|
|
|
|
|
|
def plot_wave(
|
|
Z: NDArray[Any],
|
|
x: NDArray[Any],
|
|
y: NDArray[Any],
|
|
show_figure: bool = False,
|
|
save_path: str | None = None,
|
|
):
|
|
"""
|
|
Vizualizacia interferencie vln ako heatmapa.
|
|
|
|
Vytvori farebnu mapu amplitud vln so spravnymi popismi osi,
|
|
nadpisom a color barom.
|
|
|
|
Args:
|
|
Z: 2D pole amplitud vln
|
|
x: 1D pole x suradnic
|
|
y: 1D pole y suradnic
|
|
show_figure: ak True, zobrazi graf pomocou plt.show()
|
|
save_path: ak je zadane, ulozi graf do tejto cesty
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
plt.figure(figsize=(8, 6))
|
|
|
|
# Vytvorenie heatmapy s rozsahom podla suradnic
|
|
extent = [x.min(), x.max(), y.min(), y.max()]
|
|
im = plt.imshow(
|
|
Z,
|
|
extent=extent,
|
|
origin='lower',
|
|
cmap='viridis',
|
|
aspect='auto'
|
|
)
|
|
|
|
# Pridanie colorbaru s popisom
|
|
cbar = plt.colorbar(im)
|
|
cbar.set_label('Amplituda vlny')
|
|
|
|
# Nastavenie popisov a nadpisu
|
|
plt.xlabel('X pozice')
|
|
plt.ylabel('Y pozice')
|
|
plt.title('Vlnové pole')
|
|
|
|
# Ulozenie ak je zadana cesta
|
|
if save_path is not None:
|
|
plt.savefig(save_path)
|
|
|
|
# Zobrazenie ak je pozadovane
|
|
if show_figure:
|
|
plt.show()
|
|
|
|
plt.close()
|
|
|
|
|
|
def generate_sinus(show_figure: bool = False, save_path: str | None = None):
|
|
"""
|
|
Generovanie vizualizacie sinusovych a kosinusovych funkcii.
|
|
|
|
Vytvori graf s dvoma podgrafmi:
|
|
- Horny: sin(x) a cos(x) s vyplnenou plochou medzi nimi
|
|
- Dolny: minimum (ciarkovane) a maximum (farebne podla zdroja)
|
|
oboch funkcii
|
|
|
|
Args:
|
|
show_figure: ak True, zobrazi graf pomocou plt.show()
|
|
save_path: ak je zadane, ulozi graf do tejto cesty
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
# Generovanie x hodnot od 0 do 4pi
|
|
x = np.linspace(0, 4 * np.pi, 1000)
|
|
sin_vals = np.sin(x)
|
|
cos_vals = np.cos(x)
|
|
|
|
# Vytvorenie grafu s 2 podgrafmi so zdielanymi osami x a y
|
|
_, (ax1, ax2) = plt.subplots(
|
|
2, 1, figsize=(10, 8), sharex=True, sharey=True
|
|
)
|
|
|
|
# Prvy podgraf: sin a cos s vyplnenou plochou medzi nimi
|
|
ax1.plot(x, sin_vals, label='sin(x)', color='blue')
|
|
ax1.plot(x, cos_vals, label='cos(x)', color='orange')
|
|
ax1.fill_between(x, sin_vals, cos_vals, alpha=0.3, color='green')
|
|
ax1.set_ylabel('f(x)')
|
|
ax1.grid(True, alpha=0.3)
|
|
ax1.set_ylim(-1.5, 1.5)
|
|
|
|
# Druhy podgraf: minimum (ciarkovane) a maximum (farebne)
|
|
min_vals = np.minimum(sin_vals, cos_vals)
|
|
max_vals = np.maximum(sin_vals, cos_vals)
|
|
|
|
# Vykreslenie minima ciarkovane
|
|
ax2.plot(x, min_vals, '--', color='gray', label='min')
|
|
|
|
# Urcenie, ktora funkcia je maximalna v kazdom bode
|
|
# True kde je cos max, False kde je sin max
|
|
cos_is_max = cos_vals >= sin_vals
|
|
|
|
# Vykreslenie maxima s farbami podla zdroja pomocou LineCollection
|
|
# Oranzova kde je cos max, modra kde je sin max
|
|
# Vytvorenie segmentov: body (x, y) spajane do useciek
|
|
points = np.array([x, max_vals]).T.reshape(-1, 1, 2)
|
|
segments = np.concatenate([points[:-1], points[1:]], axis=1)
|
|
|
|
# Farby pre kazdy segment podla zdroja maxima
|
|
colors = np.where(cos_is_max[:-1], 'orange', 'blue')
|
|
|
|
# Vytvorenie a pridanie LineCollection
|
|
lc = LineCollection(segments, colors=colors, linewidth=2)
|
|
ax2.add_collection(lc)
|
|
|
|
ax2.set_ylabel('f(x)')
|
|
ax2.grid(True, alpha=0.3)
|
|
|
|
# Nastavenie znaciek na x-ovej osi s LaTeX pi notaciou
|
|
pi_ticks = np.array([0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi,
|
|
5*np.pi/2, 3*np.pi, 7*np.pi/2, 4*np.pi])
|
|
pi_labels = ['0', r'$\frac{\pi}{2}$', r'$\pi$', r'$\frac{3\pi}{2}$',
|
|
r'$2\pi$', r'$\frac{5\pi}{2}$', r'$3\pi$',
|
|
r'$\frac{7\pi}{2}$', r'$4\pi$']
|
|
ax2.set_xticks(pi_ticks)
|
|
ax2.set_xticklabels(pi_labels)
|
|
ax2.set_xlabel('x')
|
|
|
|
plt.tight_layout()
|
|
|
|
# Ulozenie ak je zadana cesta
|
|
if save_path is not None:
|
|
plt.savefig(save_path)
|
|
|
|
# Zobrazenie ak je pozadovane
|
|
if show_figure:
|
|
plt.show()
|
|
|
|
plt.close()
|
|
|
|
|
|
def download_data() -> Dict[str, List[Any]]:
|
|
"""
|
|
Stiahnutie dat meteorologickych stanic z web stranky.
|
|
|
|
Stiahne a parsuje HTML tabulku z
|
|
https://ehw.fit.vutbr.cz/izv/st_zemepis_cz.html
|
|
obsahujucu informacie o ceskych meteorologickych staniciach.
|
|
|
|
Returns:
|
|
Slovnik s klucmi 'positions', 'lats', 'longs', 'heights'
|
|
obsahujuci zoznamy nazvov stanic (str), zemepisnych sirok (float),
|
|
zemepisnych dlzok (float) a vysok (float).
|
|
"""
|
|
def _clean_numeric(text: str, remove_degree: bool = True) -> str:
|
|
"""Pomocna funkcia na cistenie numerickych retazcov."""
|
|
# Vytvorenie prekladovej tabulky pre rychle nahradenie znakov
|
|
trans_dict = {',': '.', '\xa0': '', ' ': ''}
|
|
if remove_degree:
|
|
trans_dict['°'] = ''
|
|
trans_table = str.maketrans(trans_dict)
|
|
return text.strip().translate(trans_table)
|
|
|
|
# Skutocne data su v st_zemepis_cz.html
|
|
url = "https://ehw.fit.vutbr.cz/izv/st_zemepis_cz.html"
|
|
|
|
# Stiahnutie stranky
|
|
response = requests.get(url)
|
|
response.encoding = 'utf-8'
|
|
|
|
# Parsovanie HTML
|
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
|
|
# Inicializacia vysledneho slovnika
|
|
data = {
|
|
'positions': [],
|
|
'lats': [],
|
|
'longs': [],
|
|
'heights': []
|
|
}
|
|
|
|
# Najdenie vsetkych tabuliek a ziskanie datovej tabulky
|
|
tables = soup.find_all('table')
|
|
if len(tables) < 2:
|
|
return data
|
|
|
|
# Data su v druhej tabulke (prva je hlavicka)
|
|
data_table = tables[1]
|
|
|
|
# Najdenie vsetkych riadkov v tabulke
|
|
rows = data_table.find_all('tr')
|
|
|
|
# Spracovanie kazdeho riadku (preskocit hlavickovy riadok)
|
|
for row in rows[1:]:
|
|
cells = row.find_all('td')
|
|
|
|
# Potrebujeme aspon 7 buniek podla struktury tabulky:
|
|
# [nazov stanice, ikona linku, lat stupne, lat dms,
|
|
# long stupne, long dms, vyska]
|
|
if len(cells) >= 7:
|
|
# Extrahovanie nazvu stanice z prvej bunky (obsahuje <strong> tag)
|
|
station_name_tag = cells[0].find('strong')
|
|
if station_name_tag:
|
|
position = station_name_tag.get_text(strip=True)
|
|
else:
|
|
position = cells[0].get_text(strip=True)
|
|
|
|
# Cistenie numerickych hodnot pomocou optimalizovanej funkcie
|
|
lat_str = _clean_numeric(cells[2].get_text(strip=True))
|
|
long_str = _clean_numeric(cells[4].get_text(strip=True))
|
|
height_str = _clean_numeric(cells[6].get_text(strip=True))
|
|
|
|
# Konverzia na prislusne typy
|
|
try:
|
|
lat = float(lat_str)
|
|
long = float(long_str)
|
|
height = float(height_str)
|
|
|
|
# Pridanie do zoznamov
|
|
data['positions'].append(position)
|
|
data['lats'].append(lat)
|
|
data['longs'].append(long)
|
|
data['heights'].append(height)
|
|
except (ValueError, IndexError):
|
|
# Preskocenie riadkov s neplatnymi datami
|
|
continue
|
|
|
|
return data
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Priklad pouzitia - demonstracia implementovanych funkcii
|
|
X = np.linspace(-10, 10, 200)
|
|
Y = np.linspace(-10, 10, 200)
|
|
A = wave_inference(X, Y, np.array([[-3, 0], [3, 0], [0, 4]]), 2)
|
|
plot_wave(A, X, Y, show_figure=False, save_path="wave_example.png")
|
|
generate_sinus(show_figure=False, save_path="sinus_example.png")
|
|
print("Vygenerovane prikladove grafy: wave_example.png, sinus_example.png")
|