Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Réimplémentation de la sensibilité #441

Open
wants to merge 30 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c571627
feat(map): Add layerGroups by id_type
Jul 12, 2021
56dac33
feat(map): Zoom, snogylop and features
Jul 12, 2021
16f83aa
feat(map): Updated sql to match db modifications
Jul 13, 2021
15e5bff
style(map): add hover and click effect
Jul 15, 2021
fd8c979
refactor: changed lastObservationsCommuneMaille
Jul 19, 2021
bc3a976
style: improved style function and changed colors
Jul 19, 2021
eb2c73e
style: Add main-color-rgb to liste espece
Jul 20, 2021
246639e
feat: better layering and click management
Jul 20, 2021
c05b02f
style: removed unsused css directive added before
Jul 20, 2021
59146e5
style: forgot to put back rgba for tabEspece.hover
Jul 20, 2021
f847c92
fix: corrected sql query and corrected id_type
Jul 21, 2021
0482100
refactor: add function to gather better city obs
Jul 21, 2021
c0a66e8
feat: worked on communes to make the sensi work
Jul 21, 2021
f9aa473
fix: removed unused import
Jul 21, 2021
1e43d77
chore: remove useless case
Jul 26, 2021
dc551be
fix(sql): returns the date object, not a str
Jul 26, 2021
f85ef5b
fix(sql): corrected bug when no species are found
Jul 26, 2021
ec88d60
feat(sql): Changed vm to be compatible with sensib
Jul 26, 2021
5e91d4f
style(species): Add css variable for species tab
Jul 26, 2021
c486cdc
fix(map): bump leaflet version and rm snogilop
Jul 26, 2021
79218de
style(species): add rgb equivalent primary color
Jul 26, 2021
0a644cf
fix(species): Corrected a city bug in FicheEspece
Jul 29, 2021
1163790
feat(popup): Removes duplicates in popup
Jul 29, 2021
358bd88
feat(sql): observations_maille.sql is more robust
Aug 3, 2021
44e7486
calculate geom on sensitivity level
TheoLechemia Sep 20, 2022
54ae9cc
fix db install + some front repercution
TheoLechemia Sep 21, 2022
1d110c0
set defaut maille in parameters
TheoLechemia Sep 22, 2022
a6bef1c
Fix bug on communes
TheoLechemia Sep 22, 2022
6a48d89
up
TheoLechemia Dec 20, 2022
28d005f
add missing unique index for refresh
TheoLechemia Dec 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions atlas/atlasRoutes.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ def ficheEspece(cd_ref):
altitudes = vmAltitudesRepository.getAltitudesChilds(connection, cd_ref)
months = vmMoisRepository.getMonthlyObservationsChilds(connection, cd_ref)
synonyme = vmTaxrefRepository.getSynonymy(connection, cd_ref)
communes = vmCommunesRepository.getCommunesObservationsChilds(connection, cd_ref)
if current_app.config["AFFICHAGE_MAILLE"]:
communes = vmCommunesRepository.getCommunesObservationsChildsMailles(connection, cd_ref)
else:
communes = vmCommunesRepository.getCommunesObservationsChilds(connection, cd_ref)
taxonomyHierarchy = vmTaxrefRepository.getAllTaxonomy(session, cd_ref)
firstPhoto = vmMedias.getFirstPhoto(
connection, cd_ref, current_app.config["ATTR_MAIN_PHOTO"]
Expand Down Expand Up @@ -191,7 +194,7 @@ def ficheCommune(insee):
session = utils.loadSession()
connection = utils.engine.connect()

listTaxons = vmTaxonsRepository.getTaxonsCommunes(connection, insee)

commune = vmCommunesRepository.getCommuneFromInsee(connection, insee)
if current_app.config["AFFICHAGE_MAILLE"]:
observations = vmObservationsMaillesRepository.lastObservationsCommuneMaille(
Expand All @@ -201,6 +204,8 @@ def ficheCommune(insee):
observations = vmObservationsRepository.lastObservationsCommune(
connection, current_app.config["NB_LAST_OBS"], insee
)

listTaxons = vmTaxonsRepository.getTaxonsCommunes(connection, insee)

observers = vmObservationsRepository.getObserversCommunes(connection, insee)

Expand Down
4 changes: 2 additions & 2 deletions atlas/configuration/settings.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ limit_shp=/home/`whoami`/atlas/data/ref/territoire.shp
# Mon territoire se situe en métropole ? Dans ce cas, on utilise les mailles fournies par l'INPN
metropole=true

# Choisissez alors la taille de vos mailles à utiliser (en km) / Valeurs possibles 1, 5 ou 10
taillemaille=5
# Choisissez alors la taille de vos mailles à utiliser (en km) / Valeurs possibles M1, M5 ou M10
taillemaille=M5

# Si 'metropole=false', rajoutez dans le dossier /data/ref un SHP des mailles de votre territoire et renseignez son chemin
chemin_custom_maille=/home/`whoami`/atlas/data/ref/custom_maille.shp
Expand Down
1 change: 1 addition & 0 deletions atlas/modeles/entities/vmObservations.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class VmObservationsMailles(Base):
metadata,
Column("id_observation", Integer, primary_key=True, unique=True),
Column("id_maille", Integer),
Column("type_code", Integer),
Column("the_geom", Geometry),
Column("geojson_maille", String(1000)),
Column("annee", String(1000)),
Expand Down
19 changes: 19 additions & 0 deletions atlas/modeles/repositories/vmCommunesRepository.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,22 @@ def getCommunesObservationsChilds(connection, cd_ref):
temp = {'insee': r.insee, 'commune_maj': r.commune_maj}
listCommunes.append(temp)
return listCommunes

def getCommunesObservationsChildsMailles(connection, cd_ref):
sql = """
SELECT DISTINCT (com.insee) as insee, com.commune_maj
FROM atlas.vm_communes com
JOIN atlas.vm_observations_mailles obs
ON st_intersects(obs.the_geom, com.the_geom)
WHERE obs.cd_ref in (
SELECT * from atlas.find_all_taxons_childs(:thiscdref)
)
OR obs.cd_ref = :thiscdref
ORDER BY com.commune_maj ASC
"""
req = connection.execute(text(sql), thiscdref=cd_ref)
listCommunes = list()
for r in req:
temp = {'insee': r.insee, 'commune_maj': r.commune_maj}
listCommunes.append(temp)
return listCommunes
35 changes: 17 additions & 18 deletions atlas/modeles/repositories/vmObservationsMaillesRepository.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ def getObservationsMaillesChilds(session, cd_ref, year_min=None, year_max=None):
func.count(VmObservationsMailles.id_observation).label("nb_obs"),
func.max(VmObservationsMailles.annee).label("last_observation"),
VmObservationsMailles.id_maille,
VmObservationsMailles.geojson_maille,
VmObservationsMailles.type_code,
VmObservationsMailles.geojson_maille
)
.group_by(VmObservationsMailles.id_maille, VmObservationsMailles.geojson_maille)
.group_by(VmObservationsMailles.id_maille, VmObservationsMailles.geojson_maille, VmObservationsMailles.type_code)
.filter(
or_(
VmObservationsMailles.cd_ref.in_(subquery),
Expand All @@ -37,6 +38,7 @@ def getObservationsMaillesChilds(session, cd_ref, year_min=None, year_max=None):
geometry=json.loads(o.geojson_maille),
properties={
"id_maille": o.id_maille,
"type_code": o.type_code,
"nb_observations": o.nb_obs,
"last_observation": o.last_observation,
},
Expand Down Expand Up @@ -73,6 +75,7 @@ def lastObservationsMailles(connection, mylimit, idPhoto):
temp = {
"id_observation": o.id_observation,
"id_maille": o.id_maille,
"type_code": o.type_code,
"cd_ref": o.cd_ref,
"dateobs": str(o.dateobs),
"altitude_retenue": o.altitude_retenue,
Expand All @@ -88,24 +91,19 @@ def lastObservationsMailles(connection, mylimit, idPhoto):

def lastObservationsCommuneMaille(connection, mylimit, insee):
sql = """
WITH last_obs AS (
SELECT
obs.cd_ref, obs.dateobs, t.lb_nom,
t.nom_vern, obs.the_geom_point as l_geom
FROM atlas.vm_observations obs
t.nom_vern, obs.the_geom as l_geom,
obs.geojson_maille, obs.id_maille,
obs.type_code
FROM atlas.vm_observations_mailles obs
JOIN atlas.vm_communes c
ON ST_Intersects(obs.the_geom_point, c.the_geom)
ON ST_Intersects(obs.the_geom, c.the_geom)
JOIN atlas.vm_taxons t
ON obs.cd_ref = t.cd_ref
WHERE c.insee = :thisInsee
ORDER BY obs.dateobs DESC
LIMIT :thislimit
)
SELECT l.lb_nom, l.nom_vern, l.cd_ref, m.id_maille, m.geojson_maille
FROM atlas.t_mailles_territoire m
JOIN last_obs l
ON st_intersects(l.l_geom, m.the_geom)
GROUP BY l.lb_nom, l.cd_ref, m.id_maille, l.nom_vern, m.geojson_maille
"""
observations = connection.execute(text(sql), thisInsee=insee, thislimit=mylimit)
obsList = list()
Expand All @@ -116,9 +114,11 @@ def lastObservationsCommuneMaille(connection, mylimit, insee):
taxon = o.lb_nom
temp = {
"cd_ref": o.cd_ref,
"dateobs": o.dateobs,
"taxon": taxon,
"geojson_maille": json.loads(o.geojson_maille),
"id_maille": o.id_maille,
"type_code": o.type_code,
}
obsList.append(temp)
return obsList
Expand All @@ -127,14 +127,12 @@ def lastObservationsCommuneMaille(connection, mylimit, insee):
# Use for API
def getObservationsTaxonCommuneMaille(connection, insee, cd_ref):
sql = """
SELECT
o.cd_ref, t.id_maille, t.geojson_maille,
SELECT
o.cd_ref, o.id_maille, o.type_code, o.geojson_maille, o.the_geom,
extract(YEAR FROM o.dateobs) as annee
FROM atlas.vm_observations o
FROM atlas.vm_observations_mailles o
JOIN atlas.vm_communes c
ON ST_INTERSECTS(o.the_geom_point, c.the_geom)
JOIN atlas.t_mailles_territoire t
ON ST_INTERSECTS(t.the_geom, o.the_geom_point)
ON ST_INTERSECTS(o.the_geom, c.the_geom)
WHERE o.cd_ref = :thiscdref AND c.insee = :thisInsee
ORDER BY id_maille
"""
Expand All @@ -143,6 +141,7 @@ def getObservationsTaxonCommuneMaille(connection, insee, cd_ref):
for o in observations:
temp = {
"id_maille": o.id_maille,
"type_code": o.type_code,
"nb_observations": 1,
"annee": o.annee,
"geojson_maille": json.loads(o.geojson_maille),
Expand Down
2 changes: 1 addition & 1 deletion atlas/modeles/repositories/vmObservationsRepository.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def lastObservationsCommune(connection, mylimit, insee):
temp = dict(o)
temp.pop("the_geom_point", None)
temp["geojson_point"] = json.loads(o.geojson_point or "{}")
temp["dateobs"] = str(o.dateobs)
temp["dateobs"] = o.dateobs
obsList.append(temp)
return obsList

Expand Down
77 changes: 74 additions & 3 deletions atlas/modeles/repositories/vmTaxonsRepository.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,84 @@

# -*- coding:utf-8 -*-

from collections import Counter
from flask import current_app

import unicodedata

from sqlalchemy.sql import text
from .. import utils

#FIXME : cette fonction n'a pas d'utilité
def get_taxons_from_obs(connection, observations: dict):
"""
Gets infos on taxons present in the observations

Args:
observations (dict): observation dictionnary

Returns:
dict: dictionnary of all info on taxons. Contains:
- taxons
- 'nom_complet_html'
- 'nb_obs'
- 'nom_vern'
- 'cd_ref'
- 'last_obs'
- 'group2_inpn'
- 'patrimonial'
- 'protection_stricte'
- 'path'
- 'id_media'
- nbObsTotal
- nb_obs_total
"""
# Init
taxon_commune_list = []
nb_obs_total = 0

# Get the nb of observations for each cd_ref to have the nb_obs
count = Counter(obs['cd_ref'] for obs in observations)
cd_refs = tuple(count.keys())

if not cd_refs:
return {'taxons': taxon_commune_list,
'nbObsTotal': nb_obs_total}

# Get the most recent observation date for each cd_ref.
# TODO: there must be a better way than 2 loops...
max_obs = {cd_ref: max(obs['dateobs'] for obs in observations if obs['cd_ref'] == cd_ref) for cd_ref in cd_refs}

# Request
sql = """
SELECT DISTINCT
t.cd_ref, t.nom_complet_html, t.nom_vern,
t.group2_inpn, t.patrimonial, t.protection_stricte,
m.url, m.chemin, m.id_media
from atlas.vm_taxons t
LEFT JOIN atlas.vm_medias m ON m.cd_ref=t.cd_ref AND m.id_type={}
WHERE t.cd_ref in ({})
GROUP BY t.cd_ref, t.nom_vern, t.nom_complet_html, t.group2_inpn,
t.patrimonial, t.protection_stricte, m.url, m.chemin, m.id_media
ORDER BY t.cd_ref
""".format(current_app.config['ATTR_MAIN_PHOTO'], ','.join(str(k) for k in cd_refs))

req = connection.execute(text(sql))

for r in req:
nb_obs = count[r.cd_ref]
temp = {
'nom_complet_html': r.nom_complet_html,
'nb_obs': nb_obs,
'nom_vern': r.nom_vern,
'cd_ref': r.cd_ref,
'last_obs': max_obs[r.cd_ref].year,
'group2_inpn': utils.deleteAccent(r.group2_inpn),
'patrimonial': r.patrimonial,
'protection_stricte': r.protection_stricte,
'path': utils.findPath(r),
'id_media': r.id_media
}
taxon_commune_list.append(temp)
nb_obs_total = nb_obs_total + nb_obs
return {'taxons': taxon_commune_list, 'nbObsTotal': nb_obs_total}


# With distinct the result in a array not an object, 0: lb_nom, 1: nom_vern
Expand Down
77 changes: 62 additions & 15 deletions data/atlas.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,69 @@ CREATE INDEX ON atlas.vm_taxref (nom_complet);
CREATE INDEX ON atlas.vm_taxref (nom_valide);


CREATE MATERIALIZED VIEW atlas.vm_cor_area_synthese
TABLESPACE pg_default
AS SELECT sa.id_synthese,
sa.id_area,
a.centroid,
st_transform(a.geom, 4326) AS geom,
st_asgeojson(st_transform(a.geom, 4326)) AS geojson_4326,
st_transform(a.centroid, 4326) AS centroid_4326,
t.type_code,
sensi.cd_nomenclature,
CASE
WHEN sensi.cd_nomenclature::text = '1'::text AND t.type_code::text = 'M1'::text THEN true
WHEN sensi.cd_nomenclature::text = '2'::text AND t.type_code::text = 'M5'::text THEN true
WHEN sensi.cd_nomenclature::text = '3'::text AND t.type_code::text = 'M10'::text THEN true
WHEN (sensi.cd_nomenclature::text = '0'::TEXT OR sensi.cd_nomenclature::text IS NULL) AND t.type_code::text = :default_maille::text THEN true
ELSE false
END AS is_blurred_geom
Comment on lines +27 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Une suggestion, en mettant ce code SQL dans une fonction cela permettrait de facilement pouvoir changer la correspondance valeur de sensibilité et maille à utiliser.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je pense que c'est géré ou devrait être géré au niveau de la BDD source (GeoNature dans une majorité des cas), pas au niveau de GeoNature-atlas.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je pense que c'est géré ou devrait être géré au niveau de la BDD source (GeoNature dans une majorité des cas), pas au niveau de GeoNature-atlas.

Oui, idéalement. Mais avec l'utilisation de table via Foreign Data Wrapper, je ne sais pas si on peut utiliser des fonctions de la base de données source.

FROM synthese.synthese s
JOIN synthese.cor_area_synthese sa ON sa.id_synthese = s.id_synthese
JOIN ref_geo.l_areas a ON sa.id_area = a.id_area
JOIN ref_geo.bib_areas_types t ON a.id_type = t.id_type
LEFT JOIN synthese.t_nomenclatures sensi ON s.id_nomenclature_sensitivity = sensi.id_nomenclature
WHERE (t.type_code::text = ANY (ARRAY['M1'::character varying, 'M5'::character varying, 'M10'::character varying]::text[]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ici, aussi, il serait peut être nécessaire d'utiliser une fonction pour pouvoir personnaliser les zones à utiliser.

AND (NOT sensi.cd_nomenclature::text = '4'::TEXT OR sensi.cd_nomenclature IS NULL )
WITH DATA;
CREATE UNIQUE INDEX i_vm_cor_area_synthese ON atlas.vm_cor_area_synthese USING btree (id_synthese, id_area );

--Toutes les observations

--DROP materialized view atlas.vm_observations;
CREATE MATERIALIZED VIEW atlas.vm_observations AS
SELECT s.id_synthese AS id_observation,
s.insee,
s.dateobs,
s.observateurs,
s.altitude_retenue,
s.the_geom_point::geometry('POINT',3857),
s.effectif_total,
tx.cd_ref,
st_asgeojson(ST_Transform(ST_SetSrid(s.the_geom_point, 3857), 4326)) as geojson_point,
diffusion_level
FROM synthese.syntheseff s
LEFT JOIN atlas.vm_taxref tx ON tx.cd_nom = s.cd_nom
JOIN atlas.t_layer_territoire m ON ST_Intersects(m.the_geom, s.the_geom_point);

CREATE MATERIALIZED VIEW atlas.vm_observations AS
WITH centroid AS (
SELECT st_centroid(st_union(cor.geom)) AS geom_point, s.id_synthese
FROM synthese.synthese s
JOIN atlas.vm_cor_area_synthese cor ON cor.id_synthese = s.id_synthese
WHERE cor.id_synthese = s.id_synthese AND cor.is_blurred_geom IS TRUE
GROUP BY s.id_synthese
)
SELECT s.id_synthese AS id_observation,
com.insee ,
s.date_min AS dateobs,
(s.altitude_min + s.altitude_max) / 2 AS altitude_retenue,
s.observers AS observateurs,
tx.cd_ref,
s.id_dataset,
c.geom_point,
CASE
WHEN sensi.cd_nomenclature = '0' THEN st_transform(s.the_geom_point, 3857)
ELSE st_transform(c.geom_point, 3857)
END AS the_geom_point,
CASE
WHEN sensi.cd_nomenclature = '0' THEN st_asgeojson(st_transform(s.the_geom_point, 4326))
ELSE st_asgeojson(st_transform(c.geom_point, 4326))
END AS geojson_point,
sensi.cd_nomenclature AS cd_sensitivity
FROM synthese.synthese s
JOIN atlas.vm_taxref tx ON tx.cd_nom = s.cd_nom
LEFT JOIN synthese.t_nomenclatures sensi ON s.id_nomenclature_sensitivity = sensi.id_nomenclature
JOIN centroid c ON c.id_synthese = s.id_synthese
Copy link
Contributor

@jpm-cbna jpm-cbna Sep 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

La sous-requête "centroid", ne récupérant que les observations où is_blurred_geom vaut true, est ce que l'on ne va pas récupérer ici avec cette jointure que les observations floutées ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'ai compris finalement que les mailles utilisées par défaut lorsqu'on force l'Atlas à afficher uniquement des mailles sont aussi comprises dans is_blurred_geom = true... Donc, normalement, toutes les observations sont prises en compte.

JOIN atlas.l_communes com ON st_intersects(st_transform(s.the_geom_point, 3857), com.the_geom)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afin d'associer les observations à flouter à la commune correspondant au centroïde de la géométrie de l'observations, il me semble que le st_intersect() devrait se faire sur le champ c.geom_point.

;


CREATE UNIQUE INDEX ON atlas.vm_observations (id_observation);
CREATE INDEX ON atlas.vm_observations (cd_ref);
Expand Down Expand Up @@ -269,7 +315,7 @@ c.commune_maj,
c.the_geom,
st_asgeojson(st_transform(c.the_geom, 4326)) as commune_geojson
FROM atlas.l_communes c
JOIN atlas.t_layer_territoire t ON ST_CONTAINS(ST_BUFFER(t.the_geom,200), c.the_geom);
JOIN atlas.t_layer_territoire t ON ST_INTERSECTS(t.the_geom, c.the_geom);

CREATE UNIQUE INDEX ON atlas.vm_communes (insee);
CREATE INDEX index_gist_vm_communes_the_geom ON atlas.vm_communes USING gist (the_geom);
Expand Down Expand Up @@ -454,6 +500,7 @@ CREATE OR REPLACE FUNCTION atlas.refresh_materialized_view_data()
RETURNS VOID AS $$
BEGIN

REFRESH MATERIALIZED VIEW CONCURRENTLY atlas.vm_cor_area_synthese;
REFRESH MATERIALIZED VIEW CONCURRENTLY atlas.vm_observations;
REFRESH MATERIALIZED VIEW CONCURRENTLY atlas.vm_observations_mailles;
REFRESH MATERIALIZED VIEW CONCURRENTLY atlas.vm_mois;
Expand Down
Loading