From 42491f59f2beec6cc1e89685c57584dc43ba1733 Mon Sep 17 00:00:00 2001 From: Jakub Dakowski Date: Tue, 11 Jan 2022 20:51:11 +0100 Subject: [PATCH] add plumber --- .gitignore | 3 +- README.md | 13 +- opis projektu.Rmd | 17 +- opis-projektu.html | 577 +++++++++++++++++++++++++++++++++++++++++++++ opis-projektu.md | 471 ++++++++++++++++++++++++++++++++++++ opis-projektu.pdf | Bin 261472 -> 261786 bytes 6 files changed, 1071 insertions(+), 10 deletions(-) create mode 100644 opis-projektu.html create mode 100644 opis-projektu.md diff --git a/.gitignore b/.gitignore index c47b891..66378a8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ .RData .Ruserdata .httr-oauth -tmp \ No newline at end of file +tmp +*.Rproj \ No newline at end of file diff --git a/README.md b/README.md index 5df5b9d..3abb1e6 100644 --- a/README.md +++ b/README.md @@ -357,6 +357,7 @@ ludziom bezpieczną egzystencję, w systemach automatycznych powinien być jak najprostszy. Program pozostawia więc wiele do życzenia, ale wciąż uzupełnia lukę ignorowaną przez organ ostrzeżenia wydający. +## Informacja o użytych pakietach ## Informacja o użytych pakietach @@ -364,7 +365,7 @@ uzupełnia lukę ignorowaną przez organ ostrzeżenia wydający. -+ @@ -405,16 +406,24 @@ uzupełnia lukę ignorowaną przez organ ostrzeżenia wydający. + + + + + + +
Hadley Wickham 10.02.2019
plumberGenerowanie API ze skryptów R11 776Barret Schloerke24.03.2021
## Polecana literatura oraz linki do wykorzystanych/przydatnych stron - Zdecydowanie nie dokumentacja API IMGW +- - - Stack Overflow - - [Regular Expression Language - Quick Reference](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference) - [Markdown table - generator](https://www.tablesgenerator.com/markdown_tables) + generator](https://www.tablesgenerator.com/markdown_tables) \ No newline at end of file diff --git a/opis projektu.Rmd b/opis projektu.Rmd index 813c40f..b1a642c 100644 --- a/opis projektu.Rmd +++ b/opis projektu.Rmd @@ -3,9 +3,10 @@ title: "Scrapper ostrzeżeń pogodowych IMGW" author: "Jakub Dakowski" date: "11.01.2021" output: + md_document: default + html_document: default pdf_document: df_print: paged - html_document: default --- ```{r setup, include=FALSE} @@ -311,15 +312,17 @@ We wszystkich komunikatach, które autor napotkał algorytm zadziałał poprawni ## Informacja o użytych pakietach -| Nazwa paczki | Opis | Miesięczne pobrania | Autorzy | Data opublikowania | -|-------------:|-------------------------------------------|---------------------|----------------|--------------------| -| `xml2` | Pobieranie dokumentów HTML | 620 159 | Hadley Wickham | 30.11.2021 | -| `tidyverse` | Zestaw bibliotek wspomagających pracę w R | 689 554 | Hadley Wickham | 15.04.2021 | -| `pdftools` | Scrapowanie tekstu z plików PDF | brak danych | Jeroen Ooms | 06.05.2021 | -| `stringr` | Przetwarzanie ciągów znakowych, regex | 806 997 | Hadley Wickham | 10.02.2019 | +| Nazwa paczki | Opis | Miesięczne pobrania | Autorzy | Data opublikowania | +|-------------:|-------------------------------------------|---------------------|------------------|--------------------| +| `xml2` | Pobieranie dokumentów HTML | 620 159 | Hadley Wickham | 30.11.2021 | +| `tidyverse` | Zestaw bibliotek wspomagających pracę w R | 689 554 | Hadley Wickham | 15.04.2021 | +| `pdftools` | Scrapowanie tekstu z plików PDF | brak danych | Jeroen Ooms | 06.05.2021 | +| `stringr` | Przetwarzanie ciągów znakowych, regex | 806 997 | Hadley Wickham | 10.02.2019 | +| `plumber` | Generowanie API ze skryptów R | 11 776 | Barret Schloerke | 24.03.2021 | ## Polecana literatura oraz linki do wykorzystanych/przydatnych stron - Zdecydowanie nie dokumentacja API IMGW +- - - Stack Overflow - diff --git a/opis-projektu.html b/opis-projektu.html new file mode 100644 index 0000000..2976b9e --- /dev/null +++ b/opis-projektu.html @@ -0,0 +1,577 @@ + + + + + + + + + + + + + + +Scrapper ostrzeżeń pogodowych IMGW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+

Opis teoretyczny

+
+

Motywacja

+

Od ponad stu lat ludzkość może obserwować skutki swojej industrialnej działalności na klimacie. Wśród skutków pojawiają się między innymi zmiany w temperaturze globalnej. Wspomniany wzrost temperatury stanowi sam w sobie zagrożenie zdrowotne dla ludzi. Oprócz tego, powoduje on wyższe szanse na takie anomalie jak fale ciepła, czy susze. Wraz z nasileniem się tych zjawisk, skutki zdrowotne będą także się nasilać, wymuszając na wielu osobach zmiany w trybie życia. W związku z tym pojawia się potrzeba uwzględniania w działaniu różnych aplikacji informacji o ostrzeżeniach klimatycznych. Niestety jednak IMGW - organ odpowiedzialny za tworzenie tych ostrzeżeń dla Polski - publikuje je w jednej z najtrudniejszych do automatyzacji form - plikach PDF.

+
+
+

Czym jest ten program?

+

Projekt ten jest przedsięwzięciem z zakresu inżynierii danych mając za zadanie sprowadzić dane PDF do jaśniejszego formatu JSON. Dokonywane jest to przy pomocy biblioteki plumber, która umożliwia utworzenie z kodu R działającego API.

+

API to posiada dwa endpointy. Jeden udostępnia wszystkie ostrzeżenia. Drugi natomiast pozwala na wyszukiwananie ostrzeżeń na podstawie nazw powiatu.

+
+
+
+

Dane zbierane przez scrapper

+

Największym problemem tej pracy jest mała ilość dostępnej dokumentacji na temat sposobu działania ostrzeżeń. Większość informacji na ich temat autor musiał wywnioskować na własną rękę.

