diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0f5c2a..98136ca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,32 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +----- +## [v1.4.0] - 2023-12-08 + +### Add + +- Transect management (import, selection in environment table) +- Action to open csv data folder + + +### Modified + +- `End` environment entities are only created for export +- Merge processes in background +- Keep only one side gps entities during Merge process +- Fixes `speed` and `course` attributes in gps layer +- Add some fields in species table +- Fixes relation between boat and survey table +- Fixes `state` field into `StrateType` +- Add field `session` and `routeType` in sighting and follower export +- Merge behaviour fields into one behaviourSpecies field +- Prevent crash on `stretchLastSection` option + ----- ## [v1.3.2] - 2023-05-09 -## Modified +### Modified - EffortGroup became a complex id and it is copied into Sighting/Followers entities during export - EffortLeg added and copied in Sighting entities export @@ -22,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.3.1] - 2023-01-04 -## Modified +### Modified - Remove gps shortcut - Fix typographic error in documentation @@ -46,7 +68,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Unvalidated filter to help validation - Boat layer -## Modified +### Modified - GPS can be enable/disable (button + shortcut). Disable GPS also finishes effort - Merge : dateCheckbox checked by default @@ -63,7 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.2.3] - 2022-07-06 -## Modified +### Modified - Plateform (environment table) is not a read-only column anymore. @@ -206,6 +228,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +[v1.4.0]: https://github.com/hytechimaging/sammo-boat/releases/tag/v1.4.0 [v1.3.2]: https://github.com/hytechimaging/sammo-boat/releases/tag/v1.3.2 [v1.3.1]: https://github.com/hytechimaging/sammo-boat/releases/tag/v1.3.1 [v1.3.0]: https://github.com/hytechimaging/sammo-boat/releases/tag/v1.3.0 diff --git a/Makefile b/Makefile index d86dd71d..4a3545fa 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := "1.3.2" +VERSION := "1.4.0" TMPDIR := "/tmp/sammo-boat-$(VERSION)" venv: diff --git a/README.md b/README.md index 3b97f1c2..278375c5 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ In particular, this tool is used by the [Megascope](https://www.observatoire-pel SAMMO-Boat is a QGIS extension that redesigns interface and handles GPS and microphone support. The GPS trace, the effort data and observations are directly seen on the map. Observers focus on animal detection and can quickly fulfill this detection by relying on audio record. This record can be used afterwards to add the missing information and to validate observations according defined standards. -![](https://github.com/hytechimaging/sammo-boat/blob/main/images/interface.png?raw=true) \ No newline at end of file +![](https://github.com/hytechimaging/sammo-boat/blob/main/doc/source/images/interface.png?raw=true) diff --git a/data/behav.csv b/data/behav.csv new file mode 100644 index 00000000..6f70b0b6 --- /dev/null +++ b/data/behav.csv @@ -0,0 +1,17 @@ +behav,behav_cat +attacked,behavBird +with_prey,behavBird +klepto,behavBird +diving,behavBird +follow_boat,behavBird +random_flight,behavBird +circular_flight,behavBird +straight_flight,behavBird +bow,behavMam +milling,behavMam +fast_swimming,behavMam +slow_swimming,behavMam +diving,behavMam +breaching,behavMam +fishing,behavShip +route,behavShip diff --git a/data/boat.csv b/data/boat.csv index 4549cb29..f5ba9dbc 100644 --- a/data/boat.csv +++ b/data/boat.csv @@ -1,4 +1,5 @@ name Thalassa Europe -PourquoiPas \ No newline at end of file +PourquoiPas +MiguelOliver diff --git a/data/observers.csv b/data/observers.csv new file mode 100644 index 00000000..620ed9de --- /dev/null +++ b/data/observers.csv @@ -0,0 +1 @@ +observer,firstName,lastName,organisation,contact diff --git a/data/plateform.csv b/data/plateform.csv index 78d4f5f3..4af6e36d 100644 --- a/data/plateform.csv +++ b/data/plateform.csv @@ -1,9 +1,11 @@ ship,plateform,plateformHeight Thalassa,deck,8 -Thalassa,bridge,14 -Thalassa,upper_bridge,16.5 +Thalassa,bridge_inside,14 +Thalassa,upper_bridge_outside,16.5 Europe,deck,4 -Europe,bridge,4 +Europe,bridge_inside,4 PourquoiPas,deck,16.3 -PourquoiPas,bridge,18.6 -PourquoiPas,upper_bridge,21.6 +PourquoiPas,bridge_inside,18.6 +PourquoiPas,upper_bridge_outside,21.6 +MiguelOliver,bridge_inside,8 +MiguelOliver,bridge_outside,8 diff --git a/data/species.csv b/data/species.csv new file mode 100644 index 00000000..0cb6d2db --- /dev/null +++ b/data/species.csv @@ -0,0 +1,465 @@ +species,behav_cat,taxon_fr,family_fr,group_fr,name_fr,name_latin,taxon_eng,family_eng,group_eng,name_eng,name_spa +ADMIBO,behavShip,Activite humaine,Navire,Etat,"Bateau de l'Etat (douanes, marine)","Administrative boat (navy, custom, coast guard)",Human activity,Ship,Administrative vessel,"Administrative boat (navy, custom, coast guard)","Barco militar, guardacostas, adouana" +AQUABO,behavShip,Activite humaine,Navire,Aquaculture,Bateau aquaculture,Aquaculture vessel,Human activity,Ship,Aquaculture,Aquaculture vessel, +BOAT,behavShip,Activite humaine,Navire,Bateau non identifie,Bateau non identifie,Non identified ship,Human activity,Ship,Ship unidentif.,Non identified ship,Barco spp +BULKBO,behavShip,Activite humaine,Navire,Commerce,Bateau vraquier,Bulk cargo,Human activity,Ship,Transport,Bulk cargo,Barco vracier +BUOY,,Activite humaine,Bouee,Peche,Bouee de peche,"Fishing buoy, setnet",Human activity,Buoy,Fishing activity,"Fishing buoy, setnet",Boya pesca +CANOBO,behavShip,Activite humaine,Navire,Peche,Canot de peche artisanale,Small traditional fishing boat,Human activity,Ship,Fishing activity,Small traditional fishing boat, +CARGOB,behavShip,Activite humaine,Navire,Commerce,Bateau transport marchandise,"Merchant ship (containership, cargo, tanker)",Human activity,Ship,Transport,"Merchant ship (containership, cargo, tanker)",Barco comercial +CONTBO,behavShip,Activite humaine,Navire,Commerce,Bateau porte-container,Containership,Human activity,Ship,Transport,Containership,Barco porta contenedores +CRUIBO,behavShip,Activite humaine,Navire,Plaisance,Bateau de croisiere,Cruise ship,Human activity,Ship,Recreational boating,Cruise ship, +FAD,,Activite humaine,Bouee,Peche,Dispositif de Concentration de Poisson (DCP),Fishing Aggregating Device,Human activity,Buoy,Fishing activity,Fishing Aggregating Device, +FERRYB,behavShip,Activite humaine,Navire,Commerce,Ferry,Ferry,Human activity,Ship,Transport,Ferry,Ferry +FISHBO,behavShip,Activite humaine,Navire,Peche,Bateau de peche pro,Fishing boat (professional),Human activity,Ship,Fishing activity,Fishing boat (professional),Barco pesca +FISHFA,,Activite humaine,Autre activite,Service,Ferme aquacole,Fishing farm,Human activity,Other Activity,Service,Fishing farm, +FISHTR,,Activite humaine,Dechet,Dechet de peche,Dechet de peche,"Fishing trash (net part, buoy)",Human activity,Trash,Fish trash,"Fishing trash (net part, buoy)",Basura pesca +IRONTR,,Activite humaine,Dechet,Dechet autre,Dechet metal,Iron or Metal Trash,Human activity,Trash,Other trash,Iron or Metal trash, +LONGBO,behavShip,Activite humaine,Navire,Peche,Bateau palangrier,Longliner,Human activity,Ship,Fishing activity,Longliner,Barco palangrero +MINIBO,behavShip,Activite humaine,Navire,Commerce,Bateau minier,Mining boat,Human activity,Ship,Transport,Mining boat, +MOTOBO,behavShip,Activite humaine,Navire,Plaisance,Bateau a petit moteur,Small motor boat,Human activity,Ship,Recreational boating,Small motor boat,Barco petitmotor +NETBO,behavShip,Activite humaine,Navire,Peche,Bateau fileyeur,Gill-netter,Human activity,Ship,Fishing activity,Gill-netter,Barco enmalle +OIL,,Activite humaine,Dechet,Dechet autre,Dechet hydrocarbure (nappe),Oil slick,Human activity,Trash,Other trash,Oil slick,Contaminacion hidrocarburo +PASSBO,behavShip,Activite humaine,Navire,Peche,"Bateau art dormant (fileyeur, caseyeur)",Boat using for Passive fishing gear,Human activity,Ship,Fishing activity,Boat using for Passive fishing gear, +PATRAB,behavShip,Activite humaine,Navire,Peche,Bateau chalutboeuf,Pair trawler,Human activity,Ship,Fishing activity,Pair trawler,Barco baca arrastre +PIROG,behavShip,Activite humaine,Navire,Peche,Pirogue de peche,Pirogue,Human activity,Ship,Fishing activity,Pirogue, +PIRSPO,behavShip,Activite humaine,Navire,Plaisance,Pirogue sportive,Sport pirogue,Human activity,Ship,Recreational boating,Sport pirogue, +PLANE,behavShip,Activite humaine,Autre activite,Avion,Avion,Plane,Human activity,Other Activity,Plane,Plane,Avion +PLASTR,,Activite humaine,Dechet,Dechet autre,Dechet plastique,Plastic trash,Human activity,Trash,Other trash,Plastic trash,Basura plastico +PLATFO,,Activite humaine,Autre activite,Plateforme,Plateforme,Platform,Human activity,Other Activity,Platform,Platform,Plataforma +PLEABO,behavShip,Activite humaine,Navire,Plaisance,Bateau de plaisance ind.,Pleasure boat,Human activity,Ship,Recreational boating,Pleasure boat,Barco recreo +POLYTR,,Activite humaine,Dechet,Dechet autre,Dechet polystyrene,Dechet polystyrene,Human activity,Trash,Other trash,Polystyrene trash, +POTBO,behavShip,Activite humaine,Navire,Peche,Bateau caseyeur,Pot vessel,Human activity,Ship,Fishing activity,Pot vessel,Barco nasero +RESCUB,behavShip,Activite humaine,Navire,Etat,Bateau de secours,Search And Rescue vessel,Human activity,Ship,Administrative vessel,Search And Rescue vessel,Barco de Rescate +RESEBO,behavShip,Activite humaine,Navire,Service,Navire scientifique,Research vessel (science),Human activity,Ship,Service,Research vessel (science),Navio oceanografico +RUBBTR,,Activite humaine,Dechet,Dechet autre,Dechet caoutchouc,Rubber trash,Human activity,Trash,Other trash,Rubber trash,Basura goma +SAILBO,behavShip,Activite humaine,Navire,Plaisance,Bateau de plaisance a voile,Sailing boat,Human activity,Ship,Recreational boating,Sailing boat,Barco vela +SEINBO,behavShip,Activite humaine,Navire,Peche,"Bateau senneur, bolincheur",Seiner,Human activity,Ship,Fishing activity,Seiner,"Barco boliche, cerqueros" +SERVBO,behavShip,Activite humaine,Navire,Service,Bateau de maintenance,Service boat,Human activity,Ship,Service,service boat for wind farm, +SKBO,behavShip,Activite humaine,Navire,Peche,Bateau fileyeur SK,Gill-netter SK boat,Human activity,Ship,Fishing activity,Gill-netter SK boat, +SMALTR,,Activite humaine,Dechet,Dechet autre,Micro dechet,"Trash (plastic, wood, oil)",Human activity,Trash,Other trash,Macro litter,micro residuos +TANKER,behavShip,Activite humaine,Navire,Commerce,"Bateau petrolier, chimiquier, gazier","Tanker (oil, gaz, chemical)",Human activity,Ship,Transport,"Tanker (oil, gaz, chemical)","Barco petrolero, gas, sustancia quimica" +TAPOBO,behavShip,Activite humaine,Navire,Peche,Bateau fileyeur tapouille,Guianese Gill-netter,Human activity,Ship,Fishing activity,Guianese Gill-netter, +TRASH,,Activite humaine,Dechet,Dechet autre,"Dechet (bois, plastic,..)","Trash (plastic, wood,..)",Human activity,Trash,Other trash,"Trash (plastic, wood,..)",Macro residuos +TRAWLB,behavShip,Activite humaine,Navire,Peche,Bateau chalutier,Trawler,Human activity,Ship,Fishing activity,Trawler,Barco arrastrero +WOODTR,,Activite humaine,Dechet,Dechet autre,Dechet bois (non naturel),Unnatural wood,Human activity,Trash,Other trash,Unnatural wood,Basura madera +RUBPLA,,Activite humaine,Dechet,Dechet autre,Dechet caoutchouc,Rubber trash,Human activity,Trash,Other trash,Rubber trash,Basura goma +METATR,,Activite humaine,Dechet,Dechet autre,Dechet metal,Metal trash,Human activity,Trash,Other trash,Metal trash,Basura de metal +BAGPLA,,Activite humaine,Dechet,Dechet autre,Dechet sac platique,Plastic bag,Human activity,Trash,Other trash,Plastic bag,Bolsas de plastico +BOTPLA,,Activite humaine,Dechet,Dechet autre,Dechet bouteille plastique,Bottle plastic,Human activity,Trash,Other trash,Bottle plastic,Botellas de plastico +CLOPLA,,Activite humaine,Dechet,Dechet autre,Dechet vetement,Clothes with plastic,Human activity,Trash,Other trash,Clothes with plastic,Ropa con plastico +BOXPLA,,Activite humaine,Dechet,Dechet autre,Dechet caisse plastique,Plastic box,Human activity,Trash,Other trash,Plastic box,Cajas en plastico +SMAPLA,,Activite humaine,Dechet,Dechet autre,Dechet residu plastique,Small plastic,Human activity,Trash,Other trash,Small plastic,Pequeno trozos de plastico +BIGPLA,,Activite humaine,Dechet,Dechet autre,Dechet bache plastique,Large plastic unity,Human activity,Trash,Other trash,Large plastic unity,Grande trozos de plastico +SMAPOL,,Activite humaine,Dechet,Dechet autre,Dechet morceau polystyrene,Small foamed polystyrene,Human activity,Trash,Other trash,Small foamed polystyrene,Pequeno trozos de corcho +BIGPOL,,Activite humaine,Dechet,Dechet autre,Dechet grand polystyrene,Big foamed polystyrene,Human activity,Trash,Other trash,Big foamed polystyrene,Grande trozos de corcho +PALWOO,,Activite humaine,Dechet,Dechet autre,Dechet Palette,Wodden pallet,Human activity,Trash,Other trash,Wodden pallet,Palao +BOXWOO,,Activite humaine,Dechet,Dechet autre,Dechet cagette,Wooden crates for packaging,Human activity,Trash,Other trash,Wooden crates for packaging,Caja en madera +TABWOO,,Activite humaine,Dechet,Dechet autre,Dechet bois usine,Processed wooden items,Human activity,Trash,Other trash,Processed wooden items,Madera procesada +CANMET,,Activite humaine,Dechet,Dechet autre,Dechet boite de conserve ou canette,Metal drinks of food cans,Human activity,Trash,Other trash,Metal drinks of food cans,Lata de bebida o comida +DRUMET,,Activite humaine,Dechet,Dechet autre,Dechet bidon metal,Metal drums or barrels,Human activity,Trash,Other trash,Metal drums or barrels,Bidones de metal +NETFIS,,Activite humaine,Dechet,Dechet de peche,Dechet filet de peche,Fishing net trash,Human activity,Trash,Fish trash,Fishing net trash,Redes de pesca +ROPFIS,,Activite humaine,Dechet,Dechet de peche,Dechet bout et corde de peche,Fishing rope,Human activity,Trash,Fish trash,Fishing rope,Cabos +BOXFIS,,Activite humaine,Dechet,Dechet de peche,Dechet caisse de peche solide,Fish boxes rigid,Human activity,Trash,Fish trash,Fish boxes rigid,Cajas de pescado rigida +POLFIS,,Activite humaine,Dechet,Dechet de peche,Dechet caisse de peche polystyrene,Fish boxes polystyrene,Human activity,Trash,Fish trash,Fish boxes polystyrene,Cajas de pescado de corcho +LINFIS,,Activite humaine,Dechet,Dechet de peche,Dechet fil de peche,Plastic fishing line,Human activity,Trash,Fish trash,Plastic fishing line,Sedal de pesca +BUOFIS,,Activite humaine,Dechet,Dechet de peche,Dechet bouee,Plastic float or buoy,Human activity,Trash,Fish trash,Plastic float or buoy,Boya de pesca +FRONT,,Autre,Other,Parametres hydrologiques,Front de maree,Tidal front,Other,Other,Hydrologic parameters,Tidal front,Frente marea +NONE,,Autre,Other,Other,Pas de suiveurs,No scavengers,Other,Other,Other,No scavengers, +OTHER,,Autre,Other,Other,Autre observation,Other sighting,Other,Other,Other,Other sighting, +PLANCT,,Autre,Plancton,Plancton,Plancton,Plankton sp.,Other,Plankton,Plankton,Plankton, +SLICKS,,Autre,Other,Parametres hydrologiques,Nappe de convergence,Slick fields,Other,Other,Hydrologic parameters,Slick fields,Capa de convergencia +AETNAR,behavMam,Autre faune marine,Elasmobranche,Raie,Raie leopard,Aetobatus narinari,Other Marine Wildlife,Elasmobranch,Ray,Spotted Eagle Ray, +ALOVUL,behavMam,Autre faune marine,Elasmobranche,Requin,Requin renard commun,Alopias vulpinus,Other Marine Wildlife,Elasmobranch,Shark,Fox shark,Pez zorro +BALCAR,behavMam,Autre faune marine,Poisson,Petit poisson,Baliste ind.,Balistes sp.,Other Marine Wildlife,Fish,Small fish,Gray triggerfish sp.,Pez ballesta +CARCAR,behavMam,Autre faune marine,Elasmobranche,Requin,Grand requin blanc,Carcharodon carcharias,Other Marine Wildlife,Elasmobranch,Shark,Great white shark, +CARLON,behavMam,Autre faune marine,Elasmobranche,Requin,Requin oceanique,Carcharhinus longimanus,Other Marine Wildlife,Elasmobranch,Shark,Oceanic Whitetip Shark, +CETMAX,behavMam,Autre faune marine,Elasmobranche,Requin,Requin pelerin,Cetorhinus maximus,Other Marine Wildlife,Elasmobranch,Shark,Basking shark,Tiburon peregrino +CHESPP,behavMam,Autre faune marine,Tortue,Chelonide,Tortue a ecailles ind.,Cheloniidae sp.,Other Marine Wildlife,Turtle,Cheloniid,Hard-shelled seaturtle sp.,Quelonia +CARETT,behavMam,Autre faune marine,Tortue,Chelonide,Tortue caouanne,Caretta caretta,Other Marine Wildlife,Turtle,Cheloniid,Loggerhead turtle,Tortuga boba +CHISPP,behavMam,Autre faune marine,Other fauna,Chiroptera,Chauve-souris ind.,Chiroptera sp.,Other Marine Wildlife,Bat,Chiroptera,Bat sp.,muercielago +CORHIP,behavMam,Autre faune marine,Poisson,Grand poisson,Daurade coryphene,Coryphaena hippurus,Other Marine Wildlife,Fish,Large fish,Common dolphinfish, +DASSPP,behavMam,Autre faune marine,Elasmobranche,Raie,Raie pastenague ind.,Dasyatis sp.,Other Marine Wildlife,Elasmobranch,Ray,Stingray sp., +DERCOR,behavMam,Autre faune marine,Tortue,Dermochelyide,Tortue luth,Dermochelys coriacea,Other Marine Wildlife,Turtle,Dermochelyid,Leatherback turtle,Tortuga Laud +EXOCET,behavMam,Autre faune marine,Poisson,Petit poisson,Exocet ind.,Exocetus sp.,Other Marine Wildlife,Fish,Small fish,Flying fish sp.,Exoceti spp +FISH,behavMam,Autre faune marine,Poisson,Grand poisson,Poisson ind.,Osteichyens sp.,Other Marine Wildlife,Fish,Large fish,Fish sp., +GALCUV,behavMam,Autre faune marine,Elasmobranche,Requin,Requin tigre,Galeocerdo cuvier,Other Marine Wildlife,Elasmobranch,Shark,Tiger shark, +JELLY,behavMam,Autre faune marine,Other fauna,Cnidaria,Meduse ind.,Cnidaria sp.,Other Marine Wildlife,Cnidaria,Cnidaria,Jelly fish sp., +LAMNAS,behavMam,Autre faune marine,Elasmobranche,Requin,Requin-taupe commun,Lamna nasus,Other Marine Wildlife,Elasmobranch,Shark,Porbeagle, +LARFIS,behavMam,Autre faune marine,Poisson,Grand poisson,Grand poisson ind.,Osteichyens sp.,Other Marine Wildlife,Fish,Large fish,Large Fish sp.,Pez spp +MANBIR,behavMam,Autre faune marine,Elasmobranche,Raie,Raie manta,Manta birostris,Other Marine Wildlife,Elasmobranch,Ray,Manta ray, +MOBMOB,behavMam,Autre faune marine,Elasmobranche,Raie,Raie diable de mer,Mobula mobular,Other Marine Wildlife,Elasmobranch,Ray,Giant devil ray, +MOBSPP,behavMam,Autre faune marine,Elasmobranche,Raie,Diable de mer ind.,Mobula sp.,Other Marine Wildlife,Elasmobranch,Ray,Devil ray sp., +MOLMOL,behavMam,Autre faune marine,Poisson,Grand poisson,Poisson lune,Mola mola,Other Marine Wildlife,Fish,Large fish,Sunfish,Pez luna +ODOFER,behavMam,Autre faune marine,Elasmobranche,Requin,Requin feroce,Odontaspis ferox,Other Marine Wildlife,Elasmobranch,Shark,Smalltooth sand tiger,Tiburon feroce +PRIGLA,behavMam,Autre faune marine,Elasmobranche,Requin,Requin peau-bleue,Prionace glauca,Other Marine Wildlife,Elasmobranch,Shark,Blue shark,Tiburon azul +RAYSPP,behavMam,Autre faune marine,Elasmobranche,Raie,Raie ind.,Rajimorphii sp.,Other Marine Wildlife,Elasmobranch,Ray,Ray sp., +RHITYP,behavMam,Autre faune marine,Elasmobranche,Requin,Requin baleine,Rhincodon typus,Other Marine Wildlife,Elasmobranch,Shark,Whale shark, +SHARK,behavMam,Autre faune marine,Elasmobranche,Requin,Requin ind.,Selachimorpha sp.,Other Marine Wildlife,Elasmobranch,Shark,Shark sp.,Tiburon spp +SMAFIS,behavMam,Autre faune marine,Poisson,Petit poisson,Petit poisson ind.,Osteichyens sp.,Other Marine Wildlife,Fish,Small fish,Small Fish sp.,Pez spp +SPHSPP,behavMam,Autre faune marine,Elasmobranche,Requin,Requin marteau ind.,Sphyrna sp.,Other Marine Wildlife,Elasmobranch,Shark,Hammerhead shark sp., +THUALA,behavMam,Autre faune marine,Poisson,Grand poisson,Thon blanc Germon,Thunnus alalunga,Other Marine Wildlife,Fish,Large fish,Albacore tuna,Atun blanco +THUSPP,behavMam,Autre faune marine,Poisson,Grand poisson,Thon / Bonite ind.,Thunnus / Sarda sp.,Other Marine Wildlife,Fish,Large fish,Tuna / Bonito sp.,Tunido spp +THUTHY,behavMam,Autre faune marine,Poisson,Grand poisson,Thon rouge atlantique,Thunnus thynnus,Other Marine Wildlife,Fish,Large fish,Atlantic bluefin tuna,Atun aleta azul +TURTLE,behavMam,Autre faune marine,Tortue,Tortue ind.,Tortue ind.,Cheloniidae / Dermochelyidae sp.,Other Marine Wildlife,Turtle,Turtle sp.,Turtle sp., +XIPGLA,behavMam,Autre faune marine,Poisson,Grand poisson,Espadon / Marlin / Voilier ind.,Xiphiidae / Istiophoridae sp.,Other Marine Wildlife,Fish,Large fish,"Swordfish, sailfish, marlin sp.", +BALACU,behavMam,Mammifere marin,Balaenopteridae,Petit baleinopteride,Petit rorqual,Balaenoptera acutorostrata,Marine mammal,Baleinopteridae,Small Baleinopteriid,Minke whale,Rorcual aliblanco +BALBON,behavMam,Mammifere marin,Balaenopteridae,Petit baleinopteride,Petit rorqual antarctique,Balaenoptera bonaerensis,Marine mammal,Baleinopteridae,Small Baleinopteriid,Antarctic minke whale, +BALBOR,behavMam,Mammifere marin,Balaenopteridae,Grand baleinopteride,Rorqual boreal,Balaenoptera borealis,Marine mammal,Baleinopteridae,Large Baleinopteriid,Sei whale, +BALEDE,behavMam,Mammifere marin,Balaenopteridae,Grand baleinopteride,Rorqual de Bryde,Balaenoptera edeni,Marine mammal,Baleinopteridae,Large Baleinopteriid,Bryde's whale, +BALMUS,behavMam,Mammifere marin,Balaenopteridae,Grand baleinopteride,Rorqual bleu,Balaenoptera musculus,Marine mammal,Baleinopteridae,Large Baleinopteriid,Blue whale, +BALOMU,behavMam,Mammifere marin,Balaenopteridae,Petit baleinopteride,Rorqual d'Omura,Balaenoptera omurai,Marine mammal,Baleinopteridae,Small Baleinopteriid,Omura's whale, +BALPHY,behavMam,Mammifere marin,Balaenopteridae,Grand baleinopteride,Rorqual commun,Balaenoptera physalus,Marine mammal,Baleinopteridae,Large Baleinopteriid,Fin whale,Rorcual comun +BALSPP,behavMam,Mammifere marin,Balaenopteridae,Grand baleinopteride,Rorqual ind.,Balaenopteridae sp.,Marine mammal,Baleinopteridae,Large Baleinopteriid,Balaenopterid sp.,Rorcual spp +BERARN,behavMam,Mammifere marin,Ziphiidae,Autre baleine a bec,Berardie d'Arnoux,Berardius arnuxii,Marine mammal,Ziphiidae,Other beaked whale,Arnoux's Beaked Whale, +CETSPP,behavMam,Mammifere marin,Cetacea,Cetace ind.,Cetace ind.,Cetacea sp.,Marine mammal,Cetacea,Cetacean unidentif.,Cetacea sp.,Cetaceo spp +DELDEL,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin commun,Delphinus delphis,Marine mammal,Delphininae,Small Delphininae,Common dolphin,Delfin comun +DELSPP,behavMam,Mammifere marin,Delphinidae,Delphinide ind.,Delphinide ind.,Delphinidae sp.,Marine mammal,Delphinidae,Delphiniid,Delphinid sp.,Delfinido spp +DUGDUG,behavMam,Mammifere marin,Sirenien,Dugongidae,Dugong,Dugong dugon,Marine mammal,Sirenian,Dugongiid,Dugong, +EUBAUS,behavMam,Mammifere marin,Balaenidae,Balaenidae,Baleine franche australe,Eubalaena australis,Marine mammal,Balaenidae ,Balaeniid,Southern right whale, +FERATT,behavMam,Mammifere marin,Globicephalinae,Petit globicephaline,Orque pygmee,Feresa attenuata,Marine mammal,Globicephalinae,Small globicephalinae,Pygmy killer whale, +GLOMAC,behavMam,Mammifere marin,Globicephalinae,Grand globicephaline,Globicephale tropical,Globicephala macrorhynchus,Marine mammal,Globicephalinae,Large globicephalinae,Short-finned pilot whale, +GLOMEL,behavMam,Mammifere marin,Globicephalinae,Grand globicephaline,Globicephale noir,Globicephala melas,Marine mammal,Globicephalinae,Large globicephalinae,Long-finned pilot whale,Calderon comun +GLOPSE,behavMam,Mammifere marin,Globicephalinae,Grand globicephaline,Globicephale / Pseudorque,Globicephala / Pseudorca sp.,Marine mammal,Globicephalinae,Large globicephalinae,pilot whale / False killer whale, +GLOSPP,behavMam,Mammifere marin,Globicephalinae,Grand globicephaline,Globicephale noir / tropical,Globicephala sp.,Marine mammal,Globicephalinae,Large globicephalinae,Short / Long finned pilot whale, +GRAGRI,behavMam,Mammifere marin,Globicephalinae,Petit globicephaline,Dauphin de Risso,Grampus griseus,Marine mammal,Globicephalinae,Small globicephalinae,Risso's dolphin,Delfin risso +HALGRY,behavMam,Mammifere marin,Phocidae,Phoque,Phoque gris,Halichoerus grypus,Marine mammal,Phocidae,Seal,Grey seal (Atlantic seal), +HYPAMP,behavMam,Mammifere marin,Ziphiidae,Autre baleine a bec,Hyperoodon boreal,Hyperoodon ampullatus,Marine mammal,Ziphiidae,Other beaked whale,Northern bottlenose whale, +HYPPLA,behavMam,Mammifere marin,Ziphiidae,Autre baleine a bec,Hyperoodon austral,Hyperoodon planifrons,Marine mammal,Ziphiidae,Other beaked whale,Southern Bottlenose Whale, +INDPAC,behavMam,Mammifere marin,Ziphiidae,Autre baleine a bec,Indopacete de Longman,Indopacetus pacificus,Marine mammal,Ziphiidae,Other beaked whale,Longman's beaked whale, +KOGBRE,behavMam,Mammifere marin,Cachalot,Kogiidae,Cachalot pygmee,Kogia breviceps,Marine mammal,Cachalot,Kogiid,Pygmy sperm whale, +KOGSIM,behavMam,Mammifere marin,Cachalot,Kogiidae,Cachalot nain,Kogia sima,Marine mammal,Cachalot,Kogiid,Dwarf sperm whale, +KOGSPP,behavMam,Mammifere marin,Cachalot,Kogiide,Cachalot pygmee / nain,Kogia sp.,Marine mammal,Cachalot,Kogiid,Pygmy / Dwarf sperm whale, +LAGACU,behavMam,Mammifere marin,Delphininae,Grand delphinine,Lagenorhynque a flancs blancs,Lagenorhynchus acutus,Marine mammal,Delphininae,Large delphininae,Atlantic white-sided dolphin, +LAGALB,behavMam,Mammifere marin,Delphininae,Grand delphinine,Lagenorhynque a bec blanc,Lagenorhynchus albirostris,Marine mammal,Delphininae,Large delphininae,White-beaked dolphin, +LAGCRU,behavMam,Mammifere marin,Delphininae,Grand delphinine,Lagenorhynque sablier,Lagenorhynchus cruciger,Marine mammal,Delphininae,Large delphininae,Hourglass dolphin, +LAGHOS,behavMam,Mammifere marin,Delphininae,Grand delphinine,Dauphin de Fraser,Lagenodelphis hosei,Marine mammal,Delphininae,Large delphininae,Fraser's dolphin, +LAGOBS,behavMam,Mammifere marin,Delphininae,Grand delphinine,Lagenorhynque obscur,Lagenorhynchus obscurus,Marine mammal,Delphininae,Large delphininae,Dusky dolphin, +LARCET,behavMam,Mammifere marin,Cetacea,Cetace ind.,Grand cetace ind.,Large Cetacea sp.,Marine mammal,Cetacea,Cetacean unidentif,Large Cetacea sp.,Rorcual / Cachalote / Ballena +LARDEL,behavMam,Mammifere marin,Delphininae,Grand delphinine,Grand delphinine,Large delphininae sp.,Marine mammal,Delphininae,Large delphininae,Large delphininae sp.,Grande Delfin spp +LISPER,behavMam,Mammifere marin,Delphininae,Grand delphinine,Dauphin de Peron,Lissodelphis peronii,Marine mammal,Delphininae,Large delphininae,Southern right whale doplhin, +MEDCET,behavMam,Mammifere marin,Cetacea,Cetace ind.,Moyen cetace ind.,Medium Cetacea sp.,Marine mammal,Cetacea,Cetacean unidentif.,Medium Cetacea sp.,Ballena de pico / R. aliblanco / Calderon / Orca +MEGNOV,behavMam,Mammifere marin,Balaenopteridae,Grand baleinopteride,Baleine a bosse,Megaptera novaeangliae,Marine mammal,Baleinopteridae,Large Baleinopteriid,Humpback Whale,Ballena jorobada +MESBID,behavMam,Mammifere marin,Ziphiidae,Mesoplodon,Mesoplodon de Sowerby,Mesoplodon bidens,Marine mammal,Ziphiidae,Mesoplodon,Sowerby's beaked whale,Ballena de pico de Sowerby +MESDEN,behavMam,Mammifere marin,Ziphiidae,Mesoplodon,Mesoplodon de Blainville,Mesoplodon densirostris,Marine mammal,Ziphiidae,Mesoplodon,Blainville's beaked whale,Ballena de pico de Blainville +MESEUR,behavMam,Mammifere marin,Ziphiidae,Mesoplodon,Mesoplodon de Gervais,Mesoplodon europaeus,Marine mammal,Ziphiidae,Mesoplodon,Gervais' beaked whale, +MESGIN,behavMam,Mammifere marin,Ziphiidae,Mesoplodon,Mesoplodon japonais,Mesoplodon ginkgodens,Marine mammal,Ziphiidae,Mesoplodon,Ginkgo-toothed beaked whale, +MESGRA,behavMam,Mammifere marin,Ziphiidae,Mesoplodon,Mesoplodon de Gray,Mesoplodon grayi,Marine mammal,Ziphiidae,Mesoplodon,Gray's beaked whale, +MESMIR,behavMam,Mammifere marin,Ziphiidae,Mesoplodon,Mesoplodon de True,Mesoplodon mirus,Marine mammal,Ziphiidae,Mesoplodon,True's beaked whale, +MESSPP,behavMam,Mammifere marin,Ziphiidae,Mesoplodon,Mesoplodon ind.,Mesoplodon sp.,Marine mammal,Ziphiidae,Mesoplodon,Mesoplodont whales sp.,Ballena de pico spp +MONMON,behavMam,Mammifere marin,Phocidae,Phoque,Phoque moine,Monachus monachus,Marine mammal,Phocidae,Seal,Mediterranean Monk Seal, +ORCORC,behavMam,Mammifere marin,Globicephalinae,Grand globicephaline,Orque,Orcinus orca,Marine mammal,Globicephalinae,Large globicephalinae,Killer whale,Orca +PEPELE,behavMam,Mammifere marin,Globicephalinae,Petit globicephaline,Peponocephale,Peponocephala electra,Marine mammal,Globicephalinae,Small globicephalinae,Melon-headed whale, +PEPFER,behavMam,Mammifere marin,Globicephalinae,Petit globicephaline,Peponocephale / orque pygmee,Peponocephala / Feresa sp.,Marine mammal,Globicephalinae,Small globicephalinae,Melon-headed / Pygmy killer whale, +PHOCID,behavMam,Mammifere marin,Phocidae,Phoque,Phoque ind.,Phocidae sp.,Marine mammal,Phocidae,Seal,Seal sp.,foca spp +PHOPHO,behavMam,Mammifere marin,Phocoenidae,Phocoenidae,Marsouin commun,Phocoena phocoena,Marine mammal,Phocoenidae,Phocoeniid,Harbour porpoise,Marsopa comun +PHOVIT,behavMam,Mammifere marin,Phocidae,Phoque,Phoque veau-marin,Phoca vitulina,Marine mammal,Phocidae,Seal,Harbor seal (Common seal),foca comun +PHYMAC,behavMam,Mammifere marin,Cachalot,Physeteridae,Cachalot macrocephale,Physeter macrocephalus,Marine mammal,Cachalot,Physeteriid,Sperm whale,Cachalote +PSECRA,behavMam,Mammifere marin,Globicephalinae,Grand globicephaline,Pseudorque,Pseudorca crassidens,Marine mammal,Globicephalinae,Large globicephalinae,False killer whale,Orca bastarda +SMACET,behavMam,Mammifere marin,Cetacea,Cetace ind.,Petit cetace ind.,Small Cetacea sp.,Marine mammal,Cetacea,Cetacean unidentif.,Small Cetacea sp.,Delfin / marsopa / Cachalote pigmeo +SMADEL,behavMam,Mammifere marin,Delphininae,Petit delphinine,Petit delphinine ind.,Small delphininae sp.,Marine mammal,Delphininae,Small delphininae,Small delphininae sp.,Pequenito Delfin spp +SOTGUI,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin de Guyane,Sotalia guianensis,Marine mammal,Delphininae,Small delphininae,Tucuxi, +SOUCHI,behavMam,Mammifere marin,Delphininae,Grand delphinine,Dauphin a bosse,Sousa chinensis,Marine mammal,Delphininae,Large delphininae,Indo-Pacific hump-backed dolphin, +STEATT,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin tachete pantropical,Stenella attenuata,Marine mammal,Delphininae,Small delphininae,Pantropical spotted dolphin, +STEBRE,behavMam,Mammifere marin,Delphininae,Grand delphinine,Steno,Steno bredanensis,Marine mammal,Delphininae,Large delphininae,Rough-toothed dolphin, +STECLY,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin clymene,Stenella clymene,Marine mammal,Delphininae,Small delphininae,Clymene dolphin (Short-snouted spinner dolphin), +STECOE,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin bleu et blanc,Stenella coeruleoalba,Marine mammal,Delphininae,Small delphininae,Striped dolphin,Delfin listado +STEDEL,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin bleu et blanc / commun,Stenella / Delphinus sp.,Marine mammal,Delphininae,Small delphininae,Striped / Common dolphin, +STEFRO,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin tachete de l'Atlantique,Stenella frontalis,Marine mammal,Delphininae,Small delphininae,Atlantic spotted dolphin, +STELON,behavMam,Mammifere marin,Delphininae,Petit delphinine,Dauphin a long bec,Stenella longirostris,Marine mammal,Delphininae,Small delphininae,Spinner dolphin (Long-snouter spinner dolphin), +TRIMAN,behavMam,Mammifere marin,Sirenien,Trichechidae,Lamentin,Trichechus manatus,Marine mammal,Sirenian,Trichechiid,Manatee, +TURADU,behavMam,Mammifere marin,Delphininae,Grand delphinine,Grand dauphin indo-pacifique,Tursiops aduncus,Marine mammal,Delphininae,Large delphininae,Indo-Pacific Bottlenose dolphin, +TURSPP,behavMam,Mammifere marin,Delphininae,Grand delphinine,Grand dauphin oceanique / indo-pacifique,Tursiops sp.,Marine mammal,Delphininae,Large delphininae,Indo-Pacific / Common Bottlenose dolphin, +TURTRU,behavMam,Mammifere marin,Delphininae,Grand delphinine,Grand dauphin,Tursiops truncatus,Marine mammal,Delphininae,Large delphininae,Bottlenose dolphin,Delfin mular +ZIPCAV,behavMam,Mammifere marin,Ziphiidae,Autre baleine a bec,Baleine a bec de Cuvier,Ziphius cavirostris,Marine mammal,Ziphiidae,Other beaked whale,Cuvier's beaked whale,Zifio comun +ZIPSPP,behavMam,Mammifere marin,Ziphiidae,Baleine a bec ind.,Baleine a bec ind.,Ziphiidae sp.,Marine mammal,Ziphiidae,Other beaked whale,Ziphiid sp. (Beaked whale),Zifio spp +DEADBI,,Mort,Mort,Dead,Oiseau mort,Mortem Aves,Dead,Dead,Dead,Dead Bird,Pajaro muerto +DEADMM,,Mort,Mort,Dead,Mammifere marin mort,Mortem Mammalia,Dead,Dead,Dead,Dead marine mammal ,Mamifero marino muerto +ANASPP,behavBird,Oiseau cotier,Anatidae,Canard,Canard ind.,Anas / Aythya / Melanitta sp.,Coastal Bird,Anatidae,Duck,Duck sp.,Carricero spp +ANATID,behavBird,Oiseau cotier,Anatidae,Canard,Anatide ind.,Anatidae sp.,Coastal Bird,Anatidae,Anatiid,Anatiid sp., +ANSSPP,behavBird,Oiseau cotier,Anatidae,Oie,Oie ind.,Anser sp.,Coastal Bird,Anatidae,Goose,Goose sp., +BRABER,behavBird,Oiseau cotier,Anatidae,Oie,Bernache cravant,Branta bernicla,Coastal Bird,Anatidae,Goose,Brent Goose,Barnacla Carinegra +GAVADA,behavBird,Oiseau cotier,Gaviidae,Plongeon,Plongeon a bec blanc,Gavia adamsii,Coastal Bird,Gaviidae,Diver,Yellow-billed Diver,Carricero poliglota +GAVARC,behavBird,Oiseau cotier,Gaviidae,Plongeon,Plongeon arctique,Gavia arctica,Coastal Bird,Gaviidae,Diver,Black-throated Diver,Colimbo artico +GAVIMM,behavBird,Oiseau cotier,Gaviidae,Plongeon,Plongeon imbrin,Gavia immer,Coastal Bird,Gaviidae,Diver,Great Northern Diver,Colimbo grande +GAVSPP,behavBird,Oiseau cotier,Gaviidae,Plongeon,Plongeon ind.,Gavia sp.,Coastal Bird,Gaviidae,Diver,Diver sp.,Colimbo spp +GAVSTE,behavBird,Oiseau cotier,Gaviidae,Plongeon,Plongeon catmarin,Gavia stellata,Coastal Bird,Gaviidae,Diver,Red-throated Diver,Colimbo chico +LARGRE,behavBird,Oiseau cotier,Podicipedidae,Grebe,Grand grebe ind.,Podiceps sp.,Coastal Bird,Podicipitidae,Grebe,Large grebe sp., +MELFUS,behavBird,Oiseau cotier,Anatidae,Macreuse,Macreuse brune,Melanitta fusca,Coastal Bird,Anatidae,Scoter,White-winged scoter,Negron especulado +MELNIG,behavBird,Oiseau cotier,Anatidae,Macreuse,Macreuse noire,Melanitta nigra,Coastal Bird,Anatidae,Scoter,Black scoter,Negron comun +MELSPP,behavBird,Oiseau cotier,Anatidae,Macreuse,Macreuse ind.,Melanitta sp.,Coastal Bird,Anatidae,Scoter,Scoter sp.,Negron spp +MERSER,behavBird,Oiseau cotier,Anatidae,Harle,Harle huppe,Mergus serrator,Coastal Bird,Anatidae,Merganser,Red-breasted Merganser,Serreta mediana +MERSPP,behavBird,Oiseau cotier,Anatidae,Harle,Harle ind.,Mergus sp.,Coastal Bird,Anatidae,Merganser,Merganser sp., +PODCRI,behavBird,Oiseau cotier,Podicipedidae,Grebe,Grebe huppe,Podiceps cristatus,Coastal Bird,Podicipitidae,Grebe,Great crested grebe,Somormujo lavanco +PODGRI,behavBird,Oiseau cotier,Podicipedidae,Grebe,Grebe jougris,Podiceps griseus,Coastal Bird,Podicipitidae,Grebe,Red-necked grebe,Grebio +PODSPP,behavBird,Oiseau cotier,Podicipedidae,Grebe,Grebe ind.,Podiceps sp.,Coastal Bird,Podicipitidae,Grebe,Grebe sp.,Zampullin spp +SMAGRE,behavBird,Oiseau cotier,Podicipedidae,Grebe,Petit grebe ind.,Podiceps sp.,Coastal Bird,Podicipitidae,Grebe,Small grebe sp., +SOMMOL,behavBird,Oiseau cotier,Anatidae,Canard,Eider a duvet,Somateria mollissima,Coastal Bird,Anatidae,Duck,Eider duck,Eider comun +TADTAD,behavBird,Oiseau cotier,Anatidae,Canard,Tadorne de Belon,Tadorna tadorna,Coastal Bird,Anatidae,Duck,Common shelduck,Tarro blanco +ALCSPP,behavBird,Oiseau marin,Alcidae,Alcide ind.,Alcide ind.,Alcidae sp.,Seabird,Alcidae,Auk,Auk sp.,Alcido spp +ALCTOR,behavBird,Oiseau marin,Alcidae,Pingouin ou Guillemot,Pingouin torda,Alca torda,Seabird,Alcidae,Razobill or Guillemot,Razorbill,Alca spp +ALCURI,behavBird,Oiseau marin,Alcidae,Pingouin ou Guillemot,Pingouin ou Guillemot,Alca / Uria sp.,Seabird,Alcidae,Razobill or Guillemot,Razobill or Guillemot,Alacarao +ALLALL,behavBird,Oiseau marin,Alcidae,Autre Alcide,Mergule nain,Alle Alle,Seabird,Alcidae,Other auk,Little auk,Mergulo atlantico +ANOMIN,behavBird,Oiseau marin,Sternidae,Noddi,Noddi noir,Anous minutus,Seabird,Sternidae,Noddy,Black noddy, +ANOSPP,behavBird,Oiseau marin,Sternidae,Noddi,Noddi marron ind.,Anous sp.,Seabird,Sternidae,Noddy,Noddy sp., +ANOSTO,behavBird,Oiseau marin,Sternidae,Noddi,Noddi brun,Anous stolidus,Seabird,Sternidae,Noddy,Brown Noddy, +BLAGUL,behavBird,Oiseau marin,Laridae,Goeland noir,Goeland noir ind.,Larus sp.,Seabird,Laridae,Black gull,Large black gull sp., +BROPET,behavBird,Oiseau marin,Procellariidae,Procellaride marron,Grand procellaride marron,Brown procellariidae sp.,Seabird,Procellariidae,Brown procellariid,Brown petrel / shearwater sp., +BROTER,behavBird,Oiseau marin,Sternidae,Sterne brune,Sterne fuligineuse / bridee,Onychoprion sp.,Seabird,Sternidae,Brown tern,Sooty / Bridled Tern, +BULBUL,behavBird,Oiseau marin,Procellariidae,Procellaride marron,Petrel de Bulwer,Bulweria bulwerii,Seabird,Procellariidae,Brown procellariid,Bulwer's petrel, +CALDIO,behavBird,Oiseau marin,Procellariidae,Grand puffin,Puffin cendre / de Scopoli,Calonectris diomedea,Seabird,Procellariidae,Large shearwater,Cory / Scopoli's shearwater,Pardela cenicienta +CATSKU,behavBird,Oiseau marin,Stercorariidae,Autre Labbe,Grand labbe,Catharacta skua,Seabird,Stercorariidae,Other skua,Skua,Pagalo grande +CHLHYB,behavBird,Oiseau marin,Sternidae,Sterne grise,Guifette moustac,Chlidonias hybridus,Seabird,Sternidae,Grey tern,Wiskered tern,Fumarel cariblanco +CHLNIG,behavBird,Oiseau marin,Sternidae,Sterne grise,Guifette noire,Chlidonias niger,Seabird,Sternidae,Grey tern,Black Tern,Fumarel comun +CHLSPP,behavBird,Oiseau marin,Sternidae,Sterne grise,Guifette ind.,Chlidonias sp.,Seabird,Sternidae,Grey tern,Marsh tern sp.,Fumarel spp +CORMSP,behavBird,Oiseau marin,Phalacrocoracidae,Cormoran,Cormoran ind.,Phalacrocorax sp.,Seabird,Phalacrocoracidae,Cormorant,Cormorant or shag sp.,Cormoran spp +DIOSPP,behavBird,Oiseau marin,Diomedeidae,Albatros,Albatros ind.,Diomedeidae sp.,Seabird,Diomedeidae,Albatros,Albatross sp., +FRAARC,behavBird,Oiseau marin,Alcidae,Autre Alcide,Macareux moine,Fratercula arctica,Seabird,Alcidae,Other auk,Atlantic puffin,Frailecillo spp +FREARI,behavBird,Oiseau marin,Fregatidae,Fregate,Fregate ariel,Fregata ariel,Seabird,Fregatidae,Frigatebird,Lesser frigatebird, +FREGRA,behavBird,Oiseau marin,Hydrobatidae,Oceanite,Oceanite a ventre blanc,Fregetta grallaria,Seabird,Hydrobatidae,Storm-petrel,White-bellied Storm Petrel, +FREMAG,behavBird,Oiseau marin,Fregatidae,Fregate,Fregate superbe,Fregata magnificens,Seabird,Fregatidae,Frigatebird,Magnificent Frigatebird, +FREMIN,behavBird,Oiseau marin,Fregatidae,Fregate,Grande fregate,Fregata minor,Seabird,Fregatidae,Frigatebird,Great frigatebird, +FRESPP,behavBird,Oiseau marin,Fregatidae,Fregate,Fregate ind.,Fregata sp.,Seabird,Fregatidae,Frigatebird,Frigatebird sp., +FRETRO,behavBird,Oiseau marin,Hydrobatidae,Oceanite,Oceanite a ventre noir,Fregetta tropica,Seabird,Hydrobatidae,Storm-petrel,Black-bellied Storm Petrel, +FULGLA,behavBird,Oiseau marin,Procellariidae,Fulmar,Fulmar boreal,Fulmarus glacialis,Seabird,Procellariidae,Fulmar,Northern fulmar,Fulmar boreal +GREGUL,behavBird,Oiseau marin,Laridae,Goeland gris,Goeland gris ind.,Larus sp.,Seabird,Laridae,Grey gull,Large grey gull sp., +GREPET,behavBird,Oiseau marin,Procellariidae,Petrel gris,Petrel gris ind.,Pterodroma sp.,Seabird,Procellariidae,Grey petrel,Grey petrel sp., +GRETER,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne grise ind.,Sternidae sp.,Seabird,Sternidae,Grey tern,Grey tern sp., +GYGALB,behavBird,Oiseau marin,Sternidae,Sterne grise,Gygis blanche,Gygis sp.,Seabird,Sternidae,Grey tern,White Tern, +HYDPEL,behavBird,Oiseau marin,Hydrobatidae,Oceanite,Oceanite tempete,Hydrobates pelagicus,Seabird,Hydrobatidae,Storm-petrel,European storm-petrel,Paino comun +LARARG,behavBird,Oiseau marin,Laridae,Goeland gris,Goeland argente,Larus argentatus,Seabird,Laridae,Grey gull,European herring gull,Gaviota argentea +LARATR,behavBird,Oiseau marin,Laridae,Mouette,Mouette atricille,Leucophaeus atricilla,Seabird,Laridae,Small gull,Laughing gull, +LARAUD,behavBird,Oiseau marin,Laridae,Moyen goeland gris,Goeland d'Audouin,Ichthyaetus audouinii,Seabird,Laridae,Medium grey gull,Audouin's gull,Gaviota de Audouin +LARCAC,behavBird,Oiseau marin,Laridae,Goeland gris,Goeland pontique,Larus cachinnans,Seabird,Laridae,Grey gull,Caspian gull,Gaviota de patas amarillas +LARCAN,behavBird,Oiseau marin,Laridae,Moyen goeland gris,Goeland cendre,Larus canus,Seabird,Laridae,Medium grey gull,Common gull,Gaviota cana +LARFUS,behavBird,Oiseau marin,Laridae,Goeland noir,Goeland brun,Larus fuscus,Seabird,Laridae,Black gull,Lesser black-backed gull,Gaviota sombria +LARGEN,behavBird,Oiseau marin,Laridae,Mouette,Goeland railleur,Chroicocephalus genei,Seabird,Laridae,Small gull,Slender-billed gull, +LARGLA,behavBird,Oiseau marin,Laridae,Autre goeland,Goeland a ailes blanches,Larus glaucoides,Seabird,Laridae,Other gull,Iceland gull,Gaviota groenlandesa +LARGUL,behavBird,Oiseau marin,Laridae,Autre goeland,Grand Goeland ind.,Large laridae sp.,Seabird,Laridae,Other gull,Large gull sp.,Grande gaviota spp +LARHYP,behavBird,Oiseau marin,Laridae,Autre goeland,Goeland bourgmestre,Larus hyperboreus,Seabird,Laridae,Other gull,Glaucous gull,Gavion hiperboreo +LARMAR,behavBird,Oiseau marin,Laridae,Goeland noir,Goeland marin,Larus marinus,Seabird,Laridae,Black gull,Great black-backed gull,Gavion atlantico +LARMEL,behavBird,Oiseau marin,Laridae,Mouette,Mouette melanocephale,Ichthyaetus melanocephalus,Seabird,Laridae,Small gull,Mediterranean gull,Gaviota cabecinegra +LARMIC,behavBird,Oiseau marin,Laridae,Goeland gris,Goeland leucophee,Larus michahellis,Seabird,Laridae,Grey gull,Yellow-legged gull,Gaviota patiamarilla +LARMIN,behavBird,Oiseau marin,Laridae,Mouette,Mouette pygmee,Hydrocoloeus minutus,Seabird,Laridae,Small gull,Little gull,Gaviota enana +LARNOV,behavBird,Oiseau marin,Laridae,Mouette,Mouette argentee,Chroicocephalus novaehollandiae,Seabird,Laridae,Small gull,Silver gull, +LARPIP,behavBird,Oiseau marin,Laridae,Mouette,Mouette de Franklin,Leucophaeus pipixcan,Seabird,Laridae,Small gull,Franklin's gull,Gaviota Pipizcan +LARRID,behavBird,Oiseau marin,Laridae,Mouette,Mouette rieuse,Chroicocephalus ridibundus,Seabird,Laridae,Small gull,Black-headed gull,Gaviota reidora +LARSAB,behavBird,Oiseau marin,Laridae,Mouette,Mouette de Sabine,Xema sabini,Seabird,Laridae,Small gull,Sabine's gull,Gaviota de Sabine +LARSHE,behavBird,Oiseau marin,Procellariidae,Grand puffin,Grand puffin ind.,Calonectris / Ardenna sp.,Seabird,Procellariidae,Large shearwater,Large shearwater sp., +LARSPP,behavBird,Oiseau marin,Laridae,Laride ind.,Laride ind.,Laridae sp.,Seabird,Laridae,Lariid ,Lariid sp.,Gaviota spp +MEDGUL,behavBird,Oiseau marin,Laridae,Moyen goeland gris,Petit goeland gris ind.,Medium laridae sp.,Seabird,Laridae,Medium grey gull,Medium gull sp., +MEDTER,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne moyenne ind.,Sterna sp.,Seabird,Sternidae,Grey tern,Medium tern sp., +OCELEU,behavBird,Oiseau marin,Hydrobatidae,Oceanite,Oceanite culblanc,Oceanodroma leucorhoa,Seabird,Hydrobatidae,Storm-petrel,Leach's storm-petrel,Paino de Leach +OCEOCE,behavBird,Oiseau marin,Hydrobatidae,Oceanite,Oceanite de Wilson,Oceanites oceanicus,Seabird,Hydrobatidae,Storm-petrel,Wilson's storm-petrel,Paino Wilson +OCESPP,behavBird,Oiseau marin,Hydrobatidae,Oceanite,Oceanite ind.,Hydrobatidae sp.,Seabird,Hydrobatidae,Storm-petrel,Storm-petrel sp.,Paino spp +PELOCC,behavBird,Oiseau marin,Pelecanidae,Pelican,Pelican brun,Pelecanus occidentalis,Seabird,Pelecanidae,Pelican,Brown Pelican, +PELSPP,behavBird,Oiseau marin,Pelecanidae,Pelican,Pelican ind.,Pelecanus sp.,Seabird,Pelecanidae,Pelican,Pelican sp., +PETSPP,behavBird,Oiseau marin,Procellariidae,Petrel ind.,Petrel ind.,Pterodroma / Pseudobulweria sp.,Seabird,Procellariidae,Petrel,Petrel sp., +PHAAET,behavBird,Oiseau marin,Phaethontidae,Phaeton,Phaeton a bec rouge,Phaethon aethereus,Seabird,Phaethontidae,Tropicbird,Red-billed Tropicbird, +PHAARI,behavBird,Oiseau marin,Phalacrocoracidae,Cormoran,Cormoran huppe,Phalacrocorax aristotelis,Seabird,Phalacrocoracidae,Cormorant,European shag,Cormoran monudo +PHACAR,behavBird,Oiseau marin,Phalacrocoracidae,Cormoran,Grand cormoran,Phalacrocorax carbo,Seabird,Phalacrocoracidae,Cormorant,Great cormorant,Cormoran grande +PHAFUL,behavBird,Oiseau marin,Charadriidae,Limicole,Phalarope a bec large,Phalaropus fulicarius,Seabird,Shorebird,Charadriidae,Grey Phalarope,Falaropo picogrueso +PHALEP,behavBird,Oiseau marin,Phaethontidae,Phaeton,Phaeton a bec jaune,Phaethon lepturus,Seabird,Phaethontidae,Tropicbird,White-tailed Tropicbird, +PHAPYG,behavBird,Oiseau marin,Phalacrocoracidae,Cormoran,Cormoran pygmee,Phalacrocorax pygmeus,Seabird,Phalacrocoracidae,Cormorant,Pygmy cormorant, +PHARUB,behavBird,Oiseau marin,Phaethontidae,Phaeton,Phaeton a brins rouges,Phaethon rubricauda,Seabird,Phaethontidae,Tropicbird,Red-tailed Tropicbird, +PHASPP,behavBird,Oiseau marin,Phaethontidae,Phaeton,Phaeton ind.,Phaethon sp.,Seabird,Phaethontidae,Tropicbird,Tropicbird sp., +PROCER,behavBird,Oiseau marin,Sternidae,Noddi,Noddi bleu,Procelsterna cerulea,Seabird,Sternidae,Noddy,Blue Noddy, +PROSPP,behavBird,Oiseau marin,Procellariidae,Procellaride ,Procellaride ind.,Procellariidae sp.,Seabird,Procellariidae,Procellariid,Procellariid sp., +PSEROS,behavBird,Oiseau marin,Procellariidae,Procellaride marron,Petrel de Tahiti,Pseudobulweria rostrata,Seabird,Procellariidae,Brown procellariid,Tahiti petrel, +PTEBAR,behavBird,Oiseau marin,Procellariidae,Procellaride marron,Petrel de Barau,Pterodroma baraui,Seabird,Procellariidae,Brown procellariid,Barau's petrel, +PUFCAR,behavBird,Oiseau marin,Procellariidae,Puffin,Puffin a pieds pales,Ardenna carneipes,Seabird,Procellariidae,Shearwater,Flesh-footed Shearwater, +PUFGRA,behavBird,Oiseau marin,Procellariidae,Grand puffin,Puffin majeur,Ardenna gravis,Seabird,Procellariidae,Large shearwater,Great shearwater,Pardela capirotada +PUFGRI,behavBird,Oiseau marin,Procellariidae,Grand puffin,Puffin fuligineux,Ardenna grisea,Seabird,Procellariidae,Large shearwater,Sooty shearwater,Pardela sombria +PUFLHE,behavBird,Oiseau marin,Procellariidae,Puffin,Puffin d'Audubon,Puffinus lherminieri,Seabird,Procellariidae,Shearwater,Audubon's shearwater, +PUFMAU,behavBird,Oiseau marin,Procellariidae,Petit puffin,Puffin des Baleares,Puffinus mauretanicus,Seabird,Procellariidae,Small shearwater,Mediterranean shearwater,Pardela balear +PUFPAC,behavBird,Oiseau marin,Procellariidae,Puffin,Puffin du Pacifique,Ardenna pacifica,Seabird,Procellariidae,Shearwater,Wedge-tailed shearwater, +PUFPUF,behavBird,Oiseau marin,Procellariidae,Petit puffin,Puffin des anglais,Puffinus puffinus,Seabird,Procellariidae,Small shearwater,Manx shearwater,Pardela pichoneta +PUFSPP,behavBird,Oiseau marin,Procellariidae,Puffin,Puffin ind.,Puffinus / Ardenna / Calonectris sp.,Seabird,Procellariidae,Shearwater,Shearwater sp.,Pardela spp +PUFYEL,behavBird,Oiseau marin,Procellariidae,Petit puffin,Puffin yelkouan,Puffinus yelkouan,Seabird,Procellariidae,Small shearwater,Yelkouan shearwater,Pardelas mediterranea +RISTRI,behavBird,Oiseau marin,Laridae,Mouette,Mouette tridactyle,Rissa tridactyla,Seabird,Laridae,Small gull,Kittiwake gull,Gaviota tridactila +SMAGUL,behavBird,Oiseau marin,Laridae,Mouette,Mouette ind.,Small laridae sp.,Seabird,Laridae,Small gull,Small gull sp.,Pequenita gaviota spp +SMASHE,behavBird,Oiseau marin,Procellariidae,Petit puffin,Petit Puffin ind.,Puffinus sp.,Seabird,Procellariidae,Small shearwater,Small shearwater sp., +LARTER,behavBird,Oiseau marin,Sternidae,Sterne grise,Grande sterne grise ind.,Thalasseus sp.,Seabird,Sternidae,Grey tern,large tern sp., +SMATER,behavBird,Oiseau marin,Sternidae,Sterne grise,Petite sterne grise ind.,Sternula / Chlidonias sp.,Seabird,Sternidae,Grey tern,Small tern sp., +STEALB,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne naine,Sternula albifrons,Seabird,Sternidae,Grey tern,Little Tern,Charrancito +STEANA,behavBird,Oiseau marin,Sternidae,Sterne brune,Sterne bridee,Onychoprion anaethetus,Seabird,Sternidae,Brown tern,Bridled Tern, +STEARC,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne arctique,Sterna paradisaea,Seabird,Sternidae,Grey tern,Artic tern,Charran artico +STEBEN,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne voyageuse,Thalasseus bengalensis,Seabird,Sternidae,Grey tern,Lesser crested tern, +STECAS,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne caspienne,Hydroprogne caspia,Seabird,Sternidae,Grey tern,Caspian tern, +STEDOU,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne de Dougall,Sterna dougallii,Seabird,Sternidae,Grey tern,Roseate Tern,Charran Rosado +STEDUS,behavBird,Oiseau marin,Stercorariidae,Petit labbe,Labbe a longue queue,Stercorarius longicaudus,Seabird,Stercorariidae,Small skua,Long-tailed Skua,Pagalo rabero +STEFUS,behavBird,Oiseau marin,Sternidae,Sterne brune,Sterne fuligineuse,Onychoprion fuscatus,Seabird,Sternidae,Brown tern,Sooty Tern, +STEHIR,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne pierregarin,Sterna hirundo,Seabird,Sternidae,Grey tern,Common Tern,Charran comun +STEMAX,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne royale,Thalasseus maximus,Seabird,Sternidae,Grey tern,Royal Tern, +STENIL,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne hansel,Sterna nilotica,Seabird,Sternidae,Grey tern,Gull-billed tern,Pagaza piconegra +STEPAR,behavBird,Oiseau marin,Stercorariidae,Petit labbe,Labbe parasite,Stercorarius parasiticus,Seabird,Stercorariidae,Small skua,Parasitic jaeger,Pagalo parasito +STEPOM,behavBird,Oiseau marin,Stercorariidae,Petit labbe,Labbe pomarin,Stercorarius pomarinus,Seabird,Stercorariidae,Small skua,Pomarine skua,Pagalo pomarino +STERCO,behavBird,Oiseau marin,Stercorariidae,Petit labbe,Labbe ind.,Stercorarius sp.,Seabird,Stercorariidae,Small skua,Jaeger sp., +STESAN,behavBird,Oiseau marin,Sternidae,Sterne grise,Sterne caugek,Thalasseus sandvicensis,Seabird,Sternidae,Grey tern,Sandwich Tern,Charran patinegro +STESPP,behavBird,Oiseau marin,Sternidae,Sterne,Sterne ind.,Sternidae sp.,Seabird,Sternidae,Tern,Tern sp.,Charran spp +SULBAS,behavBird,Oiseau marin,Sulidae,Fou,Fou de Bassan,Morus bassanus,Seabird,Sulidae,Booby,Northern gannet,Alcatraz +SULDAC,behavBird,Oiseau marin,Sulidae,Fou,Fou masque,Sula dactylatra,Seabird,Sulidae,Booby,Blue-faced booby, +SULLEU,behavBird,Oiseau marin,Sulidae,Fou,Fou brun,Sula leucogaster,Seabird,Sulidae,Booby,Brown booby, +SULSPP,behavBird,Oiseau marin,Sulidae,Fou,Petit fou ind.,Sula sp.,Seabird,Sulidae,Booby,Booby sp., +SULSUL,behavBird,Oiseau marin,Sulidae,Fou,Fou a pieds rouges,Sula sula,Seabird,Sulidae,Booby,Red-footed booby, +THACHL,behavBird,Oiseau marin,Diomedeidae,Albatros,Albatros a bec jaune,Thalassarche chlororynchos,Seabird,Diomedeidae,Albatros,Yellow-nosed Albatros,Albatros Clororrinco +THAMEL,behavBird,Oiseau marin,Diomedeidae,Albatros,Albatros a sourcils noirs,Thalassarche melanophris,Seabird,Diomedeidae,Albatros,Black-browed Albatros,Albatros Ojeroso +URIAAL,behavBird,Oiseau marin,Alcidae,Pingouin ou Guillemot,Guillemot de troil,Uria aalge,Seabird,Alcidae,Razobill or Guillemot,Common guillemot,Arao comun +MICFUS,behavBird,Oiseau marin,Laridae,Autre goeland,Goeland ardoise ind.,Larus michahellis / fuscus,Seabird,Laridae,Other gull,Slategray gull sp,Gaviota pizarra spp +CEPGRY,behavBird,Oiseau marin,Alcidae,Autre Alcide,Guillemot a miroir,Cepphus grylle,Seabird,Alcidae,Other auk,Black guillemot,Arao aliblanco +SBIRSP,behavBird,Oiseau marin,Oiseau marin,Oiseau ind.,Oiseau marin ind.,Aves sp.,Seabird,Seabird,Seabird unid.,Seabird sp.,ave marina sp. +ACCNIS,behavBird,Oiseau terrestre,Accipitridae,Rapace,Epervier d'Europe,Accipiter nisus,Land Bird,Accipitridae,Bird of prey,European Sparrowhawk,Gavilan comun +ACRPAL,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Rousserolle verderolle,Acrocephalus palustris,Land Bird,Other bird,Passerine,Marsh warbler,Carricero poliglota +ACRSCH,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Phragmite des joncs,Acrocephalus schoenobaenus,Land Bird,Other bird,Passerine,Sedge Warbler,Carricerin comun +ACRSCI,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Rousserolle effarvate,Acrocephalus scirpaceus,Land Bird,Other bird,Passerine,Eurasian Reed Warbler,Carricero comun +ACRSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Rousserolle ind.,Acrocephalus sp.,Land Bird,Other bird,Passerine,Warbler sp.,Carricero spp +ACTHYP,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Chevalier guignette,Actitis hypoleucos,Land Bird,Scolopacidae,Shorebird,Common sandpiper,Andarrios chico +ALAARV,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Alouette des champs,Alauda arvensis,Land Bird,Other bird,Passerine,Eurasian skylark,Alondra comun +ALASPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Alouette ind.,Alauda sp.,Land Bird,Other bird,Passerine,Skylark sp.,Alondra spp +ANAACU,behavBird,Oiseau terrestre,Anatidae,Canard,Canard pilet,Anas acuta,Land Bird,Anatidae,Duck,Northern Pintail,Anade rabudo +ANACLY,behavBird,Oiseau terrestre,Anatidae,Canard,Canard souchet,Anas clypeata,Land Bird,Anatidae,Duck,Northern Shoveler,Pato cuchara +ANACRE,behavBird,Oiseau terrestre,Anatidae,Canard,Sarcelle d'hiver,Anas crecca,Land Bird,Anatidae,Duck,Common teal,Cerceta comun +ANAPEN,behavBird,Oiseau terrestre,Anatidae,Canard,Canard siffleur,Anas penelope,Land Bird,Anatidae,Duck,Eurasian Wigeon,Anade silbon +ANAPLA,behavBird,Oiseau terrestre,Anatidae,Canard,Canard colvert,Anas platyrhynchos,Land Bird,Anatidae,Duck,Mallard,Anade real +ANASTR,behavBird,Oiseau terrestre,Anatidae,Canard,Canard chipeau,Anas strepera,Land Bird,Anatidae,Duck,Gadwall,Anade friso +ANSALB,behavBird,Oiseau terrestre,Anatidae,Oie,Oie rieuse,Anser albifrons,Land Bird,Anatidae,Goose,Greater White-fronted Goose,Ansar careto +ANSANS,behavBird,Oiseau terrestre,Anatidae,Oie,Oie cendree,Anser anser,Land Bird,Anatidae,Goose,Greylag Goose,Ansar comon +ANTPET,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pipit maritime,Anthus petrosus,Land Bird,Other bird,Passerine,Eurasian rock pipit,Bisbita costero +ANTPRA,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pipit farlouse,Anthus pratensis,Land Bird,Other bird,Passerine,Meadow pipit,Bisbita comun +ANTSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pipit ind.,Anthus sp.,Land Bird,Other bird,Passerine,Pipit sp.,Bisbita spp +ANTTRI,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pipit des arbres,Anthus trivialis,Land Bird,Other bird,Passerine,Tree pipit,Pepito +APUAPU,behavBird,Oiseau terrestre,Apodidae,Martinet,Martinet noir,Apus apus,Land Bird,Other bird,Swift,Common swift,Vencejo comun +APUSPP,behavBird,Oiseau terrestre,Apodidae,Martinet,Martinet ind.,Apus sp.,Land Bird,Other bird,Swift,Swift sp.,Vencejo spp +ARDCIN,behavBird,Oiseau terrestre,Ardeidae,Echassier,Heron cendre,Ardea cinerea,Land Bird,Ardeidae,Shorebird,Grey heron,Garza real +ARDPUR,behavBird,Oiseau terrestre,Ardeidae,Echassier,Heron pourpre,Ardea purpurea,Land Bird,Ardeidae,Shorebird,Purple heron,Garza pourpre +ARDSPP,behavBird,Oiseau terrestre,Ardeidae,Echassier,Heron ind.,Ardea sp.,Land Bird,Ardeidae,Shorebird,Heron sp.,Garza spp +AREINT,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Tournepierre a collier,Arenaria interpres,Land Bird,Scolopacidae,Shorebird,Turnstone,Vuelvepiedras comun +ASIFLA,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Hibou des marais,Asio flammeus,Land Bird,Other bird,Bird of prey,Short-eared owl,Lechuza campestre +ASIOTU,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Hibou moyen-duc,Asio otus,Land Bird,Other bird,Bird of prey,Long-eared owl,Buho chico +AYTMAR,behavBird,Oiseau terrestre,Anatidae,Canard,Fuligule milouinan,Aythya marila,Land Bird,Anatidae,Duck,Greater Scaup,Porran bastardo +BIRSPP,behavBird,Oiseau terrestre,Autre oiseau,Oiseau ind.,Oiseau ind.,Aves sp.,Land Bird,Other bird,Bird unidentif.,Bird unidentif., +BRACAN,behavBird,Oiseau terrestre,Anatidae,Oie,Bernache du Canada,Branta canadensis,Coastal Bird,Anatidae,Goose,Canada Goose,Barnacla Canadiense +BUBIBI,behavBird,Oiseau terrestre,Ardeidae,Echassier,Heron garde-boeufs,Bubulcus ibis,Land Bird,Ardeidae,Shorebird,Cattle egret,Garcilla bueyera +CALALB,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Becasseau sanderling,Calidris alba,Land Bird,Scolopacidae,Shorebird,Sanderling,Correlimos tridactilo +CALALP,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Becasseau variable,Calidris alpina,Land Bird,Scolopacidae,Shorebird,Dunlin,Correlimos comun +CALCAN,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Becasseau maubeche,Calidris canutus,Land Bird,Scolopacidae,Shorebird,Red Knot,Correlimos gordo +CALMAR,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Becasseau violet,Calidris maritima,Land Bird,Scolopacidae,Shorebird,Purple Sandpiper,Correlimos Oscuro +CALMIN,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Becasseau minute,Calidris minuta,Land Bird,Scolopacidae,Shorebird,Little Stint,Correlimos menudo +CALSPP,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Becasseau ind.,Calidris sp.,Land Bird,Scolopacidae,Shorebird,Sandpiper sp.,Correlimos spp +CAPEUR,behavBird,Oiseau terrestre,Caprimulgidae,Engoulevent,Engoulevent d'Europe,Caprimulgus europaeus,Land Bird,Other bird,Caprimulgidae,European Nightjar,Chotacabras gris +CARCAN,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Linotte melodieuse,Carduelis cannabina,Land Bird,Other bird,Passerine,Common Linnet,Fringilido +CARCHL,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Verdier d'Europe,Carduelis chloris,Land Bird,Other bird,Passerine,European Greenfinch,Verderon comun +CARLIS,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Chardonneret,Carduelis carduelis,Land Bird,Other bird,Passerine,European Goldfinch,Jilguero +CARSPI,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Tarin des aulnes,Carduelis spinus,Land Bird,Other bird,Passerine,Eurasian Siskin,piafo +CASALB,behavBird,Oiseau terrestre,Ardeidae,Echassier,Grande aigrette,Casmerodius albus,Land Bird,Ardeidae,Shorebird,Great egret,Garceta Grande +CHAALE,behavBird,Oiseau terrestre,Charadriidae,Limicole,Gravelot a collier interrompu,Charadrius alexandrinus,Land Bird,Shorebird,Shorebird,Kentish Plover,Chorlitejo Patinegro +CHAHIA,behavBird,Oiseau terrestre,Charadriidae,Limicole,Grand gravelot,Charadrius hiaticula,Land Bird,Shorebird,Shorebird,Ringed Plover,Chorlitejo grande +CHASPP,behavBird,Oiseau terrestre,Charadriidae,Limicole,Gravelot ind.,Charadrius sp.,Land Bird,Charadriidae,Shorebird,Plover sp.,Chorlitejo spp +CIRAER,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Busard des roseaux,Circus aeruginosus,Land Bird,Other bird,Bird of prey,Eurasian Marsh Harrier,Aguilucho lagunero +CIRCYA,behavBird,Oiseau terrestre,Accipitridae,Rapace,Busard Saint-Martin,Circus cyaneus,Land Bird,Other bird,Bird of prey,Hen Harrier,Aguilucho Palido +CIRSPP,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Busard ind.,Circus sp.,Land Bird,Other bird,Bird of prey,Harrier sp.,Aguilucho spp +CISJUN,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Cisticole des joncs,Cisticola juncidis,Land Bird,Other bird,Passerine,Zitting Cisticola,Buitron comun +COLLIV,behavBird,Oiseau terrestre,Autre oiseau,Pigeon,Pigeon domestique Biset ,Columba livia,Land Bird,Other bird,Pigeon,Rock Pigeon,Paloma bravia +COLOEN,behavBird,Oiseau terrestre,Autre oiseau,Pigeon,Pigeon colombin,Columba oenas,Land Bird,Other bird,Pigeon,Stock Pigeon,Andarrios chico +COLPAL,behavBird,Oiseau terrestre,Autre oiseau,Pigeon,Pigeon ramier,Columba palomba,Land Bird,Other bird,Pigeon,Common Wood Pigeon,Paloma torcaz +COLSPP,behavBird,Oiseau terrestre,Autre oiseau,Pigeon,Pigeon ind.,Columba sp.,Land Bird,Other bird,Pigeon,Pigeon sp.,Paloma spp +CORCOR,behavBird,Oiseau terrestre,Autre oiseau,Corbeau,Corneille noire,Corvus corone,Land Bird,Other bird,Corvidae,Carrion crow,Corneja +CYGOLO,behavBird,Oiseau terrestre,Anatidae,Oie,Cygne tubercule,Cygnus olor,Land Bird,Anatidae,Goose,Mute swan,Cisne vulgar +DELURB,behavBird,Oiseau terrestre,Autre oiseau,Hirondelle,Hirondelle de fenetre,Delichon urbica,Land Bird,Other bird,Swallow,House martin,Avion comun +EGRGAR,behavBird,Oiseau terrestre,Ardeidae,Echassier,Aigrette garzette,Egretta garzetta,Land Bird,Ardeidae,Shorebird,Little egret,Garceta comun +EGRSPP,behavBird,Oiseau terrestre,Ardeidae,Echassier,Aigrette ind.,Egretta sp.,Land Bird,Ardeidae,Shorebird,Egret sp.,Garceta spp +EMBSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bruant ind.,Emberiza sp.,Land Bird,Other bird,Passerine,Hammer sp.,Escribano spp +ERIRUB,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Rouge gorge,Erithacus rubecula,Land Bird,Other bird,Passerine,European robin,Petirrojo spp +FALCOL,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Faucon emerillon,Falco columbarius,Land Bird,Other bird,Bird of prey,Merlin,Esmerejon +FALELE,behavBird,Oiseau terrestre,Falconidae,Rapace,Faucon d'Eleonore,Falco eleonorae,Land Bird,Falconidae,Bird of prey,Eleonora's falcon,Halcon de Eleonora +FALPER,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Faucon pelerin,Falco peregrinus,Land Bird,Other bird,Bird of prey,Peregrine falcon,Halcon peregrino +FALSPP,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Faucon ind.,Falco sp.,Land Bird,Other bird,Bird of prey,Falcon sp.,Halcon spp +FALSUB,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Faucon hobereau,Falco subbuteo,Land Bird,Other bird,Bird of prey,Eurasian hobby,Alcotan europeo +FALTIN,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Faucon crecerelle,Falco tinnunculus,Land Bird,Other bird,Bird of prey,Common kestrel,Cernicalo comun +FICHYP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Gobemouche noir,ficedula hypoleuca,Land Bird,Other bird,Passerine,Pied Flycatcher,Papamoscas cerrojillo +FICPAR,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Gobemouche nain,Ficedula parva,Land Bird,Other bird,Passerine,Red-breasted Flycatcher,Papamoscas papirrojo +FRICOE,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pinson des arbres,Fringilla coelebs,Land Bird,Other bird,Passerine,Chaffinch,Pinzon +FRIMON,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pinson du Nord,Fringilla montifringilla,Land Bird,Other bird,Passerine,Brambling,Pinsono glacio +FULATR,behavBird,Oiseau terrestre,Anatidae,Canard,Foulque macroule,Fulica atra,Land Bird,Anatidae,Duck,coot sp, +GALCHL,behavBird,Oiseau terrestre,Rallidae,Canard,Poule d'eau,Gallinula chloropus,Land Bird,Rallidae,Duck,Common moorhen,Polla de agua +GALGAL,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Becassine des marais,Gallinago gallinago,Land Bird,Shorebird,Shorebird,Common snipe, +HAEOST,behavBird,Oiseau terrestre,Haematopodidae,Limicole,Huitrier pie,Haematopus ostralegus,Land Bird,Haematopodidae,Shorebird,Eurasian Oystercatcher,Ostralego pica +HALALB,behavBird,Oiseau terrestre,Accipitridae,Rapace,Pygargue a queue blanche,Haliaeetus albicilla,Land Bird,Accipitridae,Bird of prey,White-tailed eagle,Pigargo Europeo +HIPSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Hypolais ind.,Hippolais sp.,Land Bird,Other bird,Passerine,Warbler sp.,Zarcero spp +HIRRUS,behavBird,Oiseau terrestre,Autre oiseau,Hirondelle,Hirondelle rustique,Hirundo rustica,Land Bird,Other bird,Swallow,Barn swallow,Golondrina comun +HIRSPP,behavBird,Oiseau terrestre,Autre oiseau,Hirondelle,Hirondelle ind.,Hirundo sp.,Land Bird,Other bird,Swallow,Swallow sp.,Golondrina spp +LBIRSP,behavBird,Oiseau terrestre,Autre oiseau,Oiseau ind.,Oiseau terrestre ind.,Aves sp.,Land Bird,Other bird,Bird unidentif.,Land Bird unidentif.,Ave terrestre +LANCOL,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pie grieche ecorcheur,Lanius collurio,Land Bird,Other bird,Passerine,Red-backed Shrike,Alcaudon dorsirrojo +LANEXC,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pie grieche grise,Lanius excubitor,Land Bird,Other bird,Passerine,Great Grey Shrike,Alcaudon real +LIMICO,behavBird,Oiseau terrestre,Limicole,Limicole,Limicole ind.,Limicole sp.,Land Bird,Shorebird,Shorebird,Shorebird unidentif.,Limicola spp +LIMLAP,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Barge rousse,Limosa lapponica,Land Bird,Shorebird,Shorebird,Bar-tailed Godwit,Aguja colipinta +LOCLUS,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Locustelle luscinioide,Locustella luscinioides,Land Bird,Other bird,Passerine,Savi's warbler,Buscarla unicolor +LOXCUR,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bec croise des sapins,Loxia curvirostra,Land Bird,Other bird,Passerine,Common Crossbill,Piquituerto escoces +LUSMEG,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Rossignol philomele,Luscinia megarhynchos,Land Bird,Other bird,Passerine,Common Nightingale,Ruisenor +MILMIG,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Milan noir,Milvus migrans,Land Bird,Other bird,Bird of prey,Black Kite,Milano negro +MOTALB,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bergeronnette grise,Motacilla alba,Land Bird,Other bird,Passerine,White Wagtail,Lavandera blanca +MOTCIN,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bergeronnette des ruisseaux,Motacilla cinerea,Land Bird,Other bird,Passerine,Grey Wagtail,Lavandera cascadena +MOTFLA,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bergeronnette printaniere,Motacilla flava,Land Bird,Other bird,Passerine,Yellow Wagtail,Lavandera boyera +MOTSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bergeronnette ind.,Motacilla spp,Land Bird,Other bird,Passerine,Wagtail sp.,Lavandera spp +MOTYAR,behavBird,Oiseau terrestre,Motacillidae,Passereau,Bergeronnette de Yarrell,Motacilla Yarelli,Land Bird,Other bird,Passerine,Pied wagtail,Lavandera yarrellii +MUSSTR,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Gobemouche gris,Muscicapa striata,Land Bird,Other bird,Passerine,Spotted Flycatcher,Papamoscas gris +NUMARQ,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Courlis cendre,Numenius arquata,Land Bird,Shorebird,Shorebird,Eurasian Curlew,Zarapito real +NUMPHA,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Courlis corlieu,Numenius phaeopus,Land Bird,Shorebird,Shorebird,Whimbrel,Zarapito trinador +NUMSPP,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Courlis ind.,Numenius sp.,Land Bird,Shorebird,Shorebird,Curlew sp.,Zarapito spp +OENHIS,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Traquet oreillard,Oenanthe hispanica,Land Bird,Other bird,Passerine,Black-eared Wheatear,Collalba rubia +OENOEN,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Traquet motteux,Oenanthe oenanthe,Land Bird,Other bird,Passerine,Northern Wheatear,Collalba gris +ORIORI,behavBird,Oiseau terrestre,Sturnidae,Passereau,Loriot d'Europe,Oriolus oriolus,Land Bird,Other bird,Passerine,Eurasian Golden Oriole,Oropendola europea +PANHAL,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Balbuzard pecheur,Pandion haliaetus,Land Bird,Other bird,Bird of prey,Osprey,Aguila pescadora +PASSER,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Passereau ind.,Passeriformes sp.,Land Bird,Other bird,Passerine,Passerine bird sp.,Paseriforme spp +PERAPI,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Bondree apivore,Pernis apivorus,Land Bird,Other bird,Bird of prey,European Honey-buzzard,Abejero europeo +PHIPUG,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Combattant varie,Philomachus pugnax,Land Bird,Shorebird,Shorebird,Ruff,Combatiente +PHOENI,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Rouge queue a front blanc,Phoenicurus phoenicurus,Land Bird,Other bird,Passerine,Common Redstart,Colirrojo real +PHOOCH,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Rouge queue noir,Phoenicurus ochruros,Land Bird,Other bird,Passerine,Black Redstart,Colirojo tizon +PHOROS,behavBird,Oiseau terrestre,Phoenicopteridae,Echassier,Flamant rose,Phoenicopterus roseus,Land Bird,Phoenicopteridae,Shorebird,Greater flamingo, +PHYCOL,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pouillot veloce,Phylloscopus collybita,Land Bird,Other bird,Passerine,Common Chiffchaff,Mosquitero comun +PHYINO,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pouillot a grands sourcils,Phyloscopus inornatus,Land Bird,Other bird,Passerine,Yellow-browed Warbler,Mosquitero bilistado +PHYPRO,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pouillot de Pallas,Phylloscopus proregulus,Land Bird,Other bird,Passerine,Pallas's Warbler,Mosquitero de Pallas +PHYSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pouillot ind.,Phylloscopus sp.,Land Bird,Other bird,Passerine,Chiffchaff sp.,Mosquitero spp +PHYTRO,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Pouillot fitis,Phylloscopus trochilus,Land Bird,Other bird,Passerine,Willow Warbler,Mosquitero musical +PLALEU,behavBird,Oiseau terrestre,Ardeidae,Echassier,Spatule blanche,Platalea leucorodia,Land Bird,Ardeidae,Shorebird,Eurasian Spoonbill,Espatula comun +PLENIV,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bruant des neiges,Plectrophenax nivalis,Land Bird,Other bird,Passerine,Snow Bunting,Escribano Nival +PLUAPR,behavBird,Oiseau terrestre,Charadriidae,Limicole,Pluvier dore,Pluvialis apricaria,Land Bird,Charadriidae,Shorebird,European gloden plover,Pluvilitos doratos +PLUSPP,behavBird,Oiseau terrestre,Charadriidae,Limicole,Pluvier ind.,Pluvialis sp.,Land Bird,Charadriidae,Shorebird,Plover sp.,Chorlito spp +PLUSQU,behavBird,Oiseau terrestre,Charadriidae,Limicole,Pluvier argente,Pluvialis squatarola,Land Bird,Charadriidae,Shorebird,Grey Plover,Chorlito gris +PRUMOD,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Accenteur mouchet,Prunella modularis,Land Bird,Other bird,Passerine,Hedge Accentor,Acentor comun +RAPSPP,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Rapace ind.,Falconiformes sp.,Land Bird,Other bird,Bird of prey,Bird of prey unidentif.,Rapaz spp +REGIGN,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Roitelet triple bandeau,Regulus ignicapilla,Land Bird,Other bird,Passerine,Common Firecrest,Reyezuelo +REGREG,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Roitelet huppe,Regulus regulus,Land Bird,Other bird,Passerine,Goldcrest,Reyezuelo sencillo +RIPRIP,behavBird,Oiseau terrestre,Autre oiseau,Hirondelle,Hirondelle de rivage,Riparia riparia,Land Bird,Other bird,Swallow,Sand Martin,Hirundo +SAXTOR,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Tarier patre,Saxicola torquata,Land Bird,Other bird,Passerine,European Stonechat,Tarabilla comun +SERSER,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Serin cini,Serinus serinus,Land Bird,Other bird,Passerine,European Serin,Verdecillo +STRDEC,behavBird,Oiseau terrestre,Autre oiseau,Tourterelle,Tourterelle turque,Streptopelia decaocto,Land Bird,Other bird,Dove,Eurasian Collared Dove,Tortola turca +STRSPP,behavBird,Oiseau terrestre,Autre oiseau,Tourterelle,Tourterelle ind.,Streptopelia sp.,Land Bird,Other bird,Dove,Dove sp.,Tortola spp +STRTUR,behavBird,Oiseau terrestre,Autre oiseau,Tourterelle,Tourterelle bois,Streptopelia turtur,Land Bird,Other bird,Dove,European Turtle Dove,Tortola comun +STUSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Etourneau ind.,Sturnus sp.,Land Bird,Other bird,Passerine,Starling sp.,Estornino spp +STUVUL,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Etourneau sansonnet,Sturnus vulgaris,Land Bird,Other bird,Passerine,Common Starling,Estornino +SYLATR,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Fauvette a tete noire,Sylvia atricapilla,Land Bird,Other bird,Passerine,Blackcap,Curruca capirotada +SYLBOR,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Fauvette des jardins,Sylvia borin,Land Bird,Other bird,Passerine,Garden Warbler,Curruca mosquitera +SYLCAN,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Fauvette passerinette,Sylvia cantillans,Land Bird,Other bird,Passerine,Subalpine Warbler,Curruca carrasquena +SYLCOM,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Fauvette grisette,Sylvia communis,Land Bird,Other bird,Passerine,Common Whitethroat,Curruca zarcera +SYLMEL,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Fauvette melanocephale,Sylvia melanocephala,Land Bird,Other bird,Passerine,Sardinian Warbler,Curruca cabecinegra +SYLSPP,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Fauvette ind.,Sylvia sp.,Land Bird,Other bird,Passerine,Warbler sp.,Curruca spp +TRISPP,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Chevalier ind.,Tringa sp.,Land Bird,Shorebird,Shorebird,Shank sp..,Archibebe spp +TRITOT,behavBird,Oiseau terrestre,Scolopacidae,Limicole,Chevalier gambette,Tringa totanus,Land Bird,Shorebird,Shorebird,Common Redshank,Archibebe comun +TROTRO,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Troglodyte mignon,Troglodytes troglodytes,Land Bird,Other bird,Passerine,Eurasian wren,Chochin +TURDUS,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Grive ind.,Turdus sp.,Land Bird,Other bird,Passerine,Trush sp.,Zorzal spp +TURILI,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Grive mauvis,Turdus iliacus,Land Bird,Other bird,Passerine,Redwing,Alondra comun +TURMER,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Merle noir,Turdus merula,Land Bird,Other bird,Passerine,Common Blackbird,Mirlo comun +TURPHI,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Grive musicienne,Turdus philomelos,Land Bird,Other bird,Passerine,Song Thrush,Grive musicienne +TURPIL,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Grive Litorne,Turdus pilaris,Land Bird,Other bird,Passerine,Fieldfare,Zorzal real +UPUEPO,behavBird,Oiseau terrestre,Autre oiseau,Huppe,Huppe fasciee,Upupa epops,Land Bird,Other bird,Hoopoe,Hoopoe,Abubilla +VANVAN,behavBird,Oiseau terrestre,Charadriidae,Limicole,Vanneau huppe,Vanellus vanellus,Land Bird,Charadriidae,Shorebird,Northern Lapwing,Avefria Palustria +TURVIS,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Grive draine,Turdus viscivorus,Land Bird,Other bird,Passerine,Mistle Trush,Zorzal Charlo +AEGCAU,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Mesange a longue queue,Aegithalos caudatus,Land Bird,Other bird,Passerine,Long-tailed Tit,Mito comun +EMBSCH,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Bruant des roseaux,Emberiza schoenicus,Land Bird,Other bird,Passerine,Reed Bunting,Escribano Palustre +PARMAJ,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Mesange charbonniere,Parus major,Land Bird,Other bird,Passerine,Great Tit,Carbonero comun +PASDOM,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Moineau domestique,Passer domesticus,Land Bird,Other bird,Passerine,House Sparrow,Gorrion comun +AYTFUL,behavBird,Oiseau terrestre,Anatidae,Grand canard,Fuligule morillon,Aythya fuligula,Land Bird,Anatidae,Duck,Tufted Duck,Porran monudo +BUTBUT,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Buse variable,Buteo buteo,Land Bird,Other bird,Bird of prey,Common buzzard,Busardo ratonero +ALCATT,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Martin pecheur d Europe,Alcedo atthis,Land Bird,Other bird,Passerine,Common Kingfisher,Martin pescador comun +OTUSCO,behavBird,Oiseau terrestre,Autre oiseau,Rapace,Hibou petit-duc,Otus scops,Land Bird,Other bird,Bird of prey,Eurasian Scops Owl,Autillo europeo +SYLCUR,behavBird,Oiseau terrestre,Autre oiseau,Passereau,Fauvette babillarde,Sylvia curruca,Land Bird,Other bird,Passerine,Lesser Whitethroat,Curruca zarcerilla diff --git a/data/survey.csv b/data/survey.csv new file mode 100644 index 00000000..1eecdc5e --- /dev/null +++ b/data/survey.csv @@ -0,0 +1,2 @@ +region,survey,computer,shipName,cycle,session +NEA,IBTS,computer_1,Europe,cycle_1,LEG1 \ No newline at end of file diff --git a/data/survey_type.csv b/data/survey_type.csv new file mode 100644 index 00000000..8d57a9f8 --- /dev/null +++ b/data/survey_type.csv @@ -0,0 +1,10 @@ +name +PELGAS +PELACUS_springtime +IBTS +PELACUS_automn +CAMANOC +JUVENA +CGFS +EVHOE +MOOSE \ No newline at end of file diff --git a/data/transect.csv b/data/transect.csv new file mode 100644 index 00000000..da21ef60 --- /dev/null +++ b/data/transect.csv @@ -0,0 +1 @@ +transect;strateType;subRegion;length;wkt \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index 104a7b2b..a78bd794 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -22,7 +22,7 @@ author = "Hytech-Imaging" # The full version, including alpha/beta/rc tags -release = "1.3.2" +release = "1.4.0" # -- General configuration --------------------------------------------------- diff --git a/doc/source/images/init_data_folder_action.png b/doc/source/images/init_data_folder_action.png new file mode 100644 index 00000000..e5c7b86f Binary files /dev/null and b/doc/source/images/init_data_folder_action.png differ diff --git a/doc/source/images/interface.png b/doc/source/images/interface.png index fadd27f2..c52821ea 100644 Binary files a/doc/source/images/interface.png and b/doc/source/images/interface.png differ diff --git a/doc/source/images/merge_dialog.png b/doc/source/images/merge_dialog.png index 899e4c0a..edddce2e 100644 Binary files a/doc/source/images/merge_dialog.png and b/doc/source/images/merge_dialog.png differ diff --git a/doc/source/images/settings_dialog.png b/doc/source/images/settings_dialog.png index 9fed4523..f5ec4576 100644 Binary files a/doc/source/images/settings_dialog.png and b/doc/source/images/settings_dialog.png differ diff --git a/doc/source/images/transect.svg b/doc/source/images/transect.svg new file mode 100644 index 00000000..66873918 --- /dev/null +++ b/doc/source/images/transect.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + diff --git a/doc/source/images/uml_diagram.png b/doc/source/images/uml_diagram.png new file mode 100644 index 00000000..1f7c09ff Binary files /dev/null and b/doc/source/images/uml_diagram.png differ diff --git a/doc/source/index.rst b/doc/source/index.rst index 7db3a4c8..e85a314c 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -15,7 +15,7 @@ In particular, this tool is used by the `Megascope None: if reader.receivers(reader.frame): reader.frame.disconnect(self.onGpsFrame) self.statusDock.desactivateGPS() - if self.session.environmentLayer.featureCount() and next( - self.session.environmentLayer.getFeatures( - QgsFeatureRequest().addOrderBy("dateTime", False) - ) - )["status"] != StatusCode.display(StatusCode.END): - self.session.addEnvironmentFeature(StatusCode.END) - self.tableDock.refresh( - self.session.environmentLayer, self.filterExpr - ) elif not (reader.worker and reader.worker._gps): self.iface.messageBar().pushCritical( "No GPS detected", "retry later" @@ -211,6 +204,17 @@ def createSettingsAction(self) -> SammoSettingsAction: button = SammoSettingsAction( self.mainWindow, self.toolbar, self.session ) + button.reloadTables.connect(self.reloadTables) + return button + + def createHelpAction(self) -> QAction: + button = QAction( + QIcon((Path(__file__).parent / "images" / "help.png").as_posix()), + "Help", + ) + button.setToolTip("Help") + button.triggered.connect(self.openHelp) + self.toolbar.addAction(button) return button def createMergeAction(self) -> SammoSessionAction: @@ -223,6 +227,14 @@ def initGui(self) -> None: self.shortcutAction = QAction("Create shorcuts") self.shortcutAction.triggered.connect(shortcutCreation) self.iface.addPluginToMenu("Sammo-Boat", self.shortcutAction) + self.csvInitAction = QAction("Open init data folder") + self.csvInitAction.triggered.connect(self.initDataFolder) + self.iface.addPluginToMenu("Sammo-Boat", self.csvInitAction) + + def initDataFolder(self) -> None: + QDesktopServices.openUrl( + QUrl.fromLocalFile((Path(__file__).parent / "data").as_posix()) + ) def initShortcuts(self) -> None: self.environmentShortcut = QShortcut( @@ -274,6 +286,19 @@ def initShortcuts(self) -> None: ) self.zoomOutShortcut.activated.connect(self.iface.mapCanvas().zoomOut) + def openHelp(self): + QDesktopServices.openUrl( + QUrl.fromLocalFile( + ( + Path(__file__).parent + / "doc" + / "build" + / "html" + / "index.html" + ).as_posix() + ) + ) + def unload(self): self.activateGPS() # add End environment Status if needed self.gpsReader.stop() @@ -405,7 +430,13 @@ def onGpsFrame( # we udpate the database if we don't need to wait for speed/course if not self.gps_wait: self.session.addGps( - longitude, latitude, h, m, s, speed, course + longitude, + latitude, + h, + m, + s, + self.session.lastGpsInfo["gprmc"]["speed"], + self.session.lastGpsInfo["gprmc"]["course"], ) self.session.lastCaptureTime = gpsNow @@ -426,6 +457,7 @@ def onCreateSession(self, sessionDirectory: str) -> None: # init session self.loading = True QgsProject.instance().clear() + self.tableDock.clean() self.session.init(sessionDirectory) self.session.saveAll() self.loading = False @@ -435,7 +467,6 @@ def onCreateSession(self, sessionDirectory: str) -> None: self.soundRecordingController.onNewSession(sessionDirectory) - self.tableDock.clean() self.tableDock.init( self.session.environmentLayer, self.session.sightingsLayer ) @@ -448,11 +479,23 @@ def onCreateSession(self, sessionDirectory: str) -> None: self.simuGpsAction.onNewSession() def cleanTableDock(self, layerId): - if layerId == self.session.environmentLayer.id(): + if ( + self.session.environmentLayer + and layerId == self.session.environmentLayer.id() + ): self.tableDock.removeTable(self.session.environmentLayer.name()) - elif layerId == self.session.sightingsLayer.id(): + elif ( + self.session.sightingsLayer + and layerId == self.session.sightingsLayer.id() + ): self.tableDock.removeTable(self.session.sightingsLayer.name()) + def reloadTables(self) -> None: + self.tableDock.clean() + self.tableDock.init( + self.session.environmentLayer, self.session.sightingsLayer + ) + def focusOn(self, old, new) -> None: # Set the active on attribute table focus, to use undo/redo action if not new: diff --git a/src/core/database.py b/src/core/database.py index 42b8e7a8..7f512a2a 100644 --- a/src/core/database.py +++ b/src/core/database.py @@ -14,6 +14,7 @@ QgsFeature, QgsProject, QgsWkbTypes, + QgsGeometry, QgsApplication, QgsFeatureSink, QgsVectorLayer, @@ -29,6 +30,7 @@ GPS_TABLE = "gps" SPECIES_TABLE = "species" +BEHAVIOUR_SPECIES_TABLE = "behaviour" OBSERVERS_TABLE = "observers" FOLLOWERS_TABLE = "followers" SIGHTINGS_TABLE = "sightings" @@ -36,8 +38,8 @@ WORLD_TABLE = "world" BOAT_TABLE = "boat" SURVEY_TABLE = "survey" +SURVEY_TYPE_TABLE = "survey_type" TRANSECT_TABLE = "transect" -STRATE_TABLE = "strate" PLATEFORM_TABLE = "plateform" @@ -70,7 +72,6 @@ def init(self, directory: str) -> bool: ENVIRONMENT_TABLE, QgsWkbTypes.Point, ) - self._createTable(self._createFieldsForSpeciesTable(), SPECIES_TABLE) self._createTable( self._fieldsSightings(), SIGHTINGS_TABLE, QgsWkbTypes.Point ) @@ -83,14 +84,26 @@ def init(self, directory: str) -> bool: self._createFieldsForGpsTable(), GPS_TABLE, QgsWkbTypes.Point ) - self._createTable(self._fieldsObserver(), OBSERVERS_TABLE) - # administrator table + self._createTable(self._fieldsObserver(), OBSERVERS_TABLE) + self._populateTable(OBSERVERS_TABLE, "observers.csv") + self._createTable(self._createFieldsForSpeciesTable(), SPECIES_TABLE) + self._populateTable(SPECIES_TABLE, "species.csv") + self._createTable( + self._createFieldsForBehaviourSpeciesTable(), + BEHAVIOUR_SPECIES_TABLE, + ) + self._populateTable(BEHAVIOUR_SPECIES_TABLE, "behav.csv") self._createTable(self._fieldsBoat(), BOAT_TABLE) - self._populateBoatTable() + self._populateTable(BOAT_TABLE, "boat.csv") + self._createTable(self._fieldsSurveyType(), SURVEY_TYPE_TABLE) + self._populateTable(SURVEY_TYPE_TABLE, "survey_type.csv") self._createTable(self._fieldsSurvey(), SURVEY_TABLE) - self._createTable(self._fieldsTransect(), TRANSECT_TABLE) - self._createTable(self._fieldsStrate(), STRATE_TABLE) + self._populateTable(SURVEY_TABLE, "survey.csv") + self._createTable( + self._fieldsTransect(), TRANSECT_TABLE, QgsWkbTypes.LineString + ) + self._populateTable(TRANSECT_TABLE, "transect.csv", ";") self._createTable(self._fieldsPlateform(), PLATEFORM_TABLE) self._populatePlateformTable() @@ -116,7 +129,6 @@ def lastFeature( and not mergeAction ): continue - if not feat: feat = feature elif feature.id() > feat.id(): @@ -135,6 +147,7 @@ def _createFieldsForEnvironmentTable(self) -> QgsFields: fields.append(self._createFieldShortText("right")) fields.append(QgsField("dateTime", QVariant.DateTime)) fields.append(self._createFieldShortText("plateformId")) + fields.append(self._createFieldShortText("transectId")) fields.append(self._createFieldShortText("routeType")) fields.append(QgsField("speed", QVariant.Int)) fields.append(QgsField("courseAverage", QVariant.Int)) @@ -154,9 +167,6 @@ def _createFieldsForEnvironmentTable(self) -> QgsFields: fields.append(self._createFieldShortText("camera")) fields.append(QgsField("comment", QVariant.String, len=200)) fields.append(self._createFieldShortText("center")) - fields.append(self._createFieldShortText("transect")) - fields.append(self._createFieldShortText("strate")) - fields.append(self._createFieldShortText("length")) fields.append(self._createFieldShortText("status", len=5)) fields.append(self._createFieldShortText("soundFile", len=80)) @@ -171,16 +181,30 @@ def _createFieldsForEnvironmentTable(self) -> QgsFields: def _createFieldsForSpeciesTable(self) -> QgsFields: fields = QgsFields() fields.append(self._createFieldShortText("species")) - fields.append(self._createFieldShortText("commonName")) - fields.append(self._createFieldShortText("latinName")) - fields.append(self._createFieldShortText("groupName")) - fields.append(self._createFieldShortText("family")) - fields.append(self._createFieldShortText("taxon")) + fields.append(self._createFieldShortText("behav_cat")) + fields.append(self._createFieldShortText("name_latin")) + fields.append(self._createFieldShortText("taxon_eng")) + fields.append(self._createFieldShortText("family_eng")) + fields.append(self._createFieldShortText("group_eng")) + fields.append(self._createFieldShortText("name_eng")) + fields.append(self._createFieldShortText("taxon_fr")) + fields.append(self._createFieldShortText("family_fr")) + fields.append(self._createFieldShortText("group_fr")) + fields.append(self._createFieldShortText("name_fr")) + fields.append(self._createFieldShortText("taxon_spa")) + fields.append(self._createFieldShortText("family_spa")) + fields.append(self._createFieldShortText("group_spa")) + fields.append(self._createFieldShortText("name_spa")) + return fields + + def _createFieldsForBehaviourSpeciesTable(self) -> QgsFields: + fields = QgsFields() + fields.append(self._createFieldShortText("behav")) + fields.append(self._createFieldShortText("behav_cat")) return fields def _fieldsSightings(self) -> QgsFields: fields = QgsFields() - fields.append(self._createFieldShortText("sightNum")) fields.append(QgsField("dateTime", QVariant.DateTime)) fields.append(self._createFieldShortText("observer")) fields.append(self._createFieldShortText("side")) @@ -193,9 +217,7 @@ def _fieldsSightings(self) -> QgsFields: fields.append(QgsField("angle", QVariant.Int)) fields.append(QgsField("direction", QVariant.Int)) fields.append(self._createFieldShortText("behaviour")) - fields.append(self._createFieldShortText("behavBird")) - fields.append(self._createFieldShortText("behavMam")) - fields.append(self._createFieldShortText("behavShip")) + fields.append(self._createFieldShortText("behavSpecies")) fields.append(self._createFieldShortText("behavGroup")) fields.append(QgsField("comment", QVariant.String, len=200)) fields.append(self._createFieldShortText("soundFile", len=80)) @@ -226,6 +248,7 @@ def _createFieldsForFollowersTable(self) -> QgsFields: fields.append(self._createFieldShortText("soundEnd")) fields.append(QgsField("validated", QVariant.Bool)) fields.append(QgsField("_effortGroup", QVariant.Int)) + fields.append(QgsField("_effortLeg", QVariant.Int)) fields.append(self._createFieldShortText("survey")) fields.append(self._createFieldShortText("cycle")) fields.append(self._createFieldShortText("computer")) @@ -259,6 +282,39 @@ def _fieldsBoat(self) -> QgsFields: fields.append(self._createFieldShortText("name")) return fields + def _populateTable( + self, layer_id: str, csv_name: str, delimiter="," + ) -> None: + lyr = QgsVectorLayer(self.tableUri(layer_id), "no_matter", "ogr") + file = Path(__file__).parent.parent.parent / "data" / csv_name + lines = [] + if file.exists(): + with open(file.as_posix()) as f: + lines = [ + {k: v for k, v in row.items()} + for row in csv.DictReader(f, delimiter=delimiter) + ] + lyr.startEditing() + for attr in lines: + ft = QgsFeature(lyr.fields()) + for k, v in attr.items(): + if k == "wkt": + geom = QgsGeometry.fromWkt(v) + ft.setGeometry(geom) + elif lyr.fields()[k].typeName() == "Integer": + ft[k] = int(v) + elif lyr.fields()[k].typeName() == "Real": + ft[k] = float(v) + else: + ft[k] = v + lyr.addFeature(ft) + lyr.commitChanges() + + def _fieldsSurveyType(self) -> QgsFields: + fields = QgsFields() + fields.append(self._createFieldShortText("name")) + return fields + def _populateBoatTable(self) -> None: boatLyr = QgsVectorLayer(self.tableUri(BOAT_TABLE), "boat", "ogr") file = Path(__file__).parent.parent.parent / "data" / "boat.csv" @@ -290,16 +346,9 @@ def _fieldsSurvey(self) -> QgsFields: def _fieldsTransect(self) -> QgsFields: fields = QgsFields() fields.append(self._createFieldShortText("transect")) - fields.append(self._createFieldShortText("strate")) - fields.append(QgsField("length", QVariant.Int)) - - return fields - - def _fieldsStrate(self) -> QgsFields: - fields = QgsFields() - fields.append(self._createFieldShortText("state")) + fields.append(self._createFieldShortText("strateType")) fields.append(self._createFieldShortText("subRegion")) - fields.append(self._createFieldShortText("region")) + fields.append(QgsField("length", QVariant.Int)) return fields diff --git a/src/core/layers/__init__.py b/src/core/layers/__init__.py index 68e0ebc9..4a157368 100644 --- a/src/core/layers/__init__.py +++ b/src/core/layers/__init__.py @@ -7,11 +7,12 @@ from .boat import SammoBoatLayer from .world import SammoWorldLayer from .survey import SammoSurveyLayer -from .strate import SammoStrateLayer from .species import SammoSpeciesLayer from .transect import SammoTransectLayer from .followers import SammoFollowersLayer from .observers import SammoObserversLayer from .sightings import SammoSightingsLayer from .plateform import SammoPlateformLayer +from .behav import SammoBehaviourSpeciesLayer +from .survey_type import SammoSurveyTypeLayer from .environment import SammoEnvironmentLayer diff --git a/src/core/layers/behav.py b/src/core/layers/behav.py new file mode 100644 index 00000000..5d87c1c9 --- /dev/null +++ b/src/core/layers/behav.py @@ -0,0 +1,23 @@ +# coding: utf8 + +__contact__ = "info@hytech-imaging.fr" +__copyright__ = "Copyright (c) 2023 Hytech Imaging" + +from qgis.core import QgsVectorLayer +from ..database import ( + SammoDataBase, + BEHAVIOUR_SPECIES_TABLE, +) + +from .layer import SammoLayer + + +class SammoBehaviourSpeciesLayer(SammoLayer): + def __init__(self, db: SammoDataBase): + super().__init__(db, BEHAVIOUR_SPECIES_TABLE, "Behaviour_species") + + def _init(self, layer: QgsVectorLayer) -> None: + self._init_widgets(layer) + + def _init_widgets(self, layer: QgsVectorLayer) -> None: + pass diff --git a/src/core/layers/environment.py b/src/core/layers/environment.py index 51d6fb85..42a751c9 100644 --- a/src/core/layers/environment.py +++ b/src/core/layers/environment.py @@ -19,6 +19,7 @@ ) from .layer import SammoLayer +from .transect import SammoTransectLayer from .plateform import SammoPlateformLayer from .observers import SammoObserversLayer @@ -29,6 +30,7 @@ def __init__( db: SammoDataBase, observersLayer: SammoObserversLayer, plateformLayer: SammoPlateformLayer, + transectLayer: SammoTransectLayer, ): super().__init__( db, @@ -39,6 +41,7 @@ def __init__( ) self.observersLayer = observersLayer self.plateformLayer = plateformLayer + self.transectLayer = transectLayer def _init(self, layer: QgsVectorLayer): self._init_symbology(layer) @@ -93,6 +96,29 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: layer.setEditFormConfig(form_config) layer.setFieldAlias(idx, "plateform") + # transect + idx = layer.fields().indexFromName("transectId") + cfg = { + "AllowMulti": False, + "AllowNull": True, + "Description": "transect", + "Key": "fid", + "Layer": self.transectLayer.layer.id(), + "LayerName": self.transectLayer.name, + "LayerProviderName": "ogr", + "LayerSource": self.transectLayer.uri, + "NofColumns": 1, + "OrderByValue": False, + "UseCompleter": False, + "Value": "transect", + } + setup = QgsEditorWidgetSetup("ValueRelation", cfg) + layer.setEditorWidgetSetup(idx, setup) + form_config = layer.editFormConfig() + form_config.setReadOnly(idx, False) + layer.setEditFormConfig(form_config) + layer.setFieldAlias(idx, "transect") + # route type idx = layer.fields().indexFromName("routeType") cfg = {} @@ -346,7 +372,7 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: "shipName", "computer", "transect", - "strate", + "strateType", "length", "plateformHeight", "_effortGroup", @@ -354,7 +380,7 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: idx = layer.fields().indexFromName(field) form_config = layer.editFormConfig() form_config.setReadOnly(idx, True) - if field != "dateTime": + if field not in ["dateTime", "validated"]: setup = QgsEditorWidgetSetup("Hidden", {}) layer.setEditorWidgetSetup(idx, setup) if field == "validated": @@ -374,7 +400,6 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: cfg["map"] = [ {"Begin": "Begin"}, {"Add": "Add"}, - {"End": "End"}, ] setup = QgsEditorWidgetSetup("ValueMap", cfg) layer.setEditorWidgetSetup(idx, setup) @@ -406,20 +431,24 @@ def _init_conditional_style(self, layer: QgsVectorLayer) -> None: style = QgsConditionalStyle(expr) style.setBackgroundColor(QColor("orange")) for fieldName in ["routeType", "speed", "courseAverage"]: + style.setName(fieldName) layer.conditionalStyles().setFieldStyles(fieldName, [style]) # glareFrom expr = "if (\"glareSever\" = 'none', @value != 0, False)" style = QgsConditionalStyle(expr) + style.setName("glareSever") style.setBackgroundColor(QColor("orange")) layer.conditionalStyles().setFieldStyles("glareFrom", [style]) # glareTo style = QgsConditionalStyle(expr) + style.setName("glareTo") style.setBackgroundColor(QColor("orange")) layer.conditionalStyles().setFieldStyles("glareTo", [style]) # validated - style = QgsConditionalStyle("validated is True") + style = QgsConditionalStyle("validated") + style.setName("Validated") style.setBackgroundColor(QColor(178, 223, 138)) layer.conditionalStyles().setRowStyles([style]) diff --git a/src/core/layers/layer.py b/src/core/layers/layer.py index 7d6f5b07..f96d3867 100644 --- a/src/core/layers/layer.py +++ b/src/core/layers/layer.py @@ -56,6 +56,10 @@ def addToProject(self, project: QgsProject) -> None: self._hideWidgetFid(layer) self._init(layer) + layer.startEditing() + layer.commitChanges() + layer.startEditing() + def _init(self, layer: QgsVectorLayer) -> None: pass diff --git a/src/core/layers/plateform.py b/src/core/layers/plateform.py index d3100010..1a71aa99 100644 --- a/src/core/layers/plateform.py +++ b/src/core/layers/plateform.py @@ -20,23 +20,14 @@ def _init(self, layer: QgsVectorLayer) -> None: self._init_widgets(layer) def _init_widgets(self, layer: QgsVectorLayer) -> None: - # shipName - idx = layer.fields().indexFromName("ship") - cfg = {} - cfg["map"] = [ - {"Thalassa": "Thalassa"}, - {"Europe": "Europe"}, - {"PourquoiPas": "PourquoiPas"}, - ] - setup = QgsEditorWidgetSetup("ValueMap", cfg) - layer.setEditorWidgetSetup(idx, setup) - # plateform idx = layer.fields().indexFromName("plateform") cfg = {} cfg["map"] = [ + {"bridge": "bridge"}, {"bridge_inside": "bridge_inside"}, {"bridge_outside": "bridge_outside"}, + {"upper_bridge": "upper_bridge"}, {"upper_bridge_outside": "upper_bridge_outside"}, {"upper_bridge_inside": "upper_bridge_inside"}, {"deck": "deck"}, @@ -56,3 +47,24 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: } setup = QgsEditorWidgetSetup("Range", cfg) layer.setEditorWidgetSetup(idx, setup) + + def _link_boat(self, boatLayer: SammoLayer): + # shipName + idx = self.layer.fields().indexFromName("ship") + cfg = { + "AllowMulti": False, + "AllowNull": False, + "Description": '"ship"', + "FilterExpression": "", + "Key": "name", + "Layer": boatLayer.layer.id(), + "LayerName": boatLayer.name, + "LayerProviderName": "ogr", + "LayerSource": boatLayer.uri, + "NofColumns": 1, + "OrderByValue": False, + "UseCompleter": False, + "Value": "name", + } + setup = QgsEditorWidgetSetup("ValueRelation", cfg) + self.layer.setEditorWidgetSetup(idx, setup) diff --git a/src/core/layers/sightings.py b/src/core/layers/sightings.py index e40bcd5f..228555f7 100644 --- a/src/core/layers/sightings.py +++ b/src/core/layers/sightings.py @@ -21,10 +21,15 @@ ) from .layer import SammoLayer, NULL +from .behav import SammoBehaviourSpeciesLayer class SammoSightingsLayer(SammoLayer): - def __init__(self, db: SammoDataBase): + def __init__( + self, + db: SammoDataBase, + behaviourSpeciesLayer: SammoBehaviourSpeciesLayer, + ): super().__init__( db, SIGHTINGS_TABLE, @@ -32,6 +37,7 @@ def __init__(self, db: SammoDataBase): soundAction=True, duplicateAction=True, ) + self.behaviourSpeciesLayer = behaviourSpeciesLayer def _init(self, layer: QgsVectorLayer) -> None: self._init_symbology(layer) @@ -192,51 +198,29 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: layer.setEditorWidgetSetup(idx, setup) layer.setFieldAlias(idx, "group") - # behavMam - idx = layer.fields().indexFromName("behavMam") - cfg = {} - cfg["map"] = [ - {"": NULL}, - {"bow": "bow"}, - {"milling": "milling"}, - {"fast_swimming": "fast_swimming"}, - {"slow_swimming": "slow_swimming"}, - {"diving": "diving"}, - {"breaching": "breaching"}, - ] - setup = QgsEditorWidgetSetup("ValueMap", cfg) - layer.setEditorWidgetSetup(idx, setup) - layer.setFieldAlias(idx, "mam") - - # behavBird - idx = layer.fields().indexFromName("behavBird") - cfg = {} - cfg["map"] = [ - {"": NULL}, - {"attacked": "attacked"}, - {"with_prey": "with_prey"}, - {"klepto": "klepto"}, - {"diving": "diving"}, - {"follow_boat": "follow_boat"}, - {"random_flight": "random_flight"}, - {"circular_flight": "circular_flight"}, - {"straight_flight": "straight_flight"}, - ] - setup = QgsEditorWidgetSetup("ValueMap", cfg) - layer.setEditorWidgetSetup(idx, setup) - layer.setFieldAlias(idx, "bird") - - # behavShip - idx = layer.fields().indexFromName("behavShip") - cfg = {} - cfg["map"] = [ - {"": NULL}, - {"fishing": "fishing"}, - {"route": "route"}, - ] - setup = QgsEditorWidgetSetup("ValueMap", cfg) + # behavSpecies + idx = layer.fields().indexFromName("behavSpecies") + cfg = { + "AllowMulti": False, + "AllowNull": True, + "Description": "", + "FilterExpression": ( + '"behav_cat" = attribute(get_feature(' + "layer_property('Species', 'id')" + ",'species',current_value('species')),'behav_cat')" + ), + "Key": "behav", + "Layer": self.behaviourSpeciesLayer.layer.id(), + "LayerName": self.behaviourSpeciesLayer.name, + "LayerProviderName": "ogr", + "LayerSource": self.behaviourSpeciesLayer.uri, + "NofColumns": 1, + "OrderByValue": False, + "UseCompleter": False, + "Value": "behav", + } + setup = QgsEditorWidgetSetup("ValueRelation", cfg) layer.setEditorWidgetSetup(idx, setup) - layer.setFieldAlias(idx, "ship") # soundFile, soundStart, soundEnd, dateTime for field in [ @@ -245,7 +229,6 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: "soundEnd", "dateTime", "validated", - "sightNum", "observer", "_effortGroup", ]: @@ -329,44 +312,42 @@ def _init_conditional_style(self, layer: QgsVectorLayer) -> None: 'species', attribute('species') ) - , 'taxon' + , 'behav_cat' ) ), @value is NULL, {} ) """ - addExpr = """ - ( - if( "behavMam" ,1,0) + - if( "behavBird" ,1,0) + - if( "behavShip" ,1,0) - > 1 + behavs = ( + "'" + + "','".join( + self.behaviourSpeciesLayer.layer.uniqueValues( + self.behaviourSpeciesLayer.layer.fields().indexOf( + "behav_cat" + ) + ) ) - """ - - taxons = "'Marine Mammal', 'Seabird', 'Ship'" - style = QgsConditionalStyle(expr.format(taxons, "False")) + + "'" + ) + style = QgsConditionalStyle(expr.format(behavs, "False")) style.setBackgroundColor(QColor("orange")) layer.conditionalStyles().setFieldStyles("behaviour", [style]) - # behavMam - taxon = "'Marine Mammal'" - style = QgsConditionalStyle(expr.format(taxon, addExpr)) - style.setBackgroundColor(QColor("orange")) - layer.conditionalStyles().setFieldStyles("behavMam", [style]) - - # behavBird - taxon = "'Seabird'" - style = QgsConditionalStyle(expr.format(taxon, addExpr)) - style.setBackgroundColor(QColor("orange")) - layer.conditionalStyles().setFieldStyles("behavBird", [style]) - - # behavShip - taxon = "'Ship'" - style = QgsConditionalStyle(expr.format(taxon, addExpr)) + # behavSpecies + expr = """ + attribute(get_feature( + layer_property('Species', 'id') + ,'species',"species" + ),'behav_cat') + != attribute(get_feature( + layer_property('Behaviour_species', 'id') + ,'behav',"behavSpecies" + ),'behav_cat') + """ + style = QgsConditionalStyle(expr) style.setBackgroundColor(QColor("orange")) - layer.conditionalStyles().setFieldStyles("behavShip", [style]) + layer.conditionalStyles().setFieldStyles("behavSpecies", [style]) # species expr = """ diff --git a/src/core/layers/strate.py b/src/core/layers/strate.py deleted file mode 100644 index 59d5ac2c..00000000 --- a/src/core/layers/strate.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding: utf8 - -__contact__ = "info@hytech-imaging.fr" -__copyright__ = "Copyright (c) 2022 Hytech Imaging" - -from qgis.core import QgsEditorWidgetSetup, QgsVectorLayer -from ..database import ( - SammoDataBase, - STRATE_TABLE, -) - -from .layer import SammoLayer - - -class SammoStrateLayer(SammoLayer): - def __init__(self, db: SammoDataBase): - super().__init__(db, STRATE_TABLE, "Strate") - - def _init(self, layer: QgsVectorLayer) -> None: - self._init_widgets(layer) - - def _init_widgets(self, layer: QgsVectorLayer) -> None: - # subRegion - idx = layer.fields().indexFromName("subRegion") - cfg = {} - cfg["map"] = [ - {"MED": "MED"}, - {"ATL": "ATL"}, - {"MAN": "MAN"}, - ] - setup = QgsEditorWidgetSetup("ValueMap", cfg) - layer.setEditorWidgetSetup(idx, setup) - - # region - idx = layer.fields().indexFromName("region") - cfg = {} - cfg["map"] = [ - {"NEA": "NEA"}, - {"NSP": "NSP"}, - {"EA": "EA"}, - {"WC": "WC"}, - ] - setup = QgsEditorWidgetSetup("ValueMap", cfg) - layer.setEditorWidgetSetup(idx, setup) diff --git a/src/core/layers/survey.py b/src/core/layers/survey.py index 0d409406..65e40a9d 100644 --- a/src/core/layers/survey.py +++ b/src/core/layers/survey.py @@ -11,12 +11,19 @@ ) from .layer import SammoLayer from .boat import SammoBoatLayer +from .survey_type import SammoSurveyTypeLayer class SammoSurveyLayer(SammoLayer): - def __init__(self, db: SammoDataBase, boatLayer: SammoBoatLayer): + def __init__( + self, + db: SammoDataBase, + boatLayer: SammoBoatLayer, + surveyTypeLayer: SammoSurveyTypeLayer, + ): super().__init__(db, SURVEY_TABLE, "Survey") self.boatLayer = boatLayer + self.surveyTypeLayer = surveyTypeLayer def _init(self, layer: QgsVectorLayer) -> None: self._init_widgets(layer) @@ -36,19 +43,22 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: # survey idx = layer.fields().indexFromName("survey") - cfg = {} - cfg["map"] = [ - {"PELGAS": "PELGAS"}, - {"PELACUS_springtime": "PELACUS_springtime"}, - {"IBTS": "IBTS"}, - {"PELACUS_automn": "PELACUS_automn"}, - {"CAMANOC": "CAMANOC"}, - {"JUVENA": "JUVENA"}, - {"CGFS": "CGFS"}, - {"EVHOE": "EVHOE"}, - {"MOOSE": "MOOSE"}, - ] - setup = QgsEditorWidgetSetup("ValueMap", cfg) + cfg = { + "AllowMulti": False, + "AllowNull": False, + "Description": '"survey"', + "FilterExpression": "", + "Key": "name", + "Layer": self.surveyTypeLayer.layer.id(), + "LayerName": self.surveyTypeLayer.name, + "LayerProviderName": "ogr", + "LayerSource": self.surveyTypeLayer.uri, + "NofColumns": 1, + "OrderByValue": False, + "UseCompleter": False, + "Value": "name", + } + setup = QgsEditorWidgetSetup("ValueRelation", cfg) layer.setEditorWidgetSetup(idx, setup) # shipName @@ -80,6 +90,8 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: {"LEG3": "LEG3"}, {"LEG4": "LEG4"}, {"LEG5": "LEG5"}, + {"LEG6": "LEG6"}, + {"LEG7": "LEG7"}, ] setup = QgsEditorWidgetSetup("ValueMap", cfg) layer.setEditorWidgetSetup(idx, setup) diff --git a/src/core/layers/survey_type.py b/src/core/layers/survey_type.py new file mode 100644 index 00000000..e01d260c --- /dev/null +++ b/src/core/layers/survey_type.py @@ -0,0 +1,23 @@ +# coding: utf8 + +__contact__ = "info@hytech-imaging.fr" +__copyright__ = "Copyright (c) 2023 Hytech Imaging" + +from qgis.core import QgsVectorLayer + +from ..database import ( + SammoDataBase, + SURVEY_TYPE_TABLE, +) +from .layer import SammoLayer + + +class SammoSurveyTypeLayer(SammoLayer): + def __init__(self, db: SammoDataBase): + super().__init__(db, SURVEY_TYPE_TABLE, "Survey_type") + + def _init(self, layer: QgsVectorLayer) -> None: + self._init_widgets(layer) + + def _init_widgets(self, layer: QgsVectorLayer) -> None: + pass diff --git a/src/core/layers/transect.py b/src/core/layers/transect.py index e0f56e2b..f103a6b1 100644 --- a/src/core/layers/transect.py +++ b/src/core/layers/transect.py @@ -3,7 +3,8 @@ __contact__ = "info@hytech-imaging.fr" __copyright__ = "Copyright (c) 2022 Hytech Imaging" -from qgis.core import QgsEditorWidgetSetup, QgsVectorLayer +from qgis.PyQt.QtGui import QColor +from qgis.core import QgsEditorWidgetSetup, QgsVectorLayer, QgsLineSymbol from ..database import ( SammoDataBase, TRANSECT_TABLE, @@ -17,11 +18,18 @@ def __init__(self, db: SammoDataBase): super().__init__(db, TRANSECT_TABLE, "Transect") def _init(self, layer: QgsVectorLayer) -> None: + self._init_symbology(layer) self._init_widgets(layer) + def _init_symbology(self, layer) -> None: + symbol = QgsLineSymbol.createSimple( + {"color": QColor("black"), "width": "0.5"} + ) + layer.renderer().setSymbol(symbol) + def _init_widgets(self, layer: QgsVectorLayer) -> None: # strate - idx = layer.fields().indexFromName("strate") + idx = layer.fields().indexFromName("strateType") cfg = {} cfg["map"] = [ {"Neritic": "Neritic"}, @@ -30,3 +38,14 @@ def _init_widgets(self, layer: QgsVectorLayer) -> None: ] setup = QgsEditorWidgetSetup("ValueMap", cfg) layer.setEditorWidgetSetup(idx, setup) + + # subRegion + idx = layer.fields().indexFromName("subRegion") + cfg = {} + cfg["map"] = [ + {"MED": "MED"}, + {"ATL": "ATL"}, + {"MAN": "MAN"}, + ] + setup = QgsEditorWidgetSetup("ValueMap", cfg) + layer.setEditorWidgetSetup(idx, setup) diff --git a/src/core/session.py b/src/core/session.py index b5af1c77..c0cf2866 100644 --- a/src/core/session.py +++ b/src/core/session.py @@ -3,16 +3,14 @@ __contact__ = "info@hytech-imaging.fr" __copyright__ = "Copyright (c) 2022 Hytech Imaging" -import os from pathlib import Path -from shutil import copyfile from datetime import datetime from typing import List, Optional, Dict, Union from qgis.PyQt.QtGui import QColor -from qgis.PyQt.QtWidgets import QProgressBar, QMessageBox -from qgis.PyQt.QtCore import QDate, QDateTime +from qgis.PyQt.QtWidgets import QMessageBox +from qgis.utils import iface from qgis.core import ( QgsProject, QgsGeometry, @@ -38,14 +36,15 @@ SammoBoatLayer, SammoWorldLayer, SammoSurveyLayer, - SammoStrateLayer, SammoSpeciesLayer, SammoTransectLayer, SammoPlateformLayer, SammoFollowersLayer, SammoObserversLayer, SammoSightingsLayer, + SammoSurveyTypeLayer, SammoEnvironmentLayer, + SammoBehaviourSpeciesLayer, ) from .sound_recording_controller import RecordType @@ -57,13 +56,14 @@ def __init__(self): self._gpsLayer: SammoGpsLayer = None self._worldLayer: SammoWorldLayer = None self._speciesLayer: SammoSpeciesLayer = None + self._behaviourSpeciesLayer: SammoBehaviourSpeciesLayer = None self._followersLayer: SammoFollowersLayer = None self._observersLayer: SammoObserversLayer = None self._sightingsLayer: SammoSightingsLayer = None self._environmentLayer: SammoEnvironmentLayer = None self._surveyLayer: SammoSurveyLayer = None + self._surveyTypeLayer: SammoSurveyLayer = None self._transectLayer: SammoTransectLayer = None - self._strateLayer: SammoStrateLayer = None self._plateformLayer: SammoPlateformLayer = None self.lastGpsInfo: Dict[ str, @@ -107,6 +107,10 @@ def observersLayer(self) -> QgsVectorLayer: def speciesLayer(self) -> QgsVectorLayer: return self._speciesLayer.layer + @property + def behaviourSpeciesLayer(self) -> QgsVectorLayer: + return self._behaviourSpeciesLayer.layer + @property def sightingsLayer(self) -> QgsVectorLayer: if self._sightingsLayer: @@ -126,9 +130,9 @@ def surveyLayer(self) -> QgsVectorLayer: return None @property - def strateLayer(self) -> QgsVectorLayer: - if self._strateLayer: - return self._strateLayer.layer + def surveyTypeLayer(self) -> QgsVectorLayer: + if self._surveyLayer: + return self._surveyTypeLayer.layer return None @property @@ -150,10 +154,11 @@ def allLayers(self) -> List[QgsVectorLayer]: self.gpsLayer, self.followersLayer, self.observersLayer, + self.behaviourSpeciesLayer, self.speciesLayer, self.sightingsLayer, self.surveyLayer, - self.strateLayer, + self.surveyTypeLayer, self.boatLayer, self.plateformLayer, self.transectLayer, @@ -169,19 +174,27 @@ def init(self, directory: str, load: bool = True) -> None: # Administrator table self._plateformLayer = SammoPlateformLayer(self.db) self._boatLayer = SammoBoatLayer(self.db, self._plateformLayer) - self._surveyLayer = SammoSurveyLayer(self.db, self._boatLayer) + self._surveyTypeLayer = SammoSurveyTypeLayer(self.db) + self._surveyLayer = SammoSurveyLayer( + self.db, self._boatLayer, self._surveyTypeLayer + ) self._transectLayer = SammoTransectLayer(self.db) - self._strateLayer = SammoStrateLayer(self.db) self._observersLayer = SammoObserversLayer(self.db) + self._behaviourSpeciesLayer = SammoBehaviourSpeciesLayer(self.db) self._speciesLayer = SammoSpeciesLayer(self.db) self._gpsLayer = SammoGpsLayer(self.db) - self._sightingsLayer = SammoSightingsLayer(self.db) + self._sightingsLayer = SammoSightingsLayer( + self.db, self._behaviourSpeciesLayer + ) self._followersLayer = SammoFollowersLayer( self.db, self._observersLayer, self._speciesLayer ) self._environmentLayer = SammoEnvironmentLayer( - self.db, self._observersLayer, self._plateformLayer + self.db, + self._observersLayer, + self._plateformLayer, + self._transectLayer, ) # create database if necessary @@ -192,10 +205,12 @@ def init(self, directory: str, load: bool = True) -> None: self._worldLayer.addToProject(project) self._plateformLayer.addToProject(project) self._boatLayer.addToProject(project) + self._plateformLayer._link_boat(self._boatLayer) + self._surveyTypeLayer.addToProject(project) self._surveyLayer.addToProject(project) self._transectLayer.addToProject(project) - self._strateLayer.addToProject(project) self._gpsLayer.addToProject(project) + self._behaviourSpeciesLayer.addToProject(project) self._speciesLayer.addToProject(project) self._sightingsLayer.addToProject(project) self._observersLayer.addToProject(project) @@ -223,17 +238,21 @@ def init(self, directory: str, load: bool = True) -> None: self._gpsLayer, self._boatLayer, self._worldLayer, + self._behaviourSpeciesLayer, self._speciesLayer, self._followersLayer, self._observersLayer, self._sightingsLayer, self._environmentLayer, self._surveyLayer, + self._surveyTypeLayer, self._transectLayer, - self._strateLayer, self._plateformLayer, ]: layer._init(layer.layer) + layer.layer.startEditing() # featureCount update + layer.layer.commitChanges() + self._plateformLayer._link_boat(self._boatLayer) self.environmentLayer.actions().clearActions() self._environmentLayer.addSoundAction(self.environmentLayer) self._environmentLayer.addDuplicateAction(self.environmentLayer) @@ -247,44 +266,72 @@ def init(self, directory: str, load: bool = True) -> None: self.updateRouteTypeStatus ) - def addEnvironmentFeature( - self, status: Optional[StatusCode] = None - ) -> QgsVectorLayer: + def surveyValues(self, layer: QgsVectorLayer) -> tuple: + survey = ( + next(self.surveyLayer.getFeatures()) + if self.surveyLayer.featureCount() > 0 + else None + ) + if ( + not survey + or not survey["survey"] + or not survey["cycle"] + or not survey["computer"] + or not survey["shipName"] + or not survey["session"] + ): + iface.messageBar().pushWarning( + f"{layer.name().lower()}", + "Administration table `survey` is not fulfilled," + " all sighting attributes cannot be filled", + ) + survey_value = "" + cycle_value = "" + computer_value = "" + ship_value = "" + session_value = "" + else: + survey_value = survey["survey"] + cycle_value = survey["cycle"] + computer_value = survey["computer"] + ship_value = survey["shipName"] + session_value = survey["session"] + return ( + survey_value, + cycle_value, + computer_value, + ship_value, + session_value, + ) + + def addEnvironmentFeature(self) -> QgsVectorLayer: layer = self.environmentLayer statusCode = StatusCode.display( - status - if status - else ( - StatusCode.BEGIN - if ( - layer.featureCount() - and next( - layer.getFeatures( - QgsFeatureRequest().addOrderBy("dateTime", False) - ) - )["status"] - == StatusCode.display(StatusCode.END) - ) - else StatusCode(int(bool(layer.featureCount()))) - ) + StatusCode(int(bool(layer.featureCount()))) ) - effortGroup = layer.maximumValue( - layer.fields().indexOf("_effortGroup") + + # Administration table values + ( + survey_value, + cycle_value, + computer_value, + ship_value, + session_value, + ) = self.surveyValues(layer) + + # EffortGroup management + effortGroup = max( + layer.maximumValue(layer.fields().indexOf("_effortGroup")), 0 ) - shipName = "" - if self.surveyLayer.featureCount() > 0: - shipName = next(self.surveyLayer.getFeatures())["shipName"] if effortGroup and statusCode == StatusCode.display(StatusCode.BEGIN): effortGroup += 1 - effortLeg = 0 for ft in layer.getFeatures( QgsFeatureRequest(QgsExpression(f"_effortGroup = {effortGroup}")) ): if ft["_effortLeg"] > effortLeg: effortLeg = ft["_effortLeg"] - if statusCode != StatusCode.display(StatusCode.END): - effortLeg += 1 + effortLeg += 1 self._addFeature( layer, @@ -294,7 +341,11 @@ def addEnvironmentFeature( _effortLeg=effortLeg or 1, speed=self.lastGpsInfo["gprmc"]["speed"], courseAverage=self.lastGpsInfo["gprmc"]["course"], - shipName=shipName, + survey=survey_value, + cycle=cycle_value, + session=session_value, + computer=computer_value, + shipName=ship_value, ) return layer @@ -304,14 +355,12 @@ def updateRouteTypeStatus(self, fid: int, idx: int, value: object) -> None: # feature, the index of the modified attribute and the new value of # this attribute. # If the attribute changed is routeType, we will check the previous - # feature to check if the routeType change between the two feature. + # feature to check if the routeType changes between the two feature. # If it's so, the status of the feature that has been changed is set on - # StatusCode.BEGIN to start a new route, and a new feature is created - # with a StatusCode.END to end the previous route. + # StatusCode.BEGIN to start a new route. if not self.environmentLayer or idx not in [ self.environmentLayer.fields().indexOf("routeType"), - self.environmentLayer.fields().indexOf("status"), ]: return elif ( @@ -323,43 +372,13 @@ def updateRouteTypeStatus(self, fid: int, idx: int, value: object) -> None: for prevFeat in self.environmentLayer.getFeatures(request): if prevFeat["fid"] == feat["fid"]: continue - elif prevFeat["status"] == StatusCode.display( - StatusCode.END - ) or feat["status"] == StatusCode.display(StatusCode.END): - return - elif ( - prevFeat["status"] - in [ - StatusCode.display(StatusCode.BEGIN), - StatusCode.display(StatusCode.ADD), - ] - and prevFeat["routeType"] == feat["routeType"] - ): - self.environmentLayer.changeAttributeValue( - fid, - self.environmentLayer.fields().indexOf("status"), - StatusCode.display(StatusCode.ADD), - ) + elif prevFeat["routeType"] == feat["routeType"]: self.environmentLayer.changeAttributeValue( fid, self.environmentLayer.fields().indexOf("_effortGroup"), prevFeat["_effortGroup"], ) elif prevFeat["routeType"] != feat["routeType"]: - ft = QgsVectorLayerUtils.createFeature( - self.environmentLayer - ) - ft.setGeometry(feat.geometry()) - for attr in feat.fields().names(): - if attr in ["fid", "routeType", "dateTime", "status"]: - continue - ft[attr] = feat[attr] - ft["routeType"] = prevFeat["routeType"] - ft["dateTime"] = QDateTime(feat["dateTime"]) - ft["status"] = StatusCode.display(StatusCode.END) - ft["_effortLeg"] = prevFeat["_effortLeg"] - self.environmentLayer.addFeature(ft) - self.environmentLayer.changeAttributeValue( fid, self.environmentLayer.fields().indexOf("status"), @@ -375,46 +394,54 @@ def updateRouteTypeStatus(self, fid: int, idx: int, value: object) -> None: self.environmentLayer.fields().indexOf("_effortLeg"), 1, ) - self.environmentLayer.changeAttributeValue( - fid, - self.environmentLayer.fields().indexOf("dateTime"), - ft["dateTime"].addSecs(1), - ) - break - - elif ( - self.environmentLayer - and idx == self.environmentLayer.fields().indexOf("status") - ): - feat = self.environmentLayer.getFeature(fid) - if feat["status"] != StatusCode.display(StatusCode.END): - return - request = QgsFeatureRequest().addOrderBy("dateTime", False) - for prevFeat in self.environmentLayer.getFeatures(request): - if prevFeat["fid"] == feat["fid"]: - continue - self.environmentLayer.changeAttributeValue( - fid, - self.environmentLayer.fields().indexOf("_effortLeg"), - prevFeat["_effortLeg"], - ) break def addSightingsFeature(self) -> QgsVectorLayer: layer = self.sightingsLayer - self._addFeature(layer, geom=self.lastGpsInfo["geometry"]) + survey_value, cycle_value, computer_value, _, _ = self.surveyValues( + layer + ) + effortGroup = 1 + effortLeg = 1 + if self.environmentLayer and self.environmentLayer.featureCount(): + ft = self.db.lastFeature(self.environmentLayer) + effortGroup = ft["_effortGroup"] + effortLeg = ft["_effortLeg"] + self._addFeature( + layer, + geom=self.lastGpsInfo["geometry"], + survey=survey_value, + cycle=cycle_value, + computer=computer_value, + _effortGroup=effortGroup, + _effortLeg=effortLeg, + ) return layer def addFollowersFeature( self, dt: str, geom: QgsGeometry, focalId: int, duplicate: bool ) -> None: layer = self.followersLayer + survey_value, cycle_value, computer_value, _, _ = self.surveyValues( + layer + ) + effortGroup = 1 + effortLeg = 1 + if self.environmentLayer and self.environmentLayer.featureCount(): + ft = self.db.lastFeature(self.environmentLayer) + effortGroup = ft["_effortGroup"] + effortLeg = ft["_effortLeg"] self._addFeature( layer, dt, geom, duplicate, _focalId=focalId, + survey=survey_value, + cycle=cycle_value, + computer=computer_value, + _effortGroup=effortGroup, + _effortLeg=effortLeg, ) def needsSaving(self) -> None: @@ -456,177 +483,43 @@ def saveAll(self) -> None: layer.commitChanges() layer.startEditing() - def validate(self, merge=False): + def validate(self, merge=False) -> None: selectedMode = bool( self.environmentLayer.selectedFeatureCount() + self.sightingsLayer.selectedFeatureCount() + self.followersLayer.selectedFeatureCount() ) - survey = ( - next(self.surveyLayer.getFeatures()) - if self.surveyLayer.featureCount() > 0 - else None - ) - transect = ( - next(self.transectLayer.getFeatures()) - if self.transectLayer.featureCount() > 0 - else None - ) - environmentLayer = self.environmentLayer - environmentLayer.startEditing() - - # effort status check - effortIds = environmentLayer.uniqueValues( - environmentLayer.fields().indexOf("_effortGroup") - ) - errors = [] - for effortId in effortIds: - effortIt = environmentLayer.getFeatures( - QgsFeatureRequest().setFilterExpression( - f"_effortGroup = {effortId}" - ) - ) - statusCodes = {ft["datetime"]: ft["status"] for ft in effortIt} - if ( - StatusCode.display(StatusCode.BEGIN) - not in statusCodes.values() - ): - errors.append( - f"Missing BEGIN code for effortGroup {effortId} (before " - f"{min(statusCodes).toPyDateTime().isoformat()} record)" - ) - elif ( - StatusCode.display(StatusCode.END) not in statusCodes.values() - ): - errors.append( - f"Missing END code for effortGroup {effortId} (after " - f"{max(statusCodes).toPyDateTime().isoformat()} record)" - ) - if errors: - errors.append("Please, resolve errors before validation") - QMessageBox.warning( - None, "Errors detected in effort status", "\n".join(errors) - ) - return - - featuresIterator = ( - environmentLayer.getSelectedFeatures() - if selectedMode - else environmentLayer.getFeatures() - ) - for feat in featuresIterator: - if feat["validated"]: - continue - if survey: - for attr in [ - "survey", - "cycle", - "session", - "computer", - ]: - if attr == "computer" and feat["computer"]: - continue - environmentLayer.changeAttributeValue( - feat.id(), - environmentLayer.fields().indexOf(attr), - survey[attr], - ) - if transect: - for attr in ["transect", "strate", "length"]: - environmentLayer.changeAttributeValue( - feat.id(), - environmentLayer.fields().indexOf(attr), - transect[attr], - ) + self.effortCheck(self.environmentLayer) - idx = environmentLayer.fields().indexOf("validated") - environmentLayer.changeAttributeValue( - feat.id(), - idx, - not merge, + def validateFeatures(selectedLayer: QgsVectorLayer) -> None: + selectedLayer.startEditing() + featuresIterator = ( + selectedLayer.getSelectedFeatures() + if selectedMode + else selectedLayer.getFeatures() ) - environmentLayer.commitChanges() - environmentLayer.startEditing() - - sightingsLayer = self.sightingsLayer - sightingsLayer.startEditing() - featuresIterator = ( - sightingsLayer.getSelectedFeatures() - if selectedMode - else sightingsLayer.getFeatures() - ) - for feat in featuresIterator: - if feat["validated"]: - continue - if survey: - for attr in [ - "survey", - "cycle", - "computer", - ]: - if attr == "computer" and feat["computer"]: - continue - sightingsLayer.changeAttributeValue( - feat.id(), - sightingsLayer.fields().indexOf(attr), - survey[attr], - ) - - if survey: - sightingsLayer.changeAttributeValue( + for feat in featuresIterator: + if feat["validated"]: + continue + idx = selectedLayer.fields().indexOf("validated") + selectedLayer.changeAttributeValue( feat.id(), - sightingsLayer.fields().indexOf("sightNum"), - "-".join( - [ - str(survey["survey"]), - str(survey["session"]), - str(survey["computer"]), - str(feat["fid"]), - ] - ), + idx, + not merge, ) - idx = sightingsLayer.fields().indexOf("validated") - sightingsLayer.changeAttributeValue( - feat.id(), - idx, - not merge, + selectedLayer.commitChanges() + selectedLayer.startEditing() + selectedLayer.deselect( + [f.id() for f in selectedLayer.selectedFeatures()] ) - sightingsLayer.commitChanges() - sightingsLayer.startEditing() - followersLayer = self.followersLayer - followersLayer.startEditing() - featuresIterator = ( - followersLayer.getSelectedFeatures() - if selectedMode - else followersLayer.getFeatures() - ) - idx = followersLayer.fields().indexOf("validated") - for feat in featuresIterator: - if survey: - for attr in [ - "survey", - "cycle", - "session", - "computer", - ]: - if attr == "computer" and feat["computer"]: - continue - followersLayer.changeAttributeValue( - feat.id(), - followersLayer.fields().indexOf(attr), - survey[attr], - ) - - followersLayer.changeAttributeValue( - feat.id(), - idx, - not merge, - ) - - followersLayer.commitChanges() - followersLayer.startEditing() + for layer in ( + self.environmentLayer, + self.sightingsLayer, + self.followersLayer, + ): + validateFeatures(layer) def onStopSoundRecordingForEvent( self, @@ -666,7 +559,7 @@ def addGps( sec: int, speed: Optional[float] = -9999.0, course: Optional[float] = -9999.0, - ): + ) -> None: survey = "" cycle = "" computer = "" @@ -703,6 +596,11 @@ def _addFeature( feat["dateTime"] = dt if geom: feat.setGeometry(geom) + else: + iface.messageBar().pushWarning( + f"{layer.name().lower()}", + "No geometry available, please check gps status", + ) lastFeat = SammoDataBase.lastFeature(layer) if lastFeat: @@ -724,7 +622,7 @@ def _addFeature( "shipName", "computer", "transect", - "strate", + "strateType", "length", "comment", "species", @@ -773,154 +671,101 @@ def sessionDirectory(project: QgsProject) -> str: return "" @staticmethod - def merge( - sessionADir: str, - sessionBDir: str, - sessionOutputDir: str, - progressBar: QProgressBar, - date: Optional[QDate] = None, + def effortCheck(environmentLayer) -> None: + # effort status check + effortIds = environmentLayer.uniqueValues( + environmentLayer.fields().indexOf("_effortGroup") + ) + errors = [] + for effortId in effortIds: + effortIt = environmentLayer.getFeatures( + QgsFeatureRequest().setFilterExpression( + f"_effortGroup = {effortId}" + ) + ) + statusCodes = {ft["datetime"]: ft["status"] for ft in effortIt} + if ( + StatusCode.display(StatusCode.BEGIN) + not in statusCodes.values() + ): + errors.append( + f"Missing BEGIN code for effortGroup {effortId} (before " + f"{min(statusCodes).toPyDateTime().isoformat()} record)" + ) + + if errors: + errors.append("Please, resolve errors before validation") + QMessageBox.warning( + None, "Errors detected in effort status", "\n".join(errors) + ) + return + + @staticmethod + def applyEnvAttr( + environmentLayer: QgsVectorLayer, + sightingsLayer: QgsVectorLayer, + followersLayer: QgsVectorLayer, ) -> None: - # open input session - sessionA = SammoSession() - sessionA.init(sessionADir, load=False) - sessionA.validate(True) - - sessionB = SammoSession() - sessionB.init(sessionBDir, load=False) - sessionB.validate(True) - - # create output session - sessionOutput = SammoSession() - sessionOutput.init(sessionOutputDir, load=False) - - # copy sound files to output session - progressBar.setFormat("Copying sound files") - dateInt = int(date.toPyDate().strftime("%Y%m%d")) if date else 0 - for session in [sessionA, sessionB]: - # all this can be replace with shutil.copytree with dirs_exist_ok, - # after python3.8 - for subdir in session.audioFolder.glob("*"): - if not subdir.is_dir() or int(subdir.stem) < dateInt: - continue - elif not (sessionOutput.audioFolder / subdir.name).exists(): - (sessionOutput.audioFolder / subdir.name).mkdir() - outputFolder = sessionOutput.audioFolder / subdir.name - for file in subdir.glob("*"): - if (outputFolder / file.name).exists(): - os.remove(outputFolder / file.name) - copyfile(file, outputFolder / file.name) - - # copy features from dynamic layers - dynamicLayers = [ - "environmentLayer", - "sightingsLayer", - "gpsLayer", - "followersLayer", - ] - dateRequest = QgsFeatureRequest() - if date: - dateString = date.toPyDate().strftime("%Y-%m-%d") - dateExpression = QgsExpression( - "to_date(datetime) >= " f"to_date('{dateString}')" + # Sightings + sightingsLayer.startEditing() + for feat in sightingsLayer.getFeatures(): + strDateTime = ( + feat["dateTime"].toPyDateTime().strftime("%Y-%m-%d %H:%M:%S") ) - dateRequest = QgsFeatureRequest(dateExpression) - for layer in dynamicLayers: - out = getattr(sessionOutput, layer) - nb = 0 - progressBar.setFormat(f"Copy {layer}, Total : %p%") - - newFid = 0 - lastFeature = SammoDataBase.lastFeature(out, True) - if lastFeature: - newFid = lastFeature["fid"] + 1 - tot = ( - getattr(sessionA, layer).featureCount() - + getattr(sessionB, layer).featureCount() + request = QgsFeatureRequest().setFilterExpression( + f"dateTime < to_datetime('{strDateTime}') " ) - for vl in [getattr(sessionA, layer), getattr(sessionB, layer)]: - for feature in vl.getFeatures(dateRequest): - nb += 1 - progressBar.setValue(int(100 / tot * (nb + 1))) - attrs = feature.attributes()[1:] - - exist = False - ft_datetime = ( - feature["datetime"] - .toPyDateTime() - .strftime("%Y-%m-%dT%H:%M:%S") + request.addOrderBy("dateTime", False) + for envFeat in environmentLayer.getFeatures(request): + if feat["side"] == "L": + sightingsLayer.changeAttributeValue( + feat.id(), + sightingsLayer.fields().indexOf("observer"), + envFeat["left"], ) - request = QgsFeatureRequest( - QgsExpression( - "format_date(datetime, 'yyyy-MM-ddThh:mm:ss') = " - f"'{ft_datetime}'" - ) + elif feat["side"] == "R": + sightingsLayer.changeAttributeValue( + feat.id(), + sightingsLayer.fields().indexOf("observer"), + envFeat["right"], ) - for featureOut in out.getFeatures(request): - if featureOut.attributes()[1:] == attrs: - exist = True - break - - if not exist: - feature["fid"] = newFid - newFid += 1 - - print(out.startEditing()) - print(out.addFeature(feature)) - print(out.commitChanges()) - - # gps layer - out = getattr(sessionOutput, "gpsLayer") - datetimeSet = set( - [ - ft["datetime"].toPyDateTime().replace(second=0, microsecond=0) - for ft in out.getFeatures(dateRequest) - ] - ) - nb = 0 - progressBar.setFormat("Copy gpsLayer, Total : %p%") - - newFid = 0 - lastFeature = SammoDataBase.lastFeature(out, True) - if lastFeature: - newFid = lastFeature["fid"] + 1 - tot = ( - sessionA.gpsLayer.featureCount() + sessionB.gpsLayer.featureCount() - ) - for vl in [sessionA.gpsLayer, sessionB.gpsLayer]: - for feature in vl.getFeatures(dateRequest): - nb += 1 - progressBar.setValue(int(100 / tot * (nb + 1))) - dateTimeAttr = ( - feature["datetime"] - .toPyDateTime() - .replace(second=0, microsecond=0) + elif feat["side"] == "C": + sightingsLayer.changeAttributeValue( + feat.id(), + sightingsLayer.fields().indexOf("observer"), + envFeat["center"], + ) + sightingsLayer.changeAttributeValue( + feat.id(), + sightingsLayer.fields().indexOf("_effortGroup"), + envFeat["_effortGroup"], ) - if dateTimeAttr in datetimeSet: - continue - datetimeSet.add(dateTimeAttr) - feature["fid"] = newFid - newFid += 1 - - out.startEditing() - out.addFeature(feature) - out.commitChanges() - - # copy content of static layers only if output is empty - staticLayers = [ - "speciesLayer", - "observersLayer", - "surveyLayer", - "strateLayer", - "transectLayer", - "plateformLayer", - ] - for layer in staticLayers: - out = getattr(sessionOutput, layer) - if out.featureCount() < 1: - continue + sightingsLayer.changeAttributeValue( + feat.id(), + sightingsLayer.fields().indexOf("_effortLeg"), + envFeat["_effortLeg"], + ) + break + sightingsLayer.commitChanges() + sightingsLayer.startEditing() - out.startEditing() - vl = getattr(sessionA, layer) - for feature in vl.getFeatures(dateRequest): - out.addFeature(feature) - out.commitChanges() + # Followers + followersLayer.startEditing() + for feat in followersLayer.getFeatures(): + strDateTime = ( + feat["dateTime"].toPyDateTime().strftime("%Y-%m-%d %H:%M:%S") + ) + request = QgsFeatureRequest().setFilterExpression( + f"dateTime < to_datetime('{strDateTime}') " + ) + request.addOrderBy("dateTime", False) + for envFeat in environmentLayer.getFeatures(request): + followersLayer.changeAttributeValue( + feat.id(), + followersLayer.fields().indexOf("_effortGroup"), + envFeat["_effortGroup"], + ) + break + + followersLayer.commitChanges() + followersLayer.startEditing() diff --git a/src/gui/attribute_table.py b/src/gui/attribute_table.py index 8db713c5..18aa0649 100644 --- a/src/gui/attribute_table.py +++ b/src/gui/attribute_table.py @@ -27,7 +27,7 @@ def toolbar(table: QDialog) -> QToolBar: def refresh( table: QDialog, layerName: str, - filterExpr: str = "", + filterExpr: str = "True", focus: bool = True, ) -> None: table.findChild(QLineEdit, "mFilterQuery").setValue(filterExpr) @@ -61,7 +61,7 @@ def refresh( def attributeTable( iface: QgisInterface, layer: QgsVectorLayer, - filterExpr: str = "", + filterExpr: str = "True", sortExpr: str = '"dateTime"', ) -> QDialog: # hide some columns @@ -71,21 +71,21 @@ def attributeTable( "soundFile", "soundStart", "soundEnd", - "validated", "survey", "cycle", "session", "shipName", "computer", "transect", - "strate", + "strateType", "length", - "sightNum", "plateformHeight", "observer", "_effortGroup", "_effortLeg", ] + if iface.userProfileManager().userProfile().name() == "operator": + hiddens += ["validated"] if layer.name().lower() != ENVIRONMENT_TABLE: hiddens += ["effortGroup", "effortLeg"] config = layer.attributeTableConfig() @@ -99,7 +99,7 @@ def attributeTable( layer.setAttributeTableConfig(config) # init attribute table - table = iface.showAttributeTable(layer, filterExpr or "True") + table = iface.showAttributeTable(layer, filterExpr) # hide some items last = table.layout().rowCount() - 1 diff --git a/src/gui/export.py b/src/gui/export.py index be1072fb..948beb0e 100644 --- a/src/gui/export.py +++ b/src/gui/export.py @@ -6,7 +6,7 @@ from pathlib import Path from qgis.PyQt import uic -from qgis.PyQt.QtCore import QObject, QVariant +from qgis.PyQt.QtCore import QObject, QVariant, QDateTime from qgis.PyQt.QtWidgets import ( QAction, QDialog, @@ -17,8 +17,10 @@ from qgis.core import ( QgsField, QgsWkbTypes, + QgsExpression, QgsVectorLayer, QgsFeatureRequest, + QgsVectorLayerUtils, QgsVectorFileWriter, QgsVectorLayerJoinInfo, QgsCoordinateTransformContext, @@ -52,10 +54,17 @@ def setEnabled(self, status: bool) -> None: def initGui(self, parent: QObject, toolbar: QToolBar) -> None: self.action = QAction(parent) self.action.triggered.connect(self.show) + self.action.triggered.connect(self.clean) self.action.setIcon(utils.icon("export.png")) self.action.setToolTip("Export session") toolbar.addAction(self.action) + def clean(self): + self.saveFolderEdit.setText("") + self.driverComboBox.setCurrentIndex(0) + self.progressBar.setFormat("%p%") + self.progressBar.setValue(0) + def updateSaveFolder(self) -> None: path = QFileDialog.getExistingDirectory( self, @@ -67,78 +76,12 @@ def updateSaveFolder(self) -> None: self.saveFolderEdit.setText(path) self.exportButton.setEnabled(True) - def applyEnvAttr(self): - environmentLayer = self.session.environmentLayer - # Sightings - sightingsLayer = self.session.sightingsLayer - sightingsLayer.startEditing() - for feat in sightingsLayer.getFeatures(): - strDateTime = ( - feat["dateTime"].toPyDateTime().strftime("%Y-%m-%d %H:%M:%S") - ) - request = QgsFeatureRequest().setFilterExpression( - f"dateTime < to_datetime('{strDateTime}') " - f"and status != '{StatusCode.display(StatusCode.END)}'" - ) - request.addOrderBy("dateTime", False) - for envFeat in environmentLayer.getFeatures(request): - if feat["side"] == "L": - sightingsLayer.changeAttributeValue( - feat.id(), - sightingsLayer.fields().indexOf("observer"), - envFeat["left"], - ) - elif feat["side"] == "R": - sightingsLayer.changeAttributeValue( - feat.id(), - sightingsLayer.fields().indexOf("observer"), - envFeat["right"], - ) - elif feat["side"] == "C": - sightingsLayer.changeAttributeValue( - feat.id(), - sightingsLayer.fields().indexOf("observer"), - envFeat["center"], - ) - sightingsLayer.changeAttributeValue( - feat.id(), - sightingsLayer.fields().indexOf("_effortGroup"), - envFeat["_effortGroup"], - ) - sightingsLayer.changeAttributeValue( - feat.id(), - sightingsLayer.fields().indexOf("_effortLeg"), - envFeat["_effortLeg"], - ) - break - sightingsLayer.commitChanges() - sightingsLayer.startEditing() - - # Followers - followersLayer = self.session.followersLayer - followersLayer.startEditing() - for feat in followersLayer.getFeatures(): - strDateTime = ( - feat["dateTime"].toPyDateTime().strftime("%Y-%m-%d %H:%M:%S") - ) - request = QgsFeatureRequest().setFilterExpression( - f"dateTime < to_datetime('{strDateTime}') " - f"and status != '{StatusCode.display(StatusCode.END)}'" - ) - request.addOrderBy("dateTime", False) - for envFeat in environmentLayer.getFeatures(request): - followersLayer.changeAttributeValue( - feat.id(), - followersLayer.fields().indexOf("_effortGroup"), - envFeat["_effortGroup"], - ) - break - - followersLayer.commitChanges() - followersLayer.startEditing() - def export(self) -> None: - self.applyEnvAttr() + self.session.applyEnvAttr( + self.session.environmentLayer, + self.session.sightingsLayer, + self.session.followersLayer, + ) driver = self.driverComboBox.currentText() if driver not in ["CSV", "GPKG"]: self.progressBar.setFormat("Unknown driver: aborting export") @@ -160,6 +103,9 @@ def export(self) -> None: layer.addExpressionField("x($geometry) ", field) field = QgsField("lat", QVariant.Double) layer.addExpressionField("y($geometry) ", field) + if layer.geometryType() == QgsWkbTypes.LineGeometry: + field = QgsField("wkt", QVariant.String) + layer.addExpressionField("geom_to_wkt($geometry) ", field) if layer.name().lower() in [ SIGHTINGS_TABLE, @@ -178,6 +124,11 @@ def export(self) -> None: ",'_L', _effortLeg)", field, ) + field = QgsField("_effortId", QVariant.String) + layer.addExpressionField( + "to_string(_effortGroup) + '_' + to_string(_effortLeg)", + field, + ) if layer.name().lower() in [SIGHTINGS_TABLE, ENVIRONMENT_TABLE]: field = QgsField("date", QVariant.Date) @@ -195,33 +146,55 @@ def export(self) -> None: # Add joined fields if layer.name().lower() in [SIGHTINGS_TABLE, FOLLOWERS_TABLE]: - joinLayer = QgsVectorLayer( + environJoinLayer = QgsVectorLayer( + self.session.environmentLayer.source(), + self.session.environmentLayer.name(), + ) # keepped alive until export is done + field = QgsField("_effortId", QVariant.String) + environJoinLayer.addExpressionField( + "to_string(_effortGroup) + '_' + to_string(_effortLeg)", + field, + ) + speciesJoinLayer = QgsVectorLayer( self.session.speciesLayer.source(), self.session.speciesLayer.name(), ) # keepped alive until export is done - layer.addJoin(self.sightingsLayerJoinInfo(joinLayer)) + layer.addJoin(self.obsEnvLayerJoinInfo(environJoinLayer)) + layer.addJoin(self.obsSpeLayerJoinInfo(speciesJoinLayer)) elif layer.name() == self.session.environmentLayer.name(): - joinLayer = QgsVectorLayer( + obsJoinLayer = QgsVectorLayer( self.session.observersLayer.source(), self.session.observersLayer.name(), ) # keepped alive until export is done layer.addJoin( - self.environmentLayerJoinObserverInfo(joinLayer, "left") + self.environmentLayerJoinObserverInfo(obsJoinLayer, "left") ) layer.addJoin( - self.environmentLayerJoinObserverInfo(joinLayer, "rigth") + self.environmentLayerJoinObserverInfo( + obsJoinLayer, "rigth" + ) ) layer.addJoin( - self.environmentLayerJoinObserverInfo(joinLayer, "center") + self.environmentLayerJoinObserverInfo( + obsJoinLayer, "center" + ) ) - joinLayer = QgsVectorLayer( + plateformJoinLayer = QgsVectorLayer( self.session.plateformLayer.source(), self.session.plateformLayer.name(), ) layer.addJoin( - self.environmentLayerJoinPlateformInfo(joinLayer) + self.environmentLayerJoinPlateformInfo(plateformJoinLayer) + ) + transectJoinLayer = QgsVectorLayer( + self.session.transectLayer.source(), + self.session.transectLayer.name(), + ) + layer.addJoin( + self.environmentLayerJoinTransectInfo(transectJoinLayer) ) + layer = self.addEndEffortFeature(layer) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = driver @@ -230,12 +203,13 @@ def export(self) -> None: for field in layer.fields() if field.name() not in [ - "sightNum", "validated", "plateformId", + "transectId", "_effortLeg", "_effortGroup", "_focalId", + "_effortId", ] ] QgsVectorFileWriter.writeAsVectorFormatV2( @@ -247,19 +221,40 @@ def export(self) -> None: QgsCoordinateTransformContext(), options, ) + if layer.name() == self.session.environmentLayer.name(): + self.removeEndEffort(layer) self.progressBar.setValue(int(100 / nb * (i + 1))) self.close() - def sightingsLayerJoinInfo(self, layer: QgsVectorLayer) -> None: - joinInfo = QgsVectorLayerJoinInfo() - joinInfo.setJoinLayer(layer) - joinInfo.setJoinFieldName("species") - joinInfo.setTargetFieldName("species") - joinInfo.setPrefix("species_") - joinInfo.setJoinFieldNamesSubset( - ["commonName", "latinName", "groupName", "family", "taxon"] + def obsEnvLayerJoinInfo(self, layer: QgsVectorLayer) -> None: + environmentJoinInfo = QgsVectorLayerJoinInfo() + environmentJoinInfo.setJoinLayer(layer) + environmentJoinInfo.setJoinFieldName("_effortId") + environmentJoinInfo.setTargetFieldName("_effortId") + environmentJoinInfo.setPrefix("") + environmentJoinInfo.setJoinFieldNamesSubset(["session", "routeType"]) + return environmentJoinInfo + + def obsSpeLayerJoinInfo(self, layer: QgsVectorLayer) -> None: + speciesJoinInfo = QgsVectorLayerJoinInfo() + speciesJoinInfo.setJoinLayer(layer) + speciesJoinInfo.setJoinFieldName("species") + speciesJoinInfo.setTargetFieldName("species") + speciesJoinInfo.setPrefix("species_") + speciesJoinInfo.setJoinFieldNamesSubset( + [ + "name_latin", + "name_eng", + "group_eng", + "family_eng", + "taxon_eng", + "name_fr", + "group_fr", + "family_fr", + "taxon_fr", + ] ) - return joinInfo + return speciesJoinInfo def environmentLayerJoinObserverInfo( self, layer: QgsVectorLayer, side: str @@ -282,3 +277,67 @@ def environmentLayerJoinPlateformInfo(self, layer: QgsVectorLayer) -> None: joinInfo.setPrefix("") joinInfo.setJoinFieldNamesSubset(["plateform", "plateformHeight"]) return joinInfo + + def environmentLayerJoinTransectInfo(self, layer: QgsVectorLayer) -> None: + joinInfo = QgsVectorLayerJoinInfo() + joinInfo.setJoinLayer(layer) + joinInfo.setJoinFieldName("fid") + joinInfo.setTargetFieldName("transectId") + joinInfo.setPrefix("") + joinInfo.setJoinFieldNamesSubset(["transect", "strateType", "length"]) + return joinInfo + + def addEndEffortFeature(self, layer: QgsVectorLayer) -> QgsVectorLayer: + effortGroupValues = layer.uniqueValues( + layer.fields().indexOf("effortGroup") + ) + layer.startEditing() + for effortGroupValue in effortGroupValues: + expr = QgsExpression(f"effortGroup = '{effortGroupValue}'") + request = QgsFeatureRequest(expr).addOrderBy("dateTime", False) + lastEffortFt = None + for lastEffortFt in layer.getFeatures(request): + break + if not lastEffortFt: + continue + expr = QgsExpression( + f"effortGroup != '{effortGroupValue}' and " + f"status = '{StatusCode.display(StatusCode.BEGIN)}' and " + "dateTime > " + f"'{lastEffortFt['dateTime'].toPyDateTime().isoformat()}'" + ) + request = QgsFeatureRequest(expr).addOrderBy("dateTime", True) + nextBegFt = None + for nextBegFt in layer.getFeatures(request): + break + if not nextBegFt or ( + QDateTime(lastEffortFt["dateTime"]).date() + != QDateTime(nextBegFt["dateTime"]).date() + ): + nextBegFt = lastEffortFt + dt = QDateTime(nextBegFt["dateTime"]).addSecs(1) + else: + dt = QDateTime(nextBegFt["dateTime"]).addSecs(-1) + feat = QgsVectorLayerUtils.createFeature(layer) + feat.setGeometry(nextBegFt.geometry()) + for attr in feat.fields().names(): + if attr in ["fid", "dateTime", "status"]: + continue + feat[attr] = lastEffortFt[attr] + feat["dateTime"] = dt + feat["status"] = StatusCode.display(StatusCode.END) + layer.addFeature(feat) + layer.commitChanges() + layer.startEditing() + return layer + + def removeEndEffort(self, layer: QgsVectorLayer) -> None: + layer.startEditing() + expr = QgsExpression( + f"status = '{StatusCode.display(StatusCode.END)}'" + ) + request = QgsFeatureRequest(expr) + endFts = [endFt.id() for endFt in layer.getFeatures(request)] + layer.deleteFeatures(endFts) + layer.commitChanges() + layer.startEditing() diff --git a/src/gui/followers.py b/src/gui/followers.py index e4c050fb..5d2f0ca1 100644 --- a/src/gui/followers.py +++ b/src/gui/followers.py @@ -97,8 +97,7 @@ def __init__(self, iface, geom, followerLayer): self.table.setParent(self) self.verticalLayout.addWidget(self.table) - if originDlg: # version < 3.28 compatibility - originDlg.hide() + originDlg.hide() def rowCount(self): return ( @@ -120,7 +119,7 @@ def refresh(self): SammoAttributeTable.refresh(self.table, "Followers", filterExpr) def eventFilter(self, obj, event): - if type(event) == QKeyEvent: + if type(event) is QKeyEvent: if event.key() == Qt.Key_Escape: event.ignore() return True diff --git a/src/gui/merge.py b/src/gui/merge.py index e49cdf41..a3424e75 100644 --- a/src/gui/merge.py +++ b/src/gui/merge.py @@ -3,15 +3,26 @@ __contact__ = "info@hytech-imaging.fr" __copyright__ = "Copyright (c) 2021 Hytech Imaging" +import os from pathlib import Path +from shutil import copyfile +from typing import Optional from qgis.PyQt import uic -from qgis.core import QgsProject +from qgis.utils import iface from qgis.PyQt.QtCore import pyqtSignal, QObject, QDir, QDate +from qgis.core import ( + QgsTask, + QgsProject, + QgsExpression, + QgsApplication, + QgsFeatureRequest, +) from qgis.PyQt.QtWidgets import QAction, QToolBar, QDialog, QFileDialog from ..core import utils from ..core.session import SammoSession +from ..core.database import SammoDataBase FORM_CLASS, _ = uic.loadUiType(Path(__file__).parent / "ui/merge.ui") @@ -50,10 +61,11 @@ def __init__(self): self.dateEdit.setDate(QDate.currentDate()) self.ok.clicked.connect(self.merge) - self.cancel.clicked.connect(self.close) + self.closeButton.clicked.connect(self.close) self.sessionAButton.clicked.connect(self.sessionA) self.sessionBButton.clicked.connect(self.sessionB) self.sessionMergedButton.clicked.connect(self.sessionMerged) + self.task: SammoMergeTask def sessionA(self) -> None: sessionA = QFileDialog.getExistingDirectory( @@ -90,12 +102,213 @@ def sessionMerged(self) -> None: def merge(self) -> None: QgsProject.instance().clear() - SammoSession.merge( + self.ok.setEnabled(False) + self.task = SammoMergeTask( self.sessionADir.text(), self.sessionBDir.text(), + self.sessionAGpsCheckBox.isChecked(), + self.sessionBGpsCheckBox.isChecked(), self.sessionMergedDir.text(), - self.progressBar, self.dateEdit.date() if self.dateCheckBox.isChecked() else None, ) - self.close() + self.task.begun.connect(self.hide) + self.task.taskCompleted.connect(self.after_task) + self.task.taskTerminated.connect(self.after_task) + QgsApplication.taskManager().addTask(self.task) + + def after_task(self): + if self.task.errorMsg: + iface.messageBar().pushWarning("MergeTask", self.task.errorMsg) + self.ok.setText("Failed") + else: + iface.messageBar().pushInfo("MergeTask", "Merge success") + self.ok.setText("Success") self.mergeEnded.emit(self.sessionMergedDir.text()) + self.show() + + +class SammoMergeTask(QgsTask): + def __init__( + self, + sessionADir: str, + sessionBDir: str, + sessionAGps: bool, + sessionBGps: bool, + sessionMergedDir: str, + date: Optional[QDate] = None, + ) -> None: + super().__init__("Sammo Merge Task") + self.sessionADir = sessionADir + self.sessionBDir = sessionBDir + self.sessionAGps = sessionAGps + self.sessionBGps = sessionBGps + self.sessionMergedDir = sessionMergedDir + self.date = date + self.errorMsg = "" + + def run(self) -> bool: + try: + self.merge() + except Exception as e: + self.errorMsg = ",".join([str(i) for i in e.args]) + return False + return True + + def merge(self) -> None: + # open input session + sessionA = SammoSession() + sessionA.init(self.sessionADir, load=False) + sessionA.effortCheck(sessionA.environmentLayer) + + sessionB = SammoSession() + sessionB.init(self.sessionBDir, load=False) + sessionB.effortCheck(sessionB.environmentLayer) + + # create output session + sessionOutput = SammoSession() + sessionOutput.init(self.sessionMergedDir, load=False) + + # copy sound files to output session + dateInt = ( + int(self.date.toPyDate().strftime("%Y%m%d")) if self.date else 0 + ) + progress = 0 + for i, session in enumerate([sessionA, sessionB]): + # all this can be replace with shutil.copytree with dirs_exist_ok, + # after python3.8 + for subdir in session.audioFolder.glob("*"): + if not subdir.is_dir() or int(subdir.stem) < dateInt: + continue + elif not (sessionOutput.audioFolder / subdir.name).exists(): + (sessionOutput.audioFolder / subdir.name).mkdir() + outputFolder = sessionOutput.audioFolder / subdir.name + for file in subdir.glob("*"): + if (outputFolder / file.name).exists(): + os.remove(outputFolder / file.name) + copyfile(file, outputFolder / file.name) + progress += 5 + self.setProgress(progress) + + # copy features from dynamic layers + dynamicLayers = [ + "environmentLayer", + "sightingsLayer", + "followersLayer", + ] + dateRequest = QgsFeatureRequest() + if self.date: + dateString = self.date.toPyDate().strftime("%Y-%m-%d") + dateExpression = QgsExpression( + "to_date(datetime) >= " f"to_date('{dateString}')" + ) + dateRequest = QgsFeatureRequest(dateExpression) + for i, layer in enumerate(dynamicLayers): + out = getattr(sessionOutput, layer) + + newFid = 0 + lastFeature = SammoDataBase.lastFeature(out, True) + if lastFeature: + newFid = lastFeature["fid"] + 1 + for vl in [getattr(sessionA, layer), getattr(sessionB, layer)]: + for feature in vl.getFeatures(dateRequest): + attrs = feature.attributes()[1:] + + exist = False + ft_datetime = ( + feature["datetime"] + .toPyDateTime() + .strftime("%Y-%m-%dT%H:%M:%S") + ) + request = QgsFeatureRequest( + QgsExpression( + "format_date(datetime, 'yyyy-MM-ddThh:mm:ss') = " + f"'{ft_datetime}'" + ) + ) + for featureOut in out.getFeatures(request): + if featureOut.attributes()[1:] == attrs: + exist = True + break + + if not exist: + feature["fid"] = newFid + newFid += 1 + + out.startEditing() + out.addFeature(feature) + out.commitChanges() + + if layer == "environmentLayer": + SammoSession.applyEnvAttr( + sessionOutput.environmentLayer, + sessionA.sightingsLayer, + sessionA.followersLayer, + ) + SammoSession.applyEnvAttr( + sessionOutput.environmentLayer, + sessionB.sightingsLayer, + sessionB.followersLayer, + ) + progress += 20 + self.setProgress(progress) + # gps layer + out = getattr(sessionOutput, "gpsLayer") + datetimeSet = set( + [ + ft["datetime"].toPyDateTime().replace(second=0, microsecond=0) + for ft in out.getFeatures(dateRequest) + ] + ) + + newFid = 0 + lastFeature = SammoDataBase.lastFeature(out, True) + if lastFeature: + newFid = lastFeature["fid"] + 1 + gpsVls = [] + if self.sessionAGps: + gpsVls.append(sessionA.gpsLayer) + if self.sessionBGps: + gpsVls.append(sessionB.gpsLayer) + for vl in gpsVls: + for feature in vl.getFeatures(dateRequest): + dateTimeAttr = ( + feature["datetime"] + .toPyDateTime() + .replace(second=0, microsecond=0) + ) + if dateTimeAttr in datetimeSet: + continue + datetimeSet.add(dateTimeAttr) + feature["fid"] = newFid + newFid += 1 + + out.startEditing() + out.addFeature(feature) + out.commitChanges() + progress += 10 + self.setProgress(progress) + # copy content of static layers only if output is empty + staticLayers = [ + "speciesLayer", + "behaviourSpeciesLayer", + "surveyLayer", + "surveyTypeLayer", + "transectLayer", + "plateformLayer", + "observersLayer", + "surveyTypeLayer", + "behaviourSpeciesLayer", + ] + for layer in staticLayers: + progress += 2 + self.setProgress(progress) + out = getattr(sessionOutput, layer) + if out.featureCount() < 1: + continue + + out.startEditing() + vl = getattr(sessionA, layer) + for feature in vl.getFeatures(dateRequest): + out.addFeature(feature) + out.commitChanges() + self.setProgress(100) diff --git a/src/gui/settings.py b/src/gui/settings.py index ca488505..26485898 100644 --- a/src/gui/settings.py +++ b/src/gui/settings.py @@ -7,16 +7,18 @@ from qgis.PyQt import uic from qgis.utils import iface -from qgis.PyQt.QtCore import QObject -from qgis.core import QgsVectorLayerUtils +from qgis.PyQt.QtCore import QObject, QDir, pyqtSignal +from qgis.core import QgsVectorLayerUtils, QgsVectorLayer, QgsFeature from qgis.PyQt.QtWidgets import ( QAction, QToolBar, QDialog, + QFileDialog, QVBoxLayout, QHBoxLayout, ) + from ..core import utils from ..core.session import SammoSession @@ -24,6 +26,8 @@ class SammoSettingsAction(QObject): + reloadTables: pyqtSignal = pyqtSignal() + def __init__( self, parent: QObject, toolbar: QToolBar, session: SammoSession ): @@ -43,11 +47,20 @@ def initGui(self, parent: QObject, toolbar: QToolBar): toolbar.addAction(self.action) def show(self): - dlg = SammoSettingsDialog(self.session) - dlg.exec_() + self.dlg = SammoSettingsDialog(self.session) + self.dlg.reloadTables.connect(self.reloadTables) + self.dlg.show() + self.dlg.accepted.connect(self.clear) + + def clear(self): + if self.dlg: + for dlg in self.dlg.findChildren(QDialog): + dlg.setParent(None) class SammoSettingsDialog(QDialog, FORM_CLASS): + reloadTables: pyqtSignal = pyqtSignal() + def __init__(self, session): super().__init__() self.setupUi(self) @@ -55,19 +68,20 @@ def __init__(self, session): self.session = session self.surveyButton.clicked.connect(self.surveyEdit) + self.surveyTypeButton.clicked.connect(self.surveyEdit) self.transectButton.clicked.connect(self.surveyEdit) - self.strateButton.clicked.connect(self.surveyEdit) + self.transectImportButton.clicked.connect(self.importTransect) self.boatButton.clicked.connect(self.surveyEdit) self.plateformButton.clicked.connect(self.surveyEdit) - self.closeButton.clicked.connect(self.close) + self.closeButton.clicked.connect(self.accept) def surveyEdit(self): if self.sender() == self.surveyButton: vl = self.session.surveyLayer + if self.sender() == self.surveyTypeButton: + vl = self.session.surveyTypeLayer elif self.sender() == self.transectButton: vl = self.session.transectLayer - elif self.sender() == self.strateButton: - vl = self.session.strateLayer elif self.sender() == self.plateformButton: vl = self.session.plateformLayer elif self.sender() == self.boatButton: @@ -76,19 +90,23 @@ def surveyEdit(self): if not vl.featureCount(): feat = QgsVectorLayerUtils.createFeature(vl) vl.addFeature(feat) - if vl in [self.session.boatLayer, self.session.plateformLayer]: - self.session.plateformLayer + if vl in [ + self.session.surveyTypeLayer, + self.session.boatLayer, + self.session.plateformLayer, + self.session.transectLayer, + ]: dlg = QDialog(self) dlg.setModal(True) dlg.setWindowTitle(vl.name()) table = iface.showAttributeTable(vl) originDlg = table.parent() + table.setParent(None) hLayout = QHBoxLayout(dlg) vLayout = QVBoxLayout() hLayout.addLayout(vLayout) vLayout.addWidget(table) - if originDlg: # version < 3.28 compatibility - originDlg.hide() + originDlg.hide() dlg.show() dlg.destroyed.connect(vl.commitChanges) dlg.destroyed.connect(self.session.plateformLayer.commitChanges) @@ -96,3 +114,50 @@ def surveyEdit(self): feat = next(vl.getFeatures()) iface.openFeatureForm(vl, feat) vl.commitChanges() + + def importTransect(self): + transect_path, _ = QFileDialog.getOpenFileName( + self, + "Select a ogr transect file", + QDir.currentPath(), + filter="OGR file (*.shp *.gpkg)", + options=QFileDialog.DontUseNativeDialog, + ) + if not transect_path: + return + lyr = QgsVectorLayer(transect_path, "importTransect", "ogr") + lyr.geometryType() + if lyr.geometryType() != 1: # not Linestring + iface.messageBar().pushWarning( + "Geometry Error", + "Imported transect layer is not a LineString layer, " + "please provide a LineString layer", + ) + return + elif lyr.crs().postgisSrid() != 4326: # not EPSG:4326: + iface.messageBar().pushWarning( + "CRS Error", "Convert the transect layer in EPSG:4326 first" + ) + return + + self.session.transectLayer.startEditing() + for importedFt in lyr.getFeatures(): + ft = QgsFeature(self.session.transectLayer.fields()) + ft.setGeometry(importedFt.geometry()) + for field in ft.fields(): + if field.name() == "fid": + continue + elif field.name() in lyr.fields().names(): + if field.type() is not lyr.fields()[field.name()].type(): + if field.typeName() == "Integer": + value = int(importedFt[field.name()]) + elif field.typeName() == "Real": + value = float(importedFt[field.name()]) + else: + value = importedFt[field.name()] + else: + value = importedFt[field.name()] + ft[field.name()] = value + self.session.transectLayer.addFeature(ft) + self.session.transectLayer.commitChanges() + self.reloadTables.emit() diff --git a/src/gui/status.py b/src/gui/status.py index 31c3704a..bd3dc903 100644 --- a/src/gui/status.py +++ b/src/gui/status.py @@ -7,13 +7,15 @@ import sys from qgis.PyQt import uic +from qgis.gui import QgisInterface from qgis.PyQt.QtCore import Qt, QSize, pyqtSignal -from qgis.PyQt.QtWidgets import QFrame, QLabel, QDockWidget +from qgis.core import QgsSettings, QgsFeatureRequest +from qgis.PyQt.QtWidgets import QFrame, QLabel, QDockWidget, QWidget -from qgis.core import QgsSettings, QgsFeatureRequest, QgsExpression from ..core.status import StatusCode from ..core.utils import pixmap, icon +from ..core.session import SammoSession from ..core.thread_widget import ThreadWidget FORM_CLASS, _ = uic.loadUiType( @@ -35,7 +37,7 @@ def __init__(self, parent): self.gpsButton.clicked.connect(self.activateGPS) self.init() - def updateNeedSave(self, status): + def updateNeedSave(self, status: bool): self.save.setStyleSheet(self._styleSheet(self.save, not status)) self.save.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) @@ -46,7 +48,7 @@ def updateNeedSave(self, status): px = pixmap(icon, QSize(64, 64)) self.save.setPixmap(px) - def updateRecording(self, status): + def updateRecording(self, status: bool): self.record.setStyleSheet(self._styleSheet(self.record, status)) icon_path = "record_ko.png" @@ -56,7 +58,7 @@ def updateRecording(self, status): self.record.setIcon(icon(icon_path)) self.record.setEnabled(status) - def updateEffort(self, status): + def updateEffort(self, status: bool): self.effort.setStyleSheet(self._styleSheet(self.effort, status)) self.effort.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) @@ -68,7 +70,12 @@ def updateEffort(self, status): self.effort.setPixmap(px) def updateGps( - self, status, latitude="", longitude="", speed=-9999.0, course=-9999.0 + self, + status: bool, + latitude: str = "", + longitude: str = "", + speed: float = -9999.0, + course: float = -9999.0, ): self.gpsButton.setStyleSheet(self._styleSheet(self.gpsButton, status)) self.gpsFrame.setStyleSheet(self._styleSheet(self.gpsFrame, status)) @@ -96,7 +103,7 @@ def init(self): self.updateEffort(False) self.updateGps(False) - def _styleSheet(self, widget, status): + def _styleSheet(self, widget: QWidget, status: bool): color = KO_COLOR if status: color = OK_COLOR @@ -119,7 +126,7 @@ class SammoStatusDock(QDockWidget): recordInterrupted: pyqtSignal = pyqtSignal() activateGPS: pyqtSignal = pyqtSignal() - def __init__(self, iface, session): + def __init__(self, iface: QgisInterface, session: SammoSession): super().__init__("Sammo Status", iface.mainWindow()) self.setObjectName("Sammo Status") @@ -138,7 +145,7 @@ def __init__(self, iface, session): self._widget = None self._init(iface.mainWindow()) - def setEnabled(self, status): + def setEnabled(self, status: bool): if status: location = int( QgsSettings().value( @@ -194,7 +201,7 @@ def _isEffortOn(self) -> bool: return False feat = None - request = QgsFeatureRequest(QgsExpression("routeType = 'prospection'")) + request = QgsFeatureRequest() request.addOrderBy("fid", False) for feat in layer.getFeatures(request): if feat["routeType"] == "prospection" and feat["status"] in [ @@ -203,12 +210,13 @@ def _isEffortOn(self) -> bool: ]: return True break + return False - def _onGpsOffline(self): + def _onGpsOffline(self) -> None: self._isGpsOffline = True self._widget.updateGps(False) - def _updateGpsWidgetColor(self): + def _updateGpsWidgetColor(self) -> None: if self._isGpsOffline: self._gpsWidget.setStyleSheet(self._styleSheet(False)) else: @@ -217,15 +225,15 @@ def _updateGpsWidgetColor(self): "color : rgb(0,255,0); }" ) - def _startThread(self): + def _startThread(self) -> None: if not self._thread.isProceeding: self._thread.start() - def _endThread(self): + def _endThread(self) -> None: if self._thread and self._thread.isProceeding: self._thread.stop() - def _init(self, parent): + def _init(self, parent: QWidget) -> None: self._widget = StatusWidget(self) self._widget.recordInterrupted.connect(self.recordInterrupted) self._widget.activateGPS.connect(self.activateGPS) @@ -234,7 +242,7 @@ def _init(self, parent): self.dockLocationChanged.connect(self._saveLastLocation) self.setWidget(self._widget) - def _saveLastLocation(self, location): + def _saveLastLocation(self, location: Qt.DockWidgetArea): QgsSettings().setValue( "Sammo/SammoStatusDock/Location/", int(location) ) diff --git a/src/gui/table.py b/src/gui/table.py index 6a0f46fe..c41279c7 100644 --- a/src/gui/table.py +++ b/src/gui/table.py @@ -16,12 +16,11 @@ QDialog, QWidget, QSplitter, + QTableView, QDockWidget, QVBoxLayout, ) -from ..core.status import StatusCode -from ..core.database import ENVIRONMENT_TABLE from .attribute_table import SammoAttributeTable FORM_CLASS, _ = uic.loadUiType( @@ -74,8 +73,8 @@ def __init__( self.verticalLayout.addWidget(splitter) def eventFilter(self, obj, event): - if type(obj) == QDialog: - if type(event) == QKeyEvent: + if type(obj) is QDialog: + if type(event) is QKeyEvent: if event.key() == Qt.Key_Escape: event.ignore() return True @@ -120,26 +119,24 @@ def unload(self) -> None: def refresh( self, layer: QgsVectorLayer, - filterExpr: str = "", + filterExpr: str = "True", focus: bool = True, ) -> None: table = self._widget.tables[layer.name()] - if layer.name().lower() == ENVIRONMENT_TABLE: - if filterExpr: - filterExpr += " and " - filterExpr += f"status != '{StatusCode.display(StatusCode.END)}'" SammoAttributeTable.refresh(table, layer.name(), filterExpr, focus) def removeTable(self, name: str) -> None: if name in self._widget.tables: + self._widget.tables[name].findChild( + QTableView, "mTableView" + ).horizontalHeader().setStretchLastSection(False) self._widget.tables[name].accept() - self._widget.tables.pop(name, None) def clean(self) -> None: if self._widget: - tableKeys = [k for k in self._widget.tables.keys()] - for name in tableKeys: + for name in self._widget.tables: self.removeTable(name) + self._widget.tables.clear() self.iface.removeDockWidget(self) self.iface.mainWindow().setCorner( Qt.BottomLeftCorner, Qt.LeftDockWidgetArea diff --git a/src/gui/ui/merge.ui b/src/gui/ui/merge.ui index 401ee11b..df350711 100644 --- a/src/gui/ui/merge.ui +++ b/src/gui/ui/merge.ui @@ -31,6 +31,16 @@ + + + + Gps + + + true + + + @@ -45,6 +55,13 @@ + + + + Gps + + + @@ -78,13 +95,6 @@ - - - - 0 - - - @@ -101,9 +111,9 @@ - + - Cancel + Close diff --git a/src/gui/ui/settings.ui b/src/gui/ui/settings.ui index 4132bd4a..7eccb2ef 100644 --- a/src/gui/ui/settings.ui +++ b/src/gui/ui/settings.ui @@ -7,7 +7,7 @@ 0 0 400 - 242 + 244 @@ -28,18 +28,42 @@ - + - Transect + SurveyType - - - Strate - - + + + + + Transect + + + + + + + + 40 + 16777215 + + + + Import + + + + + + + ../../../images/plus.png../../../images/plus.png + + + +