From ce3fc4ef442a73193fe5cf7dde1c129a75526e37 Mon Sep 17 00:00:00 2001
From: Olivier Maury <Olivier.Maury@inrae.fr>
Date: Fri, 19 Apr 2024 09:19:03 +0200
Subject: [PATCH 1/3] Afficher les normales. refs #24

---
 bin/start_embeddedtomcat.sh                   |   7 +
 pom.xml                                       |   2 +-
 sql/indicators.csv                            |  14 +-
 sql/init_data.h2.sql                          |  10 +-
 sql/init_data.postgresql.sql                  |  83 +-
 sql/migration.sql                             |  27 +
 sql/normalvalues.csv                          | 732 +++++++++---------
 sql/schema.tables.sql                         |  23 +-
 sql/translations.csv                          |  43 +-
 src/site/markdown/development.md              |   4 +-
 www-client/pom.xml                            |   2 +-
 .../www/client/i18n/AppConstants.java         |  12 +
 .../www/client/i18n/AppMessages.java          |  10 +
 .../fr/agrometinfo/www/client/module.gwt.xml  |   2 +-
 .../www/client/ui/chart/CreditsPlugin.java    |  41 +
 .../www/client/ui/chart/DailyValuesChart.java | 168 ++++
 .../www/client/ui/chart/package-info.java     |   4 +
 .../www/client/ui/map/CanvasTitle.java        |   5 +
 .../www/client/view/LayoutView.java           |   2 +-
 .../www/client/view/RightPanelView.java       | 144 +---
 .../www/client/i18n/AppMessages_fr.properties |   3 +-
 www-server/pom.xml                            |   2 +-
 .../www/server/dao/PraDailyValueDao.java      |  12 +-
 .../server/dao/PraDailyValueDaoHibernate.java |  40 +-
 .../www/server/model/PraDailyValue.java       |  15 +
 .../www/server/rs/ApplicationResource.java    |   3 +
 .../www/server/rs/IndicatorResource.java      |  99 ++-
 .../www/server/util/LocaleUtils.java          |   3 +
 www-server/src/main/tomcat10xconf/context.xml |   2 +-
 .../www/server/rs/IndicatorResourceTest.java  |  27 +-
 www-shared/pom.xml                            |   2 +-
 .../www/shared/dto/SummaryDTO.java            |  28 +-
 32 files changed, 949 insertions(+), 622 deletions(-)
 create mode 100644 www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/CreditsPlugin.java
 create mode 100644 www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/DailyValuesChart.java
 create mode 100644 www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/package-info.java

diff --git a/bin/start_embeddedtomcat.sh b/bin/start_embeddedtomcat.sh
index 1c30c51..4923d79 100755
--- a/bin/start_embeddedtomcat.sh
+++ b/bin/start_embeddedtomcat.sh
@@ -1,4 +1,11 @@
 #!/bin/sh
 DIR=$(dirname $0)
+export MAVEN_OPTS="--add-opens=java.base/java.lang=ALL-UNNAMED
+--add-opens=java.base/java.math=ALL-UNNAMED
+--add-opens=java.base/java.net=ALL-UNNAMED
+--add-opens=java.base/java.text=ALL-UNNAMED
+--add-opens=java.base/java.util=ALL-UNNAMED
+--add-opens=java.base/java.util.concurrent=ALL-UNNAMED
+--add-opens=java.sql/java.sql=ALL-UNNAMED"
 cd $DIR/../www-server
 mvn cargo:run -Plocal -X
diff --git a/pom.xml b/pom.xml
index bec9a8f..79ab203 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>fr.agrometinfo</groupId>
   <artifactId>www</artifactId>
-  <version>1.0.0-alpha-1</version>
+  <version>2.0.0-alpha-2</version>
   <packaging>pom</packaging>
   <name>AgroMetInfo web app</name>
   <description>Website for AgroMetInfo in Jakarta EE 10 and GWT.</description>
diff --git a/sql/indicators.csv b/sql/indicators.csv
index 1b11d89..8af19d3 100644
--- a/sql/indicators.csv
+++ b/sql/indicators.csv
@@ -6,5 +6,17 @@ maxt,year,°C,TemperatureReversed,,SEXTILES
 meant,year,°C,TemperatureReversed,,SEXTILES
 mint,year,°C,TemperatureReversed,,SEXTILES
 rainsum,year,mm,Precipitation,,SEXTILES
-rainsum,spring,mm,Precipitation,,SEXTILES
+frostdaystmin,winter,j,Blues,,SEXTILES
+hdaystmax,winter,j,YlOrRd,,SEXTILES
+hdaystmax1,winter,j,YlOrRd,,SEXTILES
+maxt,winter,°C,TemperatureReversed,,SEXTILES
+meant,winter,°C,TemperatureReversed,,SEXTILES
+mint,winter,°C,TemperatureReversed,,SEXTILES
+rainsum,winter,mm,Precipitation,,SEXTILES
+frostdaystmin,summer,j,Blues,,SEXTILES
+hdaystmax,summer,j,YlOrRd,,SEXTILES
+hdaystmax1,summer,j,YlOrRd,,SEXTILES
+maxt,summer,°C,TemperatureReversed,,SEXTILES
+meant,summer,°C,TemperatureReversed,,SEXTILES
+mint,summer,°C,TemperatureReversed,,SEXTILES
 rainsum,summer,mm,Precipitation,,SEXTILES
diff --git a/sql/init_data.h2.sql b/sql/init_data.h2.sql
index af14566..c2d3e4c 100644
--- a/sql/init_data.h2.sql
+++ b/sql/init_data.h2.sql
@@ -106,12 +106,14 @@ CREATE TEMPORARY TABLE IF NOT EXISTS tmp_normalvalue (
     period VARCHAR,
     cell INTEGER,
     doy INTEGER,
-    computedvalue DOUBLE PRECISION
+    medianvalue DOUBLE PRECISION,
+    q5 DOUBLE PRECISION,
+    q95 DOUBLE PRECISION
 );
-INSERT INTO tmp_normalvalue (indicator, period, cell, doy, computedvalue)
+INSERT INTO tmp_normalvalue (indicator, period, cell, doy, medianvalue, q5, q95)
     SELECT * FROM CSVREAD('../sql/normalvalues.csv');
-INSERT INTO normalvalue (indicator, cell, doy, computedvalue)
-    SELECT i.id, t.cell, t.doy, t.computedvalue
+INSERT INTO normalvalue (indicator, cell, doy, medianvalue, q5, q95)
+    SELECT i.id, t.cell, t.doy, t.medianvalue, t.q5, t.q95
     FROM tmp_normalvalue AS t
     JOIN indicator AS i ON i.code=t.indicator
     JOIN period AS p ON p.id=i.period
diff --git a/sql/init_data.postgresql.sql b/sql/init_data.postgresql.sql
index 84c10d4..6648baa 100644
--- a/sql/init_data.postgresql.sql
+++ b/sql/init_data.postgresql.sql
@@ -1,15 +1,5 @@
 -- Initialization script for PostgreSQL database with sample data