+

Opublikowane ostrzeżenia publikowane są w zbiorowych komunikatach dla określonych województw. Trudno powiedzieć, czy jedno województwo może mieć kilka plików. W jednym może znajdować się jednak kilka ostrzeżeń.

+

Komunikaty mogą być ogłoszeniem, aktualizacją, lub odwołaniem ostrzeżenia. Autor jednak także w tym obszarze nie ma informacji na temat sposobu działania systemu. W każdym ogłoszeniu, bądź aktualizacji znajduje się informacja o typie i stopniu zjawiska, czasie ważności, prawdopodobieństwie, przebiegu, komunikatach SMS, RSO oraz uwagach. Odwołania podają natomiast nie podają czasu ważności, prawdopodobieństwa i przebiegu, a zamiast tego dają powód odwołania oraz moment odwołania. Każdy plik dodatkowo zawiera informację o dyżurnym synoptyki, kilka informacji o numerze ostrzeżenia (dla województwa i powiatów, ale niestety nie dla kraju). Można także tam znaleźć krótką informację prawną o możliwości udostępniania. Cytując:

+
+

Wszelkie dalsze udostępnianie, rozpowszechnianie (przedruk, kopiowanie, wiadomość sms) jest dozwolone wyłącznie w formie dosłownej z bezwzględnym wskazaniem źródła informacji tj. IMGW-PIB.

+
+

Autor dokładał wszelkich starań, aby przekazywać ostrzeżenia w formie dosłownej, starając się jedynie przetransformować je do czytelnej dla komputera wersji.

+
+
+

Rozwiązanie problemu pobierania ostrzeżeń

+

Czyli inaczej opis działania algorytmu.

+
+

Pobieranie danych

+

Schemat działania systemu jest stosunkowo prosty. System rozpoczyna od pobrania listy aktualnych ostrzeżeń ze strony https://danepubliczne.imgw.pl/data/current/ost_meteo/. Dane są następnie transformowane do formy tabeli oraz czyszczone dla łatwiejszej obsługi.

+
webpage_url <- "https://danepubliczne.imgw.pl/data/current/ost_meteo/"
+webpage <- xml2::read_html(webpage_url)
+
+ost_files <- rvest::html_table(webpage)[[1]] %>% 
+        tibble::as_tibble(.name_repair = "unique") %>%
+        filter(Name != "" & Name != "Parent Directory") %>%
+        select(Name, `Last modified`) %>% 
+        rename_with(function(x){"modified_at"}, "Last modified")
+
## New names:
+## * `` -> ...1
+
ost_files
+
## # A tibble: 3 x 2
+##   Name                           modified_at     
+##   <chr>                          <chr>           
+## 1 MAW_STAN_20220111105058212.pdf 2022-01-11 11:51
+## 2 PDW_STAN_20220111103130250.pdf 2022-01-11 11:31
+## 3 PKW_STAN_20220111105058212.pdf 2022-01-11 11:51
+

Program następnie pobiera kolejne ogłoszone ostrzeżenia i kompiluje je do ramki danych. Dokonuje tego funkcja get_warns, która odwołuje się do read_warns.

+
get_warns <- function(file) {
+    webpage_url <- "https://danepubliczne.imgw.pl/data/current/ost_meteo/"
+    saved.file <- paste('tmp', file, sep="/")
+    if (!file.exists(saved.file)) {
+        download.file(paste(webpage_url, file, sep=""), saved.file, mode="wb")
+    }
+    return (read_warns(saved.file))
+}
+
+read_warns <- function(saved.file) {
+    txt <- preprocess(pdf_text(saved.file))
+    extracted <- extract(txt)
+
+    extracted$file <- saved.file
+
+    extracted$messtype[extracted['messtype']==" ZMIANA"] <- "1"
+    extracted$messtype[extracted['messtype']==" WYCOFANIE"] <- "2"
+    extracted$messtype[extracted['messtype']==""] <- "0"
+    return (extracted)
+}
+

read_warns odczytuje plik PDF o określonym adresie i przetwarza go do formy tekstowej z pomocą biblioteki pdftools. Te dane ulegają przetworzeniu przez funkcje preprocess oraz extract.

+
+
+

Ekstrakcja ostrzeżeń

+

Ponieważ przetwarzane pliki są oryginalnie przechowywane w formacie PDF, znaczna część zawartych w nich znaków nie należy do faktycznych komunikatów, a jest jedynie nagłówkami, czy stopkami dokumentu. Zadaniem funkcji preprocess jest usunięcie tych segmentów. Przeprowadza ona kolejno:

+
    +
  1. Usunięcie nowych linii, tabulacji oraz nadmiaorwych spacji.
  2. +
  3. Usunięcie numerów stron.
  4. +
  5. Usunięcie nagłówka z danymi adresowymi IMGW.
  6. +
  7. Usunięcie “ostrzeżenia dla powiatu)” znajdującego się w niepoprawnie zescrapowanym miejscu.
  8. +
  9. Dodanie nowej linii na początku każdego nowego komunikatu.
  10. +
  11. Konkatenacja oraz ponowne usunięcie nadmiarowych spacji.
  12. +
+
# Przy uruchamianiu programu funkcja jest uruchamiana wraz z setupem, nie trzeba jej uruchamiać drugi raz.
+preprocess <- function(txt) {
+   txt <- gsub("\n+| {2,}\t+", " ", txt)
+   txt <- gsub("strona \\d+ z \\d+", "", txt, ignore.case=T)
+   txt <- gsub(" Instytut Meteorologii i Gospodarki Wodnej .+  www: www\\.imgw\\.pl", "", txt, ignore.case=T)
+   txt <- gsub("Zjawisko/Stopień", "\nZjawisko/Stopień", gsub("ostrzeżenia dla powiatu\\) ", "", txt))
+   txt <- paste(txt, collapse=" ")
+   txt <- gsub(" {2,}", " ", txt)
+
+   return (txt)
+   }
+

Funkcja extract ma o wiele trudniejsze zadanie. Przyjmuje ona przetworzony tekst i rozpoczyna od przeszukania go z pomocą wyrażenia regularnego START_PATTERN, które umożliwia wykrycie województwa, numeru ostrzeżenia zbiorczego, treści ostrzeżeń jednostkowych i autora ostrzeżenia. Treść ostrzeżeń jest następnie przeszukiwana z pomocą wyrażenia PATTERN, które identyfikuje pojedyncze ostrzeżenia i wykrywa w nich konkretne dane. Kolejne wiersze odpowiadają za identyfikację:

