Skip to content

Commit 096e91d

Browse files
committed
TAX-107 | Adaptar geoserver y cliente a nuevo modelo de datos
1 parent 658e7f4 commit 096e91d

21 files changed

Lines changed: 159 additions & 120 deletions

File tree

‎README.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ Para importar el backbone de GBIF, ejecutar ./prepare-db.sh en devops/prepare-db
5151
Puede tardar 10mn en descargar.
5252

5353

54+
### Generar árbol taxonómico y geoarrow
55+
56+
```
57+
export JWT_TOKEN=$(curl http://localhost:8000/api/v1/auth/jwt/create --data '{"email": "info@geomatico.es", "password": "1234"}' -H 'content-type: application/json' | jq -r .access)
58+
curl -X POST http://localhost/api/v1/manage/generate-resources/ -H "Accept: application/json" -H "Authorization: Bearer $JWT_TOKEN" | jq -r
59+
```
60+
61+
5462
## Base de datos en Staging
5563

5664
Ahora mismo la base de datos se persiste entre despliegues. Si en algún momento en necesario resetearla, se puede hacer entrando en el servidor.

‎client/package-lock.json‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎client/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"main": "./dist/main.js",
66
"engines": {
77
"npm": ">=10.0.0 <11.0.0",
8-
"node": ">=20.0.0 <21.0.0"
8+
"node": ">=20.0.0 <23.0.0"
99
},
1010
"scripts": {
1111
"test": "npm run lint && karma start karma.config.js",

‎client/src/components/PopUpContent.spec.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {getDateLabel, getPlaceLabel} from './PopUpContent';
44
describe('PopUpContent', () => {
55
it('returns expected date labels"', async () => {
66
// WHEN / THEN
7-
expect(getDateLabel({})).to.equal(undefined);
8-
expect(getDateLabel({year: 2000, day: 20})).to.equal('2000');
9-
expect(getDateLabel({year: 2000, month: 2})).to.equal('2000-02');
10-
expect(getDateLabel({year: 2000, month: 2, day: 7})).to.equal('2000-02-07');
7+
expect(getDateLabel({}, '')).to.equal(undefined);
8+
expect(getDateLabel({eventDate: new Date('2020-10-20Z')}, 'ca')).to.equal('20/10/2020');
9+
expect(getDateLabel({eventDate: new Date('2020-10-20Z')}, 'en')).to.equal('10/20/2020');
10+
expect(getDateLabel({eventDate: new Date('2020-10-20Z')}, 'es')).to.equal('20/10/2020');
1111
});
1212

1313
it('returns expected place labels"', async () => {

‎client/src/components/PopUpContent.tsx‎

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ export type SelectedFeature = {
2828
* Required WFS properties to fill the popup.
2929
*/
3030
const PROPERTIES = [
31-
WFS_PROPERTY.year, WFS_PROPERTY.month, WFS_PROPERTY.day,
31+
WFS_PROPERTY.eventDate,
3232
WFS_PROPERTY.municipality, WFS_PROPERTY.county, WFS_PROPERTY.stateProvince,
3333
WFS_PROPERTY.scientificName
3434
];
3535

36-
export const getPlaceLabel = (properties: WfsProperties): string | undefined => {
36+
type PopupWfsProperties = Partial<Pick<WfsProperties, 'eventDate' | 'municipality' | 'county' | 'stateProvince' | 'scientificName'>>;
37+
38+
export const getPlaceLabel = (properties: PopupWfsProperties): string | undefined => {
3739
const parts = [properties.municipality, properties.county, properties.stateProvince].filter(Boolean);
3840
if (parts.length === 0) {
3941
return undefined;
@@ -44,21 +46,13 @@ export const getPlaceLabel = (properties: WfsProperties): string | undefined =>
4446
return `${parts.join(', ')} (${last})`;
4547
};
4648

47-
export const getDateLabel = (properties: WfsProperties): string | undefined => {
48-
if (properties.year === undefined) {
49-
return;
50-
} else if (!properties.month) {
51-
return `${properties.year}`;
52-
}
53-
return [properties.year, properties.month, properties.day]
54-
.filter(Boolean)
55-
.map(part => String(part).padStart(2, '0'))
56-
.join('-');
49+
export const getDateLabel = (properties: PopupWfsProperties, lang: string): string | undefined => {
50+
return properties.eventDate?.toLocaleDateString(lang);
5751
};
5852

5953
const PopUpContent: FC<PopUpContentProps> = ({selectedFeature, isTactile, museuId}) => {
60-
const {t} = useTranslation();
61-
const [wfsProperties, setWfsProperties] = useState<WfsProperties>();
54+
const {t, i18n: {language}} = useTranslation();
55+
const [wfsProperties, setWfsProperties] = useState<PopupWfsProperties>();
6256

6357
const getMoreInfoUrl = (selectedFeature: SelectedFeature) => {
6458
const split = selectedFeature?.catalognumber?.split(' ');
@@ -75,7 +69,7 @@ const PopUpContent: FC<PopUpContentProps> = ({selectedFeature, isTactile, museuI
7569

7670
const scientificNameLabel = wfsProperties?.scientificName || selectedFeature.species?.name;
7771
const placeLabel = wfsProperties && getPlaceLabel(wfsProperties);
78-
const dateLabel = wfsProperties && getDateLabel(wfsProperties);
72+
const dateLabel = wfsProperties && getDateLabel(wfsProperties, language);
7973

8074
return <Card variant="outlined">
8175
<CardContent>

‎client/src/config.ts‎

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,17 @@ export const MAPSTYLES: Array<MapStyle> = [
3232
id: 'mapstyles/landcover.json',
3333
label: 'landCover',
3434
thumbnail: 'images/landcover.png'
35-
},
36-
{
37-
id: 'mapstyles/temperature.json',
38-
label: 'temperature',
39-
thumbnail: 'images/temperature.png'
40-
},
41-
{
42-
id: 'mapstyles/rain.json',
43-
label: 'rain',
44-
thumbnail: 'images/rain.png'
4535
}
4636
];
4737

4838
export const INITIAL_MAPSTYLE_URL = MAPSTYLES[1].id;
4939

5040
export const ARROW_FIELDS = [
51-
'id', 'catalognumber', 'domain', 'kingdom', 'phylum', 'class', 'order', 'family', 'genus',
52-
'species', 'subspecies', 'basisofrecord', 'institutioncode', 'year'
41+
'id', 'catalognumber', 'basisofrecord', 'institutioncode', 'year',
42+
'domain', 'kingdom', 'phylum', 'class', 'order', 'family', 'genus', 'species', 'subspecies'
5343
];
5444

55-
export const FILTER_BY = ['institutioncode', 'basisofrecord'];
45+
export const FILTER_BY = ['basisofrecord', 'institutioncode'];
5646

5747
export const INITIAL_TAXONOMIC_LEVEL = TaxonomicLevel.kingdom;
5848
export const INITIAL_TAXONOMIC_NAME = 'Animalia';

‎client/src/wfs/wfs.spec.ts‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('wfs', () => {
5555
const params = validateUrlAndGetParams(format, urlString);
5656
expect(params.get('cql_filter')).to.equal('family_id = 1 ' +
5757
'AND institution_id = 2 AND basis_of_record_id = 3 AND genus IN (1,3) ' +
58-
'AND year >= 1920 AND year <= 2010 ' +
58+
'AND eventDate DURING 1920-01-01T00:00:00Z/2010-12-31T23:59:59Z ' +
5959
'AND BBOX(geometry,0.5,42.7,0.6,42.8)');
6060
});
6161

@@ -164,18 +164,17 @@ describe('wfs', () => {
164164
// GIVEN
165165
const featureId = 42;
166166
const propertyNames = [
167-
WFS_PROPERTY.year, WFS_PROPERTY.month, WFS_PROPERTY.day,
167+
WFS_PROPERTY.eventDate,
168168
WFS_PROPERTY.municipality, WFS_PROPERTY.county, WFS_PROPERTY.stateProvince,
169169
WFS_PROPERTY.scientificName
170170
];
171171
const response = {
172172
features: [{
173173
properties: {
174-
year: 2023,
175-
month: 11,
174+
eventDate: '2023-11-01Z',
176175
county: 'Burjassot',
177-
state_province: 'Valencia',
178-
scientific_name: 'Ornithorhynchus anatinus'
176+
stateProvince: 'Valencia',
177+
scientificName: 'Ornithorhynchus anatinus'
179178
}
180179
}]
181180
};
@@ -185,13 +184,11 @@ describe('wfs', () => {
185184
const properties = await getWfsFeatureProperties(featureId, propertyNames);
186185

187186
// THEN
188-
expect(properties?.year).to.equal(response.features[0].properties.year);
189-
expect(properties?.month).to.equal(response.features[0].properties.month);
190-
expect(properties?.day).to.equal(undefined);
187+
expect(properties?.eventDate).to.eql(new Date(response.features[0].properties.eventDate));
191188
expect(properties?.municipality).to.equal(undefined);
192189
expect(properties?.county).to.equal(response.features[0].properties.county);
193-
expect(properties?.stateProvince).to.equal(response.features[0].properties.state_province);
194-
expect(properties?.scientificName).to.equal(response.features[0].properties.scientific_name);
190+
expect(properties?.stateProvince).to.equal(response.features[0].properties.stateProvince);
191+
expect(properties?.scientificName).to.equal(response.features[0].properties.scientificName);
195192
});
196193

197194
const validateUrlAndGetParams = (expectedFormat: string, urlString: string): URLSearchParams => {

‎client/src/wfs/wfs.ts‎

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,50 @@
11
import {BBOX, Filters, Range, SubtaxonVisibility, TaxonomicLevel} from '../commonTypes';
2+
import {FeatureCollection, Point} from 'geojson';
23
import {GEOSERVER_BASE_URL} from '../config';
34

45
const FORMAT_GEOJSON = 'application/json';
56
export const WFS_TYPENAME = 'taxomap:taxomap';
67

78
/**
8-
* These depend on how the database is generated. Make sure they always match whatever is done in `91-generate-dictionaries.sql`.
9+
* These depend on how the database is generated.
910
*/
1011
export enum WFS_PROPERTY {
11-
institutionCodeId = 'institution_id',
12-
basisOfRecordId = 'basis_of_record_id',
13-
year = 'year',
14-
month = 'month',
15-
day = 'day',
16-
municipality = 'municipality',
17-
county = 'county',
18-
stateProvince = 'state_province',
1912
geometry = 'geometry',
20-
scientificName = 'scientific_name'
13+
institutionCode = 'institutionCode',
14+
collectionCode = 'collectionCode',
15+
catalogNumber = 'catalogNumber',
16+
basisOfRecord = 'basisOfRecord',
17+
taxonID = 'taxonID',
18+
scientificName = 'scientificName',
19+
decimalLatitude = 'decimalLatitude',
20+
decimalLongitude = 'decimalLongitude',
21+
eventDate = 'eventDate',
22+
countryCode = 'countryCode',
23+
stateProvince = 'stateProvince',
24+
county = 'county',
25+
municipality = 'municipality',
26+
georeferenceVerificationStatus = 'georeferenceVerificationStatus',
27+
identificationVerificationStatus = 'identificationVerificationStatus'
2128
}
2229

23-
export type WfsProperties = { [key in (keyof typeof WFS_PROPERTY)]?: number | string | undefined }
30+
export type WfsProperties = {
31+
geometry: Point,
32+
institutionCode: string,
33+
collectionCode: string,
34+
catalogNumber: string,
35+
basisOfRecord: string,
36+
taxonID: number,
37+
scientificName: string,
38+
decimalLatitude: number,
39+
decimalLongitude: number,
40+
eventDate: Date,
41+
countryCode: string,
42+
stateProvince: string,
43+
county: string,
44+
municipality: string,
45+
georeferenceVerificationStatus: string,
46+
identificationVerificationStatus: string
47+
}
2448

2549
/**
2650
* Returns the property name with the ID associated to the given taxonomic level.
@@ -57,7 +81,7 @@ const cqlTaxonIn = (subtaxonVisibility: SubtaxonVisibility | undefined) => {
5781
};
5882

5983
const cqlYearBetween = (yearRange: Range | undefined) => yearRange
60-
? `${WFS_PROPERTY.year} >= ${yearRange[0]} AND ${WFS_PROPERTY.year} <= ${yearRange[1]}`
84+
? `${WFS_PROPERTY.eventDate} DURING ${yearRange[0]}-01-01T00:00:00Z/${yearRange[1]}-12-31T23:59:59Z`
6185
: undefined;
6286

6387
const cqlBbox = (bbox: BBOX | undefined) =>
@@ -81,14 +105,15 @@ export const getWfsDownloadUrl = (
81105

82106
const cqlFilters = [
83107
cqlPropertyEquals(getPropertyName(taxon.level), taxon.id),
84-
cqlPropertyEquals(WFS_PROPERTY.institutionCodeId, institutionId),
85-
cqlPropertyEquals(WFS_PROPERTY.basisOfRecordId, basisOfRecordId),
108+
cqlPropertyEquals('institution_id', institutionId),
109+
cqlPropertyEquals('basis_of_record_id', basisOfRecordId),
86110
cqlTaxonIn(subtaxonVisibility),
87111
cqlYearBetween(yearRange),
88112
// cql_filter and BBOX query params are mutually exclusive, BBOX needs to be introduced as part of the cql_filter
89113
cqlBbox(bbox)
90114
];
91115
url.searchParams.append('cql_filter', cqlFilters.filter(Boolean).join(' AND '));
116+
url.searchParams.append('propertyName', Object.values(WFS_PROPERTY).join(','));
92117

93118
return url.toString();
94119
};
@@ -103,7 +128,7 @@ export const getWfsDownloadUrl = (
103128
export const getWfsFeatureProperties = async (
104129
id: number | undefined,
105130
propertyNames: WFS_PROPERTY[]
106-
): Promise<WfsProperties | undefined> => {
131+
): Promise<Partial<WfsProperties> | undefined> => {
107132
if (id === undefined) return undefined;
108133

109134
const url = new URL(GEOSERVER_BASE_URL + '/wfs');
@@ -118,17 +143,11 @@ export const getWfsFeatureProperties = async (
118143
const response = await fetch(url);
119144
if (!response.ok) return undefined;
120145

121-
const featureCollection = await response.json();
146+
const featureCollection = await response.json() as FeatureCollection<Point, WfsProperties>;
122147
if (!featureCollection.features) return undefined;
123-
124-
const properties = featureCollection.features[0].properties;
125-
return properties && {
126-
year: properties[WFS_PROPERTY.year],
127-
month: properties[WFS_PROPERTY.month],
128-
day: properties[WFS_PROPERTY.day],
129-
stateProvince: properties[WFS_PROPERTY.stateProvince],
130-
municipality: properties[WFS_PROPERTY.municipality],
131-
county: properties[WFS_PROPERTY.county],
132-
scientificName: properties[WFS_PROPERTY.scientificName]
133-
};
148+
const {properties} = featureCollection.features[0];
149+
if(properties.eventDate) {
150+
properties.eventDate = new Date(properties.eventDate);
151+
}
152+
return properties;
134153
};

‎client/static/images/rain.png‎

-5.08 KB
Binary file not shown.
-3.83 KB
Binary file not shown.

0 commit comments

Comments
 (0)