-
-DELETE FROM normalvalue;
-DELETE FROM dailyvalue;
-DELETE FROM cell;
-DELETE FROM department;
-DELETE FROM region;
-DELETE FROM indicator;
-DELETE FROM period;
-DELETE FROM i18n;
-DELETE FROM i18nkey;
-DELETE FROM locale;
+-- also do update.
 
 -- translations
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_translation (
@@ -19,21 +9,42 @@ CREATE TEMPORARY TABLE IF NOT EXISTS tmp_translation (
 );
 \COPY tmp_translation (key, locale, translation) FROM translations.csv WITH DELIMITER ',' HEADER CSV;
 INSERT INTO locale (languagetag)
-    SELECT DISTINCT locale FROM tmp_translation ORDER BY locale;
+    SELECT DISTINCT locale FROM tmp_translation ORDER BY locale
+    ON CONFLICT ON CONSTRAINT "UK_locale" DO NOTHING;
 INSERT INTO i18nkey (string)
-    SELECT DISTINCT key FROM tmp_translation ORDER BY key;
+    SELECT DISTINCT key FROM tmp_translation ORDER BY key
+    ON CONFLICT ON CONSTRAINT "UK_i18nkey" DO NOTHING;
 INSERT INTO i18n (i18nkey, locale, translation)
     SELECT k.id, l.id, t.translation
     FROM tmp_translation AS t
         JOIN i18nkey AS k ON k.string=t.key
         JOIN locale AS l ON l.languagetag=t.locale
-    ORDER BY k.id, l.id;
+    ORDER BY k.id, l.id
+    ON CONFLICT ON CONSTRAINT "UK_i18n" DO
+        UPDATE SET translation=EXCLUDED.translation;
 
 -- region
-\COPY region (id, name) FROM regions.csv WITH DELIMITER ',' HEADER CSV;
+CREATE TEMPORARY TABLE IF NOT EXISTS tmp_region (
+    id SERIAL,
+    name VARCHAR NOT NULL
+);
+\COPY tmp_region (id, name) FROM regions.csv WITH DELIMITER ',' HEADER CSV;
+INSERT INTO region (id, name)
+    SELECT id, name FROM tmp_region
+    ON CONFLICT ON CONSTRAINT "PK_region" DO
+        UPDATE SET name=EXCLUDED.name;
 
 -- department
-\COPY department (id, region, name) FROM departments.csv WITH DELIMITER ',' HEADER CSV;
+CREATE TEMPORARY TABLE IF NOT EXISTS tmp_department (
+    id SERIAL,
+    name VARCHAR NOT NULL,
+    region INTEGER NOT NULL
+);
+\COPY tmp_department (id, region, name) FROM departments.csv WITH DELIMITER ',' HEADER CSV;
+INSERT INTO department (id, region, name)
+    SELECT id, region, name FROM tmp_department
+    ON CONFLICT ON CONSTRAINT "PK_department" DO
+        UPDATE SET region=EXCLUDED.region, name=EXCLUDED.name;
 
 -- cell
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_cell (
@@ -55,7 +66,20 @@ CREATE TEMPORARY TABLE IF NOT EXISTS tmp_cell (
 INSERT INTO cell (id, lat, lon, lat1, lon1, lat2, lon2, lat3, lon3, lat4, lon4, altitude, department)
     SELECT t.id, lat, lon, lat1, lon1, lat2, lon2, lat3, lon3, lat4, lon4, altitude, d.id
     FROM tmp_cell AS t
-    LEFT JOIN department AS d ON d.name=t.department;
+    LEFT JOIN department AS d ON d.name=t.department
+    ON CONFLICT ON CONSTRAINT "PK_cell" DO
+        UPDATE SET lat=EXCLUDED.lat,
+                lon=EXCLUDED.lon,
+                lat1=EXCLUDED.lat1,
+                lon1=EXCLUDED.lon1,
+                lat2=EXCLUDED.lat2,
+                lon2=EXCLUDED.lon2,
+                lat3=EXCLUDED.lat3,
+                lon3=EXCLUDED.lon3,
+                lat4=EXCLUDED.lat4,
+                lon4=EXCLUDED.lon4,
+                altitude=EXCLUDED.altitude,
+                department=EXCLUDED.department;
 
 -- pra
 \ir init_data_pra.postgresql.sql;
@@ -71,7 +95,9 @@ CREATE TEMPORARY TABLE IF NOT EXISTS tmp_period (
 INSERT INTO period (code, name, phase, firstday, lastday)
     SELECT t.code, k.id, t.phase, t.firstday, t.lastday
     FROM tmp_period AS t
-    JOIN i18nkey AS k ON k.string=t.code;
+    JOIN i18nkey AS k ON k.string=t.code
+    ON CONFLICT ON CONSTRAINT "UK_period" DO
+        UPDATE SET phase=EXCLUDED.phase, firstday=EXCLUDED.firstday, lastday=EXCLUDED.lastday;
 
 -- indicator
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_indicator (
@@ -88,7 +114,9 @@ INSERT INTO indicator (code, period, name, description, unit, colorsequencename,
     FROM tmp_indicator AS t
     JOIN period AS p ON p.code=t.period
     JOIN i18nkey AS n ON n.string=t.code
-    JOIN i18nkey AS d ON d.string=t.code || '-description';
+    JOIN i18nkey AS d ON d.string=t.code || '-description'
+    ON CONFLICT ON CONSTRAINT "UK_indicator" DO
+        UPDATE SET unit=EXCLUDED.unit, colorsequencename=EXCLUDED.colorsequencename, nbofclasses=EXCLUDED.nbofclasses, quantiletype=EXCLUDED.quantiletype;
 
 -- dailyvalue
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_dailyvalue (
@@ -105,20 +133,25 @@ INSERT INTO dailyvalue (indicator, cell, date, computedvalue, comparedvalue)
     FROM tmp_dailyvalue AS t
     JOIN indicator AS i ON i.code=t.indicator
     JOIN period AS p ON p.id=i.period
-    WHERE p.code=t.period;
+    WHERE p.code=t.period
+    ON CONFLICT DO NOTHING;
 
 -- normalvalue
+-- normal values are not updated as normalvalues.csv contains fake data.
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_normalvalue (
     indicator VARCHAR,
     period VARCHAR,
     cell INTEGER,
     doy INTEGER,
-    computedvalue DOUBLE PRECISION
+    medianvalue DOUBLE PRECISION,
+    q5 DOUBLE PRECISION,
+    q95 DOUBLE PRECISION
 );
-\COPY tmp_normalvalue (indicator, period, cell, doy, computedvalue) FROM normalvalues.csv WITH DELIMITER ',' HEADER CSV;
-INSERT INTO normalvalue (indicator, cell, doy, computedvalue)
-    SELECT i.id, t.cell, t.doy, t.computedvalue
+\COPY tmp_normalvalue (indicator, period, cell, doy, medianvalue, q5, q95) FROM normalvalues.csv WITH DELIMITER ',' HEADER CSV;
+INSERT INTO normalvalue (indicator, cell, doy, medianvalue, q5, q95)
+    SELECT i.id, t.cell, t.doy, t.medianvalue, t.q5, t.q95
     FROM tmp_normalvalue AS t
     JOIN indicator AS i ON i.code=t.indicator
     JOIN period AS p ON p.id=i.period
-    WHERE p.code=t.period;
+    WHERE p.code=t.period
+    ON CONFLICT ON CONSTRAINT "UK_normalvalue" DO NOTHING;
diff --git a/sql/migration.sql b/sql/migration.sql
index e4094c6..42dc9de 100644
--- a/sql/migration.sql
+++ b/sql/migration.sql
@@ -186,6 +186,33 @@ END
 $BODY$
 language plpgsql;
 
+--
+-- #24
+--
+CREATE OR REPLACE FUNCTION upgrade20240416() RETURNS boolean AS $BODY$
+BEGIN
+    ALTER TABLE normalvalue RENAME COLUMN computedvalue TO medianvalue;
+    ALTER TABLE normalvalue ADD COLUMN q5 DOUBLE PRECISION;
+    ALTER TABLE normalvalue ADD COLUMN q95 DOUBLE PRECISION;
+    RETURN true;
+END
+$BODY$
+language plpgsql;
+
+CREATE OR REPLACE FUNCTION upgrade20240418() RETURNS boolean AS $BODY$
+BEGIN
+    ALTER TABLE period DROP CONSTRAINT "FK_period_i18n";
+    ALTER TABLE period ADD CONSTRAINT "FK_period_i18n" FOREIGN KEY (name) REFERENCES i18nkey (id);
+    ALTER TABLE indicator DROP CONSTRAINT "FK_indicator_i18n_name";
+    ALTER TABLE indicator ADD CONSTRAINT "FK_indicator_i18n_name" FOREIGN KEY (name) REFERENCES i18nkey (id);
+    ALTER TABLE indicator DROP CONSTRAINT "FK_indicator_i18n_description";
+    ALTER TABLE indicator ADD CONSTRAINT "FK_indicator_i18n_description" FOREIGN KEY (description) REFERENCES i18nkey (id);
+    CREATE INDEX "IX_dailyvalue_doy" ON dailyvalue (EXTRACT(DOY FROM date), indicator);
+    RETURN true;
+END
+$BODY$
+language plpgsql;
+
 ---
 --
 -- Keep this call at the end to apply migration functions.
diff --git a/sql/normalvalues.csv b/sql/normalvalues.csv
index 475d124..d65e21f 100644
--- a/sql/normalvalues.csv
+++ b/sql/normalvalues.csv
@@ -1,366 +1,366 @@
-indicator,period,cell,doy,computedvalue
-rainsum,year,4,1,2
-rainsum,year,4,2,4
-rainsum,year,4,3,6
-rainsum,year,4,4,8
-rainsum,year,4,5,10
-rainsum,year,4,6,12
-rainsum,year,4,7,14
-rainsum,year,4,8,16
-rainsum,year,4,9,18
-rainsum,year,4,10,20
-rainsum,year,4,11,22
-rainsum,year,4,12,24
-rainsum,year,4,13,26
-rainsum,year,4,14,28
-rainsum,year,4,15,30
-rainsum,year,4,16,32
-rainsum,year,4,17,34
-rainsum,year,4,18,36
-rainsum,year,4,19,38
-rainsum,year,4,20,40
-rainsum,year,4,21,42
-rainsum,year,4,22,44
-rainsum,year,4,23,46
-rainsum,year,4,24,48
-rainsum,year,4,25,50
-rainsum,year,4,26,52
-rainsum,year,4,27,54
-rainsum,year,4,28,56
-rainsum,year,4,29,58
-rainsum,year,4,30,60
-rainsum,year,4,31,62
-rainsum,year,4,32,64
-rainsum,year,4,33,66
-rainsum,year,4,34,68
-rainsum,year,4,35,70
-rainsum,year,4,36,72
-rainsum,year,4,37,74
-rainsum,year,4,38,76
-rainsum,year,4,39,78
-rainsum,year,4,40,80
-rainsum,year,4,41,82
-rainsum,year,4,42,84
-rainsum,year,4,43,86
-rainsum,year,4,44,88
-rainsum,year,4,45,90
-rainsum,year,4,46,92
-rainsum,year,4,47,94
-rainsum,year,4,48,96
-rainsum,year,4,49,98
-rainsum,year,4,50,100
-rainsum,year,4,51,102
-rainsum,year,4,52,104
-rainsum,year,4,53,106
-rainsum,year,4,54,108
-rainsum,year,4,55,110
-rainsum,year,4,56,112
-rainsum,year,4,57,114
-rainsum,year,4,58,116
-rainsum,year,4,59,118
-rainsum,year,4,60,120
-rainsum,year,4,61,122
-rainsum,year,4,62,124
-rainsum,year,4,63,126
-rainsum,year,4,64,128
-rainsum,year,4,65,130
-rainsum,year,4,66,132
-rainsum,year,4,67,134
-rainsum,year,4,68,136
-rainsum,year,4,69,138
-rainsum,year,4,70,140
-rainsum,year,4,71,142
-rainsum,year,4,72,144
-rainsum,year,4,73,146
-rainsum,year,4,74,148
-rainsum,year,4,75,150
-rainsum,year,4,76,152
-rainsum,year,4,77,154
-rainsum,year,4,78,156
-rainsum,year,4,79,158
-rainsum,year,4,80,160
-rainsum,year,4,81,162
-rainsum,year,4,82,164
-rainsum,year,4,83,166
-rainsum,year,4,84,168
-rainsum,year,4,85,170
-rainsum,year,4,86,172
-rainsum,year,4,87,174
-rainsum,year,4,88,176
-rainsum,year,4,89,178
-rainsum,year,4,90,180
-rainsum,year,4,91,182
-rainsum,year,4,92,184
-rainsum,year,4,93,186
-rainsum,year,4,94,188
-rainsum,year,4,95,190
-rainsum,year,4,96,192
-rainsum,year,4,97,194
-rainsum,year,4,98,196
-rainsum,year,4,99,198
-rainsum,year,4,100,200
-rainsum,year,4,101,202
-rainsum,year,4,102,204
-rainsum,year,4,103,206
-rainsum,year,4,104,208
-rainsum,year,4,105,210
-rainsum,year,4,106,212
-rainsum,year,4,107,214
-rainsum,year,4,108,216
-rainsum,year,4,109,218
-rainsum,year,4,110,220
-rainsum,year,4,111,222
-rainsum,year,4,112,224
-rainsum,year,4,113,226
-rainsum,year,4,114,228
-rainsum,year,4,115,230
-rainsum,year,4,116,232
-rainsum,year,4,117,234
-rainsum,year,4,118,236
-rainsum,year,4,119,238
-rainsum,year,4,120,240
-rainsum,year,4,121,242
-rainsum,year,4,122,244
-rainsum,year,4,123,246
-rainsum,year,4,124,248
-rainsum,year,4,125,250
-rainsum,year,4,126,252
-rainsum,year,4,127,254
-rainsum,year,4,128,256
-rainsum,year,4,129,258
-rainsum,year,4,130,260
-rainsum,year,4,131,262
-rainsum,year,4,132,264
-rainsum,year,4,133,266
-rainsum,year,4,134,268
-rainsum,year,4,135,270
-rainsum,year,4,136,272
-rainsum,year,4,137,274
-rainsum,year,4,138,276
-rainsum,year,4,139,278
-rainsum,year,4,140,280
-rainsum,year,4,141,282
-rainsum,year,4,142,284
-rainsum,year,4,143,286
-rainsum,year,4,144,288
-rainsum,year,4,145,290
-rainsum,year,4,146,292
-rainsum,year,4,147,294
-rainsum,year,4,148,296
-rainsum,year,4,149,298
-rainsum,year,4,150,300
-rainsum,year,4,151,302
-rainsum,year,4,152,304
-rainsum,year,4,153,306
-rainsum,year,4,154,308
-rainsum,year,4,155,310
-rainsum,year,4,156,312
-rainsum,year,4,157,314
-rainsum,year,4,158,316
-rainsum,year,4,159,318
-rainsum,year,4,160,320
-rainsum,year,4,161,322
-rainsum,year,4,162,324
-rainsum,year,4,163,326
-rainsum,year,4,164,328
-rainsum,year,4,165,330
-rainsum,year,4,166,332
-rainsum,year,4,167,334
-rainsum,year,4,168,336
-rainsum,year,4,169,338
-rainsum,year,4,170,340
-rainsum,year,4,171,342
-rainsum,year,4,172,344
-rainsum,year,4,173,346
-rainsum,year,4,174,348
-rainsum,year,4,175,350
-rainsum,year,4,176,352
-rainsum,year,4,177,354
-rainsum,year,4,178,356
-rainsum,year,4,179,358
-rainsum,year,4,180,360
-rainsum,year,4,181,362
-rainsum,year,4,182,364
-rainsum,year,4,183,366
-rainsum,year,4,184,368
-rainsum,year,4,185,370
-rainsum,year,4,186,372
-rainsum,year,4,187,374
-rainsum,year,4,188,376
-rainsum,year,4,189,378
-rainsum,year,4,190,380
-rainsum,year,4,191,382
-rainsum,year,4,192,384
-rainsum,year,4,193,386
-rainsum,year,4,194,388
-rainsum,year,4,195,390
-rainsum,year,4,196,392
-rainsum,year,4,197,394
-rainsum,year,4,198,396
-rainsum,year,4,199,398
-rainsum,year,4,200,400
-rainsum,year,4,201,402
-rainsum,year,4,202,404
-rainsum,year,4,203,406
-rainsum,year,4,204,408
-rainsum,year,4,205,410
-rainsum,year,4,206,412
-rainsum,year,4,207,414
-rainsum,year,4,208,416
-rainsum,year,4,209,418
-rainsum,year,4,210,420
-rainsum,year,4,211,422
-rainsum,year,4,212,424
-rainsum,year,4,213,426
-rainsum,year,4,214,428
-rainsum,year,4,215,430
-rainsum,year,4,216,432
-rainsum,year,4,217,434
-rainsum,year,4,218,436
-rainsum,year,4,219,438
-rainsum,year,4,220,440
-rainsum,year,4,221,442
-rainsum,year,4,222,444
-rainsum,year,4,223,446
-rainsum,year,4,224,448
-rainsum,year,4,225,450
-rainsum,year,4,226,452
-rainsum,year,4,227,454
-rainsum,year,4,228,456
-rainsum,year,4,229,458
-rainsum,year,4,230,460
-rainsum,year,4,231,462
-rainsum,year,4,232,464
-rainsum,year,4,233,466
-rainsum,year,4,234,468
-rainsum,year,4,235,470
-rainsum,year,4,236,472
-rainsum,year,4,237,474
-rainsum,year,4,238,476
-rainsum,year,4,239,478
-rainsum,year,4,240,480
-rainsum,year,4,241,482
-rainsum,year,4,242,484
-rainsum,year,4,243,486
-rainsum,year,4,244,488
-rainsum,year,4,245,490
-rainsum,year,4,246,492
-rainsum,year,4,247,494
-rainsum,year,4,248,496
-rainsum,year,4,249,498
-rainsum,year,4,250,500
-rainsum,year,4,251,502
-rainsum,year,4,252,504
-rainsum,year,4,253,506
-rainsum,year,4,254,508
-rainsum,year,4,255,510
-rainsum,year,4,256,512
-rainsum,year,4,257,514
-rainsum,year,4,258,516
-rainsum,year,4,259,518
-rainsum,year,4,260,520
-rainsum,year,4,261,522
-rainsum,year,4,262,524
-rainsum,year,4,263,526
-rainsum,year,4,264,528
-rainsum,year,4,265,530
-rainsum,year,4,266,532
-rainsum,year,4,267,534
-rainsum,year,4,268,536
-rainsum,year,4,269,538
-rainsum,year,4,270,540
-rainsum,year,4,271,542
-rainsum,year,4,272,544
-rainsum,year,4,273,546
-rainsum,year,4,274,548
-rainsum,year,4,275,550
-rainsum,year,4,276,552
-rainsum,year,4,277,554
-rainsum,year,4,278,556
-rainsum,year,4,279,558
-rainsum,year,4,280,560
-rainsum,year,4,281,562
-rainsum,year,4,282,564
-rainsum,year,4,283,566
-rainsum,year,4,284,568
-rainsum,year,4,285,570
-rainsum,year,4,286,572
-rainsum,year,4,287,574
-rainsum,year,4,288,576
-rainsum,year,4,289,578
-rainsum,year,4,290,580
-rainsum,year,4,291,582
-rainsum,year,4,292,584
-rainsum,year,4,293,586
-rainsum,year,4,294,588
-rainsum,year,4,295,590
-rainsum,year,4,296,592
-rainsum,year,4,297,594
-rainsum,year,4,298,596
-rainsum,year,4,299,598
-rainsum,year,4,300,600
-rainsum,year,4,301,602
-rainsum,year,4,302,604
-rainsum,year,4,303,606
-rainsum,year,4,304,608
-rainsum,year,4,305,610
-rainsum,year,4,306,612
-rainsum,year,4,307,614
-rainsum,year,4,308,616
-rainsum,year,4,309,618
-rainsum,year,4,310,620
-rainsum,year,4,311,622
-rainsum,year,4,312,624
-rainsum,year,4,313,626
-rainsum,year,4,314,628
-rainsum,year,4,315,630
-rainsum,year,4,316,632
-rainsum,year,4,317,634
-rainsum,year,4,318,636
-rainsum,year,4,319,638
-rainsum,year,4,320,640
-rainsum,year,4,321,642
-rainsum,year,4,322,644
-rainsum,year,4,323,646
-rainsum,year,4,324,648
-rainsum,year,4,325,650
-rainsum,year,4,326,652
-rainsum,year,4,327,654
-rainsum,year,4,328,656
-rainsum,year,4,329,658
-rainsum,year,4,330,660
-rainsum,year,4,331,662
-rainsum,year,4,332,664
-rainsum,year,4,333,666
-rainsum,year,4,334,668
-rainsum,year,4,335,670
-rainsum,year,4,336,672
-rainsum,year,4,337,674
-rainsum,year,4,338,676
-rainsum,year,4,339,678
-rainsum,year,4,340,680
-rainsum,year,4,341,682
-rainsum,year,4,342,684
-rainsum,year,4,343,686
-rainsum,year,4,344,688
-rainsum,year,4,345,690
-rainsum,year,4,346,692
-rainsum,year,4,347,694
-rainsum,year,4,348,696
-rainsum,year,4,349,698
-rainsum,year,4,350,700
-rainsum,year,4,351,702
-rainsum,year,4,352,704
-rainsum,year,4,353,706
-rainsum,year,4,354,708
-rainsum,year,4,355,710
-rainsum,year,4,356,712
-rainsum,year,4,357,714
-rainsum,year,4,358,716
-rainsum,year,4,359,718
-rainsum,year,4,360,720
-rainsum,year,4,361,722
-rainsum,year,4,362,724
-rainsum,year,4,363,726
-rainsum,year,4,364,728
-rainsum,year,4,365,730
\ No newline at end of file
+indicator,period,cell,doy,medianvalue,q5,q95
+rainsum,year,4,1,2,1,3
+rainsum,year,4,2,4,3,5
+rainsum,year,4,3,6,5,7
+rainsum,year,4,4,8,7,9
+rainsum,year,4,5,10,9,11
+rainsum,year,4,6,12,11,13
+rainsum,year,4,7,14,13,15
+rainsum,year,4,8,16,15,17
+rainsum,year,4,9,18,17,19
+rainsum,year,4,10,20,19,21
+rainsum,year,4,11,22,21,23
+rainsum,year,4,12,24,23,25
+rainsum,year,4,13,26,25,27
+rainsum,year,4,14,28,27,29
+rainsum,year,4,15,30,29,31
+rainsum,year,4,16,32,31,33
+rainsum,year,4,17,34,33,35
+rainsum,year,4,18,36,35,37
+rainsum,year,4,19,38,37,39
+rainsum,year,4,20,40,39,41
+rainsum,year,4,21,42,41,43
+rainsum,year,4,22,44,43,45
+rainsum,year,4,23,46,45,47
+rainsum,year,4,24,48,47,49
+rainsum,year,4,25,50,49,51
+rainsum,year,4,26,52,51,53
+rainsum,year,4,27,54,53,55
+rainsum,year,4,28,56,55,57
+rainsum,year,4,29,58,57,59
+rainsum,year,4,30,60,59,61
+rainsum,year,4,31,62,61,63
+rainsum,year,4,32,64,63,65
+rainsum,year,4,33,66,65,67
+rainsum,year,4,34,68,67,69
+rainsum,year,4,35,70,69,71
+rainsum,year,4,36,72,71,73
+rainsum,year,4,37,74,73,75
+rainsum,year,4,38,76,75,77
+rainsum,year,4,39,78,77,79
+rainsum,year,4,40,80,79,81
+rainsum,year,4,41,82,81,83
+rainsum,year,4,42,84,83,85
+rainsum,year,4,43,86,85,87
+rainsum,year,4,44,88,87,89
+rainsum,year,4,45,90,89,91
+rainsum,year,4,46,92,91,93
+rainsum,year,4,47,94,93,95
+rainsum,year,4,48,96,95,97
+rainsum,year,4,49,98,97,99
+rainsum,year,4,50,100,99,101
+rainsum,year,4,51,102,101,103
+rainsum,year,4,52,104,103,105
+rainsum,year,4,53,106,105,107
+rainsum,year,4,54,108,107,109
+rainsum,year,4,55,110,109,111
+rainsum,year,4,56,112,111,113
+rainsum,year,4,57,114,113,115
+rainsum,year,4,58,116,115,117
+rainsum,year,4,59,118,117,119
+rainsum,year,4,60,120,119,121
+rainsum,year,4,61,122,121,123
+rainsum,year,4,62,124,123,125
+rainsum,year,4,63,126,125,127
+rainsum,year,4,64,128,127,129
+rainsum,year,4,65,130,129,131
+rainsum,year,4,66,132,131,133
+rainsum,year,4,67,134,133,135
+rainsum,year,4,68,136,135,137
+rainsum,year,4,69,138,137,139
+rainsum,year,4,70,140,139,141
+rainsum,year,4,71,142,141,143
+rainsum,year,4,72,144,143,145
+rainsum,year,4,73,146,145,147
+rainsum,year,4,74,148,147,149
+rainsum,year,4,75,150,149,151
+rainsum,year,4,76,152,151,153
+rainsum,year,4,77,154,153,155
+rainsum,year,4,78,156,155,157
+rainsum,year,4,79,158,157,159
+rainsum,year,4,80,160,159,161
+rainsum,year,4,81,162,161,163
+rainsum,year,4,82,164,163,165
+rainsum,year,4,83,166,165,167
+rainsum,year,4,84,168,167,169
+rainsum,year,4,85,170,169,171
+rainsum,year,4,86,172,171,173
+rainsum,year,4,87,174,173,175
+rainsum,year,4,88,176,175,177
+rainsum,year,4,89,178,177,179
+rainsum,year,4,90,180,179,181
+rainsum,year,4,91,182,181,183
+rainsum,year,4,92,184,183,185
+rainsum,year,4,93,186,185,187
+rainsum,year,4,94,188,187,189
+rainsum,year,4,95,190,189,191
+rainsum,year,4,96,192,191,193
+rainsum,year,4,97,194,193,195
+rainsum,year,4,98,196,195,197
+rainsum,year,4,99,198,197,199
+rainsum,year,4,100,200,199,201
+rainsum,year,4,101,202,201,203
+rainsum,year,4,102,204,203,205
+rainsum,year,4,103,206,205,207
+rainsum,year,4,104,208,207,209
+rainsum,year,4,105,210,209,211
+rainsum,year,4,106,212,211,213
+rainsum,year,4,107,214,213,215
+rainsum,year,4,108,216,215,217
+rainsum,year,4,109,218,217,219
+rainsum,year,4,110,220,219,221
+rainsum,year,4,111,222,221,223
+rainsum,year,4,112,224,223,225
+rainsum,year,4,113,226,225,227
+rainsum,year,4,114,228,227,229
+rainsum,year,4,115,230,229,231
+rainsum,year,4,116,232,231,233
+rainsum,year,4,117,234,233,235
+rainsum,year,4,118,236,235,237
+rainsum,year,4,119,238,237,239
+rainsum,year,4,120,240,239,241
+rainsum,year,4,121,242,241,243
+rainsum,year,4,122,244,243,245
+rainsum,year,4,123,246,245,247
+rainsum,year,4,124,248,247,249
+rainsum,year,4,125,250,249,251
+rainsum,year,4,126,252,251,253
+rainsum,year,4,127,254,253,255
+rainsum,year,4,128,256,255,257
+rainsum,year,4,129,258,257,259
+rainsum,year,4,130,260,259,261
+rainsum,year,4,131,262,261,263
+rainsum,year,4,132,264,263,265
+rainsum,year,4,133,266,265,267
+rainsum,year,4,134,268,267,269
+rainsum,year,4,135,270,269,271
+rainsum,year,4,136,272,271,273
+rainsum,year,4,137,274,273,275
+rainsum,year,4,138,276,275,277
+rainsum,year,4,139,278,277,279
+rainsum,year,4,140,280,279,281
+rainsum,year,4,141,282,281,283
+rainsum,year,4,142,284,283,285
+rainsum,year,4,143,286,285,287
+rainsum,year,4,144,288,287,289
+rainsum,year,4,145,290,289,291
+rainsum,year,4,146,292,291,293
+rainsum,year,4,147,294,293,295
+rainsum,year,4,148,296,295,297
+rainsum,year,4,149,298,297,299
+rainsum,year,4,150,300,299,301
+rainsum,year,4,151,302,301,303
+rainsum,year,4,152,304,303,305
+rainsum,year,4,153,306,305,307
+rainsum,year,4,154,308,307,309
+rainsum,year,4,155,310,309,311
+rainsum,year,4,156,312,311,313
+rainsum,year,4,157,314,313,315
+rainsum,year,4,158,316,315,317
+rainsum,year,4,159,318,317,319
+rainsum,year,4,160,320,319,321
+rainsum,year,4,161,322,321,323
+rainsum,year,4,162,324,323,325
+rainsum,year,4,163,326,325,327
+rainsum,year,4,164,328,327,329
+rainsum,year,4,165,330,329,331
+rainsum,year,4,166,332,331,333
+rainsum,year,4,167,334,333,335
+rainsum,year,4,168,336,335,337
+rainsum,year,4,169,338,337,339
+rainsum,year,4,170,340,339,341
+rainsum,year,4,171,342,341,343
+rainsum,year,4,172,344,343,345
+rainsum,year,4,173,346,345,347
+rainsum,year,4,174,348,347,349
+rainsum,year,4,175,350,349,351
+rainsum,year,4,176,352,351,353
+rainsum,year,4,177,354,353,355
+rainsum,year,4,178,356,355,357
+rainsum,year,4,179,358,357,359
+rainsum,year,4,180,360,359,361
+rainsum,year,4,181,362,361,363
+rainsum,year,4,182,364,363,365
+rainsum,year,4,183,366,365,367
+rainsum,year,4,184,368,367,369
+rainsum,year,4,185,370,369,371
+rainsum,year,4,186,372,371,373
+rainsum,year,4,187,374,373,375
+rainsum,year,4,188,376,375,377
+rainsum,year,4,189,378,377,379
+rainsum,year,4,190,380,379,381
+rainsum,year,4,191,382,381,383
+rainsum,year,4,192,384,383,385
+rainsum,year,4,193,386,385,387
+rainsum,year,4,194,388,387,389
+rainsum,year,4,195,390,389,391
+rainsum,year,4,196,392,391,393
+rainsum,year,4,197,394,393,395
+rainsum,year,4,198,396,395,397
+rainsum,year,4,199,398,397,399
+rainsum,year,4,200,400,399,401
+rainsum,year,4,201,402,401,403
+rainsum,year,4,202,404,403,405
+rainsum,year,4,203,406,405,407
+rainsum,year,4,204,408,407,409
+rainsum,year,4,205,410,409,411
+rainsum,year,4,206,412,411,413
+rainsum,year,4,207,414,413,415
+rainsum,year,4,208,416,415,417
+rainsum,year,4,209,418,417,419
+rainsum,year,4,210,420,419,421
+rainsum,year,4,211,422,421,423
+rainsum,year,4,212,424,423,425
+rainsum,year,4,213,426,425,427
+rainsum,year,4,214,428,427,429
+rainsum,year,4,215,430,429,431
+rainsum,year,4,216,432,431,433
+rainsum,year,4,217,434,433,435
+rainsum,year,4,218,436,435,437
+rainsum,year,4,219,438,437,439
+rainsum,year,4,220,440,439,441
+rainsum,year,4,221,442,441,443
+rainsum,year,4,222,444,443,445
+rainsum,year,4,223,446,445,447
+rainsum,year,4,224,448,447,449
+rainsum,year,4,225,450,449,451
+rainsum,year,4,226,452,451,453
+rainsum,year,4,227,454,453,455
+rainsum,year,4,228,456,455,457
+rainsum,year,4,229,458,457,459
+rainsum,year,4,230,460,459,461
+rainsum,year,4,231,462,461,463
+rainsum,year,4,232,464,463,465
+rainsum,year,4,233,466,465,467
+rainsum,year,4,234,468,467,469
+rainsum,year,4,235,470,469,471
+rainsum,year,4,236,472,471,473
+rainsum,year,4,237,474,473,475
+rainsum,year,4,238,476,475,477
+rainsum,year,4,239,478,477,479
+rainsum,year,4,240,480,479,481
+rainsum,year,4,241,482,481,483
+rainsum,year,4,242,484,483,485
+rainsum,year,4,243,486,485,487
+rainsum,year,4,244,488,487,489
+rainsum,year,4,245,490,489,491
+rainsum,year,4,246,492,491,493
+rainsum,year,4,247,494,493,495
+rainsum,year,4,248,496,495,497
+rainsum,year,4,249,498,497,499
+rainsum,year,4,250,500,499,501
+rainsum,year,4,251,502,501,503
+rainsum,year,4,252,504,503,505
+rainsum,year,4,253,506,505,507
+rainsum,year,4,254,508,507,509
+rainsum,year,4,255,510,509,511
+rainsum,year,4,256,512,511,513
+rainsum,year,4,257,514,513,515
+rainsum,year,4,258,516,515,517
+rainsum,year,4,259,518,517,519
+rainsum,year,4,260,520,519,521
+rainsum,year,4,261,522,521,523
+rainsum,year,4,262,524,523,525
+rainsum,year,4,263,526,525,527
+rainsum,year,4,264,528,527,529
+rainsum,year,4,265,530,529,531
+rainsum,year,4,266,532,531,533
+rainsum,year,4,267,534,533,535
+rainsum,year,4,268,536,535,537
+rainsum,year,4,269,538,537,539
+rainsum,year,4,270,540,539,541
+rainsum,year,4,271,542,541,543
+rainsum,year,4,272,544,543,545
+rainsum,year,4,273,546,545,547
+rainsum,year,4,274,548,547,549
+rainsum,year,4,275,550,549,551
+rainsum,year,4,276,552,551,553
+rainsum,year,4,277,554,553,555
+rainsum,year,4,278,556,555,557
+rainsum,year,4,279,558,557,559
+rainsum,year,4,280,560,559,561
+rainsum,year,4,281,562,561,563
+rainsum,year,4,282,564,563,565
+rainsum,year,4,283,566,565,567
+rainsum,year,4,284,568,567,569
+rainsum,year,4,285,570,569,571
+rainsum,year,4,286,572,571,573
+rainsum,year,4,287,574,573,575
+rainsum,year,4,288,576,575,577
+rainsum,year,4,289,578,577,579
+rainsum,year,4,290,580,579,581
+rainsum,year,4,291,582,581,583
+rainsum,year,4,292,584,583,585
+rainsum,year,4,293,586,585,587
+rainsum,year,4,294,588,587,589
+rainsum,year,4,295,590,589,591
+rainsum,year,4,296,592,591,593
+rainsum,year,4,297,594,593,595
+rainsum,year,4,298,596,595,597
+rainsum,year,4,299,598,597,599
+rainsum,year,4,300,600,599,601
+rainsum,year,4,301,602,601,603
+rainsum,year,4,302,604,603,605
+rainsum,year,4,303,606,605,607
+rainsum,year,4,304,608,607,609
+rainsum,year,4,305,610,609,611
+rainsum,year,4,306,612,611,613
+rainsum,year,4,307,614,613,615
+rainsum,year,4,308,616,615,617
+rainsum,year,4,309,618,617,619
+rainsum,year,4,310,620,619,621
+rainsum,year,4,311,622,621,623
+rainsum,year,4,312,624,623,625
+rainsum,year,4,313,626,625,627
+rainsum,year,4,314,628,627,629
+rainsum,year,4,315,630,629,631
+rainsum,year,4,316,632,631,633
+rainsum,year,4,317,634,633,635
+rainsum,year,4,318,636,635,637
+rainsum,year,4,319,638,637,639
+rainsum,year,4,320,640,639,641
+rainsum,year,4,321,642,641,643
+rainsum,year,4,322,644,643,645
+rainsum,year,4,323,646,645,647
+rainsum,year,4,324,648,647,649
+rainsum,year,4,325,650,649,651
+rainsum,year,4,326,652,651,653
+rainsum,year,4,327,654,653,655
+rainsum,year,4,328,656,655,657
+rainsum,year,4,329,658,657,659
+rainsum,year,4,330,660,659,661
+rainsum,year,4,331,662,661,663
+rainsum,year,4,332,664,663,665
+rainsum,year,4,333,666,665,667
+rainsum,year,4,334,668,667,669
+rainsum,year,4,335,670,669,671
+rainsum,year,4,336,672,671,673
+rainsum,year,4,337,674,673,675
+rainsum,year,4,338,676,675,677
+rainsum,year,4,339,678,677,679
+rainsum,year,4,340,680,679,681
+rainsum,year,4,341,682,681,683
+rainsum,year,4,342,684,683,685
+rainsum,year,4,343,686,685,687
+rainsum,year,4,344,688,687,689
+rainsum,year,4,345,690,689,691
+rainsum,year,4,346,692,691,693
+rainsum,year,4,347,694,693,695
+rainsum,year,4,348,696,695,697
+rainsum,year,4,349,698,697,699
+rainsum,year,4,350,700,699,701
+rainsum,year,4,351,702,701,703
+rainsum,year,4,352,704,703,705
+rainsum,year,4,353,706,705,707
+rainsum,year,4,354,708,707,709
+rainsum,year,4,355,710,709,711
+rainsum,year,4,356,712,711,713
+rainsum,year,4,357,714,713,715
+rainsum,year,4,358,716,715,717
+rainsum,year,4,359,718,717,719
+rainsum,year,4,360,720,719,721
+rainsum,year,4,361,722,721,723
+rainsum,year,4,362,724,723,725
+rainsum,year,4,363,726,725,727
+rainsum,year,4,364,728,727,729
+rainsum,year,4,365,730,729,731
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index 169877d..93688e8 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -136,7 +136,7 @@ CREATE TABLE IF NOT EXISTS period (
     lastday VARCHAR(10) NOT NULL,
     created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
     CONSTRAINT "PK_period" PRIMARY KEY (id),
-    CONSTRAINT "FK_period_i18n" FOREIGN KEY (name) REFERENCES i18n (id),
+    CONSTRAINT "FK_period_i18n" FOREIGN KEY (name) REFERENCES i18nkey (id),
     CONSTRAINT "UK_period" UNIQUE (code)
 );
 COMMENT ON TABLE period IS 'Period when the indicator is computed.';
@@ -157,8 +157,8 @@ CREATE TABLE IF NOT EXISTS indicator (
     created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
     CONSTRAINT "PK_indicator" PRIMARY KEY (id),
     CONSTRAINT "FK_indicator_period" FOREIGN KEY(period) REFERENCES period(id),
-    CONSTRAINT "FK_indicator_i18n_name" FOREIGN KEY (name) REFERENCES i18n (id),
-    CONSTRAINT "FK_indicator_i18n_description" FOREIGN KEY (description) REFERENCES i18n (id),
+    CONSTRAINT "FK_indicator_i18n_name" FOREIGN KEY (name) REFERENCES i18nkey (id),
+    CONSTRAINT "FK_indicator_i18n_description" FOREIGN KEY (description) REFERENCES i18nkey (id),
     CONSTRAINT "CK_indicator_colorsequence" CHECK (nbofclasses IS NOT NULL OR quantiletype IS NOT NULL),
     CONSTRAINT "UK_indicator" UNIQUE (code, period)
 );
@@ -172,7 +172,9 @@ CREATE TABLE IF NOT EXISTS normalvalue (
     indicator INTEGER NOT NULL,
     cell INTEGER NOT NULL,
     doy INTEGER NOT NULL,
-    computedvalue DOUBLE PRECISION NOT NULL,
+    medianvalue DOUBLE PRECISION NOT NULL,
+    q5 DOUBLE PRECISION NOT NULL,
+    q95 DOUBLE PRECISION NOT NULL,
     created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
     CONSTRAINT "PK_normalvalue" PRIMARY KEY (id),
     CONSTRAINT "FK_normalvalue_cell" FOREIGN KEY(cell) REFERENCES cell(id),
@@ -180,7 +182,7 @@ CREATE TABLE IF NOT EXISTS normalvalue (
     CONSTRAINT "UK_normalvalue" UNIQUE (indicator, cell, doy)
 );
 COMMENT ON TABLE normalvalue IS 'Value of the indicator during the normal.';
-COMMENT ON COLUMN normalvalue.computedvalue IS 'Median value of the indicator during the normal.';
+COMMENT ON COLUMN normalvalue.medianvalue IS 'Median value of the indicator during the normal.';
 CREATE INDEX IF NOT EXISTS "IX_normalvalue" ON normalvalue USING btree (indicator, cell);
 
 CREATE TABLE IF NOT EXISTS dailyvalue (
@@ -200,6 +202,7 @@ COMMENT ON TABLE dailyvalue IS 'Value of the indicator for the day on a cell.';
 COMMENT ON COLUMN dailyvalue.computedvalue IS 'Value of the indicator.';
 COMMENT ON COLUMN dailyvalue.comparedvalue IS 'Value of the indicator compared to the normal.';
 CREATE INDEX IF NOT EXISTS "IX_dailyvalue" ON dailyvalue USING btree (date, cell);
+CREATE INDEX IF NOT EXISTS "IX_dailyvalue_doy" ON dailyvalue (EXTRACT(DOY FROM date), indicator);
 
 CREATE OR REPLACE VIEW v_i18n
 AS SELECT l.languagetag,
@@ -212,12 +215,16 @@ DROP VIEW IF EXISTS v_pra_dailyvalue;
 CREATE VIEW v_pra_dailyvalue AS
 SELECT
 	MIN(d.id) AS id,
-	indicator,
+	d.indicator,
 	cp.pra,
 	d.date,
 	SUM(d.computedvalue * cp.ratio) AS computedvalue,
-	SUM(d.computedvalue * cp.ratio) AS comparedvalue
+	SUM(d.comparedvalue * cp.ratio) AS comparedvalue,
+	SUM(n.q5 * cp.ratio) AS q5,
+	SUM(n.medianvalue * cp.ratio) AS medianvalue,
+	SUM(n.q95 * cp.ratio) AS q95
 FROM dailyvalue AS d
 	JOIN cellpra AS cp ON cp.cell=d.cell
-GROUP BY pra, indicator, date;
+	LEFT JOIN normalvalue AS n ON n.cell=d.cell AND n.doy=EXTRACT(DOY FROM d.date) AND n.indicator=d.indicator
+GROUP BY pra, d.indicator, date;
 COMMENT ON VIEW v_pra_dailyvalue IS 'Daily values weighted by PRA.'
diff --git a/sql/translations.csv b/sql/translations.csv
index 7fc9ed2..df51c15 100644
--- a/sql/translations.csv
+++ b/sql/translations.csv
@@ -1,18 +1,37 @@
 key,locale,translation
-year,fr,Année
-spring,fr,Printemps
-summer,fr,Été
+frostdaystmin,en,Frost days
 frostdaystmin,fr,Jours de gel
-frostdaystmin-description,fr,Nombre de jours de gel (Tmin < 0 °C).
-hdaystmax,fr,Jours chauds Tmax
-hdaystmax-description,fr,Nombre de jours chauds (tmax > 35 °C).
-hdaystmax1,fr,Jours chauds Tmax
-hdaystmax1-description,fr,Nombre de jours chauds (tmax > 25 °C).
+frostdaystmin-description,en,Number of frost days (Tmin < 0 °C)
+frostdaystmin-description,fr,Nombre de jours de gel (Tmin < 0 °C)
+hdaystmax1,en,Hot days
+hdaystmax1,fr,Jours chauds
+hdaystmax1-description,en,Number of hot days (tmax > 25 °C)
+hdaystmax1-description,fr,Nombre de jours chauds (tmax > 25 °C)
+hdaystmax,en,Hot days
+hdaystmax,fr,Jours chauds
+hdaystmax-description,en,Number of hot days (tmax > 35 °C)
+hdaystmax-description,fr,Nombre de jours chauds (tmax > 35 °C)
+maxt,en,Average of maximal temperatures
 maxt,fr,Moyenne des températures maximales
-maxt-description,fr,Moyenne des températures maximales.
+maxt-description,en,Average of maximal temperatures
+maxt-description,fr,Moyenne des températures maximales
+meant,en,Average of mean temperatures
 meant,fr,Moyenne des températures moyennes
-meant-description,fr,Moyenne des températures moyennes.
+meant-description,en,Average of mean temperatures
+meant-description,fr,Moyenne des températures moyennes
+mint,en,Average of minimal temperatures
 mint,fr,Moyenne des températures minimales
-mint-description,fr,Moyenne des températures minimales.
+mint-description,en,Average of minimal temperatures
+mint-description,fr,Moyenne des températures minimales
+rainsum,en,Rain sum
 rainsum,fr,Cumul de pluie
-rainsum-description,fr,Cumul des précipitations.
\ No newline at end of file
+rainsum-description,en,Total precipitation
+rainsum-description,fr,Cumul des précipitations
+spring,en,Spring
+spring,fr,Printemps
+summer,en,Summer
+summer,fr,Été
+winter,en,Winter
+winter,fr,Hiver
+year,en,Year
+year,fr,Année
diff --git a/src/site/markdown/development.md b/src/site/markdown/development.md
index aecd8c1..29141c6 100644
--- a/src/site/markdown/development.md
+++ b/src/site/markdown/development.md
@@ -81,7 +81,7 @@ You can edit Java files, CodeServer will recompile on page reloading (`F5` key).
 
 ## Outside an IDE
 
-To launch the environment outside an IDE, follow the steps below:
+To launch the environment outside an IDE, follow the steps below (after adapting `www-server/src/main/tomcat10xconf/context.xml` to your configuration):
 
 ```sh
 mvn package
@@ -96,3 +96,5 @@ You can edit Java files, CodeServer will recompile on page reloading (`F5` key).
 To reflect on Tomcat, run `mvn package -DskipTests`.
 
 To run static analyses: `mvn checkstyle:checkstyle pmd:pmd pmd:cpd`.
+
+To run one test class: `mvn test -DfailIfNoTests=false -Dsurefire.failIfNoSpecifiedTests=false -Dtest=fr.agrometinfo.www.server.rs.IndicatorResourceTest`.
\ No newline at end of file
diff --git a/www-client/pom.xml b/www-client/pom.xml
index 5ee7f06..384a0dc 100644
--- a/www-client/pom.xml
+++ b/www-client/pom.xml
@@ -7,7 +7,7 @@
     <parent>
         <groupId>fr.agrometinfo</groupId>
         <artifactId>www</artifactId>
-        <version>1.0.0-alpha-1</version>
+        <version>2.0.0-alpha-2</version>
     </parent>
 
     <artifactId>www-client</artifactId>
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index d4be9b5..ec07adb 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -269,6 +269,18 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
     @DefaultStringValue("https://agroclim.inrae.fr/services-et-outils")
     String otherAgroclimAppsUrl();
 
+    /**
+     * @return translation
+     */
+    @DefaultStringValue("Quantile 5\u00a0%")
+    String quantile05();
+
+    /**
+     * @return translation
+     */
+    @DefaultStringValue("Quantile 95\u00a0%")
+    String quantile95();
+
     /**
      * @return translation
      */
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppMessages.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppMessages.java
index 819ce01..5a9a2db 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppMessages.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppMessages.java
@@ -99,4 +99,14 @@ public interface AppMessages extends Messages {
      */
     @DefaultMessage("Region \"{0}\"")
     String region(String name);
+
+    /**
+     * @param datasetLabel   the label for the data set which appears in the legend
+     *                       and tooltips.
+     * @param formattedValue the formatted value for the tooltip.
+     * @param unit           the unit symbol
+     * @return translation
+     */
+    @DefaultMessage("{0}: {1}\u00A0{2}")
+    String tooltipLabel(String datasetLabel, String formattedValue, String unit);
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/module.gwt.xml b/www-client/src/main/java/fr/agrometinfo/www/client/module.gwt.xml
index 0ae36c0..c3ef3a0 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/module.gwt.xml
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/module.gwt.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.9.0//EN" "https://www.gwtproject.org/doctype/2.9.0/gwt-module.dtd">
+<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.11.0//EN" "https://www.gwtproject.org/doctype/2.11.0/gwt-module.dtd">
 <module rename-to="app">
 	<!-- Inherit the core Web Toolkit stuff. -->
 	<inherits name="com.google.gwt.user.User" />
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/CreditsPlugin.java b/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/CreditsPlugin.java
new file mode 100644
index 0000000..6cedeb6
--- /dev/null
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/CreditsPlugin.java
@@ -0,0 +1,41 @@
+package fr.agrometinfo.www.client.ui.chart;
+
+import org.pepstock.charba.client.IsChart;
+import org.pepstock.charba.client.dom.elements.Context2dItem;
+import org.pepstock.charba.client.enums.TextAlign;
+import org.pepstock.charba.client.plugins.AbstractPlugin;
+
+import fr.agrometinfo.www.client.util.ApplicationUtils;
+
+/**
+ * Append credits on the chart.
+ *
+ * @author Olivier Maury
+ */
+public final class CreditsPlugin extends AbstractPlugin {
+    /**
+     * Font size for text.
+     */
+    public static final int FONT_SIZE = 9;
+
+    /**
+     * Constructor.
+     */
+    public CreditsPlugin() {
+        super("Credits");
+    }
+
+    @Override
+    public void onAfterDraw(final IsChart chart) {
+        final String text = ApplicationUtils.getAttributions();
+        final Context2dItem context = chart.getCanvas().getContext2d();
+        final int xOffset = FONT_SIZE;
+        final int yOffset = FONT_SIZE;
+        final int x = chart.getCanvas().getWidth() - xOffset;
+        final int y = chart.getCanvas().getHeight() - yOffset;
+        context.setFont(FONT_SIZE + "px \"Helvetica Neue\",Helvetica,Arial,sans-serif");
+        context.setFillColor("#a6a6a6");
+        context.setTextAlign(TextAlign.RIGHT);
+        context.fillText(text, x, y);
+    }
+}
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/DailyValuesChart.java b/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/DailyValuesChart.java
new file mode 100644
index 0000000..565e2fe
--- /dev/null
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/DailyValuesChart.java
@@ -0,0 +1,168 @@
+package fr.agrometinfo.www.client.ui.chart;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.pepstock.charba.client.IsChart;
+import org.pepstock.charba.client.TimeSeriesLineChart;
+import org.pepstock.charba.client.callbacks.AbstractTooltipTitleCallback;
+import org.pepstock.charba.client.callbacks.TooltipLabelCallback;
+import org.pepstock.charba.client.colors.HtmlColor;
+import org.pepstock.charba.client.commons.Constants;
+import org.pepstock.charba.client.configuration.CartesianLinearAxis;
+import org.pepstock.charba.client.configuration.CartesianTimeSeriesAxis;
+import org.pepstock.charba.client.data.DataPoint;
+import org.pepstock.charba.client.data.Dataset;
+import org.pepstock.charba.client.data.LineDataset;
+import org.pepstock.charba.client.data.LiningDataset;
+import org.pepstock.charba.client.data.TimeSeriesItem;
+import org.pepstock.charba.client.data.TimeSeriesLineDataset;
+import org.pepstock.charba.client.enums.TimeUnit;
+import org.pepstock.charba.client.items.TooltipItem;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+
+import fr.agrometinfo.www.client.i18n.AppConstants;
+import fr.agrometinfo.www.client.i18n.AppMessages;
+import fr.agrometinfo.www.client.util.DateUtils;
+import fr.agrometinfo.www.shared.dto.SummaryDTO;
+
+/**
+ * The line chart in right panel to display daily values.
+ *
+ * @author Olivier Maury
+ */
+public final class DailyValuesChart extends TimeSeriesLineChart {
+    /**
+     * I18N constants.
+     */
+    private static final AppConstants CSTS = GWT.create(AppConstants.class);
+
+    /**
+     * I18N messages.
+     */
+    private static final AppMessages MSGS = GWT.create(AppMessages.class);
+
+    private static void setDatasetColor(final LiningDataset dataset) {
+        final double alpha = 0.2;
+        dataset.setBackgroundColor(HtmlColor.CORNFLOWER_BLUE.alpha(alpha));
+        dataset.setBorderColor(HtmlColor.CORNFLOWER_BLUE);
+        dataset.setBorderWidth(1);
+    }
+
+    /**
+     * Unit symbol.
+     */
+    private final String unit;
+
+
+    /**
+     * Constructor.
+     *
+     * @param summary the summary for the area with daily values
+     */
+    public DailyValuesChart(final SummaryDTO summary) {
+        final Map<Date, Float[]> values = summary.getDailyValues();
+        GWT.log("DailyValuesChart() " + values.size());
+        this.unit = summary.getIndicator().getUnit();
+
+        final String subtitle = MSGS.chartSubtitle(summary.getDate(), unit);
+        setTitle(CSTS.dailyValues(), subtitle);
+        setCredits();
+
+        final TimeSeriesLineDataset q5Dataset = this.newDataset();
+        setDatasetColor(q5Dataset);
+        q5Dataset.setPointRadius(0);
+        q5Dataset.setLabel(CSTS.quantile05());
+        q5Dataset.setFill(2);
+
+        final TimeSeriesLineDataset dataset = this.newDataset();
+        setDatasetColor(dataset);
+        dataset.setLabel(summary.getIndicator().getDescription());
+
+        final TimeSeriesLineDataset q95Dataset = this.newDataset();
+        setDatasetColor(q95Dataset);
+        q95Dataset.setPointRadius(0);
+        q95Dataset.setLabel(CSTS.quantile95());
+
+        final LinkedList<TimeSeriesItem> q5Data = new LinkedList<>();
+        final LinkedList<TimeSeriesItem> data = new LinkedList<>();
+        final LinkedList<TimeSeriesItem> q95Data = new LinkedList<>();
+
+        values.forEach((date, value) -> {
+            GWT.log("DailyValuesChart() " + date + "->" + value[SummaryDTO.COMPUTED_INDEX]);
+            data.add(new TimeSeriesItem(date, value[SummaryDTO.COMPUTED_INDEX]));
+            if (value[SummaryDTO.Q5_INDEX] != null) {
+                q5Data.add(new TimeSeriesItem(date, value[SummaryDTO.Q5_INDEX]));
+            }
+            if (value[SummaryDTO.Q95_INDEX] != null) {
+                q95Data.add(new TimeSeriesItem(date, value[SummaryDTO.Q95_INDEX]));
+            }
+        });
+
+        q5Dataset.setTimeSeriesData(q5Data);
+        dataset.setTimeSeriesData(data);
+        q95Dataset.setTimeSeriesData(q95Data);
+
+        final CartesianTimeSeriesAxis axis = this.getOptions().getScales().getTimeAxis();
+        axis.getTime().setUnit(TimeUnit.MONTH);
+        axis.getTime().getDisplayFormats().setDisplayFormat(TimeUnit.MONTH, "MMM");
+        axis.getTitle().setText("" + DateUtils.getYear(summary.getDate()));
+        axis.getTitle().setDisplay(true);
+
+        final CartesianLinearAxis axis2 = this.getOptions().getScales().getLinearAxis();
+        axis2.setDisplay(true);
+        axis2.setBeginAtZero(true);
+        axis2.setStacked(false);
+
+        this.getData().setDatasets(q5Dataset, dataset, q95Dataset);
+    }
+
+    private void setCredits() {
+        this.getPlugins().add(new CreditsPlugin());
+    }
+
+    private void setTitle(final String title, final String subtitle) {
+        this.getOptions().setResponsive(true);
+        this.getOptions().setAnimationEnabled(false);
+        this.getOptions().getLegend().setDisplay(false);
+        this.getOptions().setMaintainAspectRatio(false);
+        this.getOptions().getTitle().setText(title);
+        this.getOptions().getTitle().setDisplay(true);
+        if (subtitle != null) {
+            this.getOptions().getSubtitle().setText(subtitle);
+            this.getOptions().getSubtitle().setDisplay(true);
+        }
+        this.getOptions().getLayout().getPadding().setBottom(2 * CreditsPlugin.FONT_SIZE);
+
+        final DateTimeFormat dtf = DateTimeFormat.getFormat(PredefinedFormat.DATE_LONG);
+        this.getOptions().getTooltips().getCallbacks().setLabelCallback(new TooltipLabelCallback() {
+            @Override
+            public List<String> onLabel(final IsChart chart, final TooltipItem item) {
+                // checks if arguments are consistent
+                if (IsChart.isConsistent(chart) && item != null) {
+                    final Dataset dataset = chart.getData().retrieveDataset(item);
+                    final String label = MSGS.tooltipLabel(dataset.getLabel(), item.getFormattedValue(), unit);
+                    return Arrays.asList(label);
+                }
+                // if here, the arguments or the labels are not consistent
+                // then returns an empty string
+                return Arrays.asList(Constants.EMPTY_STRING);
+            }
+        });
+        this.getOptions().getTooltips().getCallbacks().setTitleCallback(new AbstractTooltipTitleCallback() {
+            @Override
+            public List<String> onTitle(final IsChart aChart, final List<TooltipItem> items) {
+                final TooltipItem item = items.iterator().next();
+                final LineDataset ds = (LineDataset) aChart.getData().getDatasets().get(1);
+                final DataPoint dp = ds.getDataPoints().get(item.getDataIndex());
+                return Arrays.asList(dtf.format(dp.getXAsDate()));
+            }
+        });
+    }
+}
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/package-info.java b/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/package-info.java
new file mode 100644
index 0000000..f833e68
--- /dev/null
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/ui/chart/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Chart in right panel using the Charba library.
+ */
+package fr.agrometinfo.www.client.ui.chart;
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasTitle.java b/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasTitle.java
index 7c50ca4..9615a5c 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasTitle.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/ui/map/CanvasTitle.java
@@ -11,6 +11,7 @@ import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.ImageElement;
 import com.google.gwt.i18n.client.NumberFormat;
 
+import elemental2.dom.DomGlobal;
 import fr.agrometinfo.www.client.util.UiUtils;
 import fr.agrometinfo.www.client.util.color.ColorInterval;
 import ol.Map;
@@ -161,6 +162,10 @@ public final class CanvasTitle extends CanvasWidget {
      * Draw the horizontal scale below the logo.
      */
     private void drawScale() {
+        if (colorIntervals == null) {
+            DomGlobal.console.error("colorIntervals must not be null!");
+            return;
+        }
         final int nbOfIntervals = colorIntervals.size();
         final double logoHeight = LOGO_WIDTH / LOGO_SIZE_RATIO;
         final int cellWidth = (int) (getWidth() / (nbOfIntervals + 0.7));
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
index e83534b..06b2599 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
@@ -634,7 +634,7 @@ implements LayoutPresenter.View, LoadingHandler {
             return;
         }
         // display periods
-        DomGlobal.console.info("LayoutView.setPeriods() : " + list);
+        GWT.log("LayoutView.setPeriods() : " + list);
         final PeriodDTO defaultPeriod = list.stream() //
                 .filter(i -> DEFAULT_PERIOD.equals(i.getCode())) //
                 .findFirst().orElse(null);
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/RightPanelView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/RightPanelView.java
index f173b6e..8fe7c34 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/RightPanelView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/RightPanelView.java
@@ -2,12 +2,6 @@ package fr.agrometinfo.www.client.view;
 
 import static org.jboss.elemento.Elements.a;
 
-import java.util.Arrays;
-import java.util.Date;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
 import org.dominokit.domino.ui.button.Button;
 import org.dominokit.domino.ui.grid.Column;
 import org.dominokit.domino.ui.grid.Row;
@@ -16,29 +10,14 @@ import org.dominokit.domino.ui.utils.DominoElement;
 import org.jboss.elemento.Elements;
 import org.jboss.elemento.EventType;
 import org.jboss.elemento.HtmlContentBuilder;
-import org.pepstock.charba.client.AbstractChart;
-import org.pepstock.charba.client.IsChart;
 import org.pepstock.charba.client.TimeSeriesLineChart;
-import org.pepstock.charba.client.callbacks.AbstractTooltipTitleCallback;
-import org.pepstock.charba.client.colors.HtmlColor;
-import org.pepstock.charba.client.configuration.CartesianLinearAxis;
-import org.pepstock.charba.client.configuration.CartesianTimeSeriesAxis;
-import org.pepstock.charba.client.data.DataPoint;
-import org.pepstock.charba.client.data.LineDataset;
-import org.pepstock.charba.client.data.LiningDataset;
-import org.pepstock.charba.client.data.TimeSeriesItem;
-import org.pepstock.charba.client.data.TimeSeriesLineDataset;
-import org.pepstock.charba.client.dom.elements.Context2dItem;
-import org.pepstock.charba.client.enums.TextAlign;
-import org.pepstock.charba.client.enums.TimeUnit;
-import org.pepstock.charba.client.items.TooltipItem;
-import org.pepstock.charba.client.plugins.AbstractPlugin;
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.i18n.client.DateTimeFormat;
 import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
 import com.google.gwt.i18n.client.NumberFormat;
 
+import elemental2.dom.DomGlobal;
 import elemental2.dom.EventListener;
 import elemental2.dom.HTMLDivElement;
 import elemental2.dom.HTMLElement;
@@ -49,8 +28,7 @@ import fr.agrometinfo.www.client.i18n.AppConstants;
 import fr.agrometinfo.www.client.i18n.AppMessages;
 import fr.agrometinfo.www.client.presenter.RightPanelPresenter;
 import fr.agrometinfo.www.client.ui.CardDetails;
-import fr.agrometinfo.www.client.util.ApplicationUtils;
-import fr.agrometinfo.www.client.util.DateUtils;
+import fr.agrometinfo.www.client.ui.chart.DailyValuesChart;
 import fr.agrometinfo.www.client.util.UiUtils;
 import fr.agrometinfo.www.shared.dto.IndicatorDTO;
 import fr.agrometinfo.www.shared.dto.SummaryDTO;
@@ -61,37 +39,6 @@ import fr.agrometinfo.www.shared.dto.SummaryDTO;
  * @author Olivier Maury
  */
 public final class RightPanelView implements RightPanelPresenter.View {
-    /**
-     * Append credits on the chart.
-     */
-    private static class CreditsPlugin extends AbstractPlugin {
-        /**
-         * Font size for text.
-         */
-        public static final int FONT_SIZE = 9;
-
-        /**
-         * Constructor.
-         */
-        protected CreditsPlugin() {
-            super("Credits");
-        }
-
-        @Override
-        public void onAfterDraw(final IsChart chart) {
-            final String text = ApplicationUtils.getAttributions();
-            final Context2dItem context = chart.getCanvas().getContext2d();
-            final int xOffset = FONT_SIZE;
-            final int yOffset = FONT_SIZE;
-            final int x = chart.getCanvas().getWidth() - xOffset;
-            final int y = chart.getCanvas().getHeight() - yOffset;
-            context.setFont(FONT_SIZE + "px \"Helvetica Neue\",Helvetica,Arial,sans-serif");
-            context.setFillColor("#a6a6a6");
-            context.setTextAlign(TextAlign.RIGHT);
-            context.fillText(text, x, y);
-        }
-    }
-
     /**
      * I18N constants.
      */
@@ -107,28 +54,6 @@ public final class RightPanelView implements RightPanelPresenter.View {
      */
     private static final AppMessages MSGS = GWT.create(AppMessages.class);
 
-    private static void setChartOptions(final AbstractChart chart, final String title, final String subtitle) {
-        chart.getOptions().setResponsive(true);
-        chart.getOptions().setAnimationEnabled(false);
-        chart.getOptions().getLegend().setDisplay(false);
-        chart.getOptions().setMaintainAspectRatio(false);
-        chart.getOptions().getTitle().setText(title);
-        chart.getOptions().getTitle().setDisplay(true);
-        if (subtitle != null) {
-            chart.getOptions().getSubtitle().setText(subtitle);
-            chart.getOptions().getSubtitle().setDisplay(true);
-        }
-        chart.getOptions().getLayout().getPadding().setBottom(2 * CreditsPlugin.FONT_SIZE);
-        chart.getPlugins().add(new CreditsPlugin());
-    }
-
-    private static void setDatasetColor(final LiningDataset dataset) {
-        final double alpha = 0.2;
-        dataset.setBackgroundColor(HtmlColor.CORNFLOWER_BLUE.alpha(alpha));
-        dataset.setBorderColor(HtmlColor.CORNFLOWER_BLUE);
-
-    }
-
     /**
      * Card for the daily average value for the user choice.
      */
@@ -202,46 +127,14 @@ public final class RightPanelView implements RightPanelPresenter.View {
      * @param div  container for the panel.
      */
     public RightPanelView(final LayoutView view, final DominoElement<HTMLElement> div) {
+        GWT.log("RightPanelView()");
         layoutView = view;
         container = div;
     }
 
-    private void createLineChart(final Map<Date, Float> values) {
-        GWT.log("RightPanelView.createBarChart() " + values.size());
-        final DateTimeFormat dtf = DateTimeFormat.getFormat(PredefinedFormat.DATE_LONG);
-        final String subtitle = MSGS.chartSubtitle(summary.getDate(), summary.getIndicator().getUnit());
-        chart = new TimeSeriesLineChart();
-        setChartOptions(chart, CSTS.dailyValues(), subtitle);
-        chart.getOptions().getTooltips().getCallbacks().setTitleCallback(new AbstractTooltipTitleCallback() {
-
-            @Override
-            public List<String> onTitle(final IsChart aChart, final List<TooltipItem> items) {
-                final TooltipItem item = items.iterator().next();
-                final LineDataset ds = (LineDataset) aChart.getData().getDatasets().get(0);
-                final DataPoint dp = ds.getDataPoints().get(item.getDataIndex());
-                return Arrays.asList(dtf.format(dp.getXAsDate()));
-            }
-
-        });
-        final TimeSeriesLineDataset dataset = chart.newDataset();
-        setDatasetColor(dataset);
-        dataset.setBorderWidth(1);
-        final List<TimeSeriesItem> data = new LinkedList<>();
-        values.forEach((date, value) -> data.add(new TimeSeriesItem(date, value)));
-        dataset.setTimeSeriesData(data);
-
-        final CartesianTimeSeriesAxis axis = chart.getOptions().getScales().getTimeAxis();
-        axis.getTime().setUnit(TimeUnit.MONTH);
-        axis.getTime().getDisplayFormats().setDisplayFormat(TimeUnit.MONTH, "MMM");
-        axis.getTitle().setText("" + DateUtils.getYear(summary.getDate()));
-        axis.getTitle().setDisplay(true);
-
-        final CartesianLinearAxis axis2 = chart.getOptions().getScales().getLinearAxis();
-        axis2.setDisplay(true);
-        axis2.setBeginAtZero(true);
-        axis2.setStacked(true);
-
-        chart.getData().setDatasets(dataset);
+    private void createLineChart() {
+        GWT.log("RightPanelView.createLineChart()");
+        chart = new DailyValuesChart(summary);
 
         lineChartContainer.setHeight("300px");
         lineChartContainer.clearElement();
@@ -249,6 +142,7 @@ public final class RightPanelView implements RightPanelPresenter.View {
     }
 
     private String getDescription(final boolean comparison) {
+        GWT.log("RightPanelView.getDescription()");
         final IndicatorDTO indicator = summary.getIndicator();
         String region = summary.getFeatureName();
         if (region == null) {
@@ -267,11 +161,13 @@ public final class RightPanelView implements RightPanelPresenter.View {
 
     @Override
     public void hide() {
+        GWT.log("RightPanelView.hide()");
         layoutView.hideRightPanel();
     }
 
     @Override
     public void init() {
+        GWT.log("RightPanelView.init()");
         backBtn.style().setDisplay("none");
 
         container.add(DominoElement.div() //
@@ -295,16 +191,19 @@ public final class RightPanelView implements RightPanelPresenter.View {
 
     @Override
     public boolean isVisible() {
+        GWT.log("RightPanelView.isVisible()");
         return layoutView.isRightPanelVisible();
     }
 
     @Override
     public void setPeriodName(final String name) {
+        GWT.log("RightPanelView.setPeriodName()");
         periodName = name;
     }
 
     @Override
     public void setPresenter(final RightPanelPresenter p) {
+        GWT.log("RightPanelView.setPresenter()");
         presenter = p;
     }
 
@@ -333,6 +232,7 @@ public final class RightPanelView implements RightPanelPresenter.View {
 
             }
         }
+
         if (data.getParentFeature() != null) {
             backBtn.getTextSpan().textContent(data.getParentFeature().getName());
             if (backBtnClickListener != null) {
@@ -359,19 +259,31 @@ public final class RightPanelView implements RightPanelPresenter.View {
 
         final IndicatorDTO indicator = data.getIndicator();
 
+        if (indicator == null) {
+            DomGlobal.console.error("data.getIndicator() must not be null!");
+            return;
+        }
         // Average
         averageCard.setTitle(indicator.getDescription());
-        averageCard.setValue(nf.format(data.getAverageValue()) + " " + indicator.getUnit());
+        if (data.getAverageValue() != null) {
+            averageCard.setValue(nf.format(data.getAverageValue()) + " " + indicator.getUnit());
+        } else {
+            averageCard.setValue("null");
+        }
         averageCard.setBody(getDescription(false));
 
         // Comparison
         comparisonCard.setTitle(CSTS.normalComparison());
         comparisonCard.setSubTitle(CSTS.comparisonPeriod());
-        comparisonCard.setValue(nf.format(data.getComparedValue()) + " " + indicator.getUnit());
+        if (data.getComparedValue() != null) {
+            comparisonCard.setValue(nf.format(data.getComparedValue()) + " " + indicator.getUnit());
+        } else {
+            comparisonCard.setValue("null");
+        }
         comparisonCard.setBody(getDescription(true));
 
         // Chart
-        createLineChart(data.getDailyValues());
+        createLineChart();
     }
 
     @Override
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppMessages_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppMessages_fr.properties
index bda912e..365a836 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppMessages_fr.properties
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppMessages_fr.properties
@@ -4,7 +4,7 @@ cell = Maille n°{0}
 chartSubtitle = {0,date,medium}. Unité : {1}
 comparedValue = Écart moyen de la valeur de l’indicateur {0} ({1}) en {2} sur {3}
 computedOn = Calcul du {0,date,long}
-failureResponse = La communication avec le serveur a échoué : {0}
+failureResponse = La communication avec le serveur a échoué\u00A0: {0}
 failureStatusCode = Code d’état HTTP : {0}
 nbOfIndicatorPeriods[\=0] = Aucune période.
 nbOfIndicatorPeriods[\=1] = Une période.
@@ -16,3 +16,4 @@ nbOfIndicators[one] = Un indicateur.
 nbOfIndicators = {0} indicateurs.
 pra = Petite région agricole «\u00A0{0}\u00A0»
 region = Région «\u00A0{0}\u00A0»
+tooltipLabel = {0}\u00A0: {1}\u00A0{2} 
diff --git a/www-server/pom.xml b/www-server/pom.xml
index 145e3c7..5f69e6e 100644
--- a/www-server/pom.xml
+++ b/www-server/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>fr.agrometinfo</groupId>
     <artifactId>www</artifactId>
-    <version>1.0.0-alpha-1</version>
+    <version>2.0.0-alpha-2</version>
   </parent>
 
   <artifactId>www-server</artifactId>
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java
index 105d1ac..d9aa2f3 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDao.java
@@ -92,9 +92,9 @@ public interface PraDailyValueDao {
      * @param indicator indicator
      * @param start      period start
      * @param end      period end
-     * @return values
+     * @return values (computed/compared, q5, median, q95)
      */
-    Map<LocalDate, Float> findDailyValues(Indicator indicator, LocalDate start, LocalDate end);
+    Map<LocalDate, Float[]> findDailyValues(Indicator indicator, LocalDate start, LocalDate end);
 
     /**
      * Daily values for the indicator between 2 dates for a PRA.
@@ -103,9 +103,9 @@ public interface PraDailyValueDao {
      * @param start      period start
      * @param end      period end
      * @param pra related PRA
-     * @return values
+     * @return values (computed/compared, q5, median, q95)
      */
-    Map<LocalDate, Float> findDailyValues(Indicator indicator, LocalDate start, LocalDate end, Pra pra);
+    Map<LocalDate, Float[]> findDailyValues(Indicator indicator, LocalDate start, LocalDate end, Pra pra);
 
     /**
      * Daily values for the indicator between 2 dates for a region.
@@ -114,9 +114,9 @@ public interface PraDailyValueDao {
      * @param start      period start
      * @param end      period end
      * @param region related region
-     * @return values
+     * @return values (computed/compared, q5, median, q95)
      */
-    Map<LocalDate, Float> findDailyValues(Indicator indicator, LocalDate start, LocalDate end, Region region);
+    Map<LocalDate, Float[]> findDailyValues(Indicator indicator, LocalDate start, LocalDate end, Region region);
 
     /**
      * @param year the year of indicator values
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java
index 9d96a3b..3beef2b 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java
@@ -13,6 +13,7 @@ import fr.agrometinfo.www.server.model.Pra;
 import fr.agrometinfo.www.server.model.PraDailyValue;
 import fr.agrometinfo.www.server.model.Region;
 import fr.agrometinfo.www.server.util.DateUtils;
+import fr.agrometinfo.www.shared.dto.SummaryDTO;
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.persistence.NoResultException;
 import jakarta.persistence.Query;
@@ -28,6 +29,13 @@ import lombok.NonNull;
 @ApplicationScoped
 public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> implements PraDailyValueDao {
 
+    private static Float toFloat(final Tuple t, final String column) {
+        if (t.get(column) == null) {
+            return null;
+        }
+        return ((Number) t.get(column)).floatValue();
+    }
+
     private static PraDailyValue toPraDailyValue(final Object[] row) {
         final PraDailyValue value = new PraDailyValue();
         int i = 0;
@@ -50,7 +58,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
             final Double[] coordinates = new Double[coords.length];
             for (int j = 0; j < coords.length; j++) {
                 if (coords[j] != null && !"NULL".equals(coords[j])) {
-                    coordinates[j] = Double.parseDouble(coords[j]);
+                    coordinates[j] = Double.valueOf(coords[j]);
                 }
             }
             pra.setCoordinates(coordinates);
@@ -153,10 +161,12 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
     }
 
     @Override
-    public final Map<LocalDate, Float> findDailyValues(final Indicator indicator, final LocalDate start,
+    public final Map<LocalDate, Float[]> findDailyValues(final Indicator indicator, final LocalDate start,
             final LocalDate end) {
         final var jpql = """
-                SELECT t.date AS date, AVG(t.computedValue) AS value
+                SELECT
+                         t.date AS date, AVG(t.computedValue) AS value,
+                         AVG(t.q5) AS q5, AVG(t.medianValue) AS median, AVG(t.q95) AS q95
                 FROM PraDailyValue AS t
                 WHERE t.indicator=:indicator AND t.date BETWEEN :start AND :end
                 GROUP BY t.date
@@ -170,10 +180,12 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
     }
 
     @Override
-    public final Map<LocalDate, Float> findDailyValues(final Indicator indicator, final LocalDate start,
+    public final Map<LocalDate, Float[]> findDailyValues(final Indicator indicator, final LocalDate start,
             final LocalDate end, final Pra pra) {
         final var jpql = """
-                SELECT t.date AS date, AVG(t.computedValue) AS value
+                SELECT
+                         t.date AS date, AVG(t.computedValue) AS value,
+                         AVG(t.q5) AS q5, AVG(t.medianValue) AS median, AVG(t.q95) AS q95
                 FROM PraDailyValue AS t
                 WHERE t.indicator=:indicator AND t.date BETWEEN :start AND :end AND t.pra=:pra
                 GROUP BY t.date
@@ -188,10 +200,12 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
     }
 
     @Override
-    public final Map<LocalDate, Float> findDailyValues(final Indicator indicator, final LocalDate start,
+    public final Map<LocalDate, Float[]> findDailyValues(final Indicator indicator, final LocalDate start,
             final LocalDate end, final Region region) {
         final var jpql = """
-                SELECT t.date AS date, AVG(t.computedValue) AS value
+                SELECT
+                         t.date AS date, AVG(t.computedValue) AS value,
+                         AVG(t.q5) AS q5, AVG(t.medianValue) AS median, AVG(t.q95) AS q95
                 FROM PraDailyValue AS t
                 WHERE t.indicator=:indicator AND t.date BETWEEN :start AND :end AND t.pra.department.region=:region
                 GROUP BY t.date
@@ -205,7 +219,7 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
         return findDailyValues(jpql, params);
     }
 
-    private Map<LocalDate, Float> findDailyValues(final String jpql, final Map<String, Object> params) {
+    private Map<LocalDate, Float[]> findDailyValues(final String jpql, final Map<String, Object> params) {
         try (ScopedEntityManager em = getScopedEntityManager()) {
             final TypedQuery<Tuple> query = em.createQuery(jpql, Tuple.class);
             if (params != null) {
@@ -214,7 +228,15 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
             return query.getResultStream()//
                     .collect(Collectors.toMap(//
                             t -> (LocalDate) t.get("date"), //
-                            t -> ((Number) t.get("value")).floatValue()));
+                            t -> {
+                                final Float[] values = new Float[SummaryDTO.Q95_INDEX + 1];
+                                values[SummaryDTO.COMPUTED_INDEX] = ((Number) t.get("value")).floatValue();
+                                values[SummaryDTO.Q5_INDEX] = toFloat(t, "q5");
+                                values[SummaryDTO.MEDIAN_INDEX] = toFloat(t, "median");
+                                values[SummaryDTO.Q95_INDEX] = toFloat(t, "q95");
+                                return values;
+                            })//
+                            );
         } catch (final NoResultException e) {
             return Map.of();
         }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/PraDailyValue.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/PraDailyValue.java
index 4238c18..acd4f74 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/PraDailyValue.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/PraDailyValue.java
@@ -49,10 +49,25 @@ public class PraDailyValue {
     @JoinColumn(name = "indicator")
     @ManyToOne
     private Indicator indicator;
+    /**
+     * Value of median of normal for the indicator.
+     */
+    @Column(name = "medianvalue")
+    private Float medianValue;
     /**
      * The PRA for the value.
      */
     @JoinColumn(name = "pra")
     @ManyToOne
     private Pra pra;
+    /**
+     * Value of quantile 5% of normal for the indicator.
+     */
+    @Column(name = "q5")
+    private Float q5;
+    /**
+     * Value of quantile 95% of normal for the indicator.
+     */
+    @Column(name = "q95")
+    private Float q95;
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java
index 6db4e6c..9718829 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java
@@ -19,6 +19,8 @@ import jakarta.inject.Inject;
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.POST;
 import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
 import lombok.extern.log4j.Log4j2;
 
 /**
@@ -60,6 +62,7 @@ public class ApplicationResource implements ApplicationService {
 
     @GET
     @Path(ApplicationService.PATH_APPLICATION_DATE)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
     @Override
     public Date getApplicationDate() {
         return applicationDate;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java
index 73edaba..08bfa16 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/IndicatorResource.java
@@ -15,13 +15,11 @@ import java.util.TreeMap;
 import org.geojson.Feature;
 import org.geojson.FeatureCollection;
 
-import fr.agrometinfo.www.server.dao.CellDao;
 import fr.agrometinfo.www.server.dao.DailyValueDao;
 import fr.agrometinfo.www.server.dao.IndicatorDao;
 import fr.agrometinfo.www.server.dao.PraDailyValueDao;
 import fr.agrometinfo.www.server.dao.PraDao;
 import fr.agrometinfo.www.server.dao.RegionDao;
-import fr.agrometinfo.www.server.dao.SimulationDao;
 import fr.agrometinfo.www.server.model.Indicator;
 import fr.agrometinfo.www.server.model.Pra;
 import fr.agrometinfo.www.server.model.PraDailyValue;
@@ -132,12 +130,6 @@ public class IndicatorResource implements IndicatorService {
     @Inject
     private CacheService cacheService;
 
-    /**
-     * DAO for cells.
-     */
-    @Inject
-    private CellDao cellDao;
-
     /**
      * DAO for {@link dailyValue}.
      */
@@ -186,12 +178,6 @@ public class IndicatorResource implements IndicatorService {
     @Context
     private HttpHeaders httpHeaders;
 
-    /**
-     * Dao for Simulation.
-     */
-    @Inject
-    private SimulationDao simulationDao;
-
     /**
      * Ensure the value of query parameter is not null and not blank.
      *
@@ -293,7 +279,8 @@ public class IndicatorResource implements IndicatorService {
     @Override
     public SummaryDTO getSummary(@QueryParam(value = "indicator") final String indicatorUid,
             @QueryParam(value = "period") final String periodCode,
-            @QueryParam(value = "level") final FeatureLevel level, @QueryParam(value = "id") final String id,
+            @QueryParam(value = "level") @NonNull final FeatureLevel level,
+            @QueryParam(value = "id") final String id,
             @QueryParam(value = "year") final Integer year) {
         checkRequired(indicatorUid, "indicator");
         checkRequired(periodCode, "period");
@@ -326,52 +313,54 @@ public class IndicatorResource implements IndicatorService {
         final var date = dailyValueDao.findLastDate(indicator, year);
         final Double averageValue;
         final Double comparedValue;
-        final Map<LocalDate, Float> tmpDailyValues;
+        final Map<LocalDate, Float[]> tmpDailyValues;
         // By default, all key-value pairs in TreeMap are sorted in their natural
         // ordering.
-        final Map<Date, Float> dailyValues = new TreeMap<>();
+        final Map<Date, Float[]> dailyValues = new TreeMap<>();
         final SimpleFeature parentFeature;
         final String featureName;
-        if (level == FeatureLevel.REGION) {
-            Integer regionId;
-            final Region region;
-            try {
-                regionId = Integer.valueOf(id);
-            } catch (final NumberFormatException e) {
-                regionId = null;
+        switch (level) {
+            case REGION -> {
+                Integer regionId;
+                final Region region;
+                try {
+                    regionId = Integer.valueOf(id);
+                } catch (final NumberFormatException e) {
+                    regionId = null;
+                }
+                if (regionId != null) {
+                    region = regionDao.find(regionId);
+                } else {
+                    region = null;
+                }
+                if (region == null) {
+                    // Metropolitan France
+                    featureName = null;
+                    averageValue = praDailyValueDao.findAverageComputedValue(indicator, date);
+                    comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date);
+                    tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay);
+                    parentFeature = null;
+                } else {
+                    averageValue = praDailyValueDao.findAverageComputedValue(indicator, date, regionId);
+                    comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date, regionId);
+                    tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, region);
+                    featureName = region.getName();
+                    parentFeature = null;
+                }
             }
-            if (regionId != null) {
-                region = regionDao.find(regionId);
-            } else {
-                region = null;
+            case PRA -> {
+                final Pra pra = praDao.findByCode(id);
+                final PraDailyValue value = praDailyValueDao.find(indicator, date, pra);
+                averageValue = value.getComputedValue().doubleValue();
+                comparedValue = value.getComparedValue().doubleValue();
+                tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, pra);
+                featureName = pra.getName();
+                parentFeature = new SimpleFeature();
+                parentFeature.setId(String.valueOf(pra.getDepartment().getRegion().getId()));
+                parentFeature.setLevel(FeatureLevel.REGION);
+                parentFeature.setName(pra.getDepartment().getRegion().getName());
             }
-            if (region == null) {
-                // Metropolitan France
-                featureName = null;
-                averageValue = praDailyValueDao.findAverageComputedValue(indicator, date);
-                comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date);
-                tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay);
-                parentFeature = null;
-            } else {
-                averageValue = praDailyValueDao.findAverageComputedValue(indicator, date, regionId);
-                comparedValue = praDailyValueDao.findAverageComparedValue(indicator, date, regionId);
-                tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, region);
-                featureName = region.getName();
-                parentFeature = null;
-            }
-        } else if (level == FeatureLevel.PRA) {
-            final Pra pra = praDao.findByCode(id);
-            final PraDailyValue value = praDailyValueDao.find(indicator, date, pra);
-            averageValue = value.getComputedValue().doubleValue();
-            comparedValue = value.getComparedValue().doubleValue();
-            tmpDailyValues = praDailyValueDao.findDailyValues(indicator, firstDay, lastDay, pra);
-            featureName = pra.getName();
-            parentFeature = new SimpleFeature();
-            parentFeature.setId(String.valueOf(pra.getDepartment().getRegion().getId()));
-            parentFeature.setLevel(FeatureLevel.REGION);
-            parentFeature.setName(pra.getDepartment().getRegion().getName());
-        } else {
-            throw new UnsupportedOperationException("Level not handled: " + level);
+            default -> throw new UnsupportedOperationException("Level not handled: " + level);
         }
         tmpDailyValues.forEach((d, v) -> dailyValues.put(DateUtils.toDate(d), v));
 
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/util/LocaleUtils.java b/www-server/src/main/java/fr/agrometinfo/www/server/util/LocaleUtils.java
index 8edc37e..871978a 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/util/LocaleUtils.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/util/LocaleUtils.java
@@ -26,6 +26,9 @@ public interface LocaleUtils {
      * @return the locale from the user request
      */
     static Locale getLocale(final HttpServletRequest request) {
+        if (request == null) {
+            return getDefaultLocale();
+        }
         // 1. URL parameter
         final String localeParam = request.getParameter("locale");
         if (localeParam != null) {
diff --git a/www-server/src/main/tomcat10xconf/context.xml b/www-server/src/main/tomcat10xconf/context.xml
index 29874ec..22ad526 100644
--- a/www-server/src/main/tomcat10xconf/context.xml
+++ b/www-server/src/main/tomcat10xconf/context.xml
@@ -29,7 +29,7 @@
     testOnBorrow="true"
     timeBetweenEvictionRunsMillis="30000"
     type="javax.sql.DataSource"
-    url="jdbc:postgresql://127.0.0.1:5432/agrometinfo?ApplicationName=AgroMetInfo"
+    url="jdbc:postgresql://127.0.0.1:5432/season?schemaName=agrometinfo&amp;ApplicationName=AgroMetInfo"
     username="agrometinfo"
     password="agrometinfo"
     validationQuery="select 1" />
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java
index 86ef704..6c56b7d 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/IndicatorResourceTest.java
@@ -1,6 +1,7 @@
 package fr.agrometinfo.www.server.rs;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 import java.io.File;
@@ -17,8 +18,6 @@ import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
-import fr.agrometinfo.www.server.dao.CellDao;
-import fr.agrometinfo.www.server.dao.CellDaoHibernate;
 import fr.agrometinfo.www.server.dao.DailyValueDao;
 import fr.agrometinfo.www.server.dao.DailyValueDaoHibernate;
 import fr.agrometinfo.www.server.dao.IndicatorDao;
@@ -33,6 +32,7 @@ import fr.agrometinfo.www.server.dao.SimulationDao;
 import fr.agrometinfo.www.server.dao.SimulationDaoHibernate;
 import fr.agrometinfo.www.server.dao.SimulationDaoHibernateTest;
 import fr.agrometinfo.www.server.service.CacheService;
+import fr.agrometinfo.www.shared.dto.SummaryDTO;
 import jakarta.ws.rs.core.Application;
 
 /**
@@ -66,7 +66,6 @@ class IndicatorResourceTest extends JerseyTest {
 
     @Override
     protected final Application configure() {
-        final CellDao cellDao = new CellDaoHibernate();
         final DailyValueDao dailyValueDao = new DailyValueDaoHibernate();
         final PraDailyValueDao praDailyValueDao = new PraDailyValueDaoHibernate();
         final PraDao praDao = new PraDaoHibernate();
@@ -79,11 +78,10 @@ class IndicatorResourceTest extends JerseyTest {
         return new ResourceConfig(IndicatorResource.class).register(new AbstractBinder() {
             @Override
             public void configure() {
-                bind(cellDao).to(CellDao.class);
                 bind(dailyValueDao).to(DailyValueDao.class);
+                bind(indicatorDao).to(IndicatorDao.class);
                 bind(praDailyValueDao).to(PraDailyValueDao.class);
                 bind(praDao).to(PraDao.class);
-                bind(indicatorDao).to(IndicatorDao.class);
                 bind(regionDao).to(RegionDao.class);
                 bind(simulationDao).to(SimulationDao.class);
                 bind(cacheService).to(CacheService.class);
@@ -91,6 +89,25 @@ class IndicatorResourceTest extends JerseyTest {
         });
     }
 
+    /**
+     * Ensure daily values are returned.
+     */
+    @Test
+    void getSummary() {
+        final SummaryDTO actual = target(IndicatorResource.PATH + SEP + IndicatorResource.PATH_SUMMARY)//
+                .queryParam("indicator", "rainsum") //
+                .queryParam("period", "year") //
+                .queryParam("level", "PRA") //
+                .queryParam("id", "59325") //
+                .queryParam("year", "2023") //
+                .request()//
+                .get(SummaryDTO.class);
+        assertNotNull(actual, "response must not be null!");
+        assertNotNull(actual.getAverageValue(), "average value must not be null!");
+        assertNotNull(actual.getComparedValue(), "compared value must not be null!");
+        assertFalse(actual.getDailyValues().isEmpty(), "daily values must exist!");
+    }
+
     /**
      * Expected values from sql/dailyvalues.csv.
      */
diff --git a/www-shared/pom.xml b/www-shared/pom.xml
index 969889e..6d48e0d 100644
--- a/www-shared/pom.xml
+++ b/www-shared/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>fr.agrometinfo</groupId>
     <artifactId>www</artifactId>
-    <version>1.0.0-alpha-1</version>
+    <version>2.0.0-alpha-2</version>
   </parent>
 
   <artifactId>www-shared</artifactId>
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SummaryDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SummaryDTO.java
index 21b1f65..140611b 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SummaryDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SummaryDTO.java
@@ -13,6 +13,22 @@ import org.dominokit.jackson.annotation.JSONMapper;
  */
 @JSONMapper
 public class SummaryDTO implements Serializable {
+    /**
+     * Index in the array for the computed daily value.
+     */
+    public static final int COMPUTED_INDEX = 0;
+    /**
+     * Index in the array for the quantile 5%.
+     */
+    public static final int Q5_INDEX = 1;
+    /**
+     * Index in the array for the quantile 50%.
+     */
+    public static final int MEDIAN_INDEX = 2;
+    /**
+     * Index in the array for the quantile 95%.
+     */
+    public static final int Q95_INDEX = 3;
     /**
      * UID for Serializable.
      */
@@ -33,9 +49,9 @@ public class SummaryDTO implements Serializable {
     private Float comparedValue;
 
     /**
-     * Last daily values for the indicator.
+     * Last daily values (computed, q5, median and q95) for the indicator.
      */
-    private Map<Date, Float> dailyValues;
+    private Map<Date, Float[]> dailyValues;
 
     /**
      * Date of the last simulation.
@@ -84,9 +100,9 @@ public class SummaryDTO implements Serializable {
     }
 
     /**
-     * @return the last daily values for the indicator.
+     * @return the last daily values (computed, q5, median and q95) for the indicator.
      */
-    public final Map<Date, Float> getDailyValues() {
+    public final Map<Date, Float[]> getDailyValues() {
         return dailyValues;
     }
 
@@ -147,9 +163,9 @@ public class SummaryDTO implements Serializable {
     }
 
     /**
-     * @param values the last daily values for the indicator.
+     * @param values the last daily values (computed, q5, median and q95) for the indicator.
      */
-    public final void setDailyValues(final Map<Date, Float> values) {
+    public final void setDailyValues(final Map<Date, Float[]> values) {
         this.dailyValues = values;
     }
 
-- 
GitLab


From 69bcf1c9c49326e3e49979d8c06e56e189d04f62 Mon Sep 17 00:00:00 2001
From: Olivier Maury <Olivier.Maury@inrae.fr>
Date: Fri, 19 Apr 2024 09:37:43 +0200
Subject: [PATCH 2/3] Corrections

---
 sql/schema.indexes.postgresql.sql                          | 1 +
 sql/schema.tables.sql                                      | 1 -
 sql/test_sql_on_postgresql.sh                              | 7 ++++---
 .../fr/agrometinfo/www/client/presenter/MapPresenter.java  | 6 ++----
 4 files changed, 7 insertions(+), 8 deletions(-)
 create mode 100644 sql/schema.indexes.postgresql.sql

diff --git a/sql/schema.indexes.postgresql.sql b/sql/schema.indexes.postgresql.sql
new file mode 100644
index 0000000..69c726e
--- /dev/null
+++ b/sql/schema.indexes.postgresql.sql
@@ -0,0 +1 @@
+CREATE INDEX IF NOT EXISTS "IX_dailyvalue_doy" ON dailyvalue (EXTRACT(DOY FROM date), indicator);
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index 93688e8..3df03ed 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -202,7 +202,6 @@ COMMENT ON TABLE dailyvalue IS 'Value of the indicator for the day on a cell.';
 COMMENT ON COLUMN dailyvalue.computedvalue IS 'Value of the indicator.';
 COMMENT ON COLUMN dailyvalue.comparedvalue IS 'Value of the indicator compared to the normal.';
 CREATE INDEX IF NOT EXISTS "IX_dailyvalue" ON dailyvalue USING btree (date, cell);
-CREATE INDEX IF NOT EXISTS "IX_dailyvalue_doy" ON dailyvalue (EXTRACT(DOY FROM date), indicator);
 
 CREATE OR REPLACE VIEW v_i18n
 AS SELECT l.languagetag,
diff --git a/sql/test_sql_on_postgresql.sh b/sql/test_sql_on_postgresql.sh
index a1a3802..af11130 100755
--- a/sql/test_sql_on_postgresql.sh
+++ b/sql/test_sql_on_postgresql.sh
@@ -2,9 +2,10 @@
 cd $(dirname $0)
 dropdb agrometinfotmp
 createdb agrometinfotmp
-psql agrometinfotmp -1 -f schema.types.postgresql.sql 
-psql agrometinfotmp -1 -f schema.tables.sql 
-psql agrometinfotmp -1 -f init_data.postgresql.sql 
+psql agrometinfotmp -1 -f schema.types.postgresql.sql
+psql agrometinfotmp -1 -f schema.tables.sql
+psql agrometinfotmp -1 -f schema.indexes.postgresql.sql
+psql agrometinfotmp -1 -f init_data.postgresql.sql
 sleep 1
 dropdb agrometinfotmp
 echo "done"
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java
index 2f84066..58bb65c 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/MapPresenter.java
@@ -100,7 +100,7 @@ public final class MapPresenter implements Presenter {
         if (choice.getComparison()) {
             titleLines.add(CSTS.normalComparison());
         }
-        titleLines.add(indicator.getDescription() + "(" + indicator.getUnit() + ")");
+        titleLines.add(indicator.getDescription() + " (" + indicator.getUnit() + ")");
         titleLines.add(regionName);
         titleLines.add(choice.getYear().toString());
         titleLines.add(lastModification);
@@ -115,9 +115,7 @@ public final class MapPresenter implements Presenter {
         }
         request.addQueryParam("year", String.valueOf(choice.getYear()));
         request.addQueryParam("comparison", String.valueOf(choice.getComparison()));
-        request.onError(throwable -> {
-            App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.END));
-        });
+        request.onError(throwable -> App.getEventBus().fireEvent(LoadingEvent.of(LoadingEventType.END)));
         request.onSuccess(response -> {
             GWT.log("MapPresenter.loadValues() success");
             view.setGeoJson(response.getBodyAsString(), indicator);
-- 
GitLab


From 055659c3c246f83a06c7f4128202a00c6ce44f15 Mon Sep 17 00:00:00 2001
From: Olivier Maury <Olivier.Maury@inrae.fr>
Date: Fri, 19 Apr 2024 09:58:42 +0200
Subject: [PATCH 3/3] Typo

---
 src/site/markdown/installation.md             |  1 +
 .../server/dao/PraDailyValueDaoHibernate.java | 21 +++++++++++++------
 www-server/src/main/tomcat10xconf/context.xml |  2 +-
 3 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/src/site/markdown/installation.md b/src/site/markdown/installation.md
index a2c2667..38b50e4 100644
--- a/src/site/markdown/installation.md
+++ b/src/site/markdown/installation.md
@@ -27,6 +27,7 @@ Otherwise, some example data are provided in initialization data for AgroMetInfo
    export PGOPTIONS="--search_path=agrometinfo"
    psql -h localhost -U agrometinfo -d agrometinfo -1 -f sql/schema.types.postgresql.sql
    psql -h localhost -U agrometinfo -d agrometinfo -1 -f sql/schema.tables.sql
+   psql -h localhost -U agrometinfo -d agrometinfo -1 -f sql/schema.indexes.postgresql.sql
    ```
 5. Add initialization data:
    ```
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java
index 3beef2b..a4ecd4d 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/PraDailyValueDaoHibernate.java
@@ -165,8 +165,11 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
             final LocalDate end) {
         final var jpql = """
                 SELECT
-                         t.date AS date, AVG(t.computedValue) AS value,
-                         AVG(t.q5) AS q5, AVG(t.medianValue) AS median, AVG(t.q95) AS q95
+                         t.date AS date,
+                         AVG(t.computedValue) AS value,
+                         AVG(t.q5) AS q5,
+                         AVG(t.medianValue) AS median,
+                         AVG(t.q95) AS q95
                 FROM PraDailyValue AS t
                 WHERE t.indicator=:indicator AND t.date BETWEEN :start AND :end
                 GROUP BY t.date
@@ -184,8 +187,11 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
             final LocalDate end, final Pra pra) {
         final var jpql = """
                 SELECT
-                         t.date AS date, AVG(t.computedValue) AS value,
-                         AVG(t.q5) AS q5, AVG(t.medianValue) AS median, AVG(t.q95) AS q95
+                         t.date AS date,
+                         AVG(t.computedValue) AS value,
+                         AVG(t.q5) AS q5,
+                         AVG(t.medianValue) AS median,
+                         AVG(t.q95) AS q95
                 FROM PraDailyValue AS t
                 WHERE t.indicator=:indicator AND t.date BETWEEN :start AND :end AND t.pra=:pra
                 GROUP BY t.date
@@ -204,8 +210,11 @@ public class PraDailyValueDaoHibernate extends DaoHibernate<PraDailyValue> imple
             final LocalDate end, final Region region) {
         final var jpql = """
                 SELECT
-                         t.date AS date, AVG(t.computedValue) AS value,
-                         AVG(t.q5) AS q5, AVG(t.medianValue) AS median, AVG(t.q95) AS q95
+                         t.date AS date,
+                         AVG(t.computedValue) AS value,
+                         AVG(t.q5) AS q5,
+                         AVG(t.medianValue) AS median,
+                         AVG(t.q95) AS q95
                 FROM PraDailyValue AS t
                 WHERE t.indicator=:indicator AND t.date BETWEEN :start AND :end AND t.pra.department.region=:region
                 GROUP BY t.date
diff --git a/www-server/src/main/tomcat10xconf/context.xml b/www-server/src/main/tomcat10xconf/context.xml
index 22ad526..29874ec 100644
--- a/www-server/src/main/tomcat10xconf/context.xml
+++ b/www-server/src/main/tomcat10xconf/context.xml
@@ -29,7 +29,7 @@
     testOnBorrow="true"
     timeBetweenEvictionRunsMillis="30000"
     type="javax.sql.DataSource"
-    url="jdbc:postgresql://127.0.0.1:5432/season?schemaName=agrometinfo&amp;ApplicationName=AgroMetInfo"
+    url="jdbc:postgresql://127.0.0.1:5432/agrometinfo?ApplicationName=AgroMetInfo"
     username="agrometinfo"
     password="agrometinfo"
     validationQuery="select 1" />
-- 
GitLab