Adcon device status
Checked once a day, straight from Adcon's own admin console: which RTUs they have flagged "not in use" -- the one place that distinguishes a known-retired unit from a station that just stopped reporting.
| Station | Khmer name | Source | Sensors | Status | Public map |
|---|
Select a station to edit its information and sensors.
Every downloadable report. Downloads are for signed-in users only, unless Allow public is ticked: then anyone can download it from the public map too. The public map always shows station data as graphs either way.
📊 The Report button on the public pages
Every public page has a Report button: a graph from any period to any other, PNG and PDF, and the map’s picture. Signed-in users see everything. For the public, the stations’ readings (and every model against a station) and the CSV download stay off until ticked here. Soil, pan and battery sensors are never public.
Station data (detail)
One station, every sensor, one row per time step.
Latest values, all stations
Storms & thunder
Thunderstorm log: every place flagged as a possible, likely or severe thunderstorm (cold cloud top, heavy radar echo, raining gauge). Storm tracks: tracked storm cells with size, speed, heading and estimated arrival. Up to 400 days per download.
Rain sources vs gauges
MOWRAM radar, GK2A and JMA side by side at the same moment,
replayable day by day, and scored against every rain gauge (/rain-compare). Signed-in users only, unless
Allow public is ticked.
Station list & GPS registry
Admin only (contains source details).
Rain sources vs gauges
MOWRAM radar, GK2A and JMA at the same moment, replayable day by day and scored against every rain gauge, with the best source and the average day. Signed-in staff only, unless Allow public is ticked. Open in a new tab ↗| # | Code | Registry name | Province | GPS | Linked live station | Δ |
|---|
Source timing
For every source: how long after its time the source itself put its newest item out (nothing here can shorten that), and how long this server then took to store it. A source counts as in step when its newest item is stored; late when it has been out longer than a few checks and is still not stored. Checked every 2 to 15 minutes, the last 24 hours.
| Source | Every | Source puts it out after | Stored here after that | Newest at the source | Out at | Stored at | Status |
|---|
Sutron data
Check that Sutron data is arriving and import it by hand. The database feed (report app) is checked every 15 minutes. Sutron publishes new readings about once an hour, so readings already stored are skipped.
Report-app feed (database, near real time)
| Station | Newest reading | Age | Sensors |
|---|
| Source | Finished | Result | Stations | Points | Message |
|---|
Custom Prediction
Using open data algorithm prediction with local station data
JMA 6-hour rain vs the model learned from history
For every 6-hour window the stations measured whether it rained. The same windows were forecast by JMA's ensemble, the model that learns each station's history, Open-Meteo and the combined model. Compared on identical windows: which is closer to reality, and are they close to each other?
Compare weather models
Pick two or more models, or all of them, and see which one stayed closest to the local stations' own readings in the database.
How these numbers are calculated
What "Physics" and "AI" mean
Physics (numerical weather prediction): the model is a computer program of the laws of physics — air motion, conservation of mass and energy, heat, moisture, radiation — solved step by step on a three-dimensional grid. It starts from the latest observations, which are blended into the previous forecast (data assimilation). GFS, ECMWF IFS and DWD ICON work this way. It needs a supercomputer, but it obeys the physical laws by construction.
AI (machine learning): a neural network is trained on decades of past weather and learns how the atmosphere usually changes from one step to the next. No physics equations are solved when it runs, so a forecast takes minutes instead of hours. It can be wrong where a situation was rare in its training data. ECMWF AIFS and Google WeatherNext 2 work this way.
This page's own correction (the table above) is neither: it is a simple statistical adjustment of Open-Meteo's forecast, learned from each station's past readings.
How the numbers are calculated
Measured value (o). The station's own hourly figure from the database, after the same sensor calibration the public map uses. Temperature, humidity and wind are compared with the average of the station's hour before and the hour of the forecast time; solar radiation and rain, which are totals over the preceding hour, with that preceding hour.
Forecast value (f). What the model had predicted for that hour, taken from Open-Meteo's archive of past forecasts for the model's grid cell over the station. Wind is in m/s.
Fair comparison. Only hours where every chosen model has a forecast and the station has a measurement are scored, so no model is tested on easier hours than another.
n = number of hours scored bias = (1/n) · Σ (f − o) positive: the model reads high MAE = (1/n) · Σ |f − o| average size of the error RMSE = √( (1/n) · Σ (f − o)² ) like MAE but large misses count more r = Σ(f−f̄)(o−ō) / √( Σ(f−f̄)² · Σ(o−ō)² ) 1 = rises and falls together with the station Mean of chosen = (1/k) · Σ f_model the average of the k models picked
Rain is judged differently, because most hours are dry and a forecast of "no rain" would otherwise score well. An hour is wet at 0.2 mm or more. H = both wet, M = station wet only, FA = model wet only:
POD = H / (H + M) share of real rain hours the model caught FAR = FA / (H + FA) share of the model's rain hours that stayed dry CSI = H / (H + M + FA) overall hit score, 1 is perfect (the models are ranked on this)
Best per station is the model with the lowest MAE there (highest CSI for rain), counted only where each model has at least 12 scored hours.
All sensors scores every chosen model on every value it provides (temperature, humidity, wind speed, gust, wind direction, pressure, solar radiation, rain) to show which suits the stations best. Each value has its own unit, so a model's error is compared with the best chosen model's for that value: MAE ÷ best MAE (for rain, best CSI ÷ CSI). 1.00 means as good as the best; 1.50 means 50% more error. A model's overall figure is the average of these over the values it provides, and the most suitable model is the lowest among models that cover every compared value.
Only a few days of overlap exist so far, so treat rankings as provisional.
References: where the methods and formulas come from
- GFS — NOAA NCEP Environmental Modeling Center, Global Forecast System
- ECMWF IFS — IFS documentation; the 4D-Var fitting step: ECMWF data assimilation
- ECMWF AIFS — AIFS – ECMWF's data-driven forecasting system (arXiv:2406.01465)
- DWD ICON — Deutscher Wetterdienst, ICON model description
- WeatherNext 2 — Google, open models for WeatherNext; research basis: GraphCast (arXiv:2212.12794) and GenCast (Nature); code: github.com/google-deepmind/graphcast; the feed used here: Open-Meteo WeatherNext page
- Past forecasts of every model — Open-Meteo Historical Forecast API
- Bias, MAE, RMSE, correlation, POD, FAR, CSI — Forecast Verification: Issues, Methods and FAQ (Australian Bureau of Meteorology / WWRP)
- Heat index (Rothfusz regression) — US National Weather Service heat index equation
- Dew point (Magnus formula, constants 17.625 and 243.04) — Dew point (Wikipedia, Magnus formula section)
The simplified formulas shown for each model are textbook forms written for this page to explain the idea; the sources above hold the exact equations. Not taken from a source: the shrinkage strength (24 and 12 pseudo-hours), the fade time of the latest reading, and the 5% and 24-hour thresholds for "better" are this project's own design choices, tuned on this network's data.
Comparison between Open-Meteo vs Local Station
Custom prediction
| Value | Now | +1 h | +3 h | +6 h | +12 h | +24 h | How good |
|---|
- better the custom forecast's average error is at least 5% smaller than raw.
- similar within 5% either way: the correction makes little difference.
- not better the custom forecast is more than 5% worse than raw, so the raw figure is shown instead.
- provisional fewer than 24 hours were tested, so it is too early to say.
- no data no measurements to test against.
Chart
| Time | Measured | Custom prediction | Open-Meteo |
|---|
How good is the correction?
| Measurement | Stations | Error before → after | Better / worse | Memory | Verdict |
|---|
5-minute rain nowcast formula
The weights behind the public "5-minute rain nowcast" card for the station currently picked above (). Public cards only name which formula produced a number; the actual maths lives here.
Satellite/radar "pattern reading" calibration
How a satellite cloud-top brightness or radar dBZ reading gets turned into an approximate mm/h: learned from every real station-hour where both that station's own gauge and its own satellite/radar reading exist, then applied to ANY point's current reading -- including a province/district with no gauge at all. See app/satellite.py:train_gray_mm and app/radar.py:train_dbz_mm.
Public feedback ("was this prediction right?")
The public 1-5 star rating anyone can leave on a station or a satellite/radar map point (app/feedback.py). Cambodia-only, rate-limited, and the visitor's own device location is checked against the target -- a record to compare by eye against what the prediction cards actually showed (each row's snapshot), never fed back into any model.
Ground gauge over radar / satellite: how "raining now" is decided
A rain gauge measures rain at its own spot,
once a minute. Radar (every ~15 min) and the satellite image (every ~10–60 min) only estimate
it from what they see aloft, and are older by the time they are drawn. So the public map trusts a fresh
gauge over both, in the browser (app/static/app.js: overlayGauges / overlaySatGauges),
re-run on every station refresh:
- Which gauges count: a station reporting rain now (
raining, rain in the last 30 min) whose latest report is no older thanGAUGE_FRESH_MIN= 45 min. - Where: every radar / satellite point within
GAUGE_RADIUS_KM= 6 km of that gauge. - Intensity: rate = mm in the last 30 min × 2 (mm/h, minimum 0.2); then
dBZ = 10·log10(200 · rate1.6)(the same Marshall–Palmer relation the radar layer uses), floored at 14 dBZ ("light"). Bands: ≥13 light, ≥26 moderate, ≥42 heavy, ≥60 severe. - Radar points: raised to at least that dBZ / band, and the gauge's own spot is added to the rain image and the falling-rain animation.
- Satellite points: label set to "likely rain", cloud shade raised to at least 200/255, next-hour chance shown as 100 %, 3-hour chance at least 90 %.
- One direction only: a gauge can raise a reading, never lower it. A dry gauge does not erase an echo, because rain can be falling a few km away.
Not applied during playback (it replays archived frames as they were). The server-side archives and the nowcast are unchanged by this rule.
Cloud top temperature (CTT) in the rain nowcast
GK2A's Level-2 cloud-top temperature
(app/satellite_ctt.py, actual °C, decoded from the product's colour legend) feeds the
5/10/15/30/60-minute nowcast as the "cloud" term:
level = clamp((−10 − Ttop) / (−10 − −60), 0, 1), added to
the log-odds as W_SAT[h] × level. It replaces the older IR-brightness level
when fresh (≤ 50 min) and is never added to it — the two measure the same thing, so adding both would
double-count. Each station's card/inputs record which one was used (cloud_proxy_source).
CTT is a cloud-top reading, not ground air temperature.
Advection (cloud motion)
Built: the prediction simulation shifts rain chance
downwind by the satellite's measured cloud motion
(app/satellite.py:cloud_motion(); position(t+Δt) = position(t) +
velocity × Δt, applied as the higher of a place's own forecast or the chance at the
upwind place, for the next 2 h). The nowcast itself still treats a reading as staying where it was seen.
How close the forecast was at every station
Overall
Bias is the average signed error: positive means the forecast reads high. Average error ignores direction and is what a reader actually feels. Hours with no sensor reading are left out of both.
- better the average error is small enough to trust: within the first limit for that value in the table below.
- fair usable but noticeably off: within the second limit for that value.
- off beyond that: the forecast should not be relied on at this station for this value.
- no data the station had no readings to compare with.
| Limits used for each value | Better within | Fair within |
|---|---|---|
| Temperature | 1 °C | 2 °C |
| Humidity | 5 % | 10 % |
| Dew point | 1 °C | 2 °C |
| Wind speed | 0.5 m/s | 1 m/s |
| Wind gust | 1 m/s | 2 m/s |
| Wind direction | 30 ° | 60 ° |
| Pressure | 2 hPa | 5 hPa |
| Sea-level pressure | 1 hPa | 3 hPa |
| Solar radiation | 50 W/m² | 100 W/m² |
| Rain | 0.5 mm per hour | 2 mm per hour |
| Evapotranspiration (ETo) | 0.05 mm per hour | 0.1 mm per hour |
| Station sensor | Open-Meteo field | Compared here |
|---|---|---|
| Temperature | temperature_2m | Yes |
| Humidity | relative_humidity_2m | Yes |
| Dew point (worked out from the station's temperature and humidity) | dew_point_2m | Yes |
| Wind speed | wind_speed_10m | Yes |
| Wind gust | wind_gusts_10m | Yes |
| Wind direction | wind_direction_10m | Yes |
| Pressure at the station | surface_pressure | Yes |
| Pressure at sea level | pressure_msl | Yes |
| Solar radiation | shortwave_radiation | Yes |
| Rain (hourly) | precipitation | Yes |
| Rain today (running total) | precipitation, added up | Same gauge as Rain, not repeated |
| Evapotranspiration (ETo) | et0_fao_evapotranspiration | Yes |
| Evaporation pan level | none | No: it is a water level, not a rate |
| Wind gust direction | none | No: Open-Meteo has no gust direction |
| Battery, fan and rain flag | none | No: device health, no weather counterpart |
| Cloud cover, visibility, UV index, apparent temperature | (Open-Meteo only) | No: the stations have no such sensor |
Wind and gust are compared in m/s. Wind direction is an angle, so the error is the shortest way round the circle and an hour's direction is a vector average. Direction is only scored in hours with at least 1 m/s of wind, because in near-calm air it is noise. Station pressure differs from the model's by the height difference between the station and the model's grid cell.
By how far ahead
| Lead | Bias | Avg error | Samples | Verdict |
|---|
| Station | Bias | Avg error | Worst | Samples | Verdict |
|---|
Not compared
| Station | Why |
|---|
Suggestions
What the agents learned overnight and what to do next. Each agent applies by itself what proved better on days it never learned from, and keeps the rest as it is; below, what a person has to do (an agent cannot visit a gauge, add a key or decide a design), then everything the agents changed and kept, with the evidence.
Applied by the agents
Kept as they are
Learning agent
Runs in its own container (weathermap-learner), apart from the website. Every night at 02:00 it learns from the rain gauges what GK2A, the radar, JMA, the other gauges and five weather models (ECMWF IFS, ECMWF AIFS, GFS, ICON, Google WeatherNext 2) showed before every quarter hour of rain and of no rain; it tests itself forward in time, on days and gauges it never saw, and the map uses it only where it beats the rule. Refreshed every 30 seconds.
Usage agent
Once a day (after 03:00, in the learning agent’s container) it reads the anonymous feature events the pages report — never a position, a route, a search or anything typed — and learns what people use, on phones or computers, in Khmer or English, at what hours, and what they open next. It writes suggestions for the team below, and gives the map a “people often look at … next” table (pairs seen in at least 10 visits only), from which a visitor’s own browser offers one suggestion after an action.
Last night’s test against the rule
Each test day and each group of gauges was predicted by a model trained only on the days before it and on the other gauges (as a square of the map without a gauge), with a threshold chosen only on earlier days. CSI = hits ÷ (hits + misses + false alarms), 1 is perfect. A model is used only when it beats the rule by 0.01 over the test days and on at least half of them; otherwise the rule stays.
Learning from the old history
Every gauge’s hourly rain since 2020 with the satellite and radar pictures of that hour. Tested forward in time (each year by models trained only on the years before it, on gauges they never saw) against the climate and persistence. The 15-minute agent then tests its chance as one more input every night and uses it only where it wins by 0.01 CSI on half the days; this switch can turn that off.
Suggestions for the team
What it leans on
How much CSI each kind of information is worth: the drop when that information is shuffled at random on the test days. 0 means the model does not need it.
What is used
Gauges set aside
Never learned from and never used for “raining now”: the faulty list of the rain prediction, and the gauges the agent found reporting rain that no source and no other gauge saw (more than half of their wet quarter hours; the usual gauge: 2 %).
What people open next
What it learns from
Radar quality
Every night, from the last 14 days of pictures, the squares of the radar’s reach it cannot be trusted on: hills that echo most of the time, its own map’s labels, borders and thin beams that never echo, wedges its beam does not reach. There every page takes the radar as “no picture” and the satellites decide.
Cloud and rain motion
The motion agent moves each picture of GK2A, JMA and the radar on by a motion and compares it with that source’s next pictures (its own interval, 30 and 60 minutes), within one square, against keeping it still. It learns which motion, how much faster and which way turned; the arrows of a source take its factor and turn only where they did better on the last 2 days.
Wind at the ground and storms
Each source’s motion against the stations’ wind vanes (within 45°), the storms’ speed against where they really were an hour later, and how often storms rained on the gauges under them.
Data alerts
Every source is judged by what really got stored: its newest picture or reading against how late it may be. A source that stops arriving shows here and in red at the top of Settings, and goes to Telegram once, again every 6 hours while it stays down, and once when it is back. Checked every minute.
Telegram
Make a bot with @BotFather in Telegram (it gives the token), add the bot to your group or start a chat with it, then put the chat id here (a group id starts with -100; @userinfobot or @getidsbot tell it). The token is kept on the server only and never shown again.
Public Telegram bot
Anyone can follow a district in Telegram: its warnings when they reach orange or red (and the all clear), and every morning at 6:00 the day's forecast, in Khmer or English. Make a second bot with @BotFather (not the alert bot above), put its token here and switch it on; it runs from the collector. The warnings page then offers "Alerts on Telegram" for the chosen district. Only each chat's id, its language and its districts are kept (no name, phone number or position); /stop forgets a chat.
Public storm alerts ការជូនដំណឹងព្យុះជាសាធារណៈ
Storm alerts for everyone, sent by the data-alert bot above to a separate public group or channel: storm now or near, likely within the hour, a typhoon within 1000 km, and optionally "storm over". Weather only: data-source, station and security alerts never go there. Khmer then English, each with buttons to the map. Add the bot to the group (as admin for a channel), send any message in it, then press Find my group.
ផ្ញើដោយបូតខាងលើ ទៅក្រុមសាធារណៈដាច់ដោយឡែក។ មានតែព័ត៌មានអាកាសធាតុប៉ុណ្ណោះ។
Donation alerts អ្នកគាំទ្រ
The donate page lists the people who paid through its ABA KHQR, newest first, read from ABA's payment alerts on Telegram. Telegram never lets a bot read another bot in a group, so: make a Telegram channel, have ABA send its payment alerts there, and add the data-alert bot (Telegram card) to it as an admin. Then a payment shows here and the channel appears below to choose. The donate page itself stays hidden until it is published.
Public map visibility
These rules apply to everyone viewing the public map. They take effect immediately.
Site title and text
At the top of the map and as the front page’s title, in each
language. Empty = the built-in text. In the subtitle, {stations} and {every} become the number of
stations and the minutes between two readings.
Site name and icons
On every page: the name is the front page’s heading and tab title and follows every other page’s own title (“Rain sources vs gauges · name”); the site icon is the browser tab’s icon and the phone’s home-screen icon; the logo is the square at the top of the front page. Empty = the built-in name and icons. On the map, its own title above (Site title and text) still comes first. Images: PNG, JPEG, WebP, ICO or SVG, up to 256 KB, square works best (SVGs with scripts are refused). Admins only.
Data collection
How often the collector pulls every source: Adcon, KMA, all stations together.
Education & Research page
The public Education & Research page does not calculate anything when someone opens it. The system works out the figures on the schedule below and stores them; visitors only read the stored result, so many visitors cost almost nothing.
| Username | Role | Status | Created |
|---|
Block visitors by place
Block the site for continents, countries, regions (provinces or states), cities or IP addresses, or allow only the places listed. nginx (the front proxy) applies it within about a minute of saving, so a blocked visitor never reaches the site: they see "403 Forbidden". Places come from the GeoLite2 City database (MaxMind, with the key under Analytics). Your own address, private addresses and the server itself are never blocked.
Places
| Type | Place | IP ranges |
|---|
Check an address
Roles
Each role includes everything the one before it can do. The server enforces this on every request; hiding a button is only a convenience.
| Role | Users | Can do |
|---|---|---|
| viewer | 0 | Sign in and read: stations, sensors, collection runs, registry, forecast comparison and prediction. |
| operator | 0 | Everything a viewer can, plus edit stations, sensors and the GPS registry. |
| admin | 0 | Everything an operator can, plus users and roles, all Settings (map visibility, collection interval, email, sign-in, backups, analytics, system, audit), visitors, and running predictions. |
Change a person's role under Users.
Outgoing email (SMTP)
Sign in with Google
Google proves who someone is. What they may do still comes from the Users tab — an account that does not exist here cannot sign in, whatever Google says.
Storage and the 5-year plan
Everything is kept for years, so this shows how fast the data grows and how long the disk lasts at that rate. The satellite / radar pictures and grids grow every hour; the rest is small. Pictures are stored as lossless WebP (every pixel kept, about a third smaller than PNG). At the warning level the page shows a banner; at the stop level the system stops storing the cropped pictures and keeps only the small hourly grids, so the database never runs out of room.
| Part | Size now | Grows per day |
|---|
Backups
The nightly measurements dump leaves out the satellite archive and the ERA5 history: the archive gets one file per day with only that day's pictures and grids (written once), the ERA5 history one dump a week. Before, every nightly dump copied the whole archive again, and 14 of them would have filled the disk. These files are on the same disk as the database: copy /backups to another machine to be safe from losing the disk itself.
Written to /backups on the server. The configuration
database holds the accounts, roles, settings and sensor calibration, so it is the
one that is painful to lose. .env is deliberately not included — it
holds the database password in clear text.
| File | What | Size | When |
|---|
Restoring is a shell operation on purpose, because it overwrites a live database:
Analytics settings
Visitor records are personal data. They are kept in the configuration database, readable by admins only, and raw rows are deleted after 400 days.
IP location database (MaxMind)
Looks up visitor IP addresses on the MaxMind GeoIP web service. Needs an account ID and licence key from your MaxMind account. Used only while "Resolve location from IP address" is on, and only if the local GeoLite2 database (kept fresh by geoipupdate on the host) is missing. The key is stored on the server in the database and is never shown again, so it survives a redeploy. It can also be set in the server's .env file (MAXMIND_ACCOUNT_ID, MAXMIND_LICENSE_KEY) as a second place; what is saved here takes priority.
Weather models
Each model's past forecasts are fetched from Open-Meteo and scored against the stations' own readings already stored in this database. Nothing else is read. Compare them on the Custom Prediction tab.
| Use | Model | Type | Stations | Hours stored | Latest | How it works |
|---|
What "Physics" and "AI" mean
Physics (numerical weather prediction): the model is a computer program of the laws of physics — air motion, conservation of mass and energy, heat, moisture, radiation — solved step by step on a three-dimensional grid. It starts from the latest observations, which are blended into the previous forecast (data assimilation). GFS, ECMWF IFS and DWD ICON work this way. It needs a supercomputer, but it obeys the physical laws by construction.
AI (machine learning): a neural network is trained on decades of past weather and learns how the atmosphere usually changes from one step to the next. No physics equations are solved when it runs, so a forecast takes minutes instead of hours. It can be wrong where a situation was rare in its training data. ECMWF AIFS and Google WeatherNext 2 work this way.
This page's own correction (the table above) is neither: it is a simple statistical adjustment of Open-Meteo's forecast, learned from each station's past readings.
How the numbers are calculated
Measured value (o). The station's own hourly figure from the database, after the same sensor calibration the public map uses. Temperature, humidity and wind are compared with the average of the station's hour before and the hour of the forecast time; solar radiation and rain, which are totals over the preceding hour, with that preceding hour.
Forecast value (f). What the model had predicted for that hour, taken from Open-Meteo's archive of past forecasts for the model's grid cell over the station. Wind is in m/s.
Fair comparison. Only hours where every chosen model has a forecast and the station has a measurement are scored, so no model is tested on easier hours than another.
n = number of hours scored bias = (1/n) · Σ (f − o) positive: the model reads high MAE = (1/n) · Σ |f − o| average size of the error RMSE = √( (1/n) · Σ (f − o)² ) like MAE but large misses count more r = Σ(f−f̄)(o−ō) / √( Σ(f−f̄)² · Σ(o−ō)² ) 1 = rises and falls together with the station Mean of chosen = (1/k) · Σ f_model the average of the k models picked
Rain is judged differently, because most hours are dry and a forecast of "no rain" would otherwise score well. An hour is wet at 0.2 mm or more. H = both wet, M = station wet only, FA = model wet only:
POD = H / (H + M) share of real rain hours the model caught FAR = FA / (H + FA) share of the model's rain hours that stayed dry CSI = H / (H + M + FA) overall hit score, 1 is perfect (the models are ranked on this)
Best per station is the model with the lowest MAE there (highest CSI for rain), counted only where each model has at least 12 scored hours.
All sensors scores every chosen model on every value it provides (temperature, humidity, wind speed, gust, wind direction, pressure, solar radiation, rain) to show which suits the stations best. Each value has its own unit, so a model's error is compared with the best chosen model's for that value: MAE ÷ best MAE (for rain, best CSI ÷ CSI). 1.00 means as good as the best; 1.50 means 50% more error. A model's overall figure is the average of these over the values it provides, and the most suitable model is the lowest among models that cover every compared value.
Only a few days of overlap exist so far, so treat rankings as provisional.
References: where the methods and formulas come from
- GFS — NOAA NCEP Environmental Modeling Center, Global Forecast System
- ECMWF IFS — IFS documentation; the 4D-Var fitting step: ECMWF data assimilation
- ECMWF AIFS — AIFS – ECMWF's data-driven forecasting system (arXiv:2406.01465)
- DWD ICON — Deutscher Wetterdienst, ICON model description
- WeatherNext 2 — Google, open models for WeatherNext; research basis: GraphCast (arXiv:2212.12794) and GenCast (Nature); code: github.com/google-deepmind/graphcast; the feed used here: Open-Meteo WeatherNext page
- Past forecasts of every model — Open-Meteo Historical Forecast API
- Bias, MAE, RMSE, correlation, POD, FAR, CSI — Forecast Verification: Issues, Methods and FAQ (Australian Bureau of Meteorology / WWRP)
- Heat index (Rothfusz regression) — US National Weather Service heat index equation
- Dew point (Magnus formula, constants 17.625 and 243.04) — Dew point (Wikipedia, Magnus formula section)
The simplified formulas shown for each model are textbook forms written for this page to explain the idea; the sources above hold the exact equations. Not taken from a source: the shrinkage strength (24 and 12 pseudo-hours), the fade time of the latest reading, and the 5% and 24-hour thresholds for "better" are this project's own design choices, tuned on this network's data.
What is inside the history: each source, how far back, what is kept, its size
Read live from the database each time this opens.
| Data | From | To | State | What is kept | Size |
|---|
History from Local Station Server
The live collection only reads the last few days. Both source systems keep years of readings and return any date range. This imports all of it as hourly and daily figures (the raw one-minute samples are not stored), so the long-range graphs and the model comparison can use it. It runs on the server in the background: you can close this page or sign out and it keeps going, and if the server restarts it carries on by itself. It pauses between requests so the source servers are not strained. Fetching never overwrites anything. Data that is already stored is ignored and only the missing periods are added, so it is safe to press again whenever you like: it asks the servers only for new days, gaps and anything that failed last time.
| Source | Stations | Done | Stored from | Stored to | Data rows |
|---|
Stations
| Source | Station | Found since | Stored from | Stored to | Years | Data rows | Hours stored | State |
|---|
Data coverage and gaps
How many hours each period should have, how many are stored, how many were filled from Open-Meteo, and how many are still missing. A station is only expected to have data from its first stored hour of that sensor up to the start of today.
| Period | Expected hours | Stored | From Open-Meteo | Still missing | Complete |
|---|
Fill gaps from Open-Meteo
Uses the station, sensor and period chosen above. Open-Meteo's own hourly history is fetched for the station's coordinates and kept only for the hours the station is missing. It is stored apart from the station readings and labelled with its dataset, so it can be shown or hidden on the graphs and CSV, removed by filter, and never changes the forecast accuracy or model comparison, which keep using real station readings only. Sensors a station never had are not invented. A model cell (about 25 km wide for ERA5, 9 km for the IFS archive) will differ from a sensor at one point.
| Dataset | Sensor | Kind | Hours filled | From | To |
|---|
Health
Anything missing, running out, or configured in a way that will cause trouble later. The rings below refresh every 5 seconds on their own; everything else on this page only on Refresh.
Runtime
This system runs on Python and PostgreSQL. There is no PHP anywhere in it, so there is no PHP status to report — the interpreter and its packages below are the equivalent.
Security
The server's own scan every night about 01:30 (OS updates, container images, CDN libraries, open ports, SSH, TLS); the result also goes to Telegram.
Loading…
Dependency vulnerabilities
Checks every installed library against the published advisory database. Run it after any upgrade.
PostgreSQL
Blocked sign-ins
An address is blocked after repeated wrong passwords and unblocks by itself after the window. Clear it here to let it try again now.
| Address | Failed tries | Status |
|---|
Protection: blocking addresses that attack the site (Fail2Ban)
The server blocks an address for a while, on the website's ports, when it does what people reading the site never do: a flood of requests, looking for weak spots, coming by the server's IP address, guessing the admin password. Each new block of the same address lasts longer (up to a week). The office's address, the server itself and Docker's own networks are never blocked. Changes are applied by the server within a minute.
| Guards against | On | Requests or tries | Within (minutes) | Block for (minutes) | Blocked now / in all |
|---|
Blocked now
| IP | Reason | Until (Cambodia time) |
|---|
Allow list: never blocked
| IP | Note | Added |
|---|
Audit
Every change made in the admin, newest first. Click a row to see its full detail.
| When | From | Action | Target | Detail |
|---|
What is inside the history: each source, how far back, what is kept, its size
Read live from the database each time this opens.
| Data | From | To | State | What is kept | Size |
|---|
Data sources now (updated 19 October 2026)
- Satellite pictures (real clouds on the public map, the 17 products on
/jma-satellite, the satellite dots): Himawari-9, Japan Meteorological Agency (JMA). A new picture every 10 minutes, about 12 minutes after the scan; 2 km over Cambodia and its borders, 5 km further out. - Rain layer (Rain Image on the public map, its replay and prediction, rain on
/clouds, the storm inference): the rain rate measured by GK2A, Korea Meteorological Administration (KMA), every 10 minutes, 2 km. Chosen over JMA after scoring both against our rain gauges. - MOWRAM radar: in use again from 18 October 2026 — on the public map’s satellite & radar layer, in the rain comparison and on
/check(below); since that afternoon from MOWRAM’s own radar server on the 2 km squares, the square popup giving its dBZ and rain rate. MOWRAM satellite (its GK2A receiver and its website’s satellite picture), added the same morning, was removed that afternoon at the user’s request with all its data. - Detail pages:
/gk2a-satellite(GK2A satellite) and/jma-satellite(JMA satellite), every 10-minute picture on one shared 0.02° grid (about 2 km, the smallest pixel either satellite publishes here) with the storm cells, kept in the database; tap a cell to read it. /clouds(Detail Satellite Images) removed on 17 October 2026: it was built on the old Cambodia GK2A mirror, which is no longer used; old links open/jma-satellite.- Public map menu (17 October 2026): Satellite images = GK2A satellite / JMA satellite (switch the map's satellite layer between the two to compare them: every 10 minutes, each 2 km square cloudy / likely rain, tap a square to read it), Rain Image, Thunderstorm; JMA prediction and JMA pins moved to More. Removed, with the history only they used (backup in
/backups/removed-components-*): Imagery, Detail Satellite Images, Satellite cloud image (its old, coarser squares), Cloud top temp. - Stations: MOWRAM automatic weather station networks, read every minute; measured on 18 October 2026, 27 send a reading every minute, 23 every 15 minutes and 32 once an hour (sent up to an hour late). Forecasts: Open-Meteo, every 8 hours (below). All sources are named on the public map (attribution line and About); the line under the map’s title shows these intervals as measured (
/api/timing). - Every new picture is looked for every minute (both satellites publish one every 10 minutes; neither has a 2-minute picture over Cambodia).
- How often, how fine (checked 17 October 2026 on the operators’ own file lists): GK2A and Himawari-9 both picture Cambodia every 10 minutes. GK2A’s 2-minute scans (rain rate, visible) cover only the Korea area (28 pictures an hour there, 6 for the full disk), its East Asia area stops at about 20°N, and Himawari’s 2.5-minute scans cover Japan and typhoon target areas — so 10 minutes is the shortest interval over Cambodia. The server looks for a new picture every minute and grids it as soon as it lands. Rain rate and cloud-top temperature are made from the infrared channels: 2 km (0.02°) is their smallest pixel. GK2A’s visible channel is 0.5 km, but over Cambodia only in the full-disk file of 260 MB every 10 minutes (37 GB a day) — not fetched.
- Thunderstorm grid (
app/thunder10.py, tablethunder10_grid): every 10 minutes, every 2 km cell possible / likely / severe from GK2A’s cloud tops and rain rate and JMA’s heavy-rain areas — the storm clouds the satellites capture (neither carries a lightning sensor; FengYun-4’s lightning data is not public over Cambodia). On the public map’s Thunderstorm layer, on both satellite pages (Thunder) and in Reports. - History: GK2A rain rate and cloud-top grids every 10 minutes from 1 January 2020 (
app/history_import.py, containerweathermap-history, ~6 GB downloaded and ~35 MB stored per day of history); its storm cells, storm grid and thunderstorm grid are made for every slot. JMA keeps only one day on its own site: its history starts 16 October 2026. Kept from 2020 on (app/partition10.pyKEEP_FROM). - Prediction (
/prediction,app/outlook.py): GK2A + JMA only, no radar; the satellite part and the model part both calibrated on our rain gauges, a gauge raining now lifts the next hours, refreshed every 10 minutes, 2 km cells. The live proof is in the Prediction demo below. - Rain comparison (
/rain-compare,app/rain_compare.py+app/mowram_radar.py, collected by the history container; admin tab Rain vs gauges): MOWRAM radar — from MOWRAM’s own radar server (its login only in the server’s environment): CAPPI 480 km, the reflectivity 2 km above the ground every 15 minutes at 1.2 km a pixel, read by its 15-colour dBZ legend and put on the shared 2 km squares (the strongest echo in each; 13 dBZ, the weakest band, is about 0.25 mm/h), with its whole history from 15 May 2026 (app/mowram_radar.py, tablemowram_radar; the pictures already stored at the radar’s own pixels were converted, the rest fetched). Until the afternoon of 18 October it was the website’s SRI pictures (app/mowram_web.py: no longer collected, its table kept). With GK2A and JMA it is scored hour by hour against every rain gauge (a source sees rain when any of its pictures in the hour shows it within ~2 km of the gauge), on the hours all three cover, every pair and per day, with the best source and the average day; refreshed every hour (each verdict stored inrain_seen). MOWRAM’s CAPPI radar server is no longer collected; what it stored is kept. - Public map, 2 km squares only (17 October 2026, late): rain (GK2A rain rate), cloud (GK2A or JMA) and thunder are drawn only as their 0.02° squares — the blurred washes, falling drops, band icons and storm outlines are gone; the province / district dots and the storm markers come back only when ticked in each layer’s legend. More card: a Colour slider (10–100%, 80% to start) for all three layers, so the map and the province names show through, Names on top (province names, district names from zoom 9 and the border above the squares) and Cambodia only for all three. Tap any square (or point at it with a mouse): its province, district and nearest village, GK2A’s rain rate as a mm/h range with its 1 / 3 / 24 h estimate, the cloud top, JMA’s reading, the thunder level, the storm cell and the nearest rain gauge (
/api/grid2km/cell,app/sat2km.pycell_info). Admin: the Rain vs gauges tab shows/rain-compareinside these pages, with its Allow public switch. - 18 October 2026. One satellite & radar layer on the public map: choose the source (GK2A, JMA, MOWRAM radar — the chosen one bold on blue), then one of its pictures (GK2A: cloud & rain squares, rain rate, cloud-top temperature, infrared, storm; JMA: its squares and its 17 pictures; MOWRAM radar: its 2 km squares). Rain Image is GK2A’s rain rate, still the first view. Live and in playback from the stored grids; a tapped square lists every source there. Source timing (
app/source_clock.py, Data Collection tab): for every source, its own delay and ours — measured KMA GK2A ~17–18 min after the slot, JMA ~12, MOWRAM radar ~2.5; ours ~0.5 min for GK2A, and JMA’s storm analysis now runs on its own thread, so it no longer holds the pictures up. Storm alarm: a storm heading into Cambodia shows one line on the map and on/prediction, blinking faster the sooner it arrives, with an alarm under 2 hours; a tap opens the storm view (GK2A infrared, the thunderstorm grid and the tracks,/?storm=1). Travel & transport (/travel,app/sectors.py): tourism sites, the two railway lines, the national roads 1–7, 48, 62, 76, 78 and boats (Tonle Sap, Mekong, coast) for every part of the day with warning levels, from the prediction — like MOWRAM’s road, railway and tourism products, but every 10 minutes. Sutron rain: RAIND (yesterday’s total) is left out and the 15-minute loggers’ running total is turned into rain per reading; 13–18 October repaired (backup in/backups). Cambodia only now blurs everything outside the border on every map. - 18 October 2026, later. Storm alarm on the public map too: the same line and alarm as
/prediction, from the same per-place arrivals (/api/storm/alarm,app/outlook.pystorm_arrival), fetched every 10 minutes and counted down every minute; under 2 hours a popup (given once a storm, level and hour, across both pages) whose See the storm opens the storm view. Storm tracks on time: tracked every minute from the newest infrared picture (app/satellite_ch.pytick,app/stormtrack.py), ~1 minute after the picture instead of 30–35; live tracks count for an hour (40 minutes before, which blanked the alarm). JMA on time (app/satellite_jma.py): the infrared and the heavy-rain picture are fetched each on its own until both areas are in (an infrared with one area was never completed, and the heavy-rain picture waited for the infrared), straight from JMA’s server past its CDN’s 10-minute copy, a pass starting every minute — stored 1–3 minutes after JMA writes them instead of 7–9. The line under the title says what is measured (/api/timing: our clock’s 6-hour medians, each station’s own interval, the forecast’s real schedule). Open-Meteo every 8 hours: a cycle now costs ~2,500 of the 9,000 calls a day, so the two-hourly cycles stopped at 13:00 and left the forecast up to 18 hours old every night (15 October went past Open-Meteo’s 10,000); the cycles are now spread over the day by their measured cost (app/forecast.pycycle_sec, 85 % of the budget). Khmer district and commune names from Cambodia’s official gazetteer (app/data/kh_gazetteer.csv, from the SBMS seed data), matched by their codes inapp/places.py: all 199 districts and 1,610 of 1,621 communes (GeoNames’ own Khmer names had lost their subscripts and vowel signs: ‘អរយាដាវ’ for អូរយ៉ាដាវ). Forecast check (/check): a new card scores MOWRAM radar, GK2A and JMA against the rain gauges, with our forecast made at the start of each hour, on the same station-hours (/api/check/sources,app/rain_compare.pycheck). Each source’s verdict at each gauge and hour is stored once (tablerain_seen: every hour, the last 15 days again every day) and the gauges are joined when scoring, so any period scores in about a second. First week, 1,105 station-hours: GK2A 0.28 (CSI), JMA 0.19, our 1-hour forecast 0.15, MOWRAM radar 0.10 (it catches few of the gauges’ rain hours); MOWRAM satellite scored 0.35 before it was removed. - 18 October 2026, MOWRAM satellite removed (the user): nothing fetches from MOWRAM’s GK2A receiver or from its website’s satellite picture any more, and nothing stores, shows or scores them.
app/satellite_prod.pykeeps only the JMA picture menu and the rain layer’s grid cells;app/satellite.py,satellite_ch.pyandsatellite_ctt.pyread only JMA and KMA;app/satellite_ref.pyand the admin panels Satellite & radar data sources and the picture evidence are gone. Deleted: the receiver’s 33 products (grids and hourly pictures), the website’s infrared, the storm backlog made from the receiver’s infrared, its clock rows and its comparison verdicts — a copy in/backups/removed-mowram-satellite-20261018. Kept: the 256-value grey mapping that puts JMA’s infrared on the scale the satellite dots and the storm thresholds use (fitted once, on both satellites). - 18 October 2026, evening. Station chance of rain with GK2A, JMA and MOWRAM radar (the drawer’s Chance of rain · station forecast card,
app/rainchance.py+app/srcnow.py): the station’s own calibrated chance, moved by what the three saw over the station in the latest 60 minutes on the 2 km squares (the rain comparison’s rule: GK2A 1 mm/h or more, the radar any echo, JMA a heavy-rain area or its likely-rain band, within ~2 km). The weights are learned from our gauges: every stored station-hour (rain_seen, 75,908 with the next hour measured) against the rain in the next hour and the next two, one logistic regression per set of sources present, the gauge’s own rain as an input so it is not counted twice. First fit: each source seeing rain raises the odds (all three, next hour: GK2A ×4.8, MOWRAM radar ×3.0, JMA ×2.2); over a dry gauge with all three seeing rain, Ankor Chey went from 34% to 61%. When none of them sees rain the chance goes down. Refitted daily and after the radar’s history is redone (kvsrcnow_model); the card names what each source saw and how many points it moved the next hour;rain_track.sat_liftkeeps those points with every tracked prediction. Replaces the fixed infrared lift (at most +12 points, from the older infrared picture) and the radar lift, which only ever went up. Method:/predict-methodsection 12. - 18 October 2026, evening. MOWRAM radar decoded properly: a pixel counts when it has a legend colour and differs from the picture’s own map (the old 5×5 one-colour rule dropped about 70 % of the echo); the whole history decoded again; its rain-comparison score (CSI) went from 0.10 to 0.27. Every grid 2 km: the rain layer, the satellite dots, the channel pictures and the storm tracking read the shared 0.02° grid directly. Each satellite where it is best (checked on our data): GK2A first for rain and clouds on the map (JMA satellite under More, not in the legend switch); JMA first for the storm tracking and alarm (21 of its 117 alarms right against 10 of GK2A’s 69, and about 5 minutes sooner); JMA’s bands matched to GK2A (cloudy from grey 145, likely rain from 245; its rain CSI 0.34); the station lift weighs every source by what the gauges say. Map: every legend lists the picture’s classes in its own colours with the 2 km squares of Cambodia in each — a tap on a row makes them blink; falling rain and lightning over the rain squares (lightning over Cambodia only, the flash around the strike); the raining squares outlined from zoom 10; Cambodia only on by default; storms outside Cambodia recorded only when heading in; buttons Satellite images & history and Radar images & history; the new page
/rada-images-mowramplays every radar picture since May; the history pages swap between GK2A, the radar and JMA on the same time. Phone page/mobile: only what is useful around the visitor — the stations near them, the rain around (the nearest rain, its strength), the nearest station’s card, storms coming within 60 km. Faster: GK2A’s files in 8 resumable parts at once (KMA served 12–15 KB/s a connection), the last 6 hours caught up after a restart; the map’s first answers kept warm. - 18 October 2026, night. District rainfall
/districts: the 197 districts on the 2 km grid with GK2A’s and the radar’s rainfall over any period (GK2A back to 2020 as its history comes in), the forecast per district, a replay hour by hour and a sortable table; each picture counts until the next one (at most an hour). Phone page: opens at zoom 11 on the visitor; zoomed in, Cambodia is no longer blurred (the Cambodia only mask is cut to the screen); the names no longer pile up; the minute’s refresh no longer pulls the map back. One grid, one set of formulas (checked across every module): every stored grid is the shared 750×800 grid of 0.02°; one rain scale for every picture and page — light under 2.5 mm/h, moderate to 10, heavy to 50, very heavy above (a radar’s dBZ through Marshall–Palmer: 29.4 / 39.0 / 50.2 dBZ) — where seven sets of numbers were in use; GK2A’s colour boxes read at their middle everywhere (90 mm/h no longer reads 86.5); GK2A’s storm cells are the cores of the thunderstorm grid, with its levels; one distance rule (111.32 km a degree) and one square area (4.84 km²); every grid read checks its size. Against the rain gauges (30 days): GK2A’s rain rate adds up to about 2.6–2.9× what the gauges caught; not corrected yet (a decision for the owner). MOWRAM’s radar in the prediction: its echo is one more key of the gauge-calibrated table of the next hours; held-out score 0.0954 against 0.0981 without it (Brier, lower is better). Fresh data: every source checked every minute (the radar too, before every 2 minutes); a new GK2A, JMA or radar picture rebuilds the next hours at once (before, every 10 minutes); the rain layer and the district sums every minute. GK2A history from 2020: only the part of each file down to the grid is fetched (44–48 %, the same grids); every hour back to 2020 first, then the half hours, then the rest; never while the live pictures are being fetched. KMA has served 12–40 KB/s a connection since 18 October 02:00 UTC, after this import took ~120 GB in half a day: at that speed the hourly pass takes about two months. Map: GK2A opens on its rain rate; ▶ in Now plays the last hour; a 2 km square of Cambodia has Accurate / Not accurate, accepted only from inside that square and from an address in Cambodia; the admin feedback page shows the address, the verdict and the square. Stations (admin): every station, the broken and never-fetched ones too, with its status, its last reading, whether it is on the public map and why not, and a show / hide switch in its row. Fixed: a slow query kept the station list past its time-out after a restart (now read by its index); the web workers could deadlock while creating their tables at start (now one at a time). Then:/districtssums up the history and the prediction with Cambodia’s average (each district by its area);/predictionsays its next hours use the GK2A and JMA satellites, MOWRAM’s radar and the ground stations, and shows the radar’s reading;/checknames what its 2 km prediction grid is made of; the radar’s legend gives the radar’s own timing. Measured delays (minutes after a picture’s time): GK2A rain rate out +14, cloud top +17; JMA +14 to +15 (its file says +12, its server gives it out 1.5–2.6 minutes later); MOWRAM’s radar +2.5 — each stored here within a minute, so on the map a satellite picture is 14–27 minutes old and a radar picture 3–19; the satellites’ own delay cannot be shortened here. The GK2A history now comes at ~7 s a file (KMA’s slowing eased): every hour back to 2020 in about ten days. Front page/(the map moved to/map; an old map link goes on to it): the visitor’s city as the browser finds it (else Phnom Penh), the main cities, the radar, the week, the next 24 hours, and tiles that say where each number comes from; its look follows the time of day at that city (morning, afternoon, evening, night from 10 minutes after sunset). Site title and text: Settings → Site title and text, English and Khmer (the subtitle may carry{stations}and{every}). Domain: the proxy’s domains come fromSITE_DOMAINSin/opt/weather/.env(comma-separated, so the old and the new answer together during a move); every link is relative; Google sign-in comes back to the domain in use (each domain’s callback must be listed with Google). - 18 October 2026, night. Every rain gauge counts (the user: “most stations have a gauge, why only 24?”): the checks and comparisons read each station’s rain per hour, which the 27 KMA stations never had — their gauge sends only its running total of the day (0.5 mm tips; it starts again at 00:01). That total is now turned into the rain of every 5 minutes (
app/collectors/kma.py,common.rain_steps: the rise above the day’s highest reading, one rule with the Sutron 15-minute loggers), and their past is filled in: exact 5-minute steps from the stored readings since 6 October, every hour from the hourly totals back to October 2021 (10,190 of 10,190 complete days add up to the day’s own total). Rain-scored stations on/check: 82 of 82 (was 55); gauges in the rain comparison: 85 (was 58), its whole history scored again. KMA’s API sends 2–3-minute averages when asked for more than a day, so it is now asked one day at a time. The Sutron hourly stations get a Rain today (the sum of their hours since midnight), and the sunshine recorders (HRSSUN, 31 stations) are read: Sunshine today, in hours. Fixed: the hourly and daily roll-up rewrote the hour and the day at the start of its 2-day window from their last minutes only, so every hour leaving the window kept 1 of its 4 readings (an Adcon hour stored 7.2 mm of 15.0); the window now starts on a whole hour and day, and 65,067 hours and 3,030 days were rebuilt from the raw readings (the old rows kept inobs_hourly_cut_backup/obs_daily_cut_backup). - 18 October 2026, night. Front page “My location” from the visitor’s own 2 km square (
/api/outlook/spot: the prediction grid’s square, satellites + radar + gauges;/api/grid2km/cell: what GK2A, JMA, the radar and the thunder grid see there, in words): rain, sky and the hours; the temperature, humidity and wind still from the nearest station. The exact position is kept in the browser only (GPS with its accuracy, a dragged pin or a tap on the map; a fix rougher than 1 km, as from an internet address, says so). Falling drops while it rains there. The phone icon is gone; Travel sits at the foot of the side bar./map?open=gk2a|radar|thunder|near|stations|jmapins|device|about|fullscreenand?show=open the map on one view. - Reports from the visitor’s square (front page, My location): what they saw in the 15 minutes before or after now (rain / thunder / no rain) and 1–5 stars for the forecast shown; kept as
go:<row>:<col>inpred_feedback(columnsseen,device) with what the card said. Accepted only from inside that 2 km square (a fresh GPS fix, 0.5 km margin), an internet address in Cambodia, and at most 2 an hour on one square from one device (a random id the browser keeps) or one address. Shown with the address in the admin feedback feed. - Front page, raining now (the owner’s rule): the card says it rains and its drops fall when either satellite sees rain on the visitor’s square (GK2A’s rain rate, or JMA’s likely-rain band or heavy-rain area), the radar has an echo there, the square’s own hour is likely rain or a gauge within 3 km is raining; thunder on the square (any thunder-grid level, or a likely-thunder hour) switches it to lightning (the bolt flickers, the card flashes). The square is read again every minute.
- Data alerts (
app/alerts.py, Settings → Alerts): every minute each source’s newest stored item against how late it may be (MOWRAM radar, GK2A, JMA 45 min; KMA stations 30; Adcon 60; Sutron 180); one down shows in red at the top of Settings (with the radar fetch’s last error, its login masked) and goes to Telegram once, every 6 hours while down and once when back. The bot token and chat id are set on that tab, kept in the config table and never shown again. Domains: camweather.info and www.camweather.info serve the same site as weather.vitsolutions.asia, each with its own Let’s Encrypt certificate (LETSENCRYPT_SINGLE_DOMAIN_CERTS). /jma-vs-prediction(app/jma_vs.py): JMA’s ensemble (/jma) and our prediction (/prediction) on the same 6-hour windows at every rain gauge within 40 km of JMA’s 25 points, each made before the window began, scored against the gauges (Brier, right, caught, false alarms, CSI, amounts), with ours also made as early as JMA’s run, the model alone and every JMA point. First 355 windows (16–18 October): ours 0.332 against JMA 0.613 — JMA’s driest member is usually above 0.2 mm, so it says rain in almost every window.- Rain timeline (
app/raintime.py,/api/rain-timeline, front page My location): the clouds’ motion measured at the spot by matching the rain pattern within ~40 km in GK2A’s two newest pictures (else the storm tracks), the newest picture moved on by its own age (~15–20 min) and then 5 minutes at a time for 2 hours with the motion held steady: when the rain should stop, start or get heavier, and whether the rain upwind is growing or weakening. - Rain timeline checked and calibrated (
app/raintime_check.py, daily in the history container,/checkcard,/api/rain-timeline/check): replayed every hour of the last 10 days at the 50 gauges reporting every 5 or 15 minutes, as made when the picture arrived (~15 min), each 15-minute block of the next 2 hours scored (CSI) against the gauge and against “as it is now”; the best rain threshold, reading width and speed scale are kept (kvraintime_cal) and used live. First run (10,156 forecasts): learned settings 0.221 in the first hour (first settings 0.162), as-it-is-now 0.233 — moving the rain pays from about 90 minutes ahead; the best settings were at the edge of the grid, which is now wider. Storm wording (NOAA): “storm” only from a tropical storm’s 62 km/h sustained wind; the satellite systems are rain clouds / thunder clouds; one wind scale (strong wind from 39 km/h, tropical storm force from 62, typhoon categories 1–5 from 119). - Following the visitor (19 October 2026): the front page takes a fresh position when it is opened, every time the visitor comes back to it (from minimised, another app or another tab) and once a minute while it is shown — only where location is already allowed, never a new prompt. A sure fix (a phone’s GPS, within 150 m) moves “My location” when the visitor has moved more than 300 m, even over a place set on the map, and the card says “You moved: My location now follows you to …”. A rough fix (a laptop or computer, from Wi-Fi or the internet address) moves it only when the move is larger than the fix’s own error; over a place set on the map, or after “Forget my location”, it only suggests the new place (“Use this place” / “Keep my place”), and a suggestion turned down is not repeated until the visitor moves on.
- Speed for 500–1000 visitors at once (19 October 2026): nginx keeps every public
/api/and/static/answer for a short time (10 s for the data, up to a year for versioned files) and one request refreshes it while the others get the copy — about 3,500 requests a second through the proxy in a 1000-connection test. Never cached: the pages (each view is counted), admin, feedback, anything with the admin cookie. Pages are kept in memory; page views are written by one queue per process. On the map, the rain and lightning animation no longer freezes zooming (draws ready-made drops, keeps its shaders; long pauses 4 s → 0.3 s). Nginx settings:/opt/proxy/tune.confandvhost.d/wx-cache.inc(all three domains). - Legend switches apply at once (19 October 2026): falling rain and lightning follow each check and uncheck immediately; two quick clicks used to load the animation twice and leave one copy that no switch could reach.
- Picture delay (19 October 2026): KMA puts each GK2A picture out ~15 min after its time (JMA ~14, Cambodia Meteo radar ~2.5); that part cannot be shortened here. Our part was 2–7 min; now the map’s two GK2A pictures have their own loop that looks every 20 s while one is due, fetches both at once and holds the history import back, and the map checks for a new picture every 20 s (was 60). The collector log gives each picture’s download and gridding seconds.
- Rain “estimated now” (19 October 2026): the newest GK2A rain picture (~15–25 min old on the map) is moved on to the present with GK2A’s own cloud motion — a motion field measured every ~100 km (rain pattern matched 20 min apart, else the cloud tops, else from the points around), the calibrated speed factor, a smooth warp. Labelled as an estimate in the rain legend, with a switch back to the picture as GK2A took it; never in playback; display only (the card’s rain decision is still GK2A’s own picture). Checked with every new picture: the estimate from 20 min before against what GK2A then saw in Cambodia, and the old picture kept as it was (CSI, last 24 h in the legend). First backtest: estimate 0.053, kept 0.022. Code:
app/gk2a_now.py,/api/sat10/now. - Site name and icons (19 October 2026): admin → Settings → Site name and icons: the name (English, Khmer), the site icon (browser tab, phone home screen) and the front page logo, on every page (
app/branding.py; admins only, audited; images up to 256 KB, SVGs with scripts refused). - Rain-cloud alarm colours (19 October 2026): by the cloud’s speed — yellow under 40 km/h, orange 40–60, red from 60; a red one pops up at once and blinks fast (the others pop up 2 h before arrival, as before). Map and prediction page.
- Estimate vs real (
/estimate-check, 19 October 2026): every “estimated now” is checked against the picture GK2A really took 10, 20 and 30 min later (15 min lies between: GK2A comes every 10), and against keeping the old picture as it was, on Cambodia’s 38,214 squares: CSI exact and within one square, rain found (POD), false rain (FAR), area ratio; 24 h / 7 / 30 days, day by day, every picture, and one check side by side (start, estimate, real, hit/miss/false maps). The month back was measured afterwards (gk2a_now.backtest, two processes in the history container, ~9 h); new pictures are checked as they come. Kept 45 days (tablesgk2a_now_field,gk2a_now_score). Linked from the rain legend and the map’s Prediction menu. - Wind from GK2A (19 October 2026): over the GK2A and JMA pictures and GK2A’s rain (not the radar), arrows coloured by Beaufort force or streamlines over a speed wash (switch in the legend), from GK2A’s cloud motion (the field the estimated-now uses, ~100 km points, yellow dots where measured); flowing streaks; the stations’ own measured wind as black arrows; hover any arrow or station for direction, km/h, m/s, Beaufort and the probable ground wind (learned); the replay moves the arrows to each time, blending between pictures. Wind rose at a place: 24 h / 7 / 30 days, in colours that stand out from the map under it, with the up to 3 nearest stations (60 km, distance shown) compared at their own place: direction within 45/90°, typical difference, turn, speed ratio. All from numbers stored per picture (
gk2a_wind, 45 days), no pictures stored. Learning (app/gk2a_learn.py, daily in the history container): the estimate’s speed factor (0.6–1.4) against GK2A’s next pictures, and the ground wind’s turn and slowing by hour against the stations, each checked on a day it did not learn from; shown on/estimate-check. - Storm watch (
/storm-watch, 19 October 2026): every GK2A cloud-top picture is checked for a tropical storm (app/tc_detect.py): a cold cloud shield (tops −40 °C or colder, 40,000 km² or more, round), turning in GK2A’s own cloud motion (cyclonic), and an eye (a warm spot walled by very cold tops in at least 7 of 8 directions) → organised storm cloud / possible tropical storm / tropical storm with an eye; tracked from picture to picture; for each: position, size, coldest top, turning, movement (its track, else the steering wind 300–700 km around), strongest cloud-level wind within 400 km, the chance to reach Cambodia in 72 h (15 ways: heading ±30°, speed ±30%, within 50 km), earliest arrival and closest approach to Phnom Penh. All kept (tabletc_detect), history from April 2026. The animation is drawn from the stored grid (white cloud by coldness over imagery of the ground), not from stored pictures. Not an official warning; area 97–112°E, 5–21°N. Also: Facebook links (fbclid) open the front page; pages carry their build and reload once if a browser shows an old copy. - Flood watch (
/flood, 19 October 2026): four views, never mixed — Now and Next 2 h from GK2A (each district’s 1/3/6/24 h rain against its own GK2A history: watch at its 95th percentile, warning 99th, severe 99.9th, +1 on soaked ground; next 2 h adds GK2A’s moved picture; fast-growing storms flagged from cloud tops 10 °C colder in 20 min), Model 12 h (the prediction’s mm, plain thresholds) and NASA measured (LANCE VIIRS flood from NOAA-20 + NOAA-21, 250 m, 3-day window: km² of flood, recurring flood, water and cloud per district, daily since 24 June 2025, kept as district numbers and a 2 km grid for the replay — no image stored). NASA’s own tiles (VIIRS/MODIS flood, NOAA-21 true colour, GPM IMERG) for any day, NASA Worldview link. Code:app/flood.py,app/nasa_flood.py. Flash floods only: the Mekong’s river floods need river levels (MRC). - Flood page, more (19 October 2026): the levels explained (watch = the district’s wettest 5% of hours, warning 1%, severe 0.1%, one step up on soaked ground) with typical impacts and indicative water depths (5–10 / 10–30 / 30+ cm — guidance: the satellite measures rain, not depth) and each district’s own thresholds in mm; NASA flood history also from MODIS (Terra + Aqua) back to January 2021 (NASA has no flood map for 2020), replay by a chosen date range; accuracy: the GK2A watch checked against NEW floods NASA measured (none in the 5 days before; cloud under 80%; river floods left out) — first result POD 16%, FAR 84%, CSI 0.09: not yet proven, and NASA sees only clear days; seasons: rainy (May–Oct) and dry (Nov–Apr) seasons compared, this season so far against the others, month by month over every year (what past years did, not a seasonal forecast). Storm watch history since April: 4,008 organised storm clouds, 36 possible tropical-storm detections (12 tracks), 5 with an eye.
- Compare sources on /check (19 October 2026): drag or tap 2, 3 or all of stations, GK2A, JMA, Cambodia Meteo radar and Open-Meteo; each pair on its own, never combined. With a station (or the model): every source read on the station’s own 2 km square, hour by hour, on the same station-hours (table
cmp_seen, back to 14 May), the gauge as the truth, each station listed. Satellites and radar alone: the whole pictures over Cambodia’s 38,214 squares at every shared time, a where-they-differ map and any one time side by side. Freshness rule: “rain now” everywhere is the one rule (below) on each source’s newest picture of the square, never a moved or calculated value; nginx never serves an old copy while refreshing. - Raining now — one rule (19 October 2026, chosen on the gauges): rain on a 2 km square when GK2A sees 5 mm/h or more, or when the Cambodia Meteo radar’s echo confirms GK2A’s rain or JMA’s; without a radar picture GK2A alone from 2.5 mm/h. Tested on 62,190 moments at 50 gauges (22 days): score 0.268 and right 93 % against 0.200 and 86 % for GK2A alone (which shows rain 3× too often); the plain 2-of-3 vote 0.234. JMA adds almost nothing to GK2A; the radar is the real check. The rule is computed for every square at once (
app/rainnow.py): the front page’s map, its card, the city buttons,/traveland the map’s popups read the same grid, so they never disagree (0 differences over Cambodia’s 38,214 squares). Checked again every day (app/rainnow_check.py) and on/check(“Raining now”). The front page shows no accuracy figures: they are on/check. - When the rain stops: the time rain this strong usually goes on at our gauges (half of the rains stop within 30 minutes) — 24 min off on average, against 40 for GK2A’s moved picture and 66 for the old “more than 2 hours”. How strong it stays: what most often followed each call (rain usually gets lighter within the hour).
- /check periods: every card has its own period (today … 12 months, the rainy season May–October, the dry season November–April, any dates) and one period sets them all; which satellite is best against the gauges, and how sure (500 re-draws of the days).
- /travel: now and the next 2 hours by the same rule on each place’s own square; places to stay, eat and visit from OpenStreetMap (hotels, guesthouses, restaurants, cafés, sights, ATMs, fuel, health, transport, bars), refreshed weekly; Directions from our own router (OSRM on OpenStreetMap’s roads of Cambodia, car / motorbike and walking) with the rain on the way at the time you get there, places on the way by the time of day (breakfast, lunch, the evening’s hotels, motels, guesthouses and dinner, the night’s hotels and bars), fuel on long drives, live tracking and spoken turn-by-turn directions (female / male voice); Nearby now; search the start and the destination, Go here on every tourism site, suggestions on the way on / off with tap-to-go and nearby now during the trip, and the trip kept in the browser across a refresh or a minimised window (20 October); Travel now without a destination: the map follows you, your speed, the rain here and ahead in your direction (within 30 km, with the time at your speed), places ahead by the time of day. Privacy: positions stay on the phone and are never stored; only the number of trips per day, district to district, and devices (a one-way hash) are counted (admin:
/admin/api/route-usage). - Raining now counts the rain gauges (21 October 2026): a gauge whose last 15 minutes measured 0.2 mm or more (its reading at most 30 minutes old) makes its own 2 km square and the 8 around it “raining”, even when the satellites and the radar see nothing — that morning a gauge measured rain all three missed. A gauge speaks only for where it stands (about 90 gauges, one or a few a province), so the satellite and radar grid stays the answer everywhere else. On
/check: at the gauges the rule now catches 93 % of the rain; from the pictures alone 50 %. The station dots on the map say “raining” by the same test. - The learning agent (its own container on the server; Settings → Learning agent shows it running, what it is doing and its results;
/checkshows them to everyone): every night at 02:00 it studies every quarter hour at every fast rain gauge — what GK2A, the radar, JMA, the other gauges and five weather models (ECMWF IFS and AIFS, GFS, ICON, Google WeatherNext 2) showed just before — and learns what really fell there, now and 15, 30, 60 and 120 minutes later. How smart it is: it sits an exam it has never seen — each of the last 7 days and each group of gauges is predicted by a model that learned only from earlier days and from the other gauges, as a square of the map without a gauge — and it replaces the site’s answer only where it wins by a clear margin on most days. It checks itself: on its first night it scored high by memorising where the gauges are and drew a ring of rain around Phnom Penh; the stricter exam caught it, the place clues were removed and nothing went to the public map. For “raining now” it does not beat the rule yet (0.277 against 0.289), so the rule stays; ahead it already beats the site’s timeline at 30 and 60 minutes (0.254 against 0.231; 0.237 against 0.210, better on 5 of 7 days) and at 1–2 hours, where the timeline foresees nothing (0.212), but its maps for those windows still show patterns that are not weather, so they stay off the public map until they look right. It finds faulty gauges by itself (Vihear Loung and Koh Thom report rain nothing else sees) and leaves them out. Every night with new data it learns again; a manager can hold the map on the rule at any time. - Radar quality (21 October 2026): over 14 days the radar’s picture showed hills echoing 60 % of the time, its own map labels, borders and thin beams never echoing, and wedges its beam does not reach. Every night the agent marks those squares (about a third of the radar’s reach) and there the satellites decide, as outside its range. At the gauges the score stays the same (0.29) and the rain caught rises from 50 % to 55 %.
- The usage agent (Settings → Usage agent): once a day it reads only anonymous counts of which features are used — never a position, a route or a search — and writes suggestions for the team. On its first day (424 visitors, 907 visits in 30 days, once the server’s own load tests were taken out): 85 % of visits only look at a page, so the first screen has to answer the question by itself; after “near me” 45 % open a station; the busiest hours are 20:00–22:00. For visitors: on the map one “people often open … next” suggestion after an action (switched off with one tap), and on the front page a link to the rain along the road (
/travel) when rain is coming. - The motion agent (every night): is the cloud and rain motion right between two pictures? Each picture of GK2A, JMA and the radar is moved on and compared with that source’s next pictures; it learns how much faster and which way the motion should be turned, the wind at the ground under it, how fast the storms really go, and whether a storm rains. First night (scored on the last 2 days only): moving the pictures beats keeping them still for every source (JMA 0.69 → 0.77, the radar 0.55 → 0.59, GK2A 0.62 → 0.65); JMA’s arrows now go 1.4× faster and turned 15°; storms move slower than their measured speed (0.6× for the satellites’ storm cells, 0.8× for the border watch), so their arrival times are later and closer to what happened; over Cambodia, strong storms rained on the gauges under them 66 % of the time within 30 minutes (growing 66 %, steady 70 %, weakening 57 %), the map’s storm popups say so.
- The agents apply what proved better — and say so: every value an agent learns replaces the one in use only where it was better on days it never learned from; the rest stays. Settings → Suggestions lists what a person should do next (a gauge to visit, a key to add, a design to decide), most important first, then every change the agents applied and kept, with the evidence.
- Visitors by province and district (Settings → Visitors): zoom to Cambodia — by GPS (only where visitors let a page use their position; the server keeps the district, never the coordinates) and by internet address. Test traffic and robots are no longer counted (14,592 of 19,436 rows were the server’s own load tests).
- The site follows the best source, re-decided every night: GK2A and JMA are checked against our rain gauges on the same moments of the last 7 days; the leader is shown first — the map opens on it, its button has a ★, the front page names it under its map — and it changes only when the other is clearly better. On 21 October: JMA (0.193 against 0.179, best on 5 of the last 8 days; GK2A was best on the other 3), so neither is always the best. Where the radar does not decide, which satellite decides “raining now” is learned the same way over 30 days (GK2A, JMA, either, or both): GK2A stays there for now. The pictures stay the same; what is shown as real comes from what the gauges confirm.
- Faster for many visitors at once (21 October 2026): the database keeps 12 GB of its own cache on the 49 GB server (4 GB before) and the cache hit in Settings → System is the last 15 minutes’; 10 web workers (5 before); “the newest picture” is looked for in the last month only (the picture history now reaches back to 2020 and is kept for ever); pictures read again and again stay in memory for 10 minutes. Load test straight on the app, without the proxy’s cache: 1,000 requests at once answered at 1,200–2,400 a second, 3,000 at once at 1,400–2,000 a second, no errors.
- The map’s player picks its picture: GK2A, JMA or the Cambodia Meteo radar (or none) right in the player, beside the period; every picture legend says what is playing back, from which source, and the time of the picture on screen — folded or not — and its counts follow the replayed picture. The map opens on the whole picture (Cambodia only is one tick away) and with no station network ticked (Stations: Sutron, Adcon, KMA).
- The agents learn what makes rain (21 October 2026): besides the pictures, every square now carries the land under it (height, hills, the sea, forest, fields, the city’s built-up ground, water and wetland, from open elevation and land-cover maps) and the air over it (temperature, humidity and how close it is to its dew point, pressure and its fall, low and middle cloud, the energy for storms, the wind and how hard it pushes up the hills). The stations’ own sensors (pressure falling, a sudden cooling, gusts, sunshine dropping) are learned for the stations themselves, never drawn on the map. Every night each forecast window is tested with the pictures alone, with the land and the air, and with the sensors, on days and gauges it never saw, and keeps only what wins.
- The satellites at each gauge: the picture 10–20 minutes before matches the gauge best (the satellites see the rain first); GK2A shows 67 % of the gauges’ rain on their own square, 79 % within ±20 minutes; the land changes its meaning (best over cities, poorest over forest and the floodplain). Moving the rain back for the satellites’ viewing angle (parallax) was tested and did not help the rule, so the pictures stay as drawn.
- Why the ground wind is not the clouds’: KMA and Adcon stations only (not Sutron, not Phnom Penh’s buildings); they agree 38 % of the time — 49 % with no rain near, 31 % with rain within 25 km (a storm’s cold air spreading out), 64 % on the hills, 31 % in the city; the ground wind is about half the clouds’ speed and turned 20–30°. The arrows stay as the clouds move.
- Sounds and a moving sky: on the map, thunder with each lightning strike and a wind sound by the wind speed, each with its switch and a speaker button in the player (never before a click); on the front page the sky moves with the weather — clouds drifting, rain falling, a storm’s lightning — behind the page and the weather card.
- Seven weather-service pages (21 October 2026), each on its own and linked from the front page’s menu:
/warnings(yellow / orange / red for every district: heavy rain, flash flood, thunderstorm, strong wind, heat, poor air, high river, tropical storm, rough sea; an alert for your district; a warning line on the front page),/rivers(the Mekong and the Tonle Sap: flow, the next 30 days, the usual for the date — GloFAS),/typhoon(tracks and 5-day forecasts from JMA’s typhoon centre, how close each comes to Cambodia; GDACS alerts),/farm(water balance, spraying and drying hours, days since rain, this month against the usual, soil moisture),/air(air quality and haze, CAMS),/heat(feels-like temperature and UV),/sea(waves and wind for boats off the coast and on the Tonle Sap). All open data fetched on its own clock, kept for ever; the front page also reads the weather aloud. - History from 2020 on every weather-service page (21 October 2026): a History button under each map — pick the day and hour, step, play, share a link — and the map shows that time as squares: feels-like temperature, UV, air quality (from 31 July 2022), rain, ET0 and the water balance, soil moisture, waves and gusts, each day’s warnings, every typhoon’s track over the gusts or pressure, and each river station’s flow. From Open-Meteo’s own archive of ERA5, ERA5-Ocean and CAMS on AWS (only Cambodia’s squares are read; no API calls), IBTrACS for the storms and GloFAS for the rivers; kept for ever.
- Block visitors by place (Settings › Access): continents, countries, regions, cities or IP addresses — blocked, or the only places allowed; nginx applies it within a minute (403), from the GeoLite2 database; the admin who saves and the server itself are never blocked; any address can be checked.
- Travel on Google Maps (23 October 2026): with the two Google keys in the server's .env, the travel page uses Google's map, its directions (with traffic) and its place search; without them, OpenStreetMap and our own router as before.
- System as a slide (23 October 2026): the What runs behind it drawing has a Play button: each part lights in turn with a live caption.
- The formulas (23 October 2026): How we measure names every method (learning, scores, calibrations) and shows live this week's winning satellite, GK2A or JMA.
- What runs behind it (23 October 2026): on the How we measure page, the whole system drawn live: the sources, the services (12, each with a running dot), the 7 agents that learn every night with their last run and decisions applied, the gateway, the visitors and the Claude Code agent; pictures for slides in docs/system-poc-en.png and -km.png.
- How we measure (23 October 2026): a new page, the last icon of the menu, and a card on About: an animation of how the site learns to tell rain on every 2 km square — the satellite's picture, the station's rain gauge and sensors, the learning samples, the answer for every square; a Play button, English and Khmer.
- Languages and phones (23 October 2026): every page changes between English and Khmer at once, without reloading, and follows a choice made on another page or tab; www.camweather.info goes to camweather.info; on a phone a tap on a map square opens a small card at the foot of the screen (✕ or a tap on the map closes it, More details for the whole table); the dashboard's controls are one tidy row like the radar page's.
- Protection (22 October 2026): only camweather.info (weather.vitsolutions.asia retired; the Excel export at camweather.info/excel/); no answer by the server's IP; an “Updating” page during restarts; Fail2Ban blocks floods, scanners and password guessing, with its limits in Admin > Settings > Protection; the feedback form asks a sum or an animal; a man's English voice from the server; the travel page asks for the location when a destination is chosen.
- Phones, round three (22 October 2026): on the satellite, radar and district pages the days, options and legend behind small round buttons and a slim player; the map without the extra buttons and tabs (the legend behind one round button); the prediction page's storm line one line (a tap for the rest) and its rain-chance panel a round button; the travel drive card small under the same menu; English spoken in US English.
- Every page the same menu (22 October 2026): all 27 pages in coloured icons, the most useful first, fixed at the top of a phone; an About us page with the purpose, the disclaimer (follow MOWRAM and the local authorities for official information), contact info@camweather.info and a feedback form checked for robots and kept for Admin > Feedback; pages move only up and down on a phone; a swipe left changes light/dark, a swipe right Auto/Day/Night.
- Phones first (22 October 2026): one menu on every page — small icons across the top on a phone, the page names down the left on a computer; the home page's header in two rows on a phone, only the big weather card, less padding; no Report button on a phone (the dashboard is in the menu).
- Forecaster desk (22 October 2026): for meteorologists — GFS and ECMWF charts over Southeast Asia (sea-level pressure, 850 hPa wind and humidity, 500 hPa height with the 5880 m ridge line, 200 hPa jet, CAPE, lifted index, inhibition, K index, precipitable water, freezing level) to +72 h, model soundings (Skew-T) with their indices for 8 towns, and meteograms of four models with the ensemble spread for any point of Cambodia.
- Rain chances by province (22 October 2026): the new “Rain chances” page shows, for every province and each of the next 7 days, the chance of any rain, 10 mm, heavy (20 mm) and very heavy (50 mm) rain from the 51 runs of ECMWF’s ensemble forecast, how much rain is likely, and how far the runs agree. The Telegram morning message adds the province’s chance of 20 mm or more.
- Destinations: when to go (22 October 2026): a page for the 16 tourist places — the weather now and the best 3 hours outdoors, the next 7 days, sunrise and sunset with the sky expected then (Angkor Wat’s at the top), and each month coloured best / good / fair / poor from 30 years of rain and six years of daily heat, waves and haze, with the best months to go (Angkor: November–March).
- Lightning and thunderstorms, 0–3 hours (22 October 2026): the new “⚡ Lightning” page shows every district with a storm now or within about 10 km, where storm clouds are growing fast (likely within the hour) and where the air could make storms in the next 3 hours, with clear lightning-safety advice and how often its calls came true. The Telegram bot sends “go indoors” when a storm is near a followed district.
- Telegram alerts by district (22 October 2026): the site’s own Telegram bot — anyone chooses their district (or taps “Alerts on Telegram” on the warnings page) and gets its warnings when they reach orange or red, the all clear, and every morning at 6:00 the day’s forecast, in Khmer or English. Turn it on in Settings → Alerts → Public Telegram bot with a bot made by @BotFather. Only the chat and its districts are kept.
- Satellite rain corrected to our gauges (22 October 2026): GK2A’s rain amounts are now multiplied by a factor learned every month from our rain gauges (October: × 0.49), so district totals, the dashboard, reports and the front page’s “rain today” agree with what the gauges measure; “raining now” and the rain rates stay GK2A’s own picture. The districts page can still show GK2A as it is.
- Move the Report button and the legends (22 October 2026): drag the Report button anywhere (drop it back in the corner to let it place itself) and drag a map legend by its grip ⠿ or any empty spot of it (double-click to put it back); each page remembers where. The map’s title no longer has the station-count line under it.
- A clean map, the station card at the side, the management demo (22 October 2026): the update times moved from the map’s header to a small “ⓘ Update times” link beside the data credit; the nearest station’s card waits at the right edge (tap or swipe left to open it, swipe right or › to put it back, remembered); the dashboard’s sources in Khmer; a 15-minute management demo in English and Khmer with pictures of the live site (
docs/MANAGEMENT-DEMO.md). - Live dashboards (22 October 2026):
/dashboardand the Report panel’s first tab: rain, heat, air, sea, rivers, warnings, storms and El Niño, the period (7 days to a year) against the one before or last year — headline cards with their change and trend, the values now marked live, the period day by day, a gauge, the top five, a share and a province table; refreshed every minute, light or dark. Rain shows the satellite’s estimate beside what the gauges measured. - JMA satellite when JMA’s real-time page is down (22 October 2026): since 03:00 Japan time on 22 October JMA’s real-time image page answers “not found”; the infrared now comes from NOAA’s archive of Himawari’s raw scans (as fast as JMA’s page) and 12 colour products from JMA’s bosai map tiles, the missed hours filled in; only JMA’s heavy-rain areas pause until the page is back. The rivers’ usual level is now made from the daily flow the site keeps.
- Travel: when to leave, heat, the dark, breaks, floods, boats, speed limits, the arrival time shared, offline (22 October 2026): the trip card compares leaving now and in 1, 2, 3 hours (rain on the way, the hottest hour, an arrival after dark); heat and UV advice for a motorbike or a walk; a warning when you arrive after dark; a 15-minute break every 2 hours with the café or fuel stop near it, and a spoken reminder while driving; flood watch, NASA’s flood water and the rivers along the road; trips to the islands go to the pier and show the boat and the sea at the crossing’s hour; OpenStreetMap’s speed limits while driving; a link that shares only your arrival time (never your position); and the page and your trip work without the network.
- Reports on every page (21 October 2026): a 📊 Report button on every page: choose the data, the place and any period to any other, by the hour, the day or the month; line, area, bar, stacked bar, step and points graphs; every weather model against a station; downloads as PNG and PDF, each with its period and the time it was made; the map’s picture, Cambodia only or the whole map, as PNG or PDF. Every change redraws the report; each unit has its own axis. For the public, the stations’ readings and CSV stay off until Settings › Reports allows them (“Allow public: station readings and models”, “Allow public: download CSV”).
- El Niño and the climate outlook (
/climate), volcanoes (/volcano), the storm watch from 2020, warnings on your way (21 October 2026): El Niño now with NOAA’s and JMA’s official outlooks and ECMWF’s forecast, Cambodia’s rain and heat for the coming months and every month since 1940, the long records back to 1866; the region’s volcanoes with the Himawari Ash RGB picture every 10 minutes and the Tokyo ash advisories in plain words; the storm watch reading Himawari’s infrared back to 2020; on/travelthe warnings of the districts a route crosses and the emergency numbers. - Pages:
/(the front page),/map(map: GK2A satellite / JMA satellite switch its satellite layer, 2 km grid),/gk2a-satelliteand/jma-satellite(every product, storm cells, thunder grid, From–to days, Cambodia only),/prediction,/check,/mobile(phones),/rada-images-mowram(the radar’s pictures and history),/districts(district rainfall). Old addresses:/satellites-10min→/gk2a-satellite,/clouds→/jma-satellite.
Demo: the front page — where every number comes from open the front page ↗
The front page (/; the map is at /map) shows the city the visitor lives in (found by the browser, else Phnom Penh). Its numbers come from three places: the nearest MOWRAM station (measured), our prediction (calculated) and astronomy. Each tile says its own source. Files: app/static/home.html; data /api/stations, /api/outlook/point, /api/mowram10.
| On the page | Where it comes from | How |
|---|---|---|
| Temperature now (the big number) | The nearest MOWRAM station, within 25 km (for Phnom Penh: Kbal Thnol, 5 km away) | Its thermometer, read every minute; with no station that close, the prediction’s current hour |
| Rainfall | The same station’s rain gauge; the next 24 hours from our prediction | Millimetres since midnight; the prediction’s hourly millimetres added up |
| Feels like | Worked out from the station’s temperature and humidity | The standard heat index (NOAA), the same as on the prediction page |
| Humidity | The station’s humidity sensor | The dew point is worked out from it (the Magnus formula) |
| Wind | The station’s wind gauge and wind vane | Speed in km/h; the arrow shows where the wind comes from |
| Sunrise and sunset | Worked out from the place’s position (astronomy), not measured | The sunrise equation, to about a minute; it also sets the page’s look (night from 10 minutes after sunset) |
| Thunder | Our prediction | The highest chance in the next 6 hours, from the GK2A and JMA cloud tops, MOWRAM’s radar and the weather model |
| The next hours, the 7 days, the 24-hour chart, the cities | Our prediction (/prediction) | The next hours from the satellites and the radar, checked against our rain gauges; the days from the weather model, corrected on our stations |
| The map | MOWRAM’s radar, its newest picture | Every 15 minutes, here within about a minute of MOWRAM putting it out |
How to change the site’s title and subtitle
Settings → Site title and text: the title and the subtitle in English and Khmer → Save. They change at once on the map and on the front page. An empty box keeps the built-in text. In the subtitle, {stations} becomes the number of stations and {every} the minutes between two readings, e.g. {stations} stations · read every {every} min.
How to change the website’s domain
- Point the new domain’s DNS (an A record) to the server, 147.93.175.50.
- In
/opt/weather/.envsetSITE_DOMAINS=new.domain; to keep the old one working during the move, list both:SITE_DOMAINS=weather.vitsolutions.asia,new.domain. - Run
docker compose up -d weatherin/opt/weather: the proxy answers the new domain and makes its HTTPS certificate by itself. - If the admin page signs in with Google: add
https://new.domain/admin/api/sso/callbackto the Google project’s allowed redirect addresses. - Nothing else names the domain: every link of the site is relative.
Demo: the “real clouds” layer — how it is made and how accurate it is open the public map ↗
On the public map: the satellite panel → Show real clouds and Rain and thunder. The clouds are drawn from the real Himawari-9 (JMA) picture and the rain from the rain rate measured by GK2A (KMA); only the soft, billowy edges are decoration. Files: app/static/clouds.js, app/static/storm3d.js.
Is the cloud accurate? — the honest answer
30-second answer: “The cloud positions and sizes are real: they come from the Japanese satellite Himawari-9, a new picture every 10 minutes, 2 km over Cambodia. The white / grey / dark colour follows the real cloud thickness and, where the Korean satellite GK2A measures heavy rain, turns dark. What is not measured is the fine fluffy shape: that is generated texture, so the picture is a faithful map of where cloud is, not a photograph of each cloud.”
What is real and what is decoration
| Part | Real or generated | Source / rule |
|---|---|---|
| Where cloud is, and how big | Real | Himawari-9 picture (JMA real-time imagery): infrared 10.4 µm (band 13) at night, visible light (band 3) by day. 2 km over Cambodia and its borders (JMA area “Hi-res Asia 1”), 5 km further out (“Southeast Asia 1”), put on the same 0.02° grid as the GK2A pictures. |
| How thick / high (colour: grey-blue → white) | Real, indirect | Brightness of the picture. Infrared: colder = higher and thicker = brighter. Visible: brighter = thicker. Three thresholds make the thin, medium and thick layers. |
| Dark storm colour | Real (strong echoes only) | The rain rate measured by GK2A (KMA), about 8 mm/h or more, drawn with the radar’s old colours (38 dBZ-equivalent and up: yellow / orange / red) so the layer looks the same. The MOWRAM radar is no longer used. |
| Rain streaks | Real positions | The rain layer’s cells (/api/radar/meta → summary.band_points), made every 10 minutes from the GK2A rain rate since 17 October 2026: light / moderate / heavy / severe. More streaks where the rain is stronger. |
| Lightning | Real trigger, drawn bolt | Only over a convective core (≥ 45 dBZ) or a storm cell from /api/storm/now (cold cloud tops + radar + rain gauges). The bolt's jagged path is random; it is not a measured strike. The page never invents a storm unless Demo storm is ticked. |
| Fluffy edges, soft shadow | Generated | Fractal noise pushes the real shape around (the “realistic clouds” CSS/SVG technique); the shadow is the same shape, blurred and shifted. The Soft edges slider only changes this. |
| Drift | Real frames, smoothed | The last ~2 hours of 10-minute pictures are cross-faded: the movement is what the satellite recorded, but between two pictures it is a fade, not a physical simulation of the wind. |
How far can it be trusted? (limits)
- Size: a cloud smaller than about 4–7 km (two or three 2 km pixels) is not seen. Cloud cover is shown as a map, not as a percentage.
- Position (parallax): Himawari-9 looks at Cambodia from the east (140.7°E) at about 38° from vertical, so a tall cloud is drawn west of where it really is: about 8 km for a 10 km cloud top, about 12 km for a 16 km thunderstorm top (height × tan 38°). GK2A (128.2°E, about 25°) shifts less: 5–7 km. Not corrected yet.
- Night: in the infrared, low warm cloud (fog, stratus) is almost as warm as the ground and can be missed; thin high cirrus is cold and can look thicker than it is. By day the visible picture sees low cloud well.
- Age: the newest picture is normally 10–20 minutes old; the clock on the demo page shows the time of the picture being displayed.
- Zoom: when zoomed in, the cloud looks soft, because the source picture is coarse. The noise adds believable edges, not new detail.
- Rain and storm: the rain is the satellite’s own rain rate (GK2A, 2 km), an estimate from the cloud, not a measurement on the ground. Scored against our rain gauges (first day, 17 October 2026): it caught 42 % of the gauge-hours with rain, with many false alarms (71 %). A visual aid, not a warning product.
Where the data comes from, and what is stored
Pictures: sat_ch_frame table (visible / infrared / water vapour every 10 minutes, made from the Himawari-9 pictures since 17 October 2026, calib = 3; app/satellite_ch.py) — rain: /api/radar/frame and /api/radar/meta (now the GK2A rain rate, app/satrain.py) — storm cells: /api/storm/now.
Demo: the Prediction page (next 24 hours, 7 days, month) — the formulas and how far to trust them open the page ↗
On /prediction every province, district and station gets a prediction of rain (chance and amount), thunder, heat and storms coming from outside Cambodia. Code: app/outlook.py. Refreshed automatically: the next 24 hours every 30 minutes, the 7 days every 6 hours, the month every 12 hours.
The formulas, in plain words
30-second answer: “The two satellites, GK2A (Korea) and Himawari-9 (Japan), show the rain and the storm clouds now, every 10 minutes on one 2 km grid, so they lead the first hours; their weight fades quickly because a rain cell lives 1–3 hours, and then the weather model takes over. Every number is turned into ‘how often our own rain gauges really measured rain’, so the satellite part and the model part speak the same language. No radar is used.”
- Weight of the satellites:
w = e−hours / 2— 61 % at 1 h, 37 % at 2 h, 22 % at 3 h, 5 % at 6 h, about 0 from 12 h (hours = from the newest picture to the middle of the forecast hour). - Rain chance = w × Psat + (1 − w) × Pmodel. Psat = how often the gauges measured rain (≥ 0.2 mm in the hour) after the same satellite reading, read where the cloud comes from (upwind, moved with the measured cloud motion): GK2A rain rate (bands at 1, 3, 8, 15 mm/h) × GK2A cloud-top temperature (bands at −20, −40, −55, −70 °C) × JMA (clear / cloudy / thick / likely rain on its infrared, or a heavy-rain-potential area), each the strongest within ~3 km. Pmodel = how often the gauges measured rain when the weather model gave that chance at that lead (five bands of the model’s chance × four lead bands).
- Rain amount = w × (the gauges’ mean rain after the same satellite reading) + (1 − w) × (model rain × K), K = gauge rain ÷ model rain over the last 90 days.
- Thunder chance = w × logistic(−3 + 4 × coldness + 1.2 if inside a JMA heavy-rain area + 1.5 × share of the area within 30 km colder than −55 °C + 0.8 if the cloud top cooled 5 °C or more in 30 minutes) + (1 − w) × (the calibrated model chance × the model’s thunderstorm share); coldness = clip((−20 − cloud top) / 50, 0, 1). The satellites cannot see a lightning flash, they see the storm cloud.
- Storm arriving from outside: a storm that the server follows outside Cambodia (see the storm tracking above), moving towards Cambodia at 8 km/h or more and passing within 30 km of the place: hours = distance along its path ÷ its speed, shown for 0–6 hours; within 1 hour of that time the rain chance is at least 60 % and the thunder chance at least 40 %.
- Heat: the model temperature; in daytime in the next 3 hours, when a thick cloud (top −30 °C or colder) is overhead now, it is lowered by up to 1 °C × w (0.6, 0.4, 0.2 °C). With the humidity it becomes the NOAA heat index: caution 27, extreme caution 32, danger 41, extreme danger 54 °C (extreme caution means it feels like 32–41 °C: heat cramps and heat exhaustion are possible with long exposure or hard work in the sun). 7 days: the model’s daily rain chance, its rain × K, its highest and lowest temperature, the highest heat index of each day from the model’s hourly temperature and humidity (the same heat levels) and its thunderstorm code. Month: the same 30 days of the year in every past year at the nearest station with at least about 3 years of daily rain records; the first 10 days come from the model, the rest from that history. It gives the expected rain, rain days (1 mm or more) and hot days (35 °C or more) next to what is normal (and the range of the middle half of the years), and whether the first 10 days look wetter or drier than normal — it is not a day-by-day forecast.
- Why tables, not fixed weights: the first fit showed that satellite rain at a single point is uncertain — when GK2A shows 3 mm/h or more over a station, its gauge measures rain in that hour about 36 % of the time (43 % at 8 mm/h or more), and GK2A sees about 42 % of the hours a gauge gets rain; a parallax shift of the pictures did not help. The model’s raw hourly chance had almost no skill at a point (hours it gave 80–100 % were wet 8–17 % of the time). The tables put both on the scale of the gauges, so a raining satellite picture now and the later hours agree with each other; the live numbers are below.
- How we prove it: one hour in five is held out of every fit and used only to score it (Brier score = the mean of (chance − rained)2, 0 is perfect): the table with JMA against GK2A alone against the usual rate, and the calibrated model against its raw chance. Four times a day every station’s outlook is stored (
outlook_log) and scored the same way once its gauge has the answer (the scores below). - Any spot (2 km grid): on
/prediction, moving the mouse over the map shows, for the 2 km cell under it, the chance of rain, the rain amount and the chance of thunder for the current hour and the next 12, with the same formulas; the model part comes from the 3 nearest forecast points. - Forecast check (
/check): every forecast for a place with a ground station is kept as it was made and later compared with that station’s own rain gauge and thermometer — the station’s forecast, the 2 km grid cell its GPS is in, and the weather model alone. Places without a station are not scored: nothing measured there can show whether the forecast was right. For each number of hours ahead the page shows how often the forecast was right, how many rainy hours it caught, the false alarms, the error score against the model alone, and a chart “when we said 30 %, did it rain in 3 hours out of 10?”. The week before 17/10/2026 was re-computed afterwards with only the data that existed at the time, and is marked so. First result (613 checked hours): the satellite / radar part makes the first 1–3 hours much better than the model alone (error 40–80 % smaller), but further ahead both give a chance of rain far too high for one gauge point (it rained in only 2–12 % of the hours whatever the chance said). The next step is to calibrate the chances with these scores. - Honest limits: no one can forecast a single shower days ahead; a satellite sees cloud and rain from 36,000 km, it is not a gauge; thunder cannot yet be checked against a lightning sensor (neither satellite carries one, and FengYun-4’s lightning data is not public over Cambodia); official forecasts and warnings come from the Department of Meteorology.
| Lead | Cases | Error score (lower = better) | Always the usual chance | Temperature error °C |
|---|
Demo: the satellite picture menu (rain, cloud, sea, colour pictures) — how it is made and how reliable it is open the page ↗
The Picture menu of the former /clouds page (removed 17 October 2026, old links open /jma-satellite) lives on as the product menu of /jma-satellite, with every product of the Himawari-9 (JMA) real-time imagery: the heavy-rainfall-potential areas, 4 bands (infrared, visible, water vapour, shortwave infrared) and 12 colour (RGB) pictures and combinations — plus the satellite rain estimate.
Every picture in plain words (what it is for, how to read its colours)
…
If management asks “how do we know this is right?” — plain answer
30-second answer: “We do not calculate the weather ourselves. The Korean weather satellite GK2A measures it, the Korean Meteorological Administration (KMA) turns the measurements into pictures, and Cambodia’s Department of Meteorology publishes them. Our system only copies each picture, places it on the right spot of the map and stores it so it can be replayed. The colours are theirs, and the colour bar under the picture is cut from their own picture. Anyone can open the original file (table below) and check that it is the same.”
What our system does to a picture (the only calculations)
- Download the newest pictures from JMA’s real-time imagery server and GK2A’s rain rate from KMA’s server. Both satellites take a picture every 10 minutes; the server looks every 2 minutes, so a new picture is used within 2 minutes of being published (JMA about 12 minutes after the scan, GK2A about 19).
- Place it on the map. The rain / cloud / sea pictures show the whole half of the Earth as seen from space (a round ball). We use the satellite’s published map formula (the standard “geostationary projection” that all weather satellites use) to find, for every point of a flat map, which pixel of the round picture belongs there. Colour pictures already come as a flat map of this region; we only cut out the area 97–112°E, 5–21°N.
- Make the empty parts see-through (space, “no cloud” white, grey sky, the thin yellow grid and coast lines, the cream-coloured land on the sea chart), so the base map shows through. No data colour is changed.
- Store data, not pictures. Each picture is turned into cells of one shared latitude / longitude grid, 0.02° (about 2 km): GK2A’s and Himawari-9’s own 2 km squares over Cambodia, and MOWRAM’s radar from its 1.2 km pixels put on the same squares (the strongest echo in each), so every source is read square for square. For every cell the database keeps its position (latitude, longitude), the colour of the product and, where the colour bar can be read, the value. Tables
sat10_frame,jma10_frame,mowram_radar,satrain_frame,sat_prod_cell. The page redraws the picture from these cells, so any stored time can be replayed later and every cell answers “what is the value here”. - Pictures kept as they came: MOWRAM’s radar picture of every whole hour (lossless, so the history can be decoded again) and the rain layer’s hourly picture; the satellites’ 10-minute pictures are kept as their 2 km grids. Everything is kept for ever; the disk panel shows the measured growth per day and per year, and more disk space will be added when needed (tables
sat_prod_pic,sat_prod_cell). - Backlog: JMA keeps only the last 24 hours of its pictures: all of them were fetched. GK2A keeps much longer: its last 15 days are being fetched (rain rate and cloud-top temperature at full 2 km, one picture at a time, about 8 hours in all).
- Why every grid is 2 km: a grid cannot be finer than what the instrument and the published data can see. KMA publishes GK2A’s rain rate and cloud-top temperature, and JMA Himawari-9’s infrared, at 0.02° over Cambodia: about 2 km, the smallest square either satellite gives here (GK2A’s visible 0.5 km comes only in a full-disk file of 260 MB every 10 minutes, not fetched). MOWRAM’s radar picture is 1.2 km a pixel; it is put on the same 2 km squares (the strongest echo in each), so the radar, the two satellites and the rain gauges are compared square for square, and one tapped square answers for every source.
- Radar grid, stored for ever: every radar picture (every 15 minutes) is stored as its 2 km squares (table
mowram_radar): a pixel is an echo when it has one of the radar’s 15 legend colours and differs from the picture’s own map underneath (terrain, borders, rivers, town names: the median of the clearest stored pictures); each square keeps the strongest echo, shown with the textbook rain rate (Marshall–Palmer), for example “42–45 dBZ ≈ 15–24 mm/h”. Until the evening of 18 October 2026 a pixel counted only when most of its 5×5 neighbourhood had its exact colour, which dropped about 70 % of the echo; the whole history (from 15 May 2026) was decoded again. The whole-hour pictures are kept as they came, so this can be repeated. - Not kept: the operators’ full-size files (KMA’s GK2A and JMA’s Himawari pictures) are not stored as they come: they are read, turned into 2 km grid cells (and a picture for the map), and thrown away.
- Value from the colour (every picture with a colour bar): each Level-2 picture carries its own colour bar. The system finds the colour boxes of that bar in the picture itself, gives every box the value printed under the bar, and snaps every pixel to its box (anything that is not a bar colour, such as the grey cloud drawn under the rain, is “no value”). The box number is stored with each grid cell, so the mouse-over shows, with the colour and its colour code: rainfall rate as a range (for example “≈ 6–7 mm/h”; the rain bar is uneven, so where no number is printed two boxes share the printed range), cloud top temperature (≈ °C, boxes of 2 °C), cloud top pressure (hPa), cloud top height (km), sea surface temperature (°C, boxes of 1 °C) and cloud phase (water / ice / uncertain). The basic channels show their brightness (0–255; for infrared, brighter = colder); the RGB pictures have no number scale, only colours to interpret.
- Position check and correction (16 October 2026): every picture’s place on the map was tested by projecting Cambodia’s border onto the yellow border line drawn in the picture itself. The rain / cloud / sea pictures matched (96 % of the border points on the line). The Cambodia-area pictures (basic channels and RGB) did not: the first calibration put them about 1.7° (about 190 km) too far north. It is corrected (95–96 % on the line, checked on pictures 14, 7 and 2 days old). Consequences, said plainly: satellite readings per place stored before the correction were read at the wrong spot, so the history before that time is no longer shown or used for learning; the stored grids of today were fetched again with the correct position.
- Replay: the Play back History menu can replay either the grid (the stored cells, every 10 minutes for the main products, every hour for all) or the cropped picture (every whole hour).
- When: every product is stored once an hour (all 33) and kept for ever (never deleted) for the history replay and for forecasting later; the important ones (rain, cloud, sea, infrared, visible, water vapour, true colour, convective storm) are also stored every 10 minutes and kept 30 days. The others are fetched every 10 minutes while somebody has them open.
How reliable is each kind of picture? (honest)
| Picture | What it really is | How far to trust it |
|---|---|---|
| Basic channels, RGB | The satellite’s own measurement, only coloured | High for where clouds are. Detail is about 2 km (coarser at the edge); smaller clouds are not seen. Visible light only works in daytime. |
| Cloud top temperature / height / pressure | KMA estimate from the infrared channels | Good for thick high cloud (thunderstorm tops). Less exact for thin cloud, and the height of low cloud is uncertain. A very cold, very high top is the sign of a strong storm. |
| Cloud phase | KMA estimate: water or ice at the cloud top | Medium. A guide, with an “uncertain” class of its own. |
| Rainfall rate | KMA estimate of rain from the cloud, not a measurement of rain | Medium / low for amounts. It tells where rain is probable well; the mm per hour can be wrong by a lot, it can miss short heavy showers under small clouds, and it can show rain that evaporates before the ground. The rain gauges and the radar are the reference, this picture is not. |
| Sea surface temperature | KMA estimate for cloud-free sea | Good where the sky is clear; nothing under cloud (shown empty). |
Limits to say out loud
- The satellite looks from the side (128°E), so tall clouds appear a few kilometres west of their true place (about 6–9 km for a thunderstorm). Not corrected.
- Pictures are normally 10–20 minutes old when they appear. The page shows the real time of every picture (Cambodia time).
- These are for watching the weather, not an official warning. The official forecast and warnings come from the Department of Meteorology.
- Measured against our own rain gauges, hour by hour at every station (first day, 17 October 2026): GK2A rain rate — caught 42 % of rain hours, 71 % false alarms, CSI 0.21; JMA’s best signal — CSI 0.17. So the map uses GK2A for rain. The numbers will be updated as the days add up.
What the page can do (for a demonstration)
- Picture menu: real clouds (animated, with rain and thunder) or any of the 17 Himawari-9 (JMA) products, one by one. A short explanation appears under the picture.
- Replay: latest 1 hour by default, up to 3 days of recent pictures, and a Play back History button: choose any stored day and hour (the menu lists the days that have data) and replay it, any of the 33 pictures, then Back to live. Buttons: first, previous, play / pause, next, latest, and a slider. Speed 1× to 20× (1× = one picture a second).
- Several layers together: in Play back History, tick several pictures (for example cloud top temperature + rainfall rate) and they play on one time line, rain on top. Incoming storms (followed by the server, outside Cambodia, up to about 300 km) and Thunderstorm cells are drawn at the time of each picture, live and in the playback, and an Incoming storm history card lists them while there are any (hidden when there are none). View Cambodia Only hides everything outside the border.
- Mouse over: latitude and longitude, the picture name, its time, the colour and, where known, the value (for example °C) of the stored cell under the pointer.
- Space bar = pause and capture: freezes the picture and shows its time (Cambodia time) and the latitude / longitude of the pointer, with a marker on the map. Click the map to move the point, Copy to paste it into a report, Space again to continue.
- Full screen button, three base maps (map, terrain & vegetation, satellite), and an English / Khmer button that translates the whole page, including the generated texts.
- Opacity slider for the product pictures, so the map underneath can be seen.
Storms coming from other countries (the arrows on the cloud view) — how reliable?
The system looks for very cold cloud tops outside Cambodia in the wide infrared picture, then compares the same patch of cloud in the picture from 40 minutes earlier to see how far it moved. Distance divided by time gives the speed and direction; the distance to the nearest border point divided by the speed towards it gives the “reaches Cambodia in about …” time.
- Safeguards added: a movement is believed only if the match is clearly better than “did not move”; speeds above 70 km/h are rejected as noise; a storm is called approaching only if it moves mostly towards the border (at least 8 km/h) and would arrive within 6 hours (a storm cell lives about 1–3 hours, so a longer forecast would be a guess).
- Stored for ever: the server repeats the same analysis for every new picture (JMA’s infrared first, GK2A’s cloud tops while JMA is late) and saves every storm heading to Cambodia’s border (since 18 October 2026; before, every storm within about 300 km): latitude, longitude and its cell of the 2 km grid, size, coldness, speed, direction, distance and hours to the border, country, in tables
storm_track_run/storm_track(app/stormtrack.py,/api/storm/track/at?ts=). Small data, kept for ever, for replay and for training a forecast later. - Play as real image or as grid (on the former
/cloudspage, removed 17 October 2026; the satellite pages/gk2a-satelliteand/jma-satellitenow show every 0.02° cell): the real-cloud view has Play as: Real image (the clouds as they look) or Grid (the same data as its 2 km squares: cloud thickness and rain, in the picture’s own colours). The grid lines are drawn (when zoomed out, every 5th / 10th line, and the label says so), and the mouse shows the cell, its position and its rain in dBZ and mm/h. Every product of the picture menu can show its grid lines too (Show grid). - Map projection checked: the stored grids are even in latitude, the web map is Mercator. Laying a picture straight between its top and bottom edge would have put the middle rows up to 0.13° (about 15 km) off over the grid’s box, 97–112°E, 5–21°N. Every picture and grid is now drawn row by row at its own latitude, so a cell sits where it belongs.
- Disk alert: the data is kept for ever, so the system watches the disk. At 80 % used the admin page shows a warning, at 90 % a critical warning, and at 95 % the collector stops storing the cropped pictures, the 10-minute pictures and the radar (keeping only the small hourly grid cells), so the database never runs out of room. Adding disk space removes the alert automatically.
- Play back History options: Replay as Grid (the default) or Image. For the grid there are two ways, and only the chosen one counts: a length from a start time (1, 2, 3, 6 or 12 hours, 1, 2, 3 or 7 days) or from one date and time to another (From and To side by side; the length is switched off, so a length picked by mistake cannot change the period). Image opens its own From / To hours, and every hour that really has an image is marked with ✓. For the chosen days the menu shows, for every ticked layer, how many grid pictures and images are stored and suggests what can be replayed. At most 7 days at a time. View Cambodia Only works in every view (real clouds, grid, every product), for the pictures, the rain cells and the grid lines. Every floating card (clock, colour bar, storm list, capture card, rain key, history panel, player) can be dragged to another place so it never covers what you need; the place is remembered, and a double-click puts the card back. The clock also steps aside by itself when the history panel opens.
- How “reaches Cambodia in about …” is calculated (live and history playback, and the weather map): (1) the storm’s speed and direction come from how far the same cold cloud moved between two satellite pictures (40 minutes apart live, one hour apart in history); (2) the nearest point of Cambodia’s border is found; (3) only the part of the speed that points at that border point counts; (4) hours = distance to the border ÷ that speed. It is shown only when the storm moves mostly towards the border (at least 8 km/h) and would arrive within 6 hours; it is written as hours and minutes (for example 2 h 44 min). On the weather map, switch on Thunderstorm: the storms outside Cambodia appear as arrows (red = heading this way), the legend lists those heading towards Cambodia with their hours, and a click on a storm opens its summary (country, position, cloud top, speed, direction, distance, hours, time seen). Honest limit: a storm can grow, weaken or turn, so the hours can be early or late by an hour or more.
- How the storms are found (since 18 October 2026): on the shared 2 km grid, JMA’s infrared first (its pictures land about 5 minutes before GK2A’s; checked on 202 slots both covered: 21 of JMA’s 117 storms said to be heading in were followed by radar rain of 30 dBZ near their border point within an hour of the time given, 10 of GK2A’s 69), GK2A’s cloud tops while JMA has had no new picture for an hour. A storm is a patch of very cold cloud tops (about −41 °C and colder) of at least 26 squares (about 130 km²); its movement is the best match of the same cloud 40 minutes earlier, on a linear cloud-top-temperature scale. The 15-day backlog from MOWRAM’s GK2A receiver was removed with that source.
- Honest limits: this is a motion estimate of cloud, not a weather model. Storms grow, split and die, so the arrival time can be early or late by an hour or more. It is a heads-up, not a warning.
Where the meteorology knowledge comes from (what the system “knows” about reading a satellite picture)
The system does not invent weather rules. Each rule used to read or draw a picture comes from published, widely used meteorology practice, or from the data providers themselves. In plain words:
| What the system does | Knowledge used | Where it comes from |
|---|---|---|
| Knows what each picture means (visible, infrared, water vapour, RGB, rain rate, cloud top, sea temperature) | Each channel sees something different: visible = sunlight reflected by cloud (day only); infrared = heat of the cloud top (day and night); water vapour = moisture high in the air; the Level-2 products are KMA’s own calculations. | Japan Meteorological Agency (JMA) Meteorological Satellite Center: Himawari real-time imagery and its product guides; Korea Meteorological Administration (KMA) National Meteorological Satellite Center: GK2A rain rate and cloud-top temperature. |
| Places the round Earth picture on a flat map | The standard formula for a satellite that stays above one point of the equator (“geostationary projection”), with GK2A’s published position (128.2°E, 35 786 km up). | CGMS LRIT/HRIT Global Specification (the same formula Himawari, GOES and Meteosat use). |
| Reads a colour as a value (for example −66.8 °C) | Each colour of the picture is matched to the colour bar printed under it. | KMA’s / JMA’s colour bar of the picture itself. |
| Calls a cloud “thunderstorm” | Tropical thunderstorm tops are very cold: about −55 °C or colder is a tall rain cloud, −62 °C likely a thunderstorm, −70 °C or colder a severe one. | In line with common satellite nowcasting practice: cloud tops colder than about −52 to −55 °C are widely used to mark deep rain clouds, and colder than about −70 °C often marks the strongest storm tops. The exact numbers in this system (−55 / −62 / −70) were set from that practice, not fitted in Cambodia. |
| Reads the colour pictures (RGB) | Known recipes: airmass (warm / cool / dry air), dust (pink = dust), convective storm (yellow / red = strong storm tops), natural, fog, day & night. | EUMETSAT RGB colour interpretation guides; KMA’s GK2A RGB pictures follow the same recipes. Used only for looking, never in the prediction. |
| Reads the radar | Radar echo strength (dBZ) grows with rain. This system reads every echo as a rain rate first, with the textbook Marshall–Palmer relation, and names it on one scale for every picture and page: light under 2.5 mm/h (under 29 dBZ), moderate to 10 mm/h (39 dBZ), heavy to 50 mm/h (50 dBZ), very heavy above; 45 dBZ and more counts as a storm core (the animation’s lightning). It also learns its own link from our gauges (calibration panel). | Marshall & Palmer (1948) and standard radar meteorology; MOWRAM radar colour legend. |
| Follows a storm and says when it may reach Cambodia | Find the same patch of cloud in two pictures and measure how far it moved (“cloud motion”); a storm cell usually lives 1–3 hours, so no arrival is announced beyond 6 hours. | The atmospheric-motion-vector idea used by every satellite centre; storm life-cycle knowledge from nowcasting practice. |
| Warns that tall clouds look shifted | A satellite looking from the side sees a tall cloud top displaced away from it (“parallax”): height × tan(viewing angle), about 6–9 km here. | Simple geometry, standard in satellite meteorology. |
| Checks itself against the ground | The satellite and radar readings are compared with our own rain gauges hour by hour. | Our own stations (the calibration panel above); this is the only part measured in Cambodia by us. |
How the satellite reading is used in the rain prediction (the “next 5 to 60 minutes” nowcast)
Important, because many people rely on the prediction: the satellite is only one of five inputs, and the weakest one. It can see a tall cloud forming before the rain gauge or the radar does, but a cold cloud top does not always rain on the ground.
- What is read: for each station, the cloud top temperature overhead (GK2A Level-2, °C). If that is not fresh (older than 50 minutes), the infrared 10.5 µm brightness is used instead. Never both, so the same cloud is not counted twice. A reading older than 50 / 70 minutes is treated as “no reading”, not used.
- How it is turned into a number: a cloud top of −10 °C or warmer counts 0, −60 °C or colder counts 1, in between it grows evenly (infrared: grey level 40 → 0, 230 → 1).
- How much it weighs: it is added to the chance of rain with a fixed weight that is smaller than “it is raining at the gauge now” and smaller than the radar, and that fades for longer times (weight 1.0 for the next 5–10 minutes, 0.6 for 30 minutes, 0.3 for 60 minutes). Radar weighs 2.4 → 0.2, rain at the gauge now 3.2 → 0.3; falling pressure and rising humidity matter most at 60 minutes.
- Not learned yet: these satellite and radar weights are set by hand, not fitted from data, because there is not yet enough satellite history. The pressure / humidity part is learned per station from its own history where enough data exists.
- Not used in the prediction: the colour (RGB) pictures, the rainfall-rate picture, sea temperature, cloud phase and the storm arrows are for looking only. They are stored (grid cells) so that, later, they can be tested against the gauges and added only if they prove to help.
- Every prediction shows its inputs and their age (admin info icon), so nobody has to trust a number blindly.
Before relying on it more (plan)
- Compare the stored hourly grid cells (cloud top temperature, rainfall rate) with the rain gauges for several months, station by station, including the rainy season.
- Only when that comparison shows real skill, fit the satellite weight from data instead of by hand, and show the measured accuracy here.
- Until then: the prediction is an aid; official forecasts and warnings remain those of the Department of Meteorology.
Honest note: the thresholds above are general practice, not tuned for Cambodia yet, except where the gauge comparison shows it. They are good for seeing and following weather, not for official warnings.
How to check it yourself
- Open the operators' own pages: JMA's Himawari-9 pictures (data.jma.go.jp) and KMA's GK2A pictures (nmsc.kma.go.kr).
- Open
/jma-satelliteor/gk2a-satellite, choose the same picture and time. The shapes and colours are the same, only re-drawn on a flat map. - Compare with the radar and the rain-gauge map of the main page for the same hour: rain should sit under the cold, high cloud.
Demo: how the code reads a satellite picture, cell by cell
This is the newest real infrared picture the system stored. The code never "looks" at the picture like a person: it divides it into small squares, averages the grey level in each, and applies a few fixed threshold rules. Switch the view to see each component, and click any square to see its numbers and which rule fired.
If someone asks “how does the code read the data, what is the grid, which pattern is used?” — ready answers
30-second answer: “Every 10 minutes the system downloads the satellite data of the region from KMA (GK2A) and JMA (Himawari-9). It is already a grid of small squares, each 2 km wide, and each square has a number — for the infrared a brightness from 0 to 255: brighter means a colder, taller cloud top. The code goes through the squares inside Cambodia's border and applies a few fixed rules: above 90 it calls it cloud, above 170 it calls it likely rain. Touching squares are joined into one patch so the map can draw a shape. It is rule-based pattern reading of brightness, not a person looking at the picture and not a lightning detector.”
1. How does the code read the data?
- Fetch — the satellite data is downloaded from the operators themselves every 10 minutes: GK2A from KMA (
app/satellite_nmsc.py) and Himawari-9 from JMA (app/satellite_jma.py), each new picture within a minute or two of its release. - Locate — every square of the shared grid has a fixed latitude/longitude (0.02°: 97–112°E, 5–21°N, 750 × 800 squares), so the code knows which square is which place; the map and the readings use the same squares.
- Read — for every station and province/district point it reads the brightness of the pixel at that spot (0–255). For the cloud-top temperature product it instead reads the pixel's colour and finds the nearest colour in the picture's own legend bar to get °C.
- Classify — threshold rules turn the number into a label (clear / cloudy / likely rain).
- Store & show — saved in the database; the map draws it; a ground gauge that is catching rain, or the radar, overrides the satellite hint.
2. What is the grid?
One shared grid: 0.02° squares (about 2 km) for GK2A and JMA (the radar on its own grid). The code visits every cell that lies inside Cambodia's national border (a point-in-polygon test against the border outline), labels it, and counts how many cells are cloudy or likely rain — that is the “cloud cover %” number. Touching cloudy cells are grouped into one patch (see the pattern table), and each patch is drawn as a traced outline on the map. The squares in this demo are bigger than real cells only so you can see them.
3. Which pattern is used?
| Pattern | Where it is used | In plain words |
|---|---|---|
| Threshold classification | cloud / likely rain, cold level | “If brightness is at least X it is class Y.” Fixed numbers: 90, 170; scale 40–230. |
| Colour-legend lookup | cloud-top °C (satellite_ctt.py) | Find which legend colour the pixel is closest to, read the °C written beside that colour. |
| Flood fill (4-connected clustering) | cloud / rain patches (radar.cluster_blobs) | Start at a cloudy cell, keep adding neighbours that share an edge; one connected group = one patch. |
| Inverse-distance weighting | the soft cloud / rain texture on the map | Between the known points, fill in a value from the nearby ones, nearer points counting more. |
| Rule-based inference | thunderstorm / lightning layer | “Cloud top colder than −62°C or radar above 42 dBZ → likely storm.” No sensor is involved. |
| Block matching | cloud motion (cloud_motion()) | Slide the previous picture a few pixels each way, keep the shift that matches best: gives direction and speed. |
| Ground-truth override | map “raining now” | A rain gauge within 6 km that is measuring rain wins over any satellite or radar estimate. |
| Learned calibration (the only trained part) | grey / dBZ → mm per hour | Once a day, compare past satellite readings with what gauges actually measured, and average per level. |
4. Is this artificial intelligence? Is the lightning real?
The picture reading is not AI — it is fixed, visible rules you can read in the code box below. Only the grey→mm calibration is learned, from gauge history. The lightning layer is inferred: GK2A has no lightning sensor, so it marks places where the cloud top is very cold and/or the radar echo is heavy, because that is where thunderstorms occur. It says “possible / likely / severe”, never “lightning detected”.
5. How accurate is it?
The satellite grey level is a proxy (a tall cold cloud often rains). That is why the site always shows it as “likely” and lets gauges and radar override it. Accuracy numbers for the learned part are in the “pattern reading calibration” panel on the Custom Prediction tab.
6. What is the scaling grid?
“Scaling” is how a square on the screen is tied to a real size on the ground. Three steps, all straight-line arithmetic:
- Pixel → degrees. Every picture is on the shared 0.02° grid (97–112°E, 5–21°N), so
lon = 97 + (column + 0.5) × 0.02andlat = 21 − (row + 0.5) × 0.02. MOWRAM’s radar picture (1.2 km a pixel from the Phnom Penh centre point) is put on the same squares, the strongest echo in each. - Degrees → kilometres. One degree of latitude is always 111.32 km; one degree of
longitude is shorter the further from the equator:
111.32 × cos(latitude)(about 108.6 km at 12.6°N). So one square is about 2.2 km wide × 2.2 km tall (about 4.8 km²), for every source. - Cell size → step. The code can read every pixel (step 1) or every n-th pixel (step n); a cell is then n pixels wide, n×the pixel size in km.
| Step (pixels per cell) | Infrared cell | Radar cell | Trade-off |
|---|---|---|---|
| 1 (production) | ~2.2 km · ~38,200 squares in Cambodia · read in ~1 s | the same 2.2 km squares (the radar’s 1.2 km pixels, the strongest in each) | finest the source can honestly give; village scale |
A smaller cell is not more detail than the source has: asking for 1 km cells of the infrared, which is read on 2 km squares, would only repeat the same value. The cell can never be finer than one source pixel, which is why production stops at step 1. On the map, the dots and icons also scale with the zoom level (smaller zoomed out, up to 1.3× zoomed in) so crowded areas stay readable; that is only drawing size, it does not change what is measured.
Click a square on the picture.
1 · Cloud
Mean grey of the square. grey ≥ 145 → cloudy, otherwise clear.
White = cloud top in the infrared picture: the colder the top, the whiter.
2 · Raining (proxy)
grey ≥ 245 → likely rain. A very cold, tall top is the signature of
rain-bearing cloud — it is a hint, never a measurement. Radar and gauges decide the real
amount (and a gauge that is catching rain overrides this).
3 · Temperature
cold level = (grey − 40) / (230 − 40), clamped 0–1: 0 = warm ground or sea,
1 = very cold cloud top. The actual °C comes only from GK2A's cloud-top-temperature product, read at
each place (the white dots). Not the air temperature people feel.
4 · Lightning / storm
No lightning sensor covers Cambodia, so it is inferred at each place from two real numbers:
severe top ≤ −70°C or radar ≥ 50 dBZ; likely top ≤ −62°C or
≥ 42 dBZ; possible top ≤ −55°C with radar ≥ 30 dBZ or a raining gauge nearby.
Squares with no place point use a demo-only proxy (grey ≥ 215).
The real code (Python, app/satellite.py + app/satellite_ch.py)
LABEL_BANDS = ((0, "clear"), (145, "cloudy"), (245, "likely_rain"))
IR_WARM, IR_COLD = 40, 230
def label_for(gray):
lab = LABEL_BANDS[0][1]
for edge, name in LABEL_BANDS:
if gray >= edge:
lab = name
return lab
def cold_level(gray): # used by the rain nowcast
return max(0.0, min(1.0, (gray - IR_WARM) / (IR_COLD - IR_WARM)))
def storm_level(ctt, dbz, gauge):
if (ctt is not None and ctt <= -70) or (dbz is not None and dbz >= 50): return 3
if (ctt is not None and ctt <= -62) or (dbz is not None and dbz >= 42): return 2
if ctt is not None and ctt <= -55 and ((dbz is not None and dbz >= 30) or gauge): return 1
return 01 · Read the colour → dBZ
The radar picture is drawn in a fixed colour scale (its legend). For each pixel the code finds the nearest legend colour (sum of the red, green and blue differences ≤ 18) and takes the dBZ written beside it: 13 pale cyan … 26 green … 42 orange … 55 dark red … 60 black. A pixel that matches no legend colour is just map background — no echo.
2 · Ignore thin lines
Province borders and town dots are drawn in near-black 1–2 pixel strokes that exactly match the 60 dBZ colour. A real echo covers many neighbouring pixels, so the code only accepts a level that a majority of a small neighbourhood agrees on (the demo: ≥ 3 pixels in the square). Borders are rejected, storms are not.
3 · dBZ → rain rate
Marshall–Palmer: rain mm/h = (10dBZ/10 / 200)1/1.6. It is an
estimate: a generic textbook relation. Every day the system also learns a real, local table by
comparing past dBZ with what the gauges measured (see the calibration panel).
4 · Bands & patches
≥13 light · ≥26 moderate · ≥42 heavy · ≥60 severe.
Touching echo cells are joined (4-connected flood fill) into one patch with a traced outline, stored per
fetch. The grid here is the shared 2 km squares.
The real code (Python, app/radar.py)
CAPPI_DBZ_COLORS = ((60,(0,0,0)), (55,(190,0,0)), (50,(255,64,0)), (45,(255,128,0)),
(42,(255,192,0)), (38,(255,255,0)), (35,(0,57,0)), ... (13,(165,255,255)))
COLOR_MATCH_MAX = 18
LABEL_BANDS = ((0,"clear"), (13,"light"), (26,"moderate"), (42,"heavy"), (60,"severe"))
def _dbz_for(rgb): # nearest legend colour
best, best_d = None, None
for dbz, c in CAPPI_DBZ_COLORS:
d = abs(rgb[0]-c[0]) + abs(rgb[1]-c[1]) + abs(rgb[2]-c[2])
if best_d is None or d < best_d: best, best_d = dbz, d
return best if best_d is not None and best_d <= COLOR_MATCH_MAX else None
def rain_mmh(dbz): # Marshall-Palmer Z = 200 R^1.6
z = 10 ** (dbz / 10.0)
return round((z / 200.0) ** (1 / 1.6), 2)In production every cell is one square of the shared 2 km grid, not these larger squares — the demo's cell-size menu lets you move between the two.
Where does the data come from, and where is it stored when finished?
Flow, the same for every source:
Source website → fetch (collector container, on a timer) → read (the rules above, per
pixel / per place) → store (PostgreSQL database, plus the picture itself) → serve
(/api/…, cached 1–5 min) → the public map and this admin page.
| Satellite cloud (infrared) | Cloud-top temperature | Weather radar | |
|---|---|---|---|
| Source | GK2A satellite from KMA’s National Meteorological Satellite Center nmsc.kma.go.kr: its 2 km products over the region (rain rate, cloud-top temperature, channels), every 10 minutes |
Himawari-9 satellite from JMA www.data.jma.go.jp/mscweb/data/himawari/: its 2 km and 5 km pictures (infrared, heavy-rain areas and 15 more), every 10 minutes |
MOWRAM radar server (login required; credentials only in the server environment). The code lists the
newest CAPPI480 image for Phnom Penh and downloads it. |
| Fetched by | app/satellite.py every 10 min (a new picture appears about hourly) |
app/satellite_ctt.py every 10 min | app/radar.py every 15 min (MOWRAM's own refresh) |
| How the picture becomes numbers | Crop to Cambodia using the picture's own lat/lon grid; read the grey level at every station and province/district; scan every pixel inside the border, label it, group touching cells; compare with the previous picture for cloud motion | Convert lat/lon to a pixel with the geostationary-satellite projection; read the pixel's colour; nearest colour in the picture's legend bar → °C | Crop away MOWRAM's text panel; majority-vote nearest legend colour in a small window at each point → dBZ → mm/h; scan every pixel, group echo cells |
| Stored — short term (live) | sat_obs (grey per point per fetch, kept ~2 h), sat_blobs (patches),
sat_motion; key-value sat_summary (cover %) |
sat_ctt_obs (°C per point per fetch, kept ~2 h) |
radar_obs (dBZ per point per fetch, kept ~2 h), radar_blobs (patches),
key-value radar_summary |
| Stored — permanent (history, learning, replay) | sat_archive one row per point per hour; sat_ch_frame the picture itself, 400 days |
sat_ctt_archive one row per point per hour |
radar_archive one row per point per hour (dBZ, label, mm/h) |
| Picture files | last 8 on the server disk, plus in the database (above) | last 6 on disk | last 8 on disk (/geo/radar) |
| Used for | map cloud layer, rain-chance hint, nowcast, thunderstorm layer | nowcast (replaces the grey hint when fresh), thunderstorm layer, cloud-top card | map rain layer, nowcast, learned dBZ→mm calibration, thunderstorm layer |
Everything lives in the PostgreSQL database
weather_data on the weather server. A live check of every source (is it answering, how old is the
newest picture, how old is what we hold) is in Custom Prediction → Satellite & radar data sources.
| Category | Tables | Rows (approx.) | Share | Disk |
|---|
Growth
| Period | Rows added | Size added |
|---|
Every table
| Category | Database | Table | Rows (approx.) | Exact rows | Disk |
|---|
Row counts are PostgreSQL's own estimates (instant, usually within a few %). Press Count on a table for the exact figure. Monthly partitions of the readings table are added into one line.
| Database | Table | Approx. rows |
|---|
Preview
Website feedback
Messages sent with the feedback form on the public About page (/about#feedback). Each one passed the "I am not a robot" check; the sender is kept only as a scrambled code (no address, no position). Newest first.
Your own visits are not counted (office network + this browser).
Where visitors come from
Testing addresses (not counted)
Find an address by its start, its provider or its city, then exclude it as testing: its network (the /24 of an IPv4 address, the /64 of an IPv6 one) is never counted again and its past visits are deleted, the daily totals rebuilt. Count again undoes the exclusion from now on; deleted visits do not come back.
| Address | Visits | Last seen | Provider | City | Browser |
|---|
Visitors in Cambodia by province and district
By GPS: only visitors who let the front page or Travel use their position, counted once a page; the server keeps the district it falls in, never the coordinates (green circles on the map). By internet address: the city the provider is in, approximate (blue circles). The period is the map’s.
By province
| Province | By GPS | GPS visitors | Places looked up | By internet address |
|---|
By district (GPS)
| District | Province | Visits | Visitors |
|---|
By day
| Day | Visits | Unique IPs |
|---|
Most opened
| What | Item | Clicks |
|---|
Where from
| City | Country | Visits | Unique |
|---|
Recent
| When | IP | Event | Item | Location |
|---|