+
    +
  • Zjawiska i stopnia zagrożenia (a także bycia modyfikacją/usunięciem danego ostrzeżenia)
  • +
  • Zagrożonych powiatów
  • +
  • Czasu rozpoczęcia
  • +
  • Daty rozpoczęcia
  • +
  • Czasu zakończenia
  • +
  • Daty zakończenia
  • +
  • Prawdopodobieństwa
  • +
  • Opisu przebiegu
  • +
  • Jeżeli ogłoszenie jest odwołaniem ostrzeżenia: +
      +
    • Czasu odwołania
    • +
    • Przyczyny odwołania
    • +
  • +
  • Komunikatu SMS
  • +
  • Komunikatu RSO
  • +
  • Uwag do ostrzeżenia
  • +
+

Wyniki tego przeszukiwania są następnie transformowane do ramki danych, która uzupełniana jest o pozostałe informacje pochodzące z przeszukiwania z pomocą START_PATTERN oraz numer ostrzeżenia w danym pliku (na podstawie tego można potem utworzyć klucz składający się z nazwy pliku i numeru ostrzeżenia w pliku).

+
# Przy uruchamianiu programu funkcja jest uruchamiana wraz z setupem, nie trzeba jej uruchamiać drugi raz.
+extract <- function(txt) {
+  START_PATTERN <- paste(
+    "Zasięg ostrzeżeń w województwie WOJEWÓDZTWO (?<voivodeship>[\\w\\-]+)",
+    "OSTRZEŻENIA METEOROLOGICZNE ZBIORCZO NR (?<id>\\d+) WYKAZ OBOWIĄZUJĄCYCH OSTRZEŻEŃ",
+    "o godz\\. \\d\\d:\\d\\d dnia \\d\\d\\.\\d\\d\\.\\d{4}",
+    "(?<text>(?:\\n|.)+)",
+    "Dyżurny synoptyk (?<creator>.+?(?= IMGW-PIB))",
+    sep=" "
+  )
+  PATTERN <- paste(
+    "Zjawisko/Stopień zagrożenia (?<event>[\\w ]+)/(?<lvl>\\d+)(?<messtype>| \\w+)",
+    "Obszar \\(w nawiasie numer powiaty: (?<regions>(?:[\\w- ]+\\(\\d+\\)(?:, )*)+)",
+    "(?:Ważność od godz\\. (?<starthour>\\d\\d\\:\\d\\d)",
+    "dnia (?<startday>\\d\\d\\.\\d\\d\\.\\d{4})",
+    "do godz\\. (?<endhour>\\d\\d\\:\\d\\d)",
+    "dnia (?<endday>\\d\\d\\.\\d\\d\\.\\d{4})",
+    "Prawdopodobieństwo (?<prob>\\d{1,3}\\%)",
+    "Przebieg (?<how>.+?(?= SMS))|Czas",
+    "odwołania godz\\. (?<hour>\\d\\d:\\d\\d) dnia (?<day>\\d\\d\\.\\d\\d\\.\\d{4})",
+    "Przyczyna (?<cancelcause>.+?(?= SMS)))",
+    "SMS (?<sms>.+?(?= RSO))",
+    "RSO (?<rso>.+?(?= Uwagi))",
+    "Uwagi (?<remarks>[^\\n]+)",
+    sep=" "
+  )
+  pat <- str_match(txt, START_PATTERN)
+  df <- as.data.frame(str_match_all(pat[4], PATTERN))
+  df$voivodeship <- str_to_lower(pat[2], locale = 'pl')
+  df$warn_id <- pat[3]
+  df$author <- pat[5]
+  df$infile <- rownames(df)
+  return (df)
+}
+

Jak nietrudno sobie wyobrazić strategia od tego momentu jest dość prosta. Oprogramowanie łączy ze sobą kolejne ramki danych:

