Projects/3BIT/winter-semester/IZV/1/part01.py
2026-04-14 19:28:46 +02:00

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")