Files
WeatherTool/dev/seed_weather.sql
T
2026-08-19 17:29:03 +03:00

155 lines
6.0 KiB
PL/PgSQL

\set ON_ERROR_STOP on
BEGIN;
CREATE TABLE IF NOT EXISTS weather (
dateTime TIMESTAMP NOT NULL,
city VARCHAR(255) NOT NULL,
tempMax DOUBLE PRECISION,
tempMin DOUBLE PRECISION,
tempAvg DOUBLE PRECISION,
precipitation DOUBLE PRECISION,
windAvg DOUBLE PRECISION,
windMax DOUBLE PRECISION,
visibilityMin DOUBLE PRECISION,
visibilityAvg DOUBLE PRECISION,
snowAvg DOUBLE PRECISION,
atmPressure DOUBLE PRECISION,
dewPoint DOUBLE PRECISION,
humidity DOUBLE PRECISION,
sunDuration DOUBLE PRECISION,
phenomena TEXT[],
UNIQUE(city, dateTime)
);
WITH
settings AS (
SELECT
date_trunc('hour', CURRENT_TIMESTAMP)::timestamp AS end_at,
(date_trunc('hour', CURRENT_TIMESTAMP) - INTERVAL '18 months')::timestamp AS start_at
),
cities(city, climate_offset) AS (
VALUES
('Ainaži', -0.8), ('Alūksne', -2.3), ('Bauska', 0.7), ('Dagda', -1.7),
('Daugavgrīva', 0.6), ('Daugavpils', -0.9), ('Dobele', 0.8), ('Gulbene', -2.0),
('Jelgava', 0.9), ('Kalnciems', 0.7), ('Kolka', 0.3), ('Kuldīga', 0.8),
('Lielpēči', 0.2), ('Liepāja', 1.1), ('Madona', -1.8), ('Mērsrags', 0.2),
('Pāvilosta', 0.9), ('Piedruja', -1.4), ('Priekuļi', -1.6), ('Rēzekne', -1.2),
('Rīga', 1.3), ('Rucava', 1.0), ('Rūjiena', -1.0), ('Saldus', 0.3),
('Sigulda', -0.8), ('Sīļi', -1.3), ('Skrīveri', -0.5), ('Skulte', 0.0),
('Stende', -0.3), ('Valmiera', -1.2), ('Ventspils', 0.9), ('Vičaki', 0.2), ('Zīlāni', -1.1),
('Zosēni', -2.1)
),
hours AS (
SELECT generate_series(settings.start_at, settings.end_at, INTERVAL '1 hour') AS observed_at
FROM settings
),
base AS (
SELECT
hours.observed_at,
cities.city,
cities.climate_offset,
abs(hashtext(cities.city || hours.observed_at::text)) AS sample_hash,
7.5
+ 12.5 * sin(2 * pi() * (extract(doy FROM hours.observed_at) - 172) / 365.25)
+ 2.8 * sin(2 * pi() * (extract(hour FROM hours.observed_at) - 9) / 24)
+ cities.climate_offset
+ 1.8 * sin(extract(epoch FROM hours.observed_at) / 173000 + cities.climate_offset) AS temperature
FROM hours
CROSS JOIN cities
),
weather_values AS (
SELECT
*,
CASE
WHEN sample_hash % 100 < 13
THEN round(((sample_hash % 190) / 10.0 + 0.2)::numeric, 1)::double precision
ELSE 0.0
END AS rain,
round((1.2 + (sample_hash % 65) / 10.0)::numeric, 1)::double precision AS wind,
round((58 + (sample_hash % 34) + 8 * cos(2 * pi() * extract(doy FROM observed_at) / 365.25))::numeric, 1)::double precision AS relative_humidity
FROM base
),
final_values AS (
SELECT
*,
GREATEST(0.0, LEAST(100.0, relative_humidity)) AS bounded_humidity,
CASE
WHEN temperature < 1.0 AND rain > 0
THEN round((rain * 0.8 + (sample_hash % 25) / 10.0)::numeric, 1)::double precision
WHEN temperature < -2.0
THEN round(((sample_hash % 80) / 10.0)::numeric, 1)::double precision
ELSE 0.0
END AS snow,
CASE
WHEN extract(hour FROM observed_at) BETWEEN 7 AND 18 AND rain = 0
THEN round((35 + sample_hash % 26)::numeric, 1)::double precision
ELSE 0.0
END AS sunshine_minutes
FROM weather_values
)
INSERT INTO weather (
dateTime, city, tempMax, tempMin, tempAvg, precipitation, windAvg, windMax,
visibilityMin, visibilityAvg, snowAvg, atmPressure, dewPoint, humidity,
sunDuration, phenomena
)
SELECT
observed_at,
city,
CASE WHEN sample_hash % 997 = 0 THEN NULL ELSE round((temperature + 1.8 + (sample_hash % 12) / 10.0)::numeric, 1)::double precision END,
CASE WHEN sample_hash % 991 = 0 THEN NULL ELSE round((temperature - 1.6 - (sample_hash % 10) / 10.0)::numeric, 1)::double precision END,
round(temperature::numeric, 1)::double precision,
rain,
wind,
round((wind + 1.5 + (sample_hash % 70) / 10.0)::numeric, 1)::double precision,
CASE
WHEN rain > 12 THEN round((0.8 + sample_hash % 20 / 10.0)::numeric, 1)::double precision
WHEN bounded_humidity > 88 THEN round((1.5 + sample_hash % 40 / 10.0)::numeric, 1)::double precision
ELSE round((8 + sample_hash % 80 / 10.0)::numeric, 1)::double precision
END,
CASE
WHEN rain > 12 THEN round((3 + sample_hash % 40 / 10.0)::numeric, 1)::double precision
ELSE round((12 + sample_hash % 90 / 10.0)::numeric, 1)::double precision
END,
snow,
round((1000 + sample_hash % 310 / 10.0 + 5 * sin(extract(epoch FROM observed_at) / 250000))::numeric, 1)::double precision,
round((temperature - (100 - bounded_humidity) / 5.0)::numeric, 1)::double precision,
CASE WHEN sample_hash % 983 = 0 THEN NULL ELSE round(bounded_humidity::numeric, 1)::double precision END,
sunshine_minutes,
CASE
WHEN snow > 0.5 THEN ARRAY['snow']::text[]
WHEN rain > 12 THEN ARRAY['heavy rain', 'overcast']::text[]
WHEN rain > 0 THEN ARRAY['rain']::text[]
WHEN bounded_humidity > 88 THEN ARRAY['fog']::text[]
WHEN wind > 6.5 THEN ARRAY['windy']::text[]
WHEN sunshine_minutes > 0 THEN ARRAY['clear']::text[]
ELSE ARRAY['cloudy']::text[]
END
FROM final_values
ON CONFLICT (city, dateTime) DO UPDATE SET
tempMax = EXCLUDED.tempMax,
tempMin = EXCLUDED.tempMin,
tempAvg = EXCLUDED.tempAvg,
precipitation = EXCLUDED.precipitation,
windAvg = EXCLUDED.windAvg,
windMax = EXCLUDED.windMax,
visibilityMin = EXCLUDED.visibilityMin,
visibilityAvg = EXCLUDED.visibilityAvg,
snowAvg = EXCLUDED.snowAvg,
atmPressure = EXCLUDED.atmPressure,
dewPoint = EXCLUDED.dewPoint,
humidity = EXCLUDED.humidity,
sunDuration = EXCLUDED.sunDuration,
phenomena = EXCLUDED.phenomena;
ANALYZE weather;
COMMIT;
SELECT
count(*) AS synthetic_rows,
count(DISTINCT city) AS stations,
min(dateTime) AS first_observation,
max(dateTime) AS last_observation
FROM weather;