Metodyka
Skąd bierzemy dane, co z nimi robimy i czego nie wiemy. Źródła, ograniczenia i cały pipeline krok po kroku.
Źródła danych
Żabka – główne źródło
Lokalizacje sklepów pochodzą z publicznego pliku JSON na zabka.pl – to ten sam plik, z którego korzysta sklepowa wyszukiwarka na ich stronie. Plik ma około 13 200 sklepów, każdy ze współrzędnymi GPS, adresem, godzinami otwarcia i flagami (piec Merrychef, otwarte w niedzielę, 24/7). Z pliku wyrzucamy dane osobowe dyrektorów, stałe pola (kraj, aktywność) i marketingowe URLe. Zostają same dane analityczne.
Podział administracyjny i mapowanie
Strukturę podziału terytorialnego Polski (trzy poziomy: województwa, powiaty i gminy) budujemy bezpośrednio z oficjalnych rejestrów GUS BDL oraz TERYT jako pierwszy krok procesu ETL. Sklepy oraz paczkomaty przypisujemy do tej hierarchii relacyjnie na podstawie ich nazw miejscowości, z wykorzystaniem oficjalnej Uniwersalnej Usługi Geokodowania GUGiK do jednoznacznego rozstrzygania przynależności mniejszych miejscowości i wsi, wraz z zapasowym dopasowaniem przestrzennym. Eliminuje to potrzebę stosowania uproszczonych plików GeoJSON i powolnych testów punkt-w-poligon.
Gospodarka – GUS BDL
Zarobki, bezrobocie i populacja na poziomie powiatów pochodzą z Banku Danych Lokalnych (zmienne 64428, 60270, 72305). Pobieramy je raz w roku. Nazwy powiatów trzeba normalizować – GUS lubi dodawać prefiksy (Powiat m.), przyrostki czasowe (od 2023) i zmieniać nazwy (jeleniogórski staje się karkonoski w 2021). Wszystko to czyścimy przed zmatchowaniem. Dokładność: dane pochodzą z roku, w którym GUS je opublikował, nie z bieżącego miesiąca.
Parki narodowe i krajobrazowe – GDOŚ
Granice parków i ich otulin pobieramy z WFS GDOŚ (warstwy ParkiNarodowe + ParkiKrajobrazowe). Mamy 259 obiektów: 46 parków narodowych z otulinami i 213 krajobrazowych. Każdy sklep sprawdzamy punkt-w-poligon – jeśli trafił do parku lub otuliny, dostaje flagę.
Wysokość terenu – GUGiK NMT
Wysokość n.p.m. z Numerycznego Modelu Terenu GUGiK (GetHByXY, współrzędne PL-1992). To jest opt-in (flaga --elevation), bo ponad 13 tysięcy zapytań HTTP. Wyniki trafiają do lokalnego cache (elevation_cache.json) – ponowne odpalenie ETL nie odpytuje punktów, które już mamy. Uwaga: serwis GUGiK przyjmuje tylko współrzędne PL-1992, nie WGS84, więc w ETL jest konwersja własnym kodem (bez pyproj).
Paczkomaty InPost
Punkty paczkowe z publicznego API InPost ShipX (typ parcel_locker). Każdy paczkomat przypisujemy do podziału administracyjnego za pomocą tego samego geokodera GUGiK i relacyjnego dopasowania co sklepy. To jest jednorazowe pobranie – sieć paczkomatów rośnie wolno, mamy około 31 800 punktów. Porównanie Żabek vs paczkomaty – z podziałem na województwa, powiaty i gminy – jest na dashboardzie.
Płazy – GBIF
Obserwacje płazów z Global Biodiversity Information Facility (klasa Amphibia, taxonKey=131) w Polsce. Mamy około 46 000 rekordów ze współrzędnymi. BallTree (haversine) zlicza obserwacje w promieniu 5 km od każdego sklepu. Najbardziej żabia Żabka ma 2 028 obserwacji – i to jest w Ursynowie, nie w żadnym parku narodowym.
Sąsiedztwo – obliczenia lokalne
Odległość do najbliższej innej Żabki obliczamy lokalnie przez BallTree (haversine, k=2). Najbardziej odosobniony sklep ma 27 km do najbliższego sąsiada (Michałowo, podlaskie). Punkt najdalszy od jakiejkolwiek Żabki – pusta przestrzeń w Bieszczadach – to około 46,5 km. Zero zapytań sieciowych, czysta geometria na danych już wczytanych.
Pogoda i niebo – Open-Meteo, OpenLightMap
Aktualne dane pogodowe z darmowego API Open-Meteo (temperatura, zachmurzenie) dla najzimniejszej i najcieplejszej lokalizacji Żabki. Podobnie najciemniejsze i najjaśniejsze niebo z OpenLightMap. Dane live, pobierane przy otwarciu strony. Nie wypiekamy ich do bazy – pogoda z wczoraj nikomu nie potrzebna.
Potok ETL
Mamy codzienny potok, który pobiera dane, czyści, wzbogaca i zapisuje do bazy. Wygląda tak:
- Pobranie. Pobieramy publiczny plik JSON z lokalizacjami sklepów. Jak API niedostępne, bierzemy z lokalnego pliku fallback z
data/input/. - Oczyszczenie. Deduplikacja po
storeId(około 32 duplikaty w źródłach), normalizacja nazw miast (LEGNICAstaje sięLegnica), usuwanie<br>i kodów pocztowych z ulic, wyrzucenie danych osobowych i stałych pól. - Wzbogacenie. Każde źródło niezależnie: regiony, sąsiedztwo, płazy, parki, elewacja, GUS, paczkomaty. Błąd jednego źródła nie przerywa potoku – kolumna zostaje NULL i ETL idzie dalej. Każde zapytanie sieciowe ma retry: 3 próby z odstępem 60 sekund.
- Diff. Porównujemy z poprzednim snapshotem: nowe
store_idto sklep otwarty, zniknięte – zamknięty. Wynik zapisujemy jako historia zmian. - Zapis do bazy. Zapis do DuckDB. Nowe kolumny dodajemy przez
ALTER TABLE ADD COLUMN IF NOT EXISTS(bez DEFAULT, bo DuckDB nie obsługuje DEFAULT w ALTER). Baza przechowuje ostatnie 6 miesięcy snapshotów – starsze usuwamy. - Czyszczenie cache. Po udanym ETL czyścimy cache Redis. Backend odbudowuje odpowiedzi na następnym zapytaniu. Redis jest opcjonalny – jak niedostępny, backend działa bez niego.
Czego nie gwarantujemy
Ograniczenia danych
- Około 218 sklepów nie ma daty otwarcia w źródłach – nie pojawiają się na wykresie historii wzrostu.
- Populacje powiatów to dane roczne z GUS, nie bieżące szacunki.
- Sklepy na granicach uproszczonych poligonów mogą trafić do sąsiedniego województwa (dotyczy około 3 punktów).
- Godziny otwarcia pochodzą ze źródła Żabki, nie z weryfikacji terenowej.
- Dane pogodowe są live – mogą różnić się od off-line bazy.
- Obserwacje GBIF zależą od aktywności obserwatorów – mniej zamieszkałe regiony mają mniej rekordów nie dlatego, że jest mniej płazów, tylko dlatego, że mniej osób tam patrzy.
- Brakujące źródło wzbogacenia zostawia kolumnę NULL – tabela jest spójna, ale niekompletna.
- Soft delete: sklepy, które zniknęły ze źródła, dostają
deleted_atw bazie. Zapytania domyślnie filtrujądeleted_at IS NULL, więc zamknięte sklepy nie psują statystyk.