+
file <- ost_files$Name[1]
+df <- get_warns(file)
+
+if (length(ost_files$Name)>1){
+  for (file in ost_files$Name[2:length(ost_files$Name)]) {
+    df <- rbind(df, get_warns(file))
+  }
+}
+
+df
+
##                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          V1
+## 1                                                                                                                                                                                                                               Zjawisko/Stopień zagrożenia Silny mróz/1 Obszar (w nawiasie numer powiaty: tatrzański(7) Ważność od godz. 21:00 dnia 11.01.2022 do godz. 08:00 dnia 12.01.2022 Prawdopodobieństwo 75% Przebieg Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h. SMS IMGW-PIB OSTRZEGA: MRÓZ/1 małopolskie (1 powiat) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: tatrzański. RSO Woj. małopolskie (1 powiat), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach Uwagi Brak.
+## 2 Zjawisko/Stopień zagrożenia Silny mróz/1 Obszar (w nawiasie numer powiaty: augustowski(4), białostocki(3), Białystok(3), bielski(3), hajnowski(3), sejneński(4), siemiatycki(3), sokólski(3), suwalski(4), Suwałki(4) Ważność od godz. 23:00 dnia 11.01.2022 do godz. 08:00 dnia 12.01.2022 Prawdopodobieństwo 70% Przebieg Prognozuje się temperaturę minimalną miejscami od -15°C do -13°C. Wiatr o średniej prędkości od 5 km/h do 10 km/h. SMS IMGW-PIB OSTRZEGA: MRÓZ/1 podlaskie (10 powiatów) od 23:00/11.01 do 08:00/12.01.2022 temp. min -15 st, wiatr 5-10 km/h. Dotyczy powiatów: augustowski, białostocki, Białystok, bielski, hajnowski, sejneński, siemiatycki, sokólski, suwalski i Suwałki. RSO Woj. podlaskie (10 powiatów), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach Uwagi Brak.
+## 3                                                                                                                                                                                                     Zjawisko/Stopień zagrożenia Silny mróz/1 Obszar (w nawiasie numer powiaty: bieszczadzki(7), leski(7) Ważność od godz. 21:00 dnia 11.01.2022 do godz. 08:00 dnia 12.01.2022 Prawdopodobieństwo 75% Przebieg Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h. SMS IMGW-PIB OSTRZEGA: MRÓZ/1 podkarpackie (2 powiaty) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: bieszczadzki i leski. RSO Woj. podkarpackie (2 powiaty), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach Uwagi Brak.
+##        event lvl messtype
+## 1 Silny mróz   1        0
+## 2 Silny mróz   1        0
+## 3 Silny mróz   1        0
+##                                                                                                                                      regions
+## 1                                                                                                                              tatrzański(7)
+## 2 augustowski(4), białostocki(3), Białystok(3), bielski(3), hajnowski(3), sejneński(4), siemiatycki(3), sokólski(3), suwalski(4), Suwałki(4)
+## 3                                                                                                                  bieszczadzki(7), leski(7)
+##   starthour   startday endhour     endday prob
+## 1     21:00 11.01.2022   08:00 12.01.2022  75%
+## 2     23:00 11.01.2022   08:00 12.01.2022  70%
+## 3     21:00 11.01.2022   08:00 12.01.2022  75%
+##                                                                                                                         how
+## 1 Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h.
+## 2        Prognozuje się temperaturę minimalną miejscami od -15°C do -13°C. Wiatr o średniej prędkości od 5 km/h do 10 km/h.
+## 3 Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h.
+##   hour  day cancelcause
+## 1 <NA> <NA>        <NA>
+## 2 <NA> <NA>        <NA>
+## 3 <NA> <NA>        <NA>
+##                                                                                                                                                                                                                                                        sms
+## 1                                                                                                        IMGW-PIB OSTRZEGA: MRÓZ/1 małopolskie (1 powiat) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: tatrzański.
+## 2 IMGW-PIB OSTRZEGA: MRÓZ/1 podlaskie (10 powiatów) od 23:00/11.01 do 08:00/12.01.2022 temp. min -15 st, wiatr 5-10 km/h. Dotyczy powiatów: augustowski, białostocki, Białystok, bielski, hajnowski, sejneński, siemiatycki, sokólski, suwalski i Suwałki.
+## 3                                                                                            IMGW-PIB OSTRZEGA: MRÓZ/1 podkarpackie (2 powiaty) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: bieszczadzki i leski.
+##                                                                                              rso
+## 1   Woj. małopolskie (1 powiat), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
+## 2  Woj. podlaskie (10 powiatów), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
+## 3 Woj. podkarpackie (2 powiaty), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
+##   remarks  voivodeship warn_id           author infile
+## 1   Brak.  małopolskie       9  Krzysztof Jania      1
+## 2   Brak.    podlaskie       5 Magdalena Pękala      1
+## 3   Brak. podkarpackie       8  Krzysztof Jania      1
+##                                 file
+## 1 tmp/MAW_STAN_20220111105058212.pdf
+## 2 tmp/PDW_STAN_20220111103130250.pdf
+## 3 tmp/PKW_STAN_20220111105058212.pdf
+
+
+

Obróbka danych

+

W tym momencie może zostać włączone filtrowanie tylko nowych ostrzeżeń. Program zmienia też zawartość zestawu kolumn:

+
    +
  • regions od teraz przechowuje wektor powiatów zamiast ciągu znakowego,
  • +
  • file jest pozbawiane nazwy folderu,
  • +
  • prob konwertowane jest na ułamek zamiart ciągu znakowego
  • +
  • Daty kompilowane są do wystandaryzowanego formatu,
  • +
  • Liczby całkowite konwertowane są do formatu liczb całkowitych.
  • +
+

Warto w tym momencie pochylić się nad format_time oraz get_places. Pierwsza skleja ze sobą dwie kolumny i przetwarza z użyciem strptime. Następnie pola NA uzupełniane są przez NULL. Druga natomiast jest prostym regexem, który dodatkowo konwertuje nazwy powiatów na małe litery.

+
format_time <- function(date, time) {
+  t <- strptime(paste(date, time), "%d.%m.%Y %H:%M")
+  t[is.na(t)] <- NULL
+  return(t)
+}
+
+get_places <- function(place_list) {
+  p <- " ?([^,]+)\\(\\d+\\),?"
+  match <- str_match_all(place_list, p)
+  return(lapply(match, function(x) {
+    return(str_to_lower(x[, 2], locale = 'pl'))
+  }))
+}
+

Poza tym program usuwa zbędne kolumny. W tym też momencie nadchodzi pora na jakże długą linię służącą do odsortowania ostrzeżeń niedotyczących danego miejsca. Taki wynik jest zwracany użytkownikowi w formacie JSON.

+
fin <- df %>%
+  # filter(messtype == '0') %>%
+  mutate(
+    regions = get_places(df$regions),
+    file = gsub("tmp/", "", file),
+
+    prob = as.numeric(sub("%", "", prob)) / 100,
+
+    starttime = format_time(df$startday, df$starthour),
+    endtime = format_time(df$endday, df$endhour),
+    canceltime = format_time(df$day, df$hour),
+
+    lvl = as.numeric(lvl),
+    messtype = as.numeric(messtype),
+    infile = as.numeric(infile),
+    warn_id = as.numeric(warn_id),
+    ) %>%
+  #mutate(include = unlist(lapply(regions, function(x) { place %in% x }))) %>% filter(include) %>% select(-c(include)) %>% 
+  # ^ umożliwia sortowanie po powiatach
+  select(-c(V1, startday, starthour, endday, endhour, day, hour))
+
+fin
+
##        event lvl messtype
+## 1 Silny mróz   1        0
+## 2 Silny mróz   1        0
+## 3 Silny mróz   1        0
+##                                                                                                        regions
+## 1                                                                                                   tatrzański
+## 2 augustowski, białostocki, białystok, bielski, hajnowski, sejneński, siemiatycki, sokólski, suwalski, suwałki
+## 3                                                                                          bieszczadzki, leski
+##   prob
+## 1 0.75
+## 2 0.70
+## 3 0.75
+##                                                                                                                         how
+## 1 Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h.
+## 2        Prognozuje się temperaturę minimalną miejscami od -15°C do -13°C. Wiatr o średniej prędkości od 5 km/h do 10 km/h.
+## 3 Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h.
+##   cancelcause
+## 1        <NA>
+## 2        <NA>
+## 3        <NA>
+##                                                                                                                                                                                                                                                        sms
+## 1                                                                                                        IMGW-PIB OSTRZEGA: MRÓZ/1 małopolskie (1 powiat) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: tatrzański.
+## 2 IMGW-PIB OSTRZEGA: MRÓZ/1 podlaskie (10 powiatów) od 23:00/11.01 do 08:00/12.01.2022 temp. min -15 st, wiatr 5-10 km/h. Dotyczy powiatów: augustowski, białostocki, Białystok, bielski, hajnowski, sejneński, siemiatycki, sokólski, suwalski i Suwałki.
+## 3                                                                                            IMGW-PIB OSTRZEGA: MRÓZ/1 podkarpackie (2 powiaty) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: bieszczadzki i leski.
+##                                                                                              rso
+## 1   Woj. małopolskie (1 powiat), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
+## 2  Woj. podlaskie (10 powiatów), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
+## 3 Woj. podkarpackie (2 powiaty), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
+##   remarks  voivodeship warn_id           author infile
+## 1   Brak.  małopolskie       9  Krzysztof Jania      1
+## 2   Brak.    podlaskie       5 Magdalena Pękala      1
+## 3   Brak. podkarpackie       8  Krzysztof Jania      1
+##                             file           starttime             endtime
+## 1 MAW_STAN_20220111105058212.pdf 2022-01-11 21:00:00 2022-01-12 08:00:00
+## 2 PDW_STAN_20220111103130250.pdf 2022-01-11 23:00:00 2022-01-12 08:00:00
+## 3 PKW_STAN_20220111105058212.pdf 2022-01-11 21:00:00 2022-01-12 08:00:00
+##   canceltime
+## 1       <NA>
+## 2       <NA>
+## 3       <NA>
+

Oczywiście, jeśli żadne ostrzeżenia nie zostaną znalezione, API zwróci pusty wektor, który w JSONie jest reprezentowany jako []. Ten warunek nie jest tu jednak wprowadzony.

+
+
+
+

Sprawdzenie założeń

+

Autor niestety wciąż nie jest pewien, czy opracowane przez niego wyrażenie regularne działa w pełni. Można je co najwyżej sprawdzić na zestawie wszystkich poprzednich ostrzeżeń, co jednak jest trudne, ze względu na ich inny sposób przechowywania na stronie (są one uprzednio kompresowane).

+
+
+

Interpretacja wyników

+

We wszystkich komunikatach, które autor napotkał algorytm zadziałał poprawnie, wskazuje to na jego wysoką skuteczność. Niestety aktualne rozwiązanie działa stosunkowo wolno i jest ograniczone, jak chodzi o zmiany formatu ogłoszeń. Założenia na temat ich formy także są dość silne, co jednak wynika z bierności IMGW. Cała potrzeba produkcji takiego oprogramowania w XXI wieku wydaje się absurdalna. Zdaniem autora, dostęp do tego typu ostrzeżeń, jako informacjach umożliwiających ludziom bezpieczną egzystencję, w systemach automatycznych powinien być jak najprostszy. Program pozostawia więc wiele do życzenia, ale wciąż uzupełnia lukę ignorowaną przez organ ostrzeżenia wydający.

+
+
+

Informacja o użytych pakietach

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nazwa paczkiOpisMiesięczne pobraniaAutorzyData opublikowania
xml2Pobieranie dokumentów HTML620 159Hadley Wickham30.11.2021
tidyverseZestaw bibliotek wspomagających pracę w R689 554Hadley Wickham15.04.2021
pdftoolsScrapowanie tekstu z plików PDFbrak danychJeroen Ooms06.05.2021
stringrPrzetwarzanie ciągów znakowych, regex806 997Hadley Wickham10.02.2019
+
+
+

Polecana literatura oraz linki do wykorzystanych/przydatnych stron

+ +
+ + + + +
+ + + + + + + + + + + + + + + diff --git a/opis-projektu.md b/opis-projektu.md new file mode 100644 index 0000000..c05b6c9 --- /dev/null +++ b/opis-projektu.md @@ -0,0 +1,471 @@ +## Opis teoretyczny + +### Motywacja + +Od ponad stu lat ludzkość może obserwować skutki swojej industrialnej +działalności na klimacie. Wśród skutków pojawiają się między innymi +zmiany w temperaturze globalnej. Wspomniany wzrost temperatury stanowi +sam w sobie zagrożenie zdrowotne dla ludzi. Oprócz tego, powoduje on +wyższe szanse na takie anomalie jak fale ciepła, czy susze. Wraz z +nasileniem się tych zjawisk, skutki zdrowotne będą także się nasilać, +wymuszając na wielu osobach zmiany w trybie życia. W związku z tym +pojawia się potrzeba uwzględniania w działaniu różnych aplikacji +informacji o ostrzeżeniach klimatycznych. Niestety jednak IMGW - organ +odpowiedzialny za tworzenie tych ostrzeżeń dla Polski - publikuje je w +jednej z najtrudniejszych do automatyzacji form - plikach PDF. + +### Czym jest ten program? + +Projekt ten jest przedsięwzięciem z zakresu inżynierii danych mając za +zadanie sprowadzić dane PDF do jaśniejszego formatu JSON. Dokonywane +jest to przy pomocy biblioteki *plumber*, która umożliwia utworzenie z +kodu R działającego API. + +API to posiada dwa endpointy. Jeden udostępnia wszystkie ostrzeżenia. +Drugi natomiast pozwala na wyszukiwananie ostrzeżeń na podstawie nazw +powiatu. + +## Dane zbierane przez scrapper + +Największym problemem tej pracy jest mała ilość dostępnej dokumentacji +na temat sposobu działania ostrzeżeń. Większość informacji na ich temat +autor musiał wywnioskować na własną rękę. + +Opublikowane ostrzeżenia publikowane są w zbiorowych komunikatach dla +określonych województw. Trudno powiedzieć, czy jedno województwo może +mieć kilka plików. W jednym może znajdować się jednak kilka ostrzeżeń. + +Komunikaty mogą być ogłoszeniem, aktualizacją, lub odwołaniem +ostrzeżenia. Autor jednak także w tym obszarze nie ma informacji na +temat sposobu działania systemu. W każdym ogłoszeniu, bądź aktualizacji +znajduje się informacja o typie i stopniu zjawiska, czasie ważności, +prawdopodobieństwie, przebiegu, komunikatach SMS, RSO oraz uwagach. +Odwołania podają natomiast nie podają czasu ważności, prawdopodobieństwa +i przebiegu, a zamiast tego dają powód odwołania oraz moment odwołania. +Każdy plik dodatkowo zawiera informację o dyżurnym synoptyki, kilka +informacji o numerze ostrzeżenia (dla województwa i powiatów, ale +niestety nie dla kraju). Można także tam znaleźć krótką informację +prawną o możliwości udostępniania. Cytując: + +> Wszelkie dalsze udostępnianie, rozpowszechnianie (przedruk, +> kopiowanie, wiadomość sms) jest dozwolone wyłącznie w formie dosłownej +> z bezwzględnym wskazaniem źródła informacji tj. IMGW-PIB. + +Autor dokładał wszelkich starań, aby przekazywać ostrzeżenia w formie +dosłownej, starając się jedynie przetransformować je do czytelnej dla +komputera wersji. + +## Rozwiązanie problemu pobierania ostrzeżeń + +Czyli inaczej opis działania algorytmu. + +### Pobieranie danych + +Schemat działania systemu jest stosunkowo prosty. System rozpoczyna od +pobrania listy aktualnych ostrzeżeń ze strony +. Dane są +następnie transformowane do formy tabeli oraz czyszczone dla łatwiejszej +obsługi. + + webpage_url <- "https://danepubliczne.imgw.pl/data/current/ost_meteo/" + webpage <- xml2::read_html(webpage_url) + + ost_files <- rvest::html_table(webpage)[[1]] %>% + tibble::as_tibble(.name_repair = "unique") %>% + filter(Name != "" & Name != "Parent Directory") %>% + select(Name, `Last modified`) %>% + rename_with(function(x) { + "modified_at" + }, "Last modified") + + ## New names: + ## * `` -> ...1 + + ost_files + + ## # A tibble: 3 x 2 + ## Name modified_at + ## + ## 1 MAW_STAN_20220111105058212.pdf 2022-01-11 11:51 + ## 2 PDW_STAN_20220111103130250.pdf 2022-01-11 11:31 + ## 3 PKW_STAN_20220111105058212.pdf 2022-01-11 11:51 + +Program następnie pobiera kolejne ogłoszone ostrzeżenia i kompiluje je +do ramki danych. Dokonuje tego funkcja `get_warns`, która odwołuje się +do `read_warns`. + + get_warns <- function(file) { + webpage_url <- "https://danepubliczne.imgw.pl/data/current/ost_meteo/" + saved.file <- paste("tmp", file, sep = "/") + if (!file.exists(saved.file)) { + download.file(paste(webpage_url, file, sep = ""), saved.file, + mode = "wb") + } + return(read_warns(saved.file)) + } + + read_warns <- function(saved.file) { + txt <- preprocess(pdf_text(saved.file)) + extracted <- extract(txt) + + extracted$file <- saved.file + + extracted$messtype[extracted["messtype"] == " ZMIANA"] <- "1" + extracted$messtype[extracted["messtype"] == " WYCOFANIE"] <- "2" + extracted$messtype[extracted["messtype"] == ""] <- "0" + return(extracted) + } + +`read_warns` odczytuje plik PDF o określonym adresie i przetwarza go do +formy tekstowej z pomocą biblioteki *pdftools*. Te dane ulegają +przetworzeniu przez funkcje `preprocess` oraz `extract`. + +### Ekstrakcja ostrzeżeń + +Ponieważ przetwarzane pliki są oryginalnie przechowywane w formacie PDF, +znaczna część zawartych w nich znaków nie należy do faktycznych +komunikatów, a jest jedynie nagłówkami, czy stopkami dokumentu. Zadaniem +funkcji `preprocess` jest usunięcie tych segmentów. Przeprowadza ona +kolejno: + +1. Usunięcie nowych linii, tabulacji oraz nadmiaorwych spacji. +2. Usunięcie numerów stron. +3. Usunięcie nagłówka z danymi adresowymi IMGW. +4. Usunięcie “ostrzeżenia dla powiatu)” znajdującego się w niepoprawnie + zescrapowanym miejscu. +5. Dodanie nowej linii na początku każdego nowego komunikatu. +6. Konkatenacja oraz ponowne usunięcie nadmiarowych spacji. + + + + # Przy uruchamianiu programu funkcja jest uruchamiana wraz z setupem, + # nie trzeba jej uruchamiać drugi raz. + preprocess <- function(txt) { + txt <- gsub("\n+| {2,}\t+", " ", txt) + txt <- gsub("strona \\d+ z \\d+", "", txt, ignore.case=T) + txt <- gsub(" Instytut Meteorologii i Gospodarki Wodnej .+ www: www\\.imgw\\.pl", + "", txt, ignore.case=T) + txt <- gsub("Zjawisko/Stopień", "\nZjawisko/Stopień", + gsub("ostrzeżenia dla powiatu\\) ", "", txt)) + txt <- paste(txt, collapse=" ") + txt <- gsub(" {2,}", " ", txt) + + return (txt) + } + +Funkcja `extract` ma o wiele trudniejsze zadanie. Przyjmuje ona +przetworzony tekst i rozpoczyna od przeszukania go z pomocą wyrażenia +regularnego `START_PATTERN`, które umożliwia wykrycie województwa, +numeru ostrzeżenia zbiorczego, treści ostrzeżeń jednostkowych i autora +ostrzeżenia. Treść ostrzeżeń jest następnie przeszukiwana z pomocą +wyrażenia `PATTERN`, które identyfikuje pojedyncze ostrzeżenia i wykrywa +w nich konkretne dane. Kolejne wiersze odpowiadają za identyfikację: + +- Zjawiska i stopnia zagrożenia (a także bycia modyfikacją/usunięciem + danego ostrzeżenia) +- Zagrożonych powiatów +- Czasu rozpoczęcia +- Daty rozpoczęcia +- Czasu zakończenia +- Daty zakończenia +- Prawdopodobieństwa +- Opisu przebiegu +- Jeżeli ogłoszenie jest odwołaniem ostrzeżenia: + - Czasu odwołania + - Przyczyny odwołania +- Komunikatu SMS +- Komunikatu RSO +- Uwag do ostrzeżenia + +Wyniki tego przeszukiwania są następnie transformowane do ramki danych, +która uzupełniana jest o pozostałe informacje pochodzące z +przeszukiwania z pomocą `START_PATTERN` oraz numer ostrzeżenia w danym +pliku (na podstawie tego można potem utworzyć klucz składający się z +nazwy pliku i numeru ostrzeżenia w pliku). + + # Przy uruchamianiu programu funkcja jest uruchamiana wraz z setupem, + # nie trzeba jej uruchamiać drugi raz. + extract <- function(txt) { + START_PATTERN <- paste( + "Zasięg ostrzeżeń w województwie WOJEWÓDZTWO (?[\\w\\-]+)", + "OSTRZEŻENIA METEOROLOGICZNE ZBIORCZO NR (?\\d+) WYKAZ OBOWIĄZUJĄCYCH OSTRZEŻEŃ", + "o godz\\. \\d\\d:\\d\\d dnia \\d\\d\\.\\d\\d\\.\\d{4}", + "(?(?:\\n|.)+)", + "Dyżurny synoptyk (?.+?(?= IMGW-PIB))", + sep=" " + ) + PATTERN <- paste( + "Zjawisko/Stopień zagrożenia (?[\\w ]+)/(?\\d+)(?| \\w+)", + "Obszar \\(w nawiasie numer powiaty: (?(?:[\\w- ]+\\(\\d+\\)(?:, )*)+)", + "(?:Ważność od godz\\. (?\\d\\d\\:\\d\\d)", + "dnia (?\\d\\d\\.\\d\\d\\.\\d{4})", + "do godz\\. (?\\d\\d\\:\\d\\d)", + "dnia (?\\d\\d\\.\\d\\d\\.\\d{4})", + "Prawdopodobieństwo (?\\d{1,3}\\%)", + "Przebieg (?.+?(?= SMS))|Czas", + "odwołania godz\\. (?\\d\\d:\\d\\d) dnia (?\\d\\d\\.\\d\\d\\.\\d{4})", + "Przyczyna (?.+?(?= SMS)))", + "SMS (?.+?(?= RSO))", + "RSO (?.+?(?= Uwagi))", + "Uwagi (?[^\\n]+)", + sep=" " + ) + pat <- str_match(txt, START_PATTERN) + df <- as.data.frame(str_match_all(pat[4], PATTERN)) + df$voivodeship <- str_to_lower(pat[2], locale = 'pl') + df$warn_id <- pat[3] + df$author <- pat[5] + df$infile <- rownames(df) + return (df) + } + +Jak nietrudno sobie wyobrazić strategia od tego momentu jest dość +prosta. Oprogramowanie łączy ze sobą kolejne ramki danych: + + file <- ost_files$Name[1] + df <- get_warns(file) + + if (length(ost_files$Name) > 1) { + for (file in ost_files$Name[2:length(ost_files$Name)]) { + df <- rbind(df, get_warns(file)) + } + } + + df + + ## V1 + ## 1 Zjawisko/Stopień zagrożenia Silny mróz/1 Obszar (w nawiasie numer powiaty: tatrzański(7) Ważność od godz. 21:00 dnia 11.01.2022 do godz. 08:00 dnia 12.01.2022 Prawdopodobieństwo 75% Przebieg Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h. SMS IMGW-PIB OSTRZEGA: MRÓZ/1 małopolskie (1 powiat) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: tatrzański. RSO Woj. małopolskie (1 powiat), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach Uwagi Brak. + ## 2 Zjawisko/Stopień zagrożenia Silny mróz/1 Obszar (w nawiasie numer powiaty: augustowski(4), białostocki(3), Białystok(3), bielski(3), hajnowski(3), sejneński(4), siemiatycki(3), sokólski(3), suwalski(4), Suwałki(4) Ważność od godz. 23:00 dnia 11.01.2022 do godz. 08:00 dnia 12.01.2022 Prawdopodobieństwo 70% Przebieg Prognozuje się temperaturę minimalną miejscami od -15°C do -13°C. Wiatr o średniej prędkości od 5 km/h do 10 km/h. SMS IMGW-PIB OSTRZEGA: MRÓZ/1 podlaskie (10 powiatów) od 23:00/11.01 do 08:00/12.01.2022 temp. min -15 st, wiatr 5-10 km/h. Dotyczy powiatów: augustowski, białostocki, Białystok, bielski, hajnowski, sejneński, siemiatycki, sokólski, suwalski i Suwałki. RSO Woj. podlaskie (10 powiatów), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach Uwagi Brak. + ## 3 Zjawisko/Stopień zagrożenia Silny mróz/1 Obszar (w nawiasie numer powiaty: bieszczadzki(7), leski(7) Ważność od godz. 21:00 dnia 11.01.2022 do godz. 08:00 dnia 12.01.2022 Prawdopodobieństwo 75% Przebieg Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h. SMS IMGW-PIB OSTRZEGA: MRÓZ/1 podkarpackie (2 powiaty) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: bieszczadzki i leski. RSO Woj. podkarpackie (2 powiaty), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach Uwagi Brak. + ## event lvl messtype + ## 1 Silny mróz 1 0 + ## 2 Silny mróz 1 0 + ## 3 Silny mróz 1 0 + ## regions + ## 1 tatrzański(7) + ## 2 augustowski(4), białostocki(3), Białystok(3), bielski(3), hajnowski(3), sejneński(4), siemiatycki(3), sokólski(3), suwalski(4), Suwałki(4) + ## 3 bieszczadzki(7), leski(7) + ## starthour startday endhour endday prob + ## 1 21:00 11.01.2022 08:00 12.01.2022 75% + ## 2 23:00 11.01.2022 08:00 12.01.2022 70% + ## 3 21:00 11.01.2022 08:00 12.01.2022 75% + ## how + ## 1 Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h. + ## 2 Prognozuje się temperaturę minimalną miejscami od -15°C do -13°C. Wiatr o średniej prędkości od 5 km/h do 10 km/h. + ## 3 Prognozuje się temperaturę minimalną w nocy miejscami od -16°C do -12°C. Wiatr o średniej prędkości od 5 km/h do 15 km/h. + ## hour day cancelcause + ## 1 + ## 2 + ## 3 + ## sms + ## 1 IMGW-PIB OSTRZEGA: MRÓZ/1 małopolskie (1 powiat) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: tatrzański. + ## 2 IMGW-PIB OSTRZEGA: MRÓZ/1 podlaskie (10 powiatów) od 23:00/11.01 do 08:00/12.01.2022 temp. min -15 st, wiatr 5-10 km/h. Dotyczy powiatów: augustowski, białostocki, Białystok, bielski, hajnowski, sejneński, siemiatycki, sokólski, suwalski i Suwałki. + ## 3 IMGW-PIB OSTRZEGA: MRÓZ/1 podkarpackie (2 powiaty) od 21:00/11.01 do 08:00/12.01.2022 temp. min -16st, wiatr 15 km/h. Dotyczy powiatów: bieszczadzki i leski. + ## rso + ## 1 Woj. małopolskie (1 powiat), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach + ## 2 Woj. podlaskie (10 powiatów), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach + ## 3 Woj. podkarpackie (2 powiaty), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach + ## remarks voivodeship warn_id author infile + ## 1 Brak. małopolskie 9 Krzysztof Jania 1 + ## 2 Brak. podlaskie 5 Magdalena Pękala 1 + ## 3 Brak. podkarpackie 8 Krzysztof Jania 1 + ## file + ## 1 tmp/MAW_STAN_20220111105058212.pdf + ## 2 tmp/PDW_STAN_20220111103130250.pdf + ## 3 tmp/PKW_STAN_20220111105058212.pdf + +### Obróbka danych + +W tym momencie może zostać włączone filtrowanie tylko nowych ostrzeżeń. +Program zmienia też zawartość zestawu kolumn: + +- `regions` od teraz przechowuje wektor powiatów zamiast ciągu + znakowego, +- `file` jest pozbawiane nazwy folderu, +- `prob` konwertowane jest na ułamek zamiart ciągu znakowego +- Daty kompilowane są do wystandaryzowanego formatu, +- Liczby całkowite konwertowane są do formatu liczb całkowitych. + +Warto w tym momencie pochylić się nad `format_time` oraz `get_places`. +Pierwsza skleja ze sobą dwie kolumny i przetwarza z użyciem `strptime`. +Następnie pola `NA` uzupełniane są przez `NULL`. Druga natomiast jest +prostym regexem, który dodatkowo konwertuje nazwy powiatów na małe +litery. + + format_time <- function(date, time) { + t <- strptime(paste(date, time), "%d.%m.%Y %H:%M") + t[is.na(t)] <- NULL + return(t) + } + + get_places <- function(place_list) { + p <- " ?([^,]+)\\(\\d+\\),?" + match <- str_match_all(place_list, p) + return(lapply(match, function(x) { + return(str_to_lower(x[, 2], locale = "pl")) + })) + } + +Poza tym program usuwa zbędne kolumny. W tym też momencie nadchodzi pora +na jakże długą linię służącą do odsortowania ostrzeżeń niedotyczących +danego miejsca. Taki wynik jest zwracany użytkownikowi w formacie JSON. + + fin <- df %>% + # filter(messtype == '0') %>% + mutate( + regions = get_places(df$regions), + file = gsub("tmp/", "", file), + + prob = as.numeric(sub("%", "", prob)) / 100, + + starttime = format_time(df$startday, df$starthour), + endtime = format_time(df$endday, df$endhour), + canceltime = format_time(df$day, df$hour), + + lvl = as.numeric(lvl), + messtype = as.numeric(messtype), + infile = as.numeric(infile), + warn_id = as.numeric(warn_id), + ) %>% + # mutate(include = unlist(lapply(regions, function(x) { place %in% x }))) + # %>% filter(include) %>% select(-c(include)) %>% + # ^ umożliwia sortowanie po powiatach + select(-c(V1, startday, starthour, endday, endhour, day, hour)) + + knitr::kable(fin %>% select(event, starttime, endtime, rso), + caption = "Przykładowy wynik działania algorytmu") + + + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Przykładowy wynik działania algorytmu
eventstarttimeendtimerso
Silny mróz2022-01-11 21:00:002022-01-12 08:00:00Woj. małopolskie (1 powiat), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
Silny mróz2022-01-11 23:00:002022-01-12 08:00:00Woj. podlaskie (10 powiatów), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
Silny mróz2022-01-11 21:00:002022-01-12 08:00:00Woj. podkarpackie (2 powiaty), IMGW-PIB wydał ostrzeżenie pierwszego stopnia o silnych mrozach
+ +Przykładowy wynik działania algorytmu + +Oczywiście, jeśli żadne ostrzeżenia nie zostaną znalezione, API zwróci +pusty wektor, który w JSONie jest reprezentowany jako `[]`. Ten warunek +nie jest tu jednak wprowadzony. + +## Sprawdzenie założeń + +Autor niestety wciąż nie jest pewien, czy opracowane przez niego +wyrażenie regularne działa w pełni. Można je co najwyżej sprawdzić na +zestawie wszystkich poprzednich ostrzeżeń, co jednak jest trudne, ze +względu na ich inny sposób przechowywania na stronie (są one uprzednio +kompresowane). + +## Interpretacja wyników + +We wszystkich komunikatach, które autor napotkał algorytm zadziałał +poprawnie, wskazuje to na jego wysoką skuteczność. Niestety aktualne +rozwiązanie działa stosunkowo wolno i jest ograniczone, jak chodzi o +zmiany formatu ogłoszeń. Założenia na temat ich formy także są dość +silne, co jednak wynika z bierności IMGW. Cała potrzeba produkcji +takiego oprogramowania w XXI wieku wydaje się absurdalna. Zdaniem +autora, dostęp do tego typu ostrzeżeń, jako informacjach umożliwiających +ludziom bezpieczną egzystencję, w systemach automatycznych powinien być +jak najprostszy. Program pozostawia więc wiele do życzenia, ale wciąż +uzupełnia lukę ignorowaną przez organ ostrzeżenia wydający. + +## Informacja o użytych pakietach + + +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nazwa paczkiOpisMiesięczne pobraniaAutorzyData opublikowania
xml2Pobieranie dokumentów HTML620 159Hadley Wickham30.11.2021
tidyverseZestaw bibliotek wspomagających pracę w R689 554Hadley Wickham15.04.2021
pdftoolsScrapowanie tekstu z plików PDFbrak danychJeroen Ooms06.05.2021
stringrPrzetwarzanie ciągów znakowych, regex806 997Hadley Wickham10.02.2019
plumberGenerowanie API ze skryptów R11 776Barret Schloerke24.03.2021
+ +## Polecana literatura oraz linki do wykorzystanych/przydatnych stron + +- Zdecydowanie nie dokumentacja API IMGW +- +- +- Stack Overflow +- +- [Regular Expression Language - Quick + Reference](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference) +- [Markdown table + generator](https://www.tablesgenerator.com/markdown_tables) diff --git a/opis-projektu.pdf b/opis-projektu.pdf index 6096a11feede187b089835557da13248cf7f59f9..3cce3117748c7bda05f51165137a71e4ea2494ec 100644 GIT binary patch delta 7599 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 OE*M~L?lNVP;Qs){p|CLk delta 7368 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