001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.api.model.occurrence;
015
016import org.gbif.api.annotation.Experimental;
017import org.gbif.api.model.common.Classification;
018import org.gbif.api.model.common.Identifier;
019import org.gbif.api.model.common.LinneanClassification;
020import org.gbif.api.model.common.LinneanClassificationKeys;
021import org.gbif.api.model.common.MediaObject;
022import org.gbif.api.util.ClassificationUtils;
023import org.gbif.api.util.IsoDateInterval;
024import org.gbif.api.vocabulary.BasisOfRecord;
025import org.gbif.api.vocabulary.Continent;
026import org.gbif.api.vocabulary.Country;
027import org.gbif.api.vocabulary.GbifRegion;
028import org.gbif.api.vocabulary.License;
029import org.gbif.api.vocabulary.OccurrenceIssue;
030import org.gbif.api.vocabulary.OccurrenceStatus;
031import org.gbif.api.vocabulary.Rank;
032import org.gbif.api.vocabulary.Sex;
033import org.gbif.api.vocabulary.TaxonomicStatus;
034import org.gbif.dwc.terms.DwcTerm;
035import org.gbif.dwc.terms.GbifTerm;
036import org.gbif.dwc.terms.Term;
037import org.gbif.dwc.terms.UnknownTerm;
038
039import java.lang.reflect.Field;
040import java.lang.reflect.Modifier;
041import java.net.URI;
042import java.util.ArrayList;
043import java.util.Arrays;
044import java.util.Collections;
045import java.util.Date;
046import java.util.EnumSet;
047import java.util.HashMap;
048import java.util.List;
049import java.util.Map;
050import java.util.Objects;
051import java.util.Optional;
052import java.util.Set;
053import java.util.stream.Collectors;
054import java.util.stream.Stream;
055
056import javax.annotation.Nullable;
057import javax.validation.constraints.Max;
058import javax.validation.constraints.Min;
059import javax.validation.constraints.NotNull;
060
061import com.fasterxml.jackson.annotation.JsonAnyGetter;
062import com.fasterxml.jackson.annotation.JsonIgnore;
063import com.fasterxml.jackson.annotation.JsonProperty;
064import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
065import com.fasterxml.jackson.databind.annotation.JsonSerialize;
066
067import io.swagger.v3.oas.annotations.ExternalDocumentation;
068import io.swagger.v3.oas.annotations.media.Schema;
069import lombok.EqualsAndHashCode;
070import lombok.ToString;
071
072/**
073 * Represents an Occurrence as interpreted by GBIF, adding typed properties on top of the verbatim ones.
074 */
075@EqualsAndHashCode
076@ToString
077@SuppressWarnings("unused")
078public class Occurrence extends VerbatimOccurrence implements LinneanClassification, LinneanClassificationKeys {
079
080  public static final String GEO_DATUM = "WGS84";
081  // keep names of ALL properties of this class in a set for jackson serialization, see #properties()
082  private static final Set<String> PROPERTIES = Collections.unmodifiableSet(
083    Stream.concat(
084      // we need to these JSON properties manually because we have a fixed getter but no field for it
085      Stream.of(DwcTerm.geodeticDatum.simpleName(), "class", DwcTerm.countryCode.simpleName(), GbifTerm.gbifRegion.simpleName(), GbifTerm.publishedByGbifRegion.simpleName()),
086      Stream.concat(Arrays.stream(Occurrence.class.getDeclaredFields()),
087        Arrays.stream(VerbatimOccurrence.class.getDeclaredFields()))
088        .filter(field -> !Modifier.isStatic(field.getModifiers()))
089        .map(Field::getName)).collect(Collectors.toSet()));
090
091  // occurrence fields
092
093  // OpenAPI documentation comes from the enumeration.
094  private BasisOfRecord basisOfRecord;
095
096  @Schema(
097    description = "The number of individuals present at the time of the Occurrence.",
098    externalDocs = @ExternalDocumentation(
099      description = "Darwin Core definition",
100      url = "https://rs.tdwg.org/dwc/terms/individualCount"
101    )
102  )
103  private Integer individualCount;
104
105  // OpenAPI documentation comes from the enumeration.
106  private OccurrenceStatus occurrenceStatus;
107
108  // OpenAPI documentation comes from the enumeration.
109  private String sex;
110
111  @Schema(
112    description = "The age class or life stage of the Organism(s) at the time the Occurrence was recorded.\n\n" +
113      "Values are aligned to the [GBIF LifeStage vocabulary](https://registry.gbif.org/vocabulary/LifeStage/concepts)",
114    externalDocs = @ExternalDocumentation(
115      description = "Darwin Core definition",
116      url = "https://rs.tdwg.org/dwc/terms/lifeStage"
117    )
118  )
119  private String lifeStage;
120
121  @Schema(
122    description = "Statement about whether an organism or organisms have been introduced to a given place and time " +
123      "through the direct or indirect activity of modern humans.\n\n" +
124      "Values are aligned to the [GBIF EstablishmentMeans vocabulary](https://registry.gbif.org/vocabulary/EstablishmentMeans/concepts)," +
125      "which is derived from the [Darwin Core EstablishmentMeans vocabulary](https://dwc.tdwg.org/em/).",
126    externalDocs = @ExternalDocumentation(
127      description = "Darwin Core definition",
128      url = "https://rs.tdwg.org/dwc/terms/establishmentMeans"
129    )
130  )
131  private String establishmentMeans;
132
133  @Schema(
134    description = "The degree to which an Organism survives, reproduces, and expands its range at the given " +
135      "place and time.\n\n" +
136      "Values are aligned to the [GBIF DegreeOfEstablishment vocabulary](https://registry.gbif.org/vocabulary/DegreeOfEstablishment/concepts)," +
137      "which is derived from the [Darwin Core DegreeOfEstablishment vocabulary](https://dwc.tdwg.org/doe/).",
138    externalDocs = @ExternalDocumentation(
139      description = "Darwin Core definition",
140      url = "https://rs.tdwg.org/dwc/terms/degreeOfEstablishment"
141    )
142  )
143  private String degreeOfEstablishment;
144
145  @Schema(
146    description = "The process by which an Organism came to be in a given place at a given time.\n\n" +
147    "Values are aligned to the [GBIF Pathway vocabulary](https://registry.gbif.org/vocabulary/Pathway/concepts)," +
148    "which is derived from the [Darwin Core Pathway vocabulary](https://dwc.tdwg.org/pw/).",
149    externalDocs = @ExternalDocumentation(
150      description = "Darwin Core definition",
151      url = "https://rs.tdwg.org/dwc/terms/pathway"
152    )
153  )
154  private String pathway;
155
156
157  @Schema(
158    description = "The map of classifications associated with this occurrence keyed by checklistKey.",
159    externalDocs = @ExternalDocumentation(
160      description = "Darwin Core definition",
161      url = "https://rs.tdwg.org/dwc/terms/"
162    )
163  )
164  private Map<String, Classification> classifications;
165
166  @Schema(
167    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the most specific " +
168      "(lowest rank) taxon for this occurrence.  This could be a synonym, see `acceptedTaxonKey`.",
169    externalDocs = @ExternalDocumentation(
170      description = "Darwin Core definition",
171      url = "https://rs.tdwg.org/dwc/terms/"
172    )
173  )
174  @Deprecated
175  private Integer taxonKey;
176
177  @Schema(
178    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the kingdom of this" +
179      "occurrence.",
180    externalDocs = @ExternalDocumentation(
181      description = "Darwin Core definition",
182      url = "https://rs.tdwg.org/dwc/terms/"
183    )
184  )
185  @Deprecated
186  private Integer kingdomKey;
187
188  @Schema(
189    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the phylum of this" +
190      "occurrence.",
191    externalDocs = @ExternalDocumentation(
192      description = "Darwin Core definition",
193      url = "https://rs.tdwg.org/dwc/terms/"
194    )
195  )
196  @Deprecated
197  private Integer phylumKey;
198
199  @Schema(
200    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the class of this" +
201      "occurrence.",
202    externalDocs = @ExternalDocumentation(
203      description = "Darwin Core definition",
204      url = "https://rs.tdwg.org/dwc/terms/"
205    )
206  )
207  @Deprecated
208  private Integer classKey;
209
210  @Schema(
211    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the order of this" +
212      "occurrence.",
213    externalDocs = @ExternalDocumentation(
214      description = "Darwin Core definition",
215      url = "https://rs.tdwg.org/dwc/terms/"
216    )
217  )
218  @Deprecated
219  private Integer orderKey;
220
221  @Schema(
222    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the family of this" +
223      "occurrence.",
224    externalDocs = @ExternalDocumentation(
225      description = "Darwin Core definition",
226      url = "https://rs.tdwg.org/dwc/terms/"
227    )
228  )
229  @Deprecated
230  private Integer familyKey;
231
232  @Schema(
233    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the genus of this" +
234      "occurrence.",
235    externalDocs = @ExternalDocumentation(
236      description = "Darwin Core definition",
237      url = "https://rs.tdwg.org/dwc/terms/"
238    )
239  )
240  @Deprecated
241  private Integer genusKey;
242
243  @Schema(
244    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the subgenus of this" +
245      "occurrence.",
246    externalDocs = @ExternalDocumentation(
247      description = "Darwin Core definition",
248      url = "https://rs.tdwg.org/dwc/terms/"
249    )
250  )
251  @Deprecated
252  private Integer subgenusKey;
253
254  @Schema(
255    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the species of this" +
256      "occurrence.",
257    externalDocs = @ExternalDocumentation(
258      description = "Darwin Core definition",
259      url = "https://rs.tdwg.org/dwc/terms/"
260    )
261  )
262  @Deprecated
263  private Integer speciesKey;
264
265  @Schema(
266    description = "A taxon key from the [GBIF backbone](https://doi.org/10.15468/39omei) for the accepted taxon of " +
267      "this occurrence.",
268    externalDocs = @ExternalDocumentation(
269      description = "Darwin Core definition",
270      url = "https://rs.tdwg.org/dwc/terms/"
271    )
272  )
273  @Deprecated
274  private Integer acceptedTaxonKey;
275
276  // taxonomy as name strings → LinneanClassification
277
278  @Schema(
279    description = "The scientific name (including authorship) for the taxon from the " +
280      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.  This could be a synonym, see " +
281      "also `acceptedScientificName`.",
282    externalDocs = @ExternalDocumentation(
283      description = "Darwin Core definition",
284      url = "https://rs.tdwg.org/dwc/terms/"
285    )
286  )
287  private String scientificName;  // the interpreted name matching taxonKey
288
289  @Schema(
290    description = "The scientific name authorship for the taxon from the " +
291      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.  This could be a synonym, see " +
292      "also `acceptedScientificName`.",
293    externalDocs = @ExternalDocumentation(
294      description = "Darwin Core definition",
295      url = "https://rs.tdwg.org/dwc/terms/"
296    )
297  )
298  private String scientificNameAuthorship;
299
300  @Schema(
301    description = "The accepted scientific name (including authorship) for the taxon from the " +
302      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
303    externalDocs = @ExternalDocumentation(
304      description = "Darwin Core definition",
305      url = "https://rs.tdwg.org/dwc/terms/"
306    )
307  )
308  private String acceptedScientificName;
309
310  @Schema(
311    description = "The kingdom name (excluding authorship) for the kingdom from the " +
312      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
313    externalDocs = @ExternalDocumentation(
314      description = "Darwin Core definition",
315      url = "https://rs.tdwg.org/dwc/terms/"
316    )
317  )
318  private String kingdom;
319
320  @Schema(
321    description = "The phylum name (excluding authorship) for the phylum from the " +
322      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
323    externalDocs = @ExternalDocumentation(
324      description = "Darwin Core definition",
325      url = "https://rs.tdwg.org/dwc/terms/"
326    )
327  )
328  private String phylum;
329
330  @Schema(
331    description = "The class name (excluding authorship) for the class from the " +
332      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
333    externalDocs = @ExternalDocumentation(
334      description = "Darwin Core definition",
335      url = "https://rs.tdwg.org/dwc/terms/"
336    )
337  )
338  @JsonProperty("class")
339  private String clazz;
340
341  @Schema(
342    description = "The order name (excluding authorship) for the order from the " +
343      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
344    externalDocs = @ExternalDocumentation(
345      description = "Darwin Core definition",
346      url = "https://rs.tdwg.org/dwc/terms/"
347    )
348  )
349  private String order;
350
351  @Schema(
352    description = "The family name (excluding authorship) for the family from the " +
353      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
354    externalDocs = @ExternalDocumentation(
355      description = "Darwin Core definition",
356      url = "https://rs.tdwg.org/dwc/terms/"
357    )
358  )
359  private String family;
360
361  @Schema(
362    description = "The genus name (excluding authorship) for the genus from the " +
363      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
364    externalDocs = @ExternalDocumentation(
365      description = "Darwin Core definition",
366      url = "https://rs.tdwg.org/dwc/terms/"
367    )
368  )
369  private String genus;
370
371  @Schema(
372    description = "The subgenus name (excluding authorship) for the subgenus from the " +
373      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
374    externalDocs = @ExternalDocumentation(
375      description = "Darwin Core definition",
376      url = "https://rs.tdwg.org/dwc/terms/"
377    )
378  )
379  private String subgenus;
380
381  @Schema(
382    description = "The species name (excluding authorship) for the species from the " +
383      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
384    externalDocs = @ExternalDocumentation(
385      description = "Darwin Core definition",
386      url = "https://rs.tdwg.org/dwc/terms/"
387    )
388  )
389  private String species;
390
391  // atomised scientific name
392
393  @Schema(
394    description = "The genus name part of the species name from the " +
395      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
396    externalDocs = @ExternalDocumentation(
397      description = "Darwin Core definition",
398      url = "https://rs.tdwg.org/dwc/terms/genericName"
399    )
400  )
401  private String genericName;
402
403  @Schema(
404    description = "The specific name part of the species name from the " +
405      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
406    externalDocs = @ExternalDocumentation(
407      description = "Darwin Core definition",
408      url = "https://rs.tdwg.org/dwc/terms/specificEpithet"
409    )
410  )
411  private String specificEpithet;
412
413  @Schema(
414    description = "The infraspecific name part of the species name from the " +
415      "[GBIF backbone](https://doi.org/10.15468/39omei) matched to this occurrence.",
416    externalDocs = @ExternalDocumentation(
417      description = "Darwin Core definition",
418      url = "https://rs.tdwg.org/dwc/terms/infraspecificEpithet"
419    )
420  )
421  private String infraspecificEpithet;
422
423  @Schema(
424    description = "The taxonomic rank of the most specific name in the scientificName.",
425    externalDocs = @ExternalDocumentation(
426      description = "Darwin Core definition",
427      url = "https://rs.tdwg.org/dwc/terms/taxonRank"
428    )
429  )
430  private Rank taxonRank;
431
432  @Schema(
433    description = "The status of the use of the scientificName as a label for a taxon.",
434    externalDocs = @ExternalDocumentation(
435      description = "Darwin Core definition",
436      url = "https://rs.tdwg.org/dwc/terms/taxonomicStatus"
437    )
438  )
439  private TaxonomicStatus taxonomicStatus;
440
441  @Schema(
442    description = "The IUCN Red List Category of the taxon of this occurrence.\n\n" +
443      "See the [GBIF vocabulary](https://rs.gbif.org/vocabulary/iucn/threat_status/) for the values and their " +
444      "definitions, and the [IUCN Red List of Threatened Species dataset in GBIF](https://doi.org/10.15468/0qnb58) " +
445      "for the version of the Red List GBIF's interpretation procedures are using.",
446    externalDocs = @ExternalDocumentation(
447      description = "GBIF vocabulary",
448      url = "https://rs.gbif.org/vocabulary/iucn/threat_status/"
449    )
450  )
451  private String iucnRedListCategory;
452
453  // identification
454
455  @Schema(
456    description = "The date on which the subject was determined as representing the Taxon.",
457    externalDocs = @ExternalDocumentation(
458      description = "Darwin Core definition",
459      url = "https://rs.tdwg.org/dwc/terms/dateIdentified"
460    )
461  )
462  private Date dateIdentified;
463
464  // location
465
466  @Schema(
467    description = "The geographic latitude (in decimal degrees, using the WGS84 datum) of the geographic centre " +
468      "of the location of the occurrence.",
469    externalDocs = @ExternalDocumentation(
470      description = "Darwin Core definition",
471      url = "https://rs.tdwg.org/dwc/terms/decimalLatitude"
472    )
473  )
474  private Double decimalLatitude;
475
476  @Schema(
477    description = "The geographic longitude (in decimal degrees, using the WGS84 datum) of the geographic centre " +
478      "of the location of the occurrence.",
479    externalDocs = @ExternalDocumentation(
480      description = "Darwin Core definition",
481      url = "https://rs.tdwg.org/dwc/terms/decimalLongitude"
482    )
483  )
484  private Double decimalLongitude;
485
486  //coordinatePrecision and coordinateUncertaintyInMeters should be BigDecimal see POR-2795
487
488  @Schema(
489    description = "A decimal representation of the precision of the coordinates given in the decimalLatitude and decimalLongitude.",
490    externalDocs = @ExternalDocumentation(
491      description = "Darwin Core definition",
492      url = "https://rs.tdwg.org/dwc/terms/coordinatePrecision"
493    )
494  )
495  private Double coordinatePrecision;
496
497  @Schema(
498    description = "The horizontal distance (in metres) from the given decimalLatitude and decimalLongitude " +
499      "describing the smallest circle containing the whole of the Location.",
500    externalDocs = @ExternalDocumentation(
501      description = "Darwin Core definition",
502      url = "https://rs.tdwg.org/dwc/terms/coordinateUncertaintyInMeters"
503    )
504  )
505  private Double coordinateUncertaintyInMeters;
506
507  @Schema(
508    description = "**Deprecated.**  This value is always null.  It is an obsolete Darwin Core term.",
509    externalDocs = @ExternalDocumentation(
510      description = "Darwin Core definition",
511      url = "https://rs.tdwg.org/dwc/terms/"
512    )
513  )
514  @Deprecated //see getter
515  private Double coordinateAccuracy;
516
517  @Schema(
518    description = "Elevation (altitude) in metres above sea level.  This is not a current Darwin Core term."
519  )
520  private Double elevation;
521
522  @Schema(
523    description = "The value of the potential error associated with the elevation.  This is not a current Darwin Core term."
524  )
525  private Double elevationAccuracy;
526
527  @Schema(
528    description = "Depth in metres below sea level.  This is not a current Darwin Core term."
529  )
530  private Double depth;
531
532  @Schema(
533    description = "The value of the potential error associated with the depth.  This is not a current Darwin Core term."
534  )
535  private Double depthAccuracy;
536
537  // OpenAPI documentation from enumeration
538  private Continent continent;
539
540  @JsonSerialize(using = Country.IsoSerializer.class)
541  @JsonDeserialize(using = Country.IsoDeserializer.class)
542  private Country country;
543
544  @Schema(
545    description = "The name of the next-smaller administrative region than country (state, province, canton, " +
546      "department, region, etc.) in which the occurrence occurs.\n\n" +
547      "This value is unaltered by GBIF's processing; see also the GADM fields.",
548    externalDocs = @ExternalDocumentation(
549      description = "Darwin Core definition",
550      url = "https://rs.tdwg.org/dwc/terms/stateProvince"
551    )
552  )
553  private String stateProvince;
554
555  @Schema(
556    description = "The administrative divisions according to the [GADM database](https://gadm.org/).\n\n" +
557      "This value is applied by GBIF's processing without consideration of the `stateProvince`, `county` or `locality` fields."
558  )
559  private Gadm gadm = new Gadm();
560
561  @Schema(
562    description = "The name of the water body in which the Location occurs.",
563    externalDocs = @ExternalDocumentation(
564      description = "Darwin Core definition",
565      url = "https://rs.tdwg.org/dwc/terms/waterBody"
566    )
567  )
568  private String waterBody;
569
570  @Schema(
571    description = "The distance in metres of the occurrence from a centroid known to be applied to occurrences " +
572      "during georeferencing.  This can potentially indicate low-precision georeferencing, check the values of " +
573      "`coordinateUncertaintyInMeters` and `georeferenceRemarks`."
574  )
575  private Double distanceFromCentroidInMeters;
576
577  @Schema(
578    description = "A list (concatenated and separated) of geographic names less specific than the information captured in the dwc:locality term.",
579    externalDocs = @ExternalDocumentation(
580      description = "Darwin Core definition",
581      url = "https://rs.tdwg.org/dwc/terms/higherGeography"
582    )
583  )
584  private String higherGeography;
585
586  @Schema(
587    description = "A list (concatenated and separated) of names of people, groups, or organizations who determined the georeference (spatial representation) for the dcterms:Location.",
588    externalDocs = @ExternalDocumentation(
589      description = "Darwin Core definition",
590      url = "https://rs.tdwg.org/dwc/terms/georeferencedBy"
591    )
592  )
593  private String georeferencedBy;
594
595  // recording event
596
597  @Schema(
598    description = "The four-digit year in which the event occurred, according to the Common Era calendar.",
599    externalDocs = @ExternalDocumentation(
600      description = "Darwin Core definition",
601      url = "https://rs.tdwg.org/dwc/terms/year"
602    )
603  )
604  private Integer year;
605
606  @Schema(
607    description = "The integer month in which the Event occurred.",
608    externalDocs = @ExternalDocumentation(
609      description = "Darwin Core definition",
610      url = "https://rs.tdwg.org/dwc/terms/month"
611    )
612  )
613  private Integer month;
614
615  @Schema(
616    description = "The integer day of the month on which the Event occurred.",
617    externalDocs = @ExternalDocumentation(
618      description = "Darwin Core definition",
619      url = "https://rs.tdwg.org/dwc/terms/day"
620    )
621  )
622  private Integer day;
623
624  @Schema(
625    description = "The date-time during which an Event occurred. For occurrences, this is the date-time when the " +
626      "event was recorded. Not suitable for a time in a geological context.",
627    externalDocs = @ExternalDocumentation(
628      description = "Darwin Core definition",
629      url = "https://rs.tdwg.org/dwc/terms/eventDate"
630    )
631  )
632  private IsoDateInterval eventDate;
633
634  @Schema(
635    description = "The latest integer day of the year on which the Event occurred (1 for 1 January, 365 for " +
636      "31 December, except in a leap year, in which case it is 366).",
637    externalDocs = @ExternalDocumentation(
638      description = "Darwin Core definition",
639      url = "https://rs.tdwg.org/dwc/terms/endDayOfYear"
640    )
641  )
642  private Integer startDayOfYear;
643
644  @Schema(
645    description = "The earliest integer day of the year on which the Event occurred (1 for 1 January, 365 for " +
646      "31 December, except in a leap year, in which case it is 366).",
647    externalDocs = @ExternalDocumentation(
648      description = "Darwin Core definition",
649      url = "https://rs.tdwg.org/dwc/terms/startDayOfYear"
650    )
651  )
652  private Integer endDayOfYear;
653
654  @Schema(
655    description = "A list (concatenated and separated) of nomenclatural types (type status, typified scientific name, " +
656      "publication) applied to the occurrence.",
657    externalDocs = @ExternalDocumentation(
658      description = "Darwin Core definition",
659      url = "https://rs.tdwg.org/dwc/terms/typeStatus"
660    )
661  )
662  private String typeStatus;
663
664  // extracted from type status, but we should propose a new dwc term for this!
665  // for example: "Paratype of Taeniopteryx metequi Ricker & Ross" is status=Paratype, typifiedName=Taeniopteryx metequi Ricker & Ross
666  @Schema(
667    description = "The scientific name that is based on the type specimen.\n\n" +
668      "This is not yet a Darwin Core term, see the [proposal to add it](https://github.com/tdwg/dwc/issues/28)."
669  )
670  private String typifiedName; // missing from DwC
671
672  @Schema(
673    description = "A specific interpretation issue found during processing and interpretation of the record.\n\n" +
674      "See the https://links.gbif.org/occurrence-issues[list of occurrence issues] and the " +
675      "https://gbif.github.io/gbif-api/apidocs/org/gbif/api/vocabulary/OccurrenceIssue.html[OccurrenceIssue enumeration] " +
676      "for possible values and definitions."
677  )
678  private Set<OccurrenceIssue> issues = EnumSet.noneOf(OccurrenceIssue.class);
679
680  // record level
681
682  @Schema(
683    description = "The most recent date-time on which the occurrence was changed, according to the publisher.",
684    externalDocs = @ExternalDocumentation(
685      description = "Dublin Core definition",
686      url = "https://purl.org/dc/terms/modified"
687    )
688  )
689  private Date modified;  // interpreted dc:modified, i.e. date changed in source
690
691  @Schema(
692    description = "The time this occurrence was last processed by GBIF's interpretation system “Pipelines”.\n\n" +
693      "This is the time the record was last changed in GBIF, **not** the time the record was last changed by the " +
694      "publisher.  Data is also reprocessed when we changed the taxonomic backbone, geographic data sources or " +
695      "other interpretation procedures.\n\n" +
696      "An earlier interpretation system distinguished between “parsing” and “interpretation”, but in the current " +
697      "system there is only one process — the two dates will always be the same."
698  )
699  private Date lastInterpreted;
700
701  @Schema(
702    description = "A related resource that is referenced, cited, or otherwise pointed to by the described resource.",
703    externalDocs = @ExternalDocumentation(
704      description = "Dublin Core definition",
705      url = "https://purl.org/dc/terms/references"
706    )
707  )
708  private URI references;
709
710  @Schema(
711    description = "A legal document giving official permission to do something with the occurrence.",
712    externalDocs = @ExternalDocumentation(
713      description = "Dublin Core definition",
714      url = "https://purl.org/dc/terms/license"
715    )
716  )
717  private License license;
718
719  @Schema(
720    description = "A number or enumeration value for the quantity of organisms.",
721    externalDocs = @ExternalDocumentation(
722      description = "Darwin Core definition",
723      url = "https://rs.tdwg.org/dwc/terms/organismQuantity"
724    )
725  )
726  private Double organismQuantity;
727
728  @Schema(
729    description = "The type of quantification system used for the quantity of organisms.",
730    externalDocs = @ExternalDocumentation(
731      description = "Darwin Core definition",
732      url = "https://rs.tdwg.org/dwc/terms/organismQuantityType"
733    )
734  )
735  private String organismQuantityType;
736
737  @Schema(
738    description = "The unit of measurement of the size (time duration, length, area, or volume) of a sample in a sampling event.",
739    externalDocs = @ExternalDocumentation(
740      description = "Darwin Core definition",
741      url = "https://rs.tdwg.org/dwc/terms/sampleSizeUnit"
742    )
743  )
744  private String sampleSizeUnit;
745
746  @Schema(
747    description = "A numeric value for a measurement of the size (time duration, length, area, or volume) of a sample in a sampling event.",
748    externalDocs = @ExternalDocumentation(
749      description = "Darwin Core definition",
750      url = "https://rs.tdwg.org/dwc/terms/sampleSizeValue"
751    )
752  )
753  private Double sampleSizeValue;
754
755  @Schema(
756    description = "The relative measurement of the quantity of the organism (i.e. without absolute units)."
757  )
758  private Double relativeOrganismQuantity;
759
760  @Schema(
761    description = "Flag occurrence when associatedSequences/extension exists"
762  )
763  private boolean isSequenced;
764
765  @Schema(
766      description = "A list (concatenated and separated) of identifiers (publication, global unique identifier, URI) "
767        + "of genetic sequence information associated with the material entity.",
768      externalDocs =
769          @ExternalDocumentation(
770              description = "Darwin Core definition",
771              url = "https://rs.tdwg.org/dwc/terms/associatedSequences"))
772  private String associatedSequences;
773
774  // interpreted extension data
775
776  @Schema(
777    description = "Alternative identifiers for the occurrence.",
778    externalDocs = @ExternalDocumentation(
779      description = "GBIF Alternative Identifiers extension",
780      url = "https://rs.gbif.org/terms/1.0/Identifier"
781    )
782  )
783  private List<Identifier> identifiers = new ArrayList<>();
784
785  @Schema(
786    description = "Multimedia related to te occurrence.",
787    externalDocs = @ExternalDocumentation(
788      description = "GBIF Multimedia extension",
789      url = "https://rs.gbif.org/terms/1.0/Multimedia"
790    )
791  )
792  private List<MediaObject> media = new ArrayList<>();
793
794  @Schema(
795    description = "Measurements or facts about the the occurrence.",
796    externalDocs = @ExternalDocumentation(
797      description = "Darwin Core definition",
798      url = "https://rs.tdwg.org/dwc/terms/MeasurementOrFact"
799    )
800  )
801  private List<MeasurementOrFact> facts = new ArrayList<>();
802
803  @Schema(
804    description = "Relationships between occurrences.",
805    externalDocs = @ExternalDocumentation(
806      description = "Darwin Core definition",
807      url = "https://rs.tdwg.org/dwc/terms/ResourceRelationship"
808    )
809  )
810  private List<OccurrenceRelation> relations = new ArrayList<>();
811
812  @Schema(
813    description = "A list of the globally unique identifiers for the person, people, groups, or organizations " +
814      "responsible for recording the original Occurrence.",
815    externalDocs = @ExternalDocumentation(
816      description = "Darwin Core definition",
817      url = "https://rs.tdwg.org/dwc/terms/recordedByID"
818    )
819  )
820  @JsonProperty("recordedByIDs")
821  private List<AgentIdentifier> recordedByIds = new ArrayList<>();
822
823  @Schema(
824    description = "A list of the globally unique identifiers for the person, people, groups, or organizations " +
825      "responsible for assigning the Taxon to the occurrence.",
826    externalDocs = @ExternalDocumentation(
827      description = "Darwin Core definition",
828      url = "https://rs.tdwg.org/dwc/terms/identifiedByID"
829    )
830  )
831  @JsonProperty("identifiedByIDs")
832  private List<AgentIdentifier> identifiedByIds = new ArrayList<>();
833
834  @Schema(
835    description = "**Experimental.**  The UUID of the institution holding the specimen occurrence, from GRSciColl."
836  )
837  @Experimental
838  private String institutionKey;
839
840  @Schema(
841    description = "**Experimental.**  The UUID of the collection containing the specimen occurrence, from GRSciColl."
842  )
843  @Experimental
844  private String collectionKey;
845
846  @Schema(
847    description = "**Experimental.**  Whether the occurrence belongs to a machine-calculated cluster of probable " +
848      "duplicate occurrences.",
849    externalDocs = @ExternalDocumentation(
850      description = "GBIF Data Blog",
851      url = "https://data-blog.gbif.org/post/clustering-occurrences/"
852    )
853  )
854  @Experimental
855  private boolean isInCluster;
856
857  @Schema(
858    description = "An identifier for the set of data. May be a global unique identifier or an identifier specific to " +
859      "a collection or institution.",
860    externalDocs = @ExternalDocumentation(
861      description = "Darwin Core definition",
862      url = "https://rs.tdwg.org/dwc/terms/datasetID"
863    )
864  )
865  private String datasetID;
866
867  @Schema(
868    description = "The name identifying the data set from which the record was derived.",
869    externalDocs = @ExternalDocumentation(
870      description = "Darwin Core definition",
871      url = "https://rs.tdwg.org/dwc/terms/"
872    )
873  )
874  private String datasetName;
875
876  @Schema(
877    description = "A list (concatenated and separated) of previous or alternate fully qualified catalogue numbers " +
878      "or other human-used identifiers for the same occurrence, whether in the current or any other data set or collection.",
879    externalDocs = @ExternalDocumentation(
880      description = "Darwin Core definition",
881      url = "https://rs.tdwg.org/dwc/terms/otherCatalogNumbers"
882    )
883  )
884  private String otherCatalogNumbers;
885
886  @Schema(
887    description = "The full name of the earliest possible geochronologic eon or lowest chrono-stratigraphic eonothem or the informal name (\"Precambrian\") attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
888    externalDocs = @ExternalDocumentation(
889      description = "Darwin Core definition",
890      url = "https://rs.tdwg.org/dwc/terms/earliestEonOrLowestEonothem"
891    )
892  )
893  private String earliestEonOrLowestEonothem;
894
895  @Schema(
896    description = "The full name of the latest possible geochronologic eon or highest chrono-stratigraphic eonothem or the informal name (\"Precambrian\") attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
897    externalDocs = @ExternalDocumentation(
898      description = "Darwin Core definition",
899      url = "https://rs.tdwg.org/dwc/terms/latestEonOrHighestEonothem"
900    )
901  )
902  private String latestEonOrHighestEonothem;
903
904  @Schema(
905    description = "The full name of the earliest possible geochronologic era or lowest chronostratigraphic erathem attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
906    externalDocs = @ExternalDocumentation(
907      description = "Darwin Core definition",
908      url = "https://rs.tdwg.org/dwc/terms/earliestEraOrLowestErathem"
909    )
910  )
911  private String earliestEraOrLowestErathem;
912
913  @Schema(
914    description = "The full name of the latest possible geochronologic era or highest chronostratigraphic erathem attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
915    externalDocs = @ExternalDocumentation(
916      description = "Darwin Core definition",
917      url = "https://rs.tdwg.org/dwc/terms/latestEraOrHighestErathem"
918    )
919  )
920  private String latestEraOrHighestErathem;
921
922  @Schema(
923    description = "The full name of the earliest possible geochronologic period or lowest chronostratigraphic system attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
924    externalDocs = @ExternalDocumentation(
925      description = "Darwin Core definition",
926      url = "https://rs.tdwg.org/dwc/terms/earliestPeriodOrLowestSystem"
927    )
928  )
929  private String earliestPeriodOrLowestSystem;
930
931  @Schema(
932    description = "The full name of the latest possible geochronologic period or highest chronostratigraphic system attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
933    externalDocs = @ExternalDocumentation(
934      description = "Darwin Core definition",
935      url = "https://rs.tdwg.org/dwc/terms/latestPeriodOrHighestSystem"
936    )
937  )
938  private String latestPeriodOrHighestSystem;
939
940  @Schema(
941    description = "The full name of the earliest possible geochronologic epoch or lowest chronostratigraphic series attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
942    externalDocs = @ExternalDocumentation(
943      description = "Darwin Core definition",
944      url = "https://rs.tdwg.org/dwc/terms/earliestEpochOrLowestSeries"
945    )
946  )
947  private String earliestEpochOrLowestSeries;
948
949  @Schema(
950    description = "The full name of the latest possible geochronologic epoch or highest chronostratigraphic series attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
951    externalDocs = @ExternalDocumentation(
952      description = "Darwin Core definition",
953      url = "https://rs.tdwg.org/dwc/terms/latestEpochOrHighestSeries"
954    )
955  )
956  private String latestEpochOrHighestSeries;
957
958  @Schema(
959    description = "The full name of the earliest possible geochronologic age or lowest chronostratigraphic stage attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
960    externalDocs = @ExternalDocumentation(
961      description = "Darwin Core definition",
962      url = "https://rs.tdwg.org/dwc/terms/earliestAgeOrLowestStage"
963    )
964  )
965  private String earliestAgeOrLowestStage;
966
967  @Schema(
968    description = "The full name of the latest possible geochronologic age or highest chronostratigraphic stage attributable to the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
969    externalDocs = @ExternalDocumentation(
970      description = "Darwin Core definition",
971      url = "https://rs.tdwg.org/dwc/terms/latestAgeOrHighestStage"
972    )
973  )
974  private String latestAgeOrHighestStage;
975
976  @Schema(
977    description = "The full name of the lowest possible geological biostratigraphic zone of the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
978    externalDocs = @ExternalDocumentation(
979      description = "Darwin Core definition",
980      url = "https://rs.tdwg.org/dwc/terms/lowestBiostratigraphicZone"
981    )
982  )
983  private String lowestBiostratigraphicZone;
984
985  @Schema(
986    description = "The full name of the highest possible geological biostratigraphic zone of the stratigraphic horizon from which the dwc:MaterialEntity was collected.",
987    externalDocs = @ExternalDocumentation(
988      description = "Darwin Core definition",
989      url = "https://rs.tdwg.org/dwc/terms/highestBiostratigraphicZone"
990    )
991  )
992  private String highestBiostratigraphicZone;
993
994  @Schema(
995    description = "The full name of the lithostratigraphic group from which the dwc:MaterialEntity was collected.",
996    externalDocs = @ExternalDocumentation(
997      description = "Darwin Core definition",
998      url = "https://rs.tdwg.org/dwc/terms/group"
999    )
1000  )
1001  private String group;
1002
1003  @Schema(
1004    description = "The full name of the lithostratigraphic formation from which the dwc:MaterialEntity was collected.",
1005    externalDocs = @ExternalDocumentation(
1006      description = "Darwin Core definition",
1007      url = "https://rs.tdwg.org/dwc/terms/formation"
1008    )
1009  )
1010  private String formation;
1011
1012  @Schema(
1013    description = "The full name of the lithostratigraphic member from which the dwc:MaterialEntity was collected.",
1014    externalDocs = @ExternalDocumentation(
1015      description = "Darwin Core definition",
1016      url = "https://rs.tdwg.org/dwc/terms/member"
1017    )
1018  )
1019  private String member;
1020
1021  @Schema(
1022    description = "The full name of the lithostratigraphic bed from which the dwc:MaterialEntity was collected.",
1023    externalDocs = @ExternalDocumentation(
1024      description = "Darwin Core definition",
1025      url = "https://rs.tdwg.org/dwc/terms/bed"
1026    )
1027  )
1028  private String bed;
1029
1030
1031  @Schema(
1032    description = "A person, group, or organization responsible for recording the original occurrence.",
1033    externalDocs = @ExternalDocumentation(
1034      description = "Darwin Core definition",
1035      url = "https://rs.tdwg.org/dwc/terms/recordedBy"
1036    )
1037  )
1038  private String recordedBy;
1039
1040  @Schema(
1041    description = "A list (concatenated and separated) of names of people, groups, or organizations who assigned the " +
1042      "Taxon to the occurrence.",
1043    externalDocs = @ExternalDocumentation(
1044      description = "Darwin Core definition",
1045      url = "https://rs.tdwg.org/dwc/terms/identifiedBy"
1046    )
1047  )
1048  private String identifiedBy;
1049
1050  @Schema(
1051    description = "A preparation or preservation method for a specimen.",
1052    externalDocs = @ExternalDocumentation(
1053      description = "Darwin Core definition",
1054      url = "https://rs.tdwg.org/dwc/terms/preparations"
1055    )
1056  )
1057  private String preparations;
1058
1059  @Schema(
1060    description = "The methods or protocols used during an Event, denoted by an IRI.",
1061    externalDocs = @ExternalDocumentation(
1062      description = "Darwin Core definition",
1063      url = "https://rs.tdwg.org/dwc/terms/samplingProtocol"
1064    )
1065  )
1066  private String samplingProtocol;
1067
1068  @Schema(
1069    description = "**Experimental.**  The DNA sequence ID of an occurrence."
1070  )
1071  @Experimental
1072  private List<String> dnaSequenceID = new ArrayList<>();
1073
1074  public Occurrence() {
1075
1076  }
1077
1078  /**
1079   * Create occurrence instance from existing verbatim one, copying over all data.
1080   */
1081  public Occurrence(@Nullable VerbatimOccurrence verbatim) {
1082    if (verbatim != null) {
1083      setKey(verbatim.getKey());
1084      setDatasetKey(verbatim.getDatasetKey());
1085      setPublishingOrgKey(verbatim.getPublishingOrgKey());
1086      setPublishingCountry(verbatim.getPublishingCountry());
1087      setProtocol(verbatim.getProtocol());
1088      setCrawlId(verbatim.getCrawlId());
1089      if (verbatim.getLastCrawled() != null) {
1090        setLastCrawled(new Date(verbatim.getLastCrawled().getTime()));
1091      }
1092      if (verbatim.getVerbatimFields() != null) {
1093        getVerbatimFields().putAll(verbatim.getVerbatimFields());
1094      }
1095      if (verbatim.getLastParsed() != null) {
1096        setLastParsed(verbatim.getLastParsed());
1097      }
1098      setExtensions(verbatim.getExtensions());
1099    }
1100  }
1101
1102  @Nullable
1103  public BasisOfRecord getBasisOfRecord() {
1104    return basisOfRecord;
1105  }
1106
1107  public void setBasisOfRecord(BasisOfRecord basisOfRecord) {
1108    this.basisOfRecord = basisOfRecord;
1109  }
1110
1111  @Nullable
1112  public Integer getIndividualCount() {
1113    return individualCount;
1114  }
1115
1116  public void setIndividualCount(Integer individualCount) {
1117    this.individualCount = individualCount;
1118  }
1119
1120  @Nullable
1121  public OccurrenceStatus getOccurrenceStatus() {
1122    return occurrenceStatus;
1123  }
1124
1125  public void setOccurrenceStatus(OccurrenceStatus occurrenceStatus) {
1126    this.occurrenceStatus = occurrenceStatus;
1127  }
1128
1129  @Nullable
1130  public String getSex() {
1131    return sex;
1132  }
1133
1134  public void setSex(String sex) {
1135    this.sex = sex;
1136  }
1137
1138  @Nullable
1139  public String getLifeStage() {
1140    return lifeStage;
1141  }
1142
1143  public void setLifeStage(String lifeStage) {
1144    this.lifeStage = lifeStage;
1145  }
1146
1147  @Nullable
1148  public String getEstablishmentMeans() {
1149    return establishmentMeans;
1150  }
1151
1152  public void setEstablishmentMeans(String establishmentMeans) {
1153    this.establishmentMeans = establishmentMeans;
1154  }
1155
1156  /**
1157   * The best matching, accepted GBIF backbone name usage representing this occurrence.
1158   * In case the verbatim scientific name and its classification can only be matched to a higher rank this will
1159   * represent the lowest matching rank. In the worst case this could just be for example Animalia.
1160   */
1161  @Nullable
1162  public Integer getTaxonKey() {
1163    return taxonKey;
1164  }
1165
1166  public void setTaxonKey(Integer taxonKey) {
1167    this.taxonKey = taxonKey;
1168  }
1169
1170  @Nullable
1171  @Override
1172  public Integer getKingdomKey() {
1173    return kingdomKey;
1174  }
1175
1176  @Override
1177  public void setKingdomKey(@Nullable Integer kingdomKey) {
1178    this.kingdomKey = kingdomKey;
1179  }
1180
1181  @Nullable
1182  @Override
1183  public Integer getPhylumKey() {
1184    return phylumKey;
1185  }
1186
1187  @Override
1188  public void setPhylumKey(@Nullable Integer phylumKey) {
1189    this.phylumKey = phylumKey;
1190  }
1191
1192  @Nullable
1193  @Override
1194  public Integer getClassKey() {
1195    return classKey;
1196  }
1197
1198  @Override
1199  public void setClassKey(@Nullable Integer classKey) {
1200    this.classKey = classKey;
1201  }
1202
1203  @Nullable
1204  @Override
1205  public Integer getOrderKey() {
1206    return orderKey;
1207  }
1208
1209  @Override
1210  public void setOrderKey(@Nullable Integer orderKey) {
1211    this.orderKey = orderKey;
1212  }
1213
1214  @Nullable
1215  @Override
1216  public Integer getFamilyKey() {
1217    return familyKey;
1218  }
1219
1220  @Override
1221  public void setFamilyKey(@Nullable Integer familyKey) {
1222    this.familyKey = familyKey;
1223  }
1224
1225  @Nullable
1226  @Override
1227  public Integer getGenusKey() {
1228    return genusKey;
1229  }
1230
1231  @Override
1232  public void setGenusKey(@Nullable Integer genusKey) {
1233    this.genusKey = genusKey;
1234  }
1235
1236  @Nullable
1237  @Override
1238  public Integer getSubgenusKey() {
1239    return subgenusKey;
1240  }
1241
1242  @Override
1243  public void setSubgenusKey(@Nullable Integer subgenusKey) {
1244    this.subgenusKey = subgenusKey;
1245  }
1246
1247  @Nullable
1248  @Override
1249  public Integer getHigherRankKey(Rank rank) {
1250    return ClassificationUtils.getHigherRankKey(this, rank);
1251  }
1252
1253  /**
1254   * An ordered map with entries for all higher Linnean ranks excluding the taxonKey itself.
1255   * The map starts with the highest rank, e.g. the kingdom and maps the name usage key to its canonical name.
1256   *
1257   * @return map of higher ranks
1258   */
1259  @NotNull
1260  @JsonIgnore
1261  public Map<Integer, String> getHigherClassificationMap() {
1262    return taxonKey == null ? ClassificationUtils.getHigherClassificationMap(this)
1263      : ClassificationUtils.getHigherClassificationMap(this, taxonKey, null, null);
1264  }
1265
1266  /**
1267   * The accepted species for this occurrence. In case the taxonKey is of a higher rank than species (e.g. genus)
1268   * speciesKey is null. In case taxonKey represents an infraspecific taxon the speciesKey points to the species
1269   * the infraspecies is classified as. In case of taxonKey being a species the speciesKey is the same.
1270   */
1271  @Nullable
1272  @Override
1273  public Integer getSpeciesKey() {
1274    return speciesKey;
1275  }
1276
1277  @Override
1278  public void setSpeciesKey(@Nullable Integer speciesKey) {
1279    this.speciesKey = speciesKey;
1280  }
1281
1282  /**
1283   * The accepted taxon key from the GBIF backbone.
1284   */
1285  @Nullable
1286  public Integer getAcceptedTaxonKey() {
1287    return acceptedTaxonKey;
1288  }
1289
1290  public void setAcceptedTaxonKey(Integer acceptedTaxonKey) {
1291    this.acceptedTaxonKey = acceptedTaxonKey;
1292  }
1293
1294  @Nullable
1295  public String getSpecificEpithet() {
1296    return specificEpithet;
1297  }
1298
1299  public void setSpecificEpithet(String specificEpithet) {
1300    this.specificEpithet = specificEpithet;
1301  }
1302
1303  @Nullable
1304  public String getInfraspecificEpithet() {
1305    return infraspecificEpithet;
1306  }
1307
1308  public void setInfraspecificEpithet(String infraspecificEpithet) {
1309    this.infraspecificEpithet = infraspecificEpithet;
1310  }
1311
1312  @Nullable
1313  public Rank getTaxonRank() {
1314    return taxonRank;
1315  }
1316
1317  public void setTaxonRank(Rank taxonRank) {
1318    this.taxonRank = taxonRank;
1319  }
1320
1321  /**
1322   * The status of the use of the scientificName as a label for a taxon.
1323   * The GBIF recommended controlled value vocabulary can be found at <a href="http://rs.gbif.org/vocabulary/gbif/taxonomic_status.xml">http://rs.gbif.org/vocabulary/gbif/taxonomic_status.xm</a>.
1324   */
1325  @Nullable
1326  public TaxonomicStatus getTaxonomicStatus() {
1327    return taxonomicStatus;
1328  }
1329
1330  public void setTaxonomicStatus(TaxonomicStatus taxonomicStatus) {
1331    this.taxonomicStatus = taxonomicStatus;
1332  }
1333
1334  /**
1335   * The IUCN Red List Category.
1336   */
1337  @Nullable
1338  public String getIucnRedListCategory() {
1339    return iucnRedListCategory;
1340  }
1341
1342  public void setIucnRedListCategory(String iucnRedListCategory) {
1343    this.iucnRedListCategory = iucnRedListCategory;
1344  }
1345
1346  /**
1347   * The scientific name for taxonKey from the GBIF backbone.
1348   */
1349  @Nullable
1350  public String getScientificName() {
1351    return scientificName;
1352  }
1353
1354  public void setScientificName(@Nullable String scientificName) {
1355    this.scientificName = scientificName;
1356  }
1357
1358  /**
1359   * The scientific name authorship for taxonKey from the GBIF backbone.
1360   */
1361  @Nullable
1362  public String getScientificNameAuthorship() {
1363    return scientificNameAuthorship;
1364  }
1365
1366  public void setScientificNameAuthorship(String scientificNameAuthorship) {
1367    this.scientificNameAuthorship = scientificNameAuthorship;
1368  }
1369
1370  /**
1371   * The verbatim scientific name as provided by the source.
1372   */
1373  @Nullable
1374  @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
1375  public String getVerbatimScientificName() {
1376    return getVerbatimField(DwcTerm.scientificName);
1377  }
1378
1379  public void setVerbatimScientificName(String scientificName) {
1380    //DO NOTHING
1381  }
1382
1383  /**
1384   * The accepted scientific name for the acceptedTaxonKey from the GBIF backbone.
1385   */
1386  @Nullable
1387  public String getAcceptedScientificName() {
1388    return acceptedScientificName;
1389  }
1390
1391  public void setAcceptedScientificName(String acceptedScientificName) {
1392    this.acceptedScientificName = acceptedScientificName;
1393  }
1394
1395  @Nullable
1396  @Override
1397  public String getKingdom() {
1398    return kingdom;
1399  }
1400
1401  @Override
1402  public void setKingdom(@Nullable String kingdom) {
1403    this.kingdom = kingdom;
1404  }
1405
1406  @Nullable
1407  @Override
1408  public String getPhylum() {
1409    return phylum;
1410  }
1411
1412  @Override
1413  public void setPhylum(@Nullable String phylum) {
1414    this.phylum = phylum;
1415  }
1416
1417  @Nullable
1418  @Override
1419  public String getClazz() {
1420    return clazz;
1421  }
1422
1423  @Override
1424  public void setClazz(@Nullable String clazz) {
1425    this.clazz = clazz;
1426  }
1427
1428  @Nullable
1429  @Override
1430  public String getOrder() {
1431    return order;
1432  }
1433
1434  @Override
1435  public void setOrder(@Nullable String order) {
1436    this.order = order;
1437  }
1438
1439  @Nullable
1440  @Override
1441  public String getFamily() {
1442    return family;
1443  }
1444
1445  @Override
1446  public void setFamily(@Nullable String family) {
1447    this.family = family;
1448  }
1449
1450  @Nullable
1451  @Override
1452  public String getGenus() {
1453    return genus;
1454  }
1455
1456  @Override
1457  public void setGenus(@Nullable String genus) {
1458    this.genus = genus;
1459  }
1460
1461  @Nullable
1462  public String getGenericName() {
1463    return genericName;
1464  }
1465
1466  public void setGenericName(String genericName) {
1467    this.genericName = genericName;
1468  }
1469
1470  @Nullable
1471  @Override
1472  public String getSubgenus() {
1473    return subgenus;
1474  }
1475
1476  @Override
1477  public void setSubgenus(@Nullable String subgenus) {
1478    this.subgenus = subgenus;
1479  }
1480
1481  @Nullable
1482  @Override
1483  public String getHigherRank(Rank rank) {
1484    return ClassificationUtils.getHigherRank(this, rank);
1485  }
1486
1487  /**
1488   * The corresponding scientific name of the speciesKey from the GBIF backbone.
1489   */
1490  @Nullable
1491  @Override
1492  public String getSpecies() {
1493    return species;
1494  }
1495
1496  @Override
1497  public void setSpecies(@Nullable String species) {
1498    this.species = species;
1499  }
1500
1501  @Nullable
1502  public Date getDateIdentified() {
1503    return dateIdentified == null ? null : new Date(dateIdentified.getTime());
1504  }
1505
1506  public void setDateIdentified(@Nullable Date dateIdentified) {
1507    this.dateIdentified = dateIdentified == null ? null : new Date(dateIdentified.getTime());
1508  }
1509
1510  /**
1511   * The decimalLongitude in decimal degrees always for the WGS84 datum. If a different geodetic datum was given the verbatim
1512   * coordinates are transformed into WGS84 values.
1513   */
1514  @Nullable
1515  public Double getDecimalLongitude() {
1516    return decimalLongitude;
1517  }
1518
1519  public void setDecimalLongitude(@Nullable Double decimalLongitude) {
1520    this.decimalLongitude = decimalLongitude;
1521  }
1522
1523  @Nullable
1524  public Double getDecimalLatitude() {
1525    return decimalLatitude;
1526  }
1527
1528  public void setDecimalLatitude(@Nullable Double decimalLatitude) {
1529    this.decimalLatitude = decimalLatitude;
1530  }
1531
1532  /**
1533   * The uncertainty radius for lat/lon in meters.
1534   */
1535  @Nullable
1536  public Double getCoordinateUncertaintyInMeters() {
1537    return coordinateUncertaintyInMeters;
1538  }
1539
1540  public void setCoordinateUncertaintyInMeters(@Nullable Double coordinateUncertaintyInMeters) {
1541    this.coordinateUncertaintyInMeters = coordinateUncertaintyInMeters;
1542  }
1543
1544  @Nullable
1545  public Double getCoordinatePrecision() {
1546    return coordinatePrecision;
1547  }
1548
1549  public void setCoordinatePrecision(Double coordinatePrecision) {
1550    this.coordinatePrecision = coordinatePrecision;
1551  }
1552
1553  /**
1554   * @deprecated to be removed in the public v2 of the API (see POR-3061)
1555   * The uncertainty for latitude in decimal degrees.
1556   * Note that the longitude degrees have a different accuracy in degrees which changes with latitude and is largest at the poles.
1557   */
1558  @Nullable
1559  @Deprecated
1560  public Double getCoordinateAccuracy() {
1561    return coordinateAccuracy;
1562  }
1563
1564  public void setCoordinateAccuracy(@Nullable Double coordinateAccuracy) {
1565    this.coordinateAccuracy = coordinateAccuracy;
1566  }
1567
1568  /**
1569   * The geodetic datum for the interpreted decimal coordinates.
1570   * This is always WGS84 if a coordinate exists as we reproject other datums into WGS84.
1571   */
1572  @Schema(
1573    description = "The geodetic datum for the interpreted decimal coordinates.\n\n" +
1574      "Coordinates are reprojected to WGS84 if they exist, so this field is either null or `WGS84`."
1575  )
1576  @Nullable
1577  public String getGeodeticDatum() {
1578    if (decimalLatitude != null) {
1579      return GEO_DATUM;
1580    }
1581    return null;
1582  }
1583
1584  /**
1585   * This private method is needed for jackson deserialization only.
1586   */
1587  private void setGeodeticDatum(String datum) {
1588    // ignore, we have a static WGS84 value
1589  }
1590
1591  /**
1592   * Elevation in meters usually above sea level (altitude).
1593   * </br>
1594   * The elevation is calculated using the equation: (minimumElevationInMeters + maximumElevationInMeters) / 2.
1595   */
1596  @Nullable
1597  public Double getElevation() {
1598    return elevation;
1599  }
1600
1601  public void setElevation(@Nullable Double elevation) {
1602    this.elevation = elevation;
1603  }
1604
1605  /**
1606   * Elevation accuracy is the uncertainty for the elevation in meters.
1607   * </br>
1608   * The elevation accuracy is calculated using the equation: (maximumElevationInMeters - minimumElevationInMeters) / 2
1609   */
1610  @Nullable
1611  public Double getElevationAccuracy() {
1612    return elevationAccuracy;
1613  }
1614
1615  public void setElevationAccuracy(@Nullable Double elevationAccuracy) {
1616    this.elevationAccuracy = elevationAccuracy;
1617  }
1618
1619  /**
1620   * Depth in meters below the surface. Complimentary to elevation, the depth can be 10 meters below the surface of a
1621   * lake in 1100m (=elevation).
1622   * </br>
1623   * The depth is calculated using the equation: (minimumDepthInMeters + maximumDepthInMeters) / 2.
1624   */
1625  @Nullable
1626  public Double getDepth() {
1627    return depth;
1628  }
1629
1630  public void setDepth(@Nullable Double depth) {
1631    this.depth = depth;
1632  }
1633
1634  /**
1635   * Depth accuracy is the uncertainty for the depth in meters.
1636   * </br>
1637   * The depth accuracy is calculated using the equation: (maximumDepthInMeters - minimumDepthInMeters) / 2
1638   */
1639  @Nullable
1640  public Double getDepthAccuracy() {
1641    return depthAccuracy;
1642  }
1643
1644  public void setDepthAccuracy(@Nullable Double depthAccuracy) {
1645    this.depthAccuracy = depthAccuracy;
1646  }
1647
1648  @Nullable
1649  public Continent getContinent() {
1650    return continent;
1651  }
1652
1653  public void setContinent(@Nullable Continent continent) {
1654    this.continent = continent;
1655  }
1656
1657  @Schema(
1658    description = "The 2-letter country code (as per ISO-3166-1) of the country, territory or area in which the " +
1659      "occurrence was recorded.",
1660    externalDocs = @ExternalDocumentation(
1661      description = "Darwin Core definition",
1662      url = "https://rs.tdwg.org/dwc/terms/countryCode"
1663    )
1664  )
1665  @Nullable
1666  @JsonProperty("countryCode")
1667  public Country getCountry() {
1668    return country;
1669  }
1670
1671  public void setCountry(@Nullable Country country) {
1672    this.country = country;
1673  }
1674
1675  @Nullable
1676  @JsonProperty("gbifRegion")
1677  public GbifRegion getGbifRegion() {
1678    return Optional.ofNullable(country).map(Country::getGbifRegion).orElse(null);
1679  }
1680
1681  private void setGbifRegion(String gbifRegion) {
1682    // ignore, setter only to avoid JSON being written into the fields map
1683  }
1684
1685  /**
1686   * Renders the country title as a JSON property country in addition to the ISO 3166 2 letter countryCode being
1687   * serialized by the regular country Java property.
1688   * Made private to use it only for JSON serialization and not within Java code.
1689   */
1690  @Schema(
1691    description = "The title (as per ISO-3166-1) of the country, territory or area in which the " +
1692      "occurrence was recorded.",
1693    externalDocs = @ExternalDocumentation(
1694      description = "Darwin Core definition",
1695      url = "https://rs.tdwg.org/dwc/terms/country"
1696    )
1697  )
1698  @Nullable
1699  @JsonProperty("country")
1700  private String getCountryTitle() {
1701    return country == null ? null : country.getTitle();
1702  }
1703
1704  private void setCountryTitle(String country) {
1705    // ignore, setter only to avoid JSON being written into the fields map
1706  }
1707
1708  @Nullable
1709  public String getStateProvince() {
1710    return stateProvince;
1711  }
1712
1713  public void setStateProvince(@Nullable String stateProvince) {
1714    this.stateProvince = stateProvince;
1715  }
1716
1717  @Nullable
1718  public String getWaterBody() {
1719    return waterBody;
1720  }
1721
1722  public void setWaterBody(@Nullable String waterBody) {
1723    this.waterBody = waterBody;
1724  }
1725
1726  /**
1727   * The distance in metres from a known centroid, e.g. a country centroid.
1728   */
1729  public Double getDistanceFromCentroidInMeters() {
1730    return distanceFromCentroidInMeters;
1731  }
1732
1733  public void setDistanceFromCentroidInMeters(Double distanceFromCentroidInMeters) {
1734    this.distanceFromCentroidInMeters = distanceFromCentroidInMeters;
1735  }
1736
1737  @Nullable
1738  public String getHigherGeography() {
1739    return higherGeography;
1740  }
1741
1742  public void setHigherGeography(String higherGeography) {
1743    this.higherGeography = higherGeography;
1744  }
1745
1746  @Nullable
1747  public String getGeoreferencedBy() {
1748    return georeferencedBy;
1749  }
1750
1751  public void setGeoreferencedBy(String georeferencedBy) {
1752    this.georeferencedBy = georeferencedBy;
1753  }
1754
1755  /**
1756   * The full year of the event date.
1757   *
1758   * @return the year of the event date
1759   */
1760  @Min(1500)
1761  @Max(2030)
1762  @Nullable
1763  public Integer getYear() {
1764    return year;
1765  }
1766
1767  public void setYear(@Nullable Integer year) {
1768    this.year = year;
1769  }
1770
1771  /**
1772   * The month of the year of the event date starting with zero for january following {@link Date}.
1773   *
1774   * @return the month of the event date
1775   */
1776  @Min(1)
1777  @Max(12)
1778  @Nullable
1779  public Integer getMonth() {
1780    return month;
1781  }
1782
1783  public void setMonth(@Nullable Integer month) {
1784    this.month = month;
1785  }
1786
1787  /**
1788   * The day of the month of the event date.
1789   *
1790   * @return the day of the event date
1791   */
1792  @Min(1)
1793  @Max(31)
1794  @Nullable
1795  public Integer getDay() {
1796    return day;
1797  }
1798
1799  public void setDay(@Nullable Integer day) {
1800    this.day = day;
1801  }
1802
1803  /**
1804   * The date the occurrence was recorded or collected.
1805   */
1806  @Nullable
1807  public IsoDateInterval getEventDate() {
1808    return eventDate == null ? null : new IsoDateInterval(eventDate.getFrom(), eventDate.getTo());
1809  }
1810
1811  public void setEventDate(@Nullable IsoDateInterval eventDate) {
1812    this.eventDate = eventDate == null ? null : new IsoDateInterval(eventDate.getFrom(), eventDate.getTo());
1813  }
1814
1815  /**
1816   * The earliest integer day of the year of the event.
1817   *
1818   * @return the earliest integer day of the event date
1819   */
1820  @Min(1)
1821  @Max(366)
1822  @Nullable
1823  public Integer getStartDayOfYear() {
1824    return startDayOfYear;
1825  }
1826
1827  public void setStartDayOfYear(@Nullable Integer startDayOfYear) {
1828    this.startDayOfYear = startDayOfYear;
1829  }
1830
1831  /**
1832   * The latest integer day of the year of the event.
1833   *
1834   * @return the latest integer day of the event date
1835   */
1836  @Min(1)
1837  @Max(366)
1838  @Nullable
1839  public Integer getEndDayOfYear() {
1840    return endDayOfYear;
1841  }
1842
1843  public void setEndDayOfYear(@Nullable Integer endDayOfYear) {
1844    this.endDayOfYear = endDayOfYear;
1845  }
1846
1847  @Nullable
1848  public String getTypeStatus() {
1849    return typeStatus;
1850  }
1851
1852  public void setTypeStatus(@Nullable String typeStatus) {
1853    this.typeStatus = typeStatus;
1854  }
1855
1856  /**
1857   * The scientific name the type status of this specimen applies to.
1858   */
1859  @Nullable
1860  public String getTypifiedName() {
1861    return typifiedName;
1862  }
1863
1864  public void setTypifiedName(@Nullable String typifiedName) {
1865    this.typifiedName = typifiedName;
1866  }
1867
1868  /**
1869   * A set of issues found for this occurrence.
1870   */
1871  @NotNull
1872  public Set<OccurrenceIssue> getIssues() {
1873    return issues;
1874  }
1875
1876  public void setIssues(Set<OccurrenceIssue> issues) {
1877    Objects.requireNonNull(issues, "Issues cannot be null");
1878    EnumSet<OccurrenceIssue> set = EnumSet.noneOf(OccurrenceIssue.class);
1879    set.addAll(issues);
1880    this.issues = set;
1881  }
1882
1883  public void addIssue(OccurrenceIssue issue) {
1884    Objects.requireNonNull(issue, "Issue needs to be specified");
1885    issues.add(issue);
1886  }
1887
1888  /**
1889   * The interpreted dc:modified from the verbatim source data.
1890   * Ideally indicating when a record was last modified in the source.
1891   */
1892  @Nullable
1893  public Date getModified() {
1894    return modified == null ? null : new Date(modified.getTime());
1895  }
1896
1897  public void setModified(@Nullable Date modified) {
1898    this.modified = modified == null ? null : new Date(modified.getTime());
1899  }
1900
1901  /**
1902   * The date this occurrence last went through the interpretation phase of the GBIF indexing.
1903   */
1904  @Nullable
1905  public Date getLastInterpreted() {
1906    return lastInterpreted == null ? null : new Date(lastInterpreted.getTime());
1907  }
1908
1909  public void setLastInterpreted(@Nullable Date lastInterpreted) {
1910    this.lastInterpreted = lastInterpreted == null ? null : new Date(lastInterpreted.getTime());
1911  }
1912
1913  /**
1914   * An external link to more details, the records "homepage".
1915   */
1916  @Nullable
1917  public URI getReferences() {
1918    return references;
1919  }
1920
1921  public void setReferences(URI references) {
1922    this.references = references;
1923  }
1924
1925  /**
1926   * A number or enumeration value for the quantity of organisms.
1927   */
1928  @Nullable
1929  public Double getOrganismQuantity() {
1930    return organismQuantity;
1931  }
1932
1933  public void setOrganismQuantity(@Nullable Double organismQuantity) {
1934    this.organismQuantity = organismQuantity;
1935  }
1936
1937  /**
1938   * The type of quantification system used for the quantity of organisms.
1939   */
1940  @Nullable
1941  public String getOrganismQuantityType() {
1942    return organismQuantityType;
1943  }
1944
1945  public void setOrganismQuantityType(@Nullable String organismQuantityType) {
1946    this.organismQuantityType = organismQuantityType;
1947  }
1948
1949  /**
1950   * The unit of measurement of the size (time duration, length, area, or volume) of a sample in a sampling event.
1951   */
1952  @Nullable
1953  public String getSampleSizeUnit() {
1954    return sampleSizeUnit;
1955  }
1956
1957  public void setSampleSizeUnit(@Nullable String sampleSizeUnit) {
1958    this.sampleSizeUnit = sampleSizeUnit;
1959  }
1960
1961  /**
1962   * A numeric value for a measurement of the size (time duration, length, area, or volume) of a sample in a sampling event.
1963   */
1964  @Nullable
1965  public Double getSampleSizeValue() {
1966    return sampleSizeValue;
1967  }
1968
1969  public void setSampleSizeValue(@Nullable Double sampleSizeValue) {
1970    this.sampleSizeValue = sampleSizeValue;
1971  }
1972
1973  /**
1974   * Calculated filed organismQuantity / sampleSizeValue, if the type is identical
1975   */
1976  @Nullable
1977  public Double getRelativeOrganismQuantity() {
1978    return relativeOrganismQuantity;
1979  }
1980
1981  public void setRelativeOrganismQuantity(@Nullable Double relativeOrganismQuantity) {
1982    this.relativeOrganismQuantity = relativeOrganismQuantity;
1983  }
1984
1985  /**
1986   * Flag occurrence when associatedSequences/extension exists
1987   */
1988  public boolean getIsSequenced() {
1989    return isSequenced;
1990  }
1991
1992  public void setIsSequenced(boolean isSequenced) {
1993    this.isSequenced = isSequenced;
1994  }
1995
1996  /**
1997   * A list (concatenated and separated) of identifiers (publication, global unique identifier, URI) of genetic
1998   * sequence information associated with the material entity.
1999   */
2000  @Nullable
2001  public String getAssociatedSequences() {
2002    return associatedSequences;
2003  }
2004
2005  public void setAssociatedSequences(String associatedSequences) {
2006    this.associatedSequences = associatedSequences;
2007  }
2008
2009  /**
2010   * Applied license to the occurrence record or dataset to which this record belongs to.
2011   */
2012  @NotNull
2013  public License getLicense() {
2014    return license;
2015  }
2016
2017  public void setLicense(License license) {
2018    this.license = license;
2019  }
2020
2021  @NotNull
2022  public List<Identifier> getIdentifiers() {
2023    return identifiers;
2024  }
2025
2026  public void setIdentifiers(List<Identifier> identifiers) {
2027    this.identifiers = identifiers;
2028  }
2029
2030  @NotNull
2031  public List<MediaObject> getMedia() {
2032    return media;
2033  }
2034
2035  public void setMedia(List<MediaObject> media) {
2036    this.media = media;
2037  }
2038
2039  @NotNull
2040  public List<MeasurementOrFact> getFacts() {
2041    return facts;
2042  }
2043
2044  public void setFacts(List<MeasurementOrFact> facts) {
2045    this.facts = facts;
2046  }
2047
2048  @NotNull
2049  public List<OccurrenceRelation> getRelations() {
2050    return relations;
2051  }
2052
2053  public void setRelations(List<OccurrenceRelation> relations) {
2054    this.relations = relations;
2055  }
2056
2057  @NotNull
2058  public List<AgentIdentifier> getRecordedByIds() {
2059    return recordedByIds;
2060  }
2061
2062  public void setRecordedByIds(List<AgentIdentifier> recordedByIds) {
2063    this.recordedByIds = recordedByIds;
2064  }
2065
2066  @NotNull
2067  public List<AgentIdentifier> getIdentifiedByIds() {
2068    return identifiedByIds;
2069  }
2070
2071  public void setIdentifiedByIds(List<AgentIdentifier> identifiedByIds) {
2072    this.identifiedByIds = identifiedByIds;
2073  }
2074
2075  @NotNull
2076  public Gadm getGadm() {
2077    return gadm;
2078  }
2079
2080  public void setGadm(Gadm gadm) {
2081    this.gadm = gadm;
2082  }
2083
2084  @Nullable
2085  @Experimental
2086  public String getInstitutionKey() {
2087    return institutionKey;
2088  }
2089
2090  public void setInstitutionKey(String institutionKey) {
2091    this.institutionKey = institutionKey;
2092  }
2093
2094  @Nullable
2095  @Experimental
2096  public String getCollectionKey() {
2097    return collectionKey;
2098  }
2099
2100  public void setCollectionKey(String collectionKey) {
2101    this.collectionKey = collectionKey;
2102  }
2103
2104  public boolean getIsInCluster() {
2105    return isInCluster;
2106  }
2107
2108  public void setIsInCluster(boolean isInCluster) {
2109    this.isInCluster = isInCluster;
2110  }
2111
2112  @Nullable
2113  public String getDegreeOfEstablishment() {
2114    return degreeOfEstablishment;
2115  }
2116
2117  public void setDegreeOfEstablishment(String degreeOfEstablishment) {
2118    this.degreeOfEstablishment = degreeOfEstablishment;
2119  }
2120
2121  @Nullable
2122  public String getPathway() {
2123    return pathway;
2124  }
2125
2126  public void setPathway(String pathway) {
2127    this.pathway = pathway;
2128  }
2129
2130  public String getDatasetID() {
2131    return datasetID;
2132  }
2133
2134  public void setDatasetID(String datasetID) {
2135    this.datasetID = datasetID;
2136  }
2137
2138  public String getDatasetName() {
2139    return datasetName;
2140  }
2141
2142  public void setDatasetName(String datasetName) {
2143    this.datasetName = datasetName;
2144  }
2145
2146  public String getOtherCatalogNumbers() {
2147    return otherCatalogNumbers;
2148  }
2149
2150  public void setOtherCatalogNumbers(String otherCatalogNumbers) {
2151    this.otherCatalogNumbers = otherCatalogNumbers;
2152  }
2153
2154  @Nullable
2155  public String getEarliestEonOrLowestEonothem() {
2156    return earliestEonOrLowestEonothem;
2157  }
2158
2159  public void setEarliestEonOrLowestEonothem(String earliestEonOrLowestEonothem) {
2160    this.earliestEonOrLowestEonothem = earliestEonOrLowestEonothem;
2161  }
2162
2163  @Nullable
2164  public String getLatestEonOrHighestEonothem() {
2165    return latestEonOrHighestEonothem;
2166  }
2167
2168  public void setLatestEonOrHighestEonothem(String latestEonOrHighestEonothem) {
2169    this.latestEonOrHighestEonothem = latestEonOrHighestEonothem;
2170  }
2171
2172  @Nullable
2173  public String getEarliestEraOrLowestErathem() {
2174    return earliestEraOrLowestErathem;
2175  }
2176
2177  public void setEarliestEraOrLowestErathem(String earliestEraOrLowestErathem) {
2178    this.earliestEraOrLowestErathem = earliestEraOrLowestErathem;
2179  }
2180
2181  @Nullable
2182  public String getLatestEraOrHighestErathem() {
2183    return latestEraOrHighestErathem;
2184  }
2185
2186  public void setLatestEraOrHighestErathem(String latestEraOrHighestErathem) {
2187    this.latestEraOrHighestErathem = latestEraOrHighestErathem;
2188  }
2189
2190  @Nullable
2191  public String getEarliestPeriodOrLowestSystem() {
2192    return earliestPeriodOrLowestSystem;
2193  }
2194
2195  public void setEarliestPeriodOrLowestSystem(String earliestPeriodOrLowestSystem) {
2196    this.earliestPeriodOrLowestSystem = earliestPeriodOrLowestSystem;
2197  }
2198
2199  @Nullable
2200  public String getLatestPeriodOrHighestSystem() {
2201    return latestPeriodOrHighestSystem;
2202  }
2203
2204  public void setLatestPeriodOrHighestSystem(String latestPeriodOrHighestSystem) {
2205    this.latestPeriodOrHighestSystem = latestPeriodOrHighestSystem;
2206  }
2207
2208  @Nullable
2209  public String getEarliestEpochOrLowestSeries() {
2210    return earliestEpochOrLowestSeries;
2211  }
2212
2213  public void setEarliestEpochOrLowestSeries(String earliestEpochOrLowestSeries) {
2214    this.earliestEpochOrLowestSeries = earliestEpochOrLowestSeries;
2215  }
2216
2217  @Nullable
2218  public String getLatestEpochOrHighestSeries() {
2219    return latestEpochOrHighestSeries;
2220  }
2221
2222  public void setLatestEpochOrHighestSeries(String latestEpochOrHighestSeries) {
2223    this.latestEpochOrHighestSeries = latestEpochOrHighestSeries;
2224  }
2225
2226  @Nullable
2227  public String getEarliestAgeOrLowestStage() {
2228    return earliestAgeOrLowestStage;
2229  }
2230
2231  public void setEarliestAgeOrLowestStage(String earliestAgeOrLowestStage) {
2232    this.earliestAgeOrLowestStage = earliestAgeOrLowestStage;
2233  }
2234
2235  @Nullable
2236  public String getLatestAgeOrHighestStage() {
2237    return latestAgeOrHighestStage;
2238  }
2239
2240  public void setLatestAgeOrHighestStage(String latestAgeOrHighestStage) {
2241    this.latestAgeOrHighestStage = latestAgeOrHighestStage;
2242  }
2243
2244  @Nullable
2245  public String getLowestBiostratigraphicZone() {
2246    return lowestBiostratigraphicZone;
2247  }
2248
2249  public void setLowestBiostratigraphicZone(String lowestBiostratigraphicZone) {
2250    this.lowestBiostratigraphicZone = lowestBiostratigraphicZone;
2251  }
2252
2253  @Nullable
2254  public String getHighestBiostratigraphicZone() {
2255    return highestBiostratigraphicZone;
2256  }
2257
2258  public void setHighestBiostratigraphicZone(String highestBiostratigraphicZone) {
2259    this.highestBiostratigraphicZone = highestBiostratigraphicZone;
2260  }
2261
2262  @Nullable
2263  public String getGroup() {
2264    return group;
2265  }
2266
2267  public void setGroup(String group) {
2268    this.group = group;
2269  }
2270
2271  @Nullable
2272  public String getFormation() {
2273    return formation;
2274  }
2275
2276  public void setFormation(String formation) {
2277    this.formation = formation;
2278  }
2279
2280  @Nullable
2281  public String getMember() {
2282    return member;
2283  }
2284
2285  public void setMember(String member) {
2286    this.member = member;
2287  }
2288
2289  @Nullable
2290  public String getBed() {
2291    return bed;
2292  }
2293
2294  public void setBed(String bed) {
2295    this.bed = bed;
2296  }
2297
2298  public String getRecordedBy() {
2299    return recordedBy;
2300  }
2301
2302  public void setRecordedBy(String recordedBy) {
2303    this.recordedBy = recordedBy;
2304  }
2305
2306  public String getIdentifiedBy() {
2307    return identifiedBy;
2308  }
2309
2310  public void setIdentifiedBy(String identifiedBy) {
2311    this.identifiedBy = identifiedBy;
2312  }
2313
2314  public String getPreparations() {
2315    return preparations;
2316  }
2317
2318  public void setPreparations(String preparations) {
2319    this.preparations = preparations;
2320  }
2321
2322  public String getSamplingProtocol() {
2323    return samplingProtocol;
2324  }
2325
2326  public void setSamplingProtocol(String samplingProtocol) {
2327    this.samplingProtocol = samplingProtocol;
2328  }
2329
2330  public List<String> getDnaSequenceID() {
2331    return dnaSequenceID;
2332  }
2333
2334  public void setDnaSequenceID(List<String> dnaSequenceID) {
2335    this.dnaSequenceID = dnaSequenceID;
2336  }
2337
2338  public Map<String, Classification> getClassifications() {
2339    return classifications;
2340  }
2341
2342  public void setClassifications(Map<String, Classification> classifications) {
2343    this.classifications = classifications;
2344  }
2345
2346  /**
2347   * Convenience method checking if any spatial validation rule has not passed.
2348   * Primarily used to indicate that the record should not be displayed on a map.
2349   */
2350  @JsonIgnore
2351  public boolean hasSpatialIssue() {
2352    for (OccurrenceIssue rule : OccurrenceIssue.GEOSPATIAL_RULES) {
2353      if (issues.contains(rule)) {
2354        return true;
2355      }
2356    }
2357    return false;
2358  }
2359
2360  /**
2361   * This private method is only for serialization via jackson and not exposed anywhere else!
2362   * It maps the verbatimField terms into properties with their simple name or qualified names for UnknownTerms.
2363   */
2364  @JsonAnyGetter
2365  private Map<String, String> jsonVerbatimFields() {
2366    Map<String, String> extendedProps = new HashMap<>();
2367    for (Map.Entry<Term, String> prop : getVerbatimFields().entrySet()) {
2368      Term t = prop.getKey();
2369      if (t instanceof UnknownTerm || PROPERTIES.contains(t.simpleName())) {
2370        extendedProps.put(t.qualifiedName(), prop.getValue());
2371      } else {
2372        // render all terms in controlled enumerations as simple names only - unless we have a property of that name already!
2373        extendedProps.put(t.simpleName(), prop.getValue());
2374      }
2375    }
2376    return extendedProps;
2377  }
2378}