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.vocabulary;
015
016import org.gbif.api.util.AnnotationUtils;
017import org.gbif.dwc.terms.DcTerm;
018import org.gbif.dwc.terms.DwcTerm;
019import org.gbif.dwc.terms.Term;
020
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Set;
026
027import org.apache.commons.lang3.ArrayUtils;
028
029import static org.gbif.api.vocabulary.InterpretationRemarkSeverity.ERROR;
030import static org.gbif.api.vocabulary.InterpretationRemarkSeverity.INFO;
031import static org.gbif.api.vocabulary.InterpretationRemarkSeverity.WARNING;
032
033/**
034 * An enumeration of validation rules for single occurrence records.
035 */
036public enum OccurrenceIssue implements InterpretationRemark {
037
038  /**
039   * Coordinate is the exact 0°, 0° coordinate, often indicating a bad null coordinate.
040   */
041  ZERO_COORDINATE(WARNING, TermsGroup.COORDINATES_TERMS_NO_DATUM),
042
043  /**
044   * Coordinate has a latitude and/or longitude value beyond the maximum (or minimum) decimal value.
045   */
046  COORDINATE_OUT_OF_RANGE(WARNING, TermsGroup.COORDINATES_TERMS_NO_DATUM),
047
048  /**
049   * Coordinate value is given in some form but GBIF is unable to interpret it.
050   */
051  COORDINATE_INVALID(WARNING, TermsGroup.COORDINATES_TERMS_NO_DATUM),
052
053  /**
054   * Original coordinate modified by rounding to 5 decimals.
055   */
056  COORDINATE_ROUNDED(INFO, TermsGroup.COORDINATES_TERMS_NO_DATUM),
057
058  /**
059   * The geodetic datum given could not be interpreted.
060   */
061  GEODETIC_DATUM_INVALID(WARNING, DwcTerm.geodeticDatum),
062
063  /**
064   * Indicating that the interpreted coordinates assume they are based on WGS84 datum as the datum
065   * was either not indicated or interpretable. See GEODETIC_DATUM_INVALID.
066   */
067  GEODETIC_DATUM_ASSUMED_WGS84(INFO, DwcTerm.geodeticDatum),
068
069  /**
070   * The original coordinate was successfully reprojected from a different geodetic datum to WGS84.
071   */
072  COORDINATE_REPROJECTED(INFO, TermsGroup.COORDINATES_TERMS),
073
074  /**
075   * The given decimal latitude and longitude could not be reprojected to WGS84 based on the
076   * provided datum.
077   */
078  COORDINATE_REPROJECTION_FAILED(WARNING, TermsGroup.COORDINATES_TERMS),
079
080  /**
081   * Indicates successful coordinate reprojection according to provided datum, but which results in
082   * a datum shift larger than 0.1 decimal degrees.
083   */
084  COORDINATE_REPROJECTION_SUSPICIOUS(WARNING, TermsGroup.COORDINATES_TERMS),
085
086  /**
087   * Indicates an invalid or very unlikely coordinate accuracy derived from precision or uncertainty
088   * in meters.
089   */
090  @Deprecated //see POR-3061
091  COORDINATE_ACCURACY_INVALID(WARNING),
092
093  /**
094   * Indicates an invalid or very unlikely coordinatePrecision
095   */
096  COORDINATE_PRECISION_INVALID(WARNING, DwcTerm.coordinatePrecision),
097
098  /**
099   * Indicates an invalid or very unlikely dwc:uncertaintyInMeters.
100   */
101  COORDINATE_UNCERTAINTY_METERS_INVALID(WARNING, DwcTerm.coordinateUncertaintyInMeters),
102
103  /**
104   * There is a mismatch between coordinate uncertainty in meters and coordinate precision.
105   */
106  @Deprecated //see POR-1804
107  COORDINATE_PRECISION_UNCERTAINTY_MISMATCH(WARNING),
108
109  /**
110   * The Footprint Spatial Reference System given could not be interpreted.
111   */
112  FOOTPRINT_SRS_INVALID(WARNING, DwcTerm.footprintSRS),
113
114  /**
115   * The Footprint Well-Known-Text conflicts with the interpreted coordinates
116   * (Decimal Latitude, Decimal Longitude etc).
117   */
118  FOOTPRINT_WKT_MISMATCH(WARNING, DwcTerm.footprintWKT),
119
120  /**
121   * The Footprint Well-Known-Text given could not be interpreted.
122   */
123  FOOTPRINT_WKT_INVALID(WARNING, DwcTerm.footprintWKT),
124
125  /**
126   * The interpreted occurrence coordinates fall outside of the indicated country.
127   */
128  COUNTRY_COORDINATE_MISMATCH(WARNING, TermsGroup.COORDINATES_COUNTRY_TERMS),
129
130  /**
131   * Interpreted country for dwc:country and dwc:countryCode contradict each other.
132   */
133  COUNTRY_MISMATCH(WARNING, TermsGroup.COUNTRY_TERMS),
134
135  /**
136   * Uninterpretable country values found.
137   */
138  COUNTRY_INVALID(WARNING, TermsGroup.COUNTRY_TERMS),
139
140  /**
141   * The interpreted country is based on the coordinates, not the verbatim string information.
142   */
143  COUNTRY_DERIVED_FROM_COORDINATES(WARNING, TermsGroup.COORDINATES_COUNTRY_TERMS),
144
145  /**
146   * The interpreted occurrence coordinates fall outside of the indicated continent.
147   */
148  CONTINENT_COORDINATE_MISMATCH(WARNING),
149
150  /**
151   * The interpreted continent and country do not match.
152   */
153  CONTINENT_COUNTRY_MISMATCH(WARNING),
154
155  /**
156   * Uninterpretable continent values found.
157   */
158  CONTINENT_INVALID(WARNING),
159
160  /**
161   * The interpreted continent is based on the country, not the verbatim string information.
162   */
163  CONTINENT_DERIVED_FROM_COUNTRY(WARNING),
164
165  /**
166   * The interpreted continent is based on the coordinates, not the verbatim string information.
167   */
168  CONTINENT_DERIVED_FROM_COORDINATES(WARNING),
169
170  /**
171   * Latitude and longitude appear to be swapped.
172   */
173  PRESUMED_SWAPPED_COORDINATE(WARNING, TermsGroup.COORDINATES_TERMS_NO_DATUM),
174
175  /**
176   * Longitude appears to be negated, e.g. 32.3 instead of -32.3
177   */
178  PRESUMED_NEGATED_LONGITUDE(WARNING, TermsGroup.COORDINATES_TERMS_NO_DATUM),
179
180  /**
181   * Latitude appears to be negated, e.g. 32.3 instead of -32.3
182   */
183  PRESUMED_NEGATED_LATITUDE(WARNING, TermsGroup.COORDINATES_TERMS_NO_DATUM),
184
185  /**
186   * The recorded date specified as the eventDate string and the individual year, month, day and/or
187   * startDayOfYear, endDayOfYear are contradictory.
188   */
189  RECORDED_DATE_MISMATCH(WARNING, TermsGroup.RECORDED_DATE_TERMS),
190
191  /**
192   * A (partial) invalid date is given, such as a non-existent date, zero month, etc.
193   */
194  RECORDED_DATE_INVALID(WARNING, TermsGroup.RECORDED_DATE_TERMS),
195
196  /**
197   * The recorded date is highly unlikely, falling either into the future or representing a very old
198   * date before 1600 thus predating modern taxonomy.
199   */
200  RECORDED_DATE_UNLIKELY(WARNING, TermsGroup.RECORDED_DATE_TERMS),
201
202  /**
203   * Matching to the taxonomic backbone can only be done using a fuzzy, non-exact match.
204   */
205  TAXON_MATCH_FUZZY(WARNING, TermsGroup.TAXONOMY_TERMS),
206
207  /**
208   * Matching to the taxonomic backbone can only be done on a higher rank and not the scientific
209   * name.
210   */
211  TAXON_MATCH_HIGHERRANK(WARNING, TermsGroup.TAXONOMY_TERMS),
212
213  /**
214   * Matching to the taxonomic backbone can only be done on a species level,
215   * but the occurrence was in fact considered a broader species aggregate/complex.
216   * @see <a href="https://github.com/gbif/portal-feedback/issues/2935">gbif/portal-feedback#2935</a>
217   */
218  TAXON_MATCH_AGGREGATE(WARNING, TermsGroup.TAXONOMY_TERMS),
219
220  /**
221   * The scientificNameID was not used when mapping the record to the GBIF backbone. This may indicate one of
222   * <ul>
223   *   <li>The ID uses a pattern not configured for use by GBIF</li>
224   *   <li>The ID did not uniquely(!) identify a concept in the checklist</li>
225   *   <li>The ID found a concept in the checklist which did not map to the backbone</li>
226   *   <li>A different ID was used, or the record names were used as no ID lookup successfully linked to the backbone</li>
227   * </ul>
228   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
229   */
230  TAXON_MATCH_SCIENTIFIC_NAME_ID_IGNORED(INFO, DwcTerm.scientificNameID),
231
232  /**
233   * The taxonConceptID was not used when mapping the record to the GBIF backbone. This may indicate one of
234   * <ul>
235   *   <li>The ID uses a pattern not configured for use by GBIF</li>
236   *   <li>The ID did not uniquely(!) identify a concept in the checklist</li>
237   *   <li>The ID found a concept in the checklist which did not map to the backbone</li>
238   *   <li>A different ID was used, or the record names were used as no ID lookup successfully linked to the backbone</li>
239   * </ul>
240   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
241   */
242  TAXON_MATCH_TAXON_CONCEPT_ID_IGNORED(INFO, DwcTerm.taxonConceptID),
243
244  /**
245   * The taxonID was not used when mapping the record to the GBIF backbone. This may indicate one of
246   * <ul>
247   *   <li>The ID uses a pattern not configured for use by GBIF</li>
248   *   <li>The ID did not uniquely(!) identify a concept in the checklist</li>
249   *   <li>The ID found a concept in the checklist which did not map to the backbone</li>
250   *   <li>A different ID was used, or the record names were used as no ID lookup successfully linked to the backbone</li>
251   * </ul>
252   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
253   */
254  TAXON_MATCH_TAXON_ID_IGNORED(INFO, DwcTerm.taxonID),
255
256  /**
257   * The scientificNameID matched a known pattern, but it was not found in the associated checklist.
258   * The backbone lookup was performed using either the names or a different ID on the record.
259   * This may indicate a poorly formatted identifier or may be caused by a newly created ID that
260   * isn't yet known in the version of the published checklist.
261   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
262   */
263  SCIENTIFIC_NAME_ID_NOT_FOUND(WARNING, DwcTerm.scientificNameID),
264
265  /**
266   * The taxonConceptID matched a known pattern, but it was not found in the associated checklist.
267   * The backbone lookup was performed using either the names or a different ID on the record.
268   * This may indicate a poorly formatted identifier or may be caused by a newly created ID that isn't yet
269   * known in the version of the published checklist.
270   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
271   */
272  TAXON_CONCEPT_ID_NOT_FOUND(WARNING, DwcTerm.taxonConceptID),
273
274  /**
275   * The taxonID matched a known pattern, but it was not found in the associated checklist.
276   * The backbone lookup was performed using either the names or a different ID on the record.
277   * This may indicate a poorly formatted identifier or may be caused by a newly created ID that isn't yet
278   * known in the version of the published checklist.
279   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
280   */
281  TAXON_ID_NOT_FOUND(WARNING, DwcTerm.taxonID),
282
283  /**
284   * The scientificName provided in the occurrence record does not precisely match the name in the registered checklist
285   * when using the scientificNameID, taxonID or taxonConceptID to look it up. Publishers are advised to check the IDs
286   * are correct, or update the formatting of the names on their records.
287   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
288   */
289  SCIENTIFIC_NAME_AND_ID_INCONSISTENT(WARNING, DwcTerm.scientificNameID, DwcTerm.taxonID, DwcTerm.taxonConceptID, DwcTerm.scientificName),
290
291  /**
292   * Matching to the taxonomic backbone cannot be done because there was no match at all, or several
293   * matches with too little information to keep them apart (potentially homonyms).
294   */
295  TAXON_MATCH_NONE(WARNING, TermsGroup.TAXONOMY_TERMS),
296
297  /**
298   * The GBIF Backbone concept was found using the scientificNameID, taxonID or taxonConceptID, but it differs from what would
299   * have been found if the classification names on the record were used. This may indicate a gap in the GBIF backbone,
300   * a poor mapping between the checklist and the backbone, or a mismatch between the classification names and the
301   * declared IDs (scientificNameID or taxonConceptID) on the occurrence record itself.
302   * @see <a href="https://github.com/gbif/pipelines/issues/217">gbif/pipelines#217</a>
303   */
304  TAXON_MATCH_NAME_AND_ID_AMBIGUOUS(WARNING, TermsGroup.TAXONOMY_TERMS),
305
306  /**
307   * Set if supplied depth is not given in the metric system, for example using feet instead of
308   * meters
309   */
310  DEPTH_NOT_METRIC(WARNING, DwcTerm.minimumDepthInMeters, DwcTerm.maximumDepthInMeters),
311
312  /**
313   * Set if depth is larger than 11,000m or negative.
314   */
315  DEPTH_UNLIKELY(WARNING, DwcTerm.minimumDepthInMeters, DwcTerm.maximumDepthInMeters),
316
317  /**
318   * Set if supplied minimum depth > maximum depth
319   */
320  DEPTH_MIN_MAX_SWAPPED(WARNING, DwcTerm.minimumDepthInMeters, DwcTerm.maximumDepthInMeters),
321
322  /**
323   * Set if depth is a non-numeric value
324   */
325  DEPTH_NON_NUMERIC(WARNING, DwcTerm.minimumDepthInMeters, DwcTerm.maximumDepthInMeters),
326
327  /**
328   * Set if elevation is above the troposphere (17km) or below 11km (Mariana Trench).
329   */
330  ELEVATION_UNLIKELY(WARNING, DwcTerm.minimumElevationInMeters, DwcTerm.maximumElevationInMeters),
331
332  /**
333   * Set if supplied minimum elevation > maximum elevation
334   */
335  ELEVATION_MIN_MAX_SWAPPED(WARNING, DwcTerm.minimumElevationInMeters,
336    DwcTerm.maximumElevationInMeters),
337
338  /**
339   * Set if supplied elevation is not given in the metric system, for example using feet instead of
340   * meters
341   */
342  ELEVATION_NOT_METRIC(WARNING, DwcTerm.minimumElevationInMeters, DwcTerm.maximumElevationInMeters),
343
344  /**
345   * Set if elevation is a non-numeric value
346   */
347  ELEVATION_NON_NUMERIC(WARNING, DwcTerm.minimumElevationInMeters,
348    DwcTerm.maximumElevationInMeters),
349
350  /**
351   * A (partial) invalid date is given for dc:modified, such as a nonexistent date, zero month, etc.
352   */
353  MODIFIED_DATE_INVALID(WARNING, DcTerm.modified),
354
355  /**
356   * The date given for dc:modified is in the future or predates Unix time (1970).
357   */
358  MODIFIED_DATE_UNLIKELY(WARNING, DcTerm.modified),
359
360  /**
361   * The date given for dwc:dateIdentified is in the future or before Linnean times (1700).
362   */
363  IDENTIFIED_DATE_UNLIKELY(WARNING, DwcTerm.dateIdentified),
364
365  /**
366   * The date given for dwc:dateIdentified is invalid and can't be interpreted at all.
367   */
368  IDENTIFIED_DATE_INVALID(WARNING, DwcTerm.dateIdentified),
369
370  /**
371   * The given basis of record is impossible to interpret or significantly different from the
372   * recommended vocabulary.
373   */
374  BASIS_OF_RECORD_INVALID(WARNING, DwcTerm.basisOfRecord),
375
376  /**
377   * The given type status is impossible to interpret or significantly different from the
378   * recommended vocabulary.
379   */
380  TYPE_STATUS_INVALID(WARNING, DwcTerm.typeStatus),
381
382  /**
383   * An invalid date is given for dc:created of a multimedia object.
384   */
385  MULTIMEDIA_DATE_INVALID(WARNING),
386
387  /**
388   * An invalid URI is given for a multimedia object.
389   */
390  MULTIMEDIA_URI_INVALID(WARNING),
391
392  /**
393   * An invalid URI is given for dc:references.
394   */
395  REFERENCES_URI_INVALID(WARNING, DcTerm.references),
396
397  /**
398   * An error occurred during interpretation, leaving the record interpretation incomplete.
399   */
400  INTERPRETATION_ERROR(ERROR),
401
402  /**
403   * The individual count value is not a positive integer
404   */
405  INDIVIDUAL_COUNT_INVALID(WARNING, DwcTerm.individualCount),
406
407  /**
408   * Example: individual count value > 0, but occurrence status is absent.
409   */
410  INDIVIDUAL_COUNT_CONFLICTS_WITH_OCCURRENCE_STATUS(WARNING, DwcTerm.individualCount),
411
412  /**
413   * Occurrence status value can't be assigned to {@link OccurrenceStatus}
414   */
415  OCCURRENCE_STATUS_UNPARSABLE(WARNING, DwcTerm.occurrenceStatus),
416
417  /**
418   * Occurrence status was inferred from the individual count value
419   */
420  OCCURRENCE_STATUS_INFERRED_FROM_INDIVIDUAL_COUNT(WARNING, DwcTerm.occurrenceStatus),
421
422  /**
423   * Occurrence status was inferred from basis of records
424   */
425  OCCURRENCE_STATUS_INFERRED_FROM_BASIS_OF_RECORD(WARNING, DwcTerm.occurrenceStatus),
426
427  /**
428   * The date given for dwc:georeferencedDate is in the future or before Linnean times (1700).
429   */
430  GEOREFERENCED_DATE_UNLIKELY(WARNING, DwcTerm.georeferencedDate),
431
432  /**
433   * The date given for dwc:georeferencedDate is invalid and can't be interpreted at all.
434   */
435  GEOREFERENCED_DATE_INVALID(WARNING, DwcTerm.georeferencedDate),
436
437  /**
438   * The given institution matches with more than 1 GRSciColl institution.
439   */
440  AMBIGUOUS_INSTITUTION(INFO, TermsGroup.INSTITUTION_TERMS),
441
442  /**
443   * The given collection matches with more than 1 GRSciColl collection.
444   */
445  AMBIGUOUS_COLLECTION(INFO, TermsGroup.COLLECTION_TERMS),
446
447  /**
448   * The given institution couldn't be matched with any GRSciColl institution.
449   */
450  INSTITUTION_MATCH_NONE(INFO, TermsGroup.INSTITUTION_TERMS),
451
452  /**
453   * The given collection couldn't be matched with any GRSciColl collection.
454   */
455  COLLECTION_MATCH_NONE(INFO, TermsGroup.COLLECTION_TERMS),
456
457  /**
458   * The given institution was fuzzily matched to a GRSciColl institution. This can happen when
459   * either the code or the ID don't match or when the institution name is used instead of the code.
460   */
461  INSTITUTION_MATCH_FUZZY(INFO, TermsGroup.INSTITUTION_TERMS),
462
463  /**
464   * The given collection was fuzzily matched to a GRSciColl collection. This can happen when either
465   * the code or the ID don't match or when the collection name is used instead of the code.
466   */
467  COLLECTION_MATCH_FUZZY(INFO, TermsGroup.COLLECTION_TERMS),
468
469  /** The collection matched doesn't belong to the institution matched. */
470  INSTITUTION_COLLECTION_MISMATCH(
471    INFO, ArrayUtils.addAll(TermsGroup.INSTITUTION_TERMS, TermsGroup.INSTITUTION_TERMS)),
472
473  /**
474   * The given owner institution is different than the given institution. Therefore we assume it
475   * could be on loan and we don't link it to the occurrence.
476   *
477   * Deprecated by {@link #DIFFERENT_OWNER_INSTITUTION}.
478   */
479  @Deprecated
480  POSSIBLY_ON_LOAN(INFO, TermsGroup.INSTITUTION_TERMS),
481
482  /**
483   * The given owner institution is different than the given institution. Therefore we assume it
484   * doesn't belong to the institution and we don't link it to the occurrence.
485   */
486  DIFFERENT_OWNER_INSTITUTION(INFO, TermsGroup.INSTITUTION_TERMS);
487
488  /**
489   * Simple helper nested class to allow grouping of Term mostly to increase readability of this
490   * class.
491   */
492  private static class TermsGroup {
493
494    static final Term[] COORDINATES_TERMS_NO_DATUM = {
495      DwcTerm.decimalLatitude,
496      DwcTerm.decimalLongitude,
497      DwcTerm.verbatimLatitude,
498      DwcTerm.verbatimLongitude,
499      DwcTerm.verbatimCoordinates
500    };
501
502    static final Term[] COORDINATES_TERMS = {
503      DwcTerm.decimalLatitude,
504      DwcTerm.decimalLongitude,
505      DwcTerm.verbatimLatitude,
506      DwcTerm.verbatimLongitude,
507      DwcTerm.verbatimCoordinates,
508      DwcTerm.geodeticDatum
509    };
510
511    static final Term[] COUNTRY_TERMS = {
512      DwcTerm.country,
513      DwcTerm.countryCode
514    };
515
516    static final Term[] COORDINATES_COUNTRY_TERMS = {
517      DwcTerm.decimalLatitude,
518      DwcTerm.decimalLongitude,
519      DwcTerm.verbatimLatitude,
520      DwcTerm.verbatimLongitude,
521      DwcTerm.verbatimCoordinates,
522      DwcTerm.geodeticDatum,
523      DwcTerm.country,
524      DwcTerm.countryCode
525    };
526
527    static final Term[] RECORDED_DATE_TERMS = {
528      DwcTerm.eventDate,
529      DwcTerm.year, DwcTerm.month, DwcTerm.day,
530      DwcTerm.startDayOfYear, DwcTerm.endDayOfYear
531    };
532
533    static final Term[] TAXONOMY_TERMS = {
534      DwcTerm.kingdom,
535      DwcTerm.phylum,
536      DwcTerm.class_,
537      DwcTerm.order,
538      DwcTerm.family,
539      DwcTerm.genus,
540      DwcTerm.scientificName,
541      DwcTerm.scientificNameAuthorship,
542      DwcTerm.genericName,
543      DwcTerm.specificEpithet,
544      DwcTerm.infraspecificEpithet,
545      DwcTerm.scientificNameID,
546      DwcTerm.taxonConceptID,
547    };
548
549    static final Term[] INSTITUTION_TERMS = {
550      DwcTerm.institutionCode, DwcTerm.institutionID, DwcTerm.ownerInstitutionCode
551    };
552
553    static final Term[] COLLECTION_TERMS = {DwcTerm.collectionCode, DwcTerm.collectionID};
554  }
555
556  private final Set<Term> relatedTerms;
557  private final InterpretationRemarkSeverity severity;
558  private final boolean isDeprecated;
559
560  /**
561   * {@link OccurrenceIssue} not linked to any specific {@link Term}.
562   */
563  OccurrenceIssue(InterpretationRemarkSeverity severity) {
564    this.severity = severity;
565    this.relatedTerms = Collections.emptySet();
566    this.isDeprecated = AnnotationUtils.isFieldDeprecated(OccurrenceIssue.class, this.name());
567  }
568
569  /**
570   * {@link OccurrenceIssue} linked to the provided {@link Term}.
571   */
572  OccurrenceIssue(InterpretationRemarkSeverity severity, Term... relatedTerms) {
573    this.severity = severity;
574    this.relatedTerms = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(relatedTerms)));
575    this.isDeprecated = AnnotationUtils.isFieldDeprecated(OccurrenceIssue.class, this.name());
576  }
577
578  @Override
579  public String getId() {
580    return name();
581  }
582
583  @Override
584  public Set<Term> getRelatedTerms() {
585    return relatedTerms;
586  }
587
588  @Override
589  public InterpretationRemarkSeverity getSeverity() {
590    return severity;
591  }
592
593  @Override
594  public boolean isDeprecated() {
595    return isDeprecated;
596  }
597
598  /**
599   * All issues that indicate problems with the coordinates and thus should not be shown on maps.
600   */
601  public static final List<OccurrenceIssue> GEOSPATIAL_RULES =
602    Collections.unmodifiableList(
603      Arrays.asList(
604        ZERO_COORDINATE,
605        COORDINATE_OUT_OF_RANGE,
606        COORDINATE_INVALID,
607        COUNTRY_COORDINATE_MISMATCH,
608        PRESUMED_SWAPPED_COORDINATE,
609        PRESUMED_NEGATED_LONGITUDE,
610        PRESUMED_NEGATED_LATITUDE));